diff --git a/README.md b/README.md index 2e97041..1bce8b5 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ The binary is written to: ```text # GUI executable produced by scripts\build-windows.bat. -dist\windows\pysentry-0.2.2-windows-amd64.exe +dist\windows\pysentry-0.2.3-windows-amd64.exe ``` Linux: @@ -101,7 +101,7 @@ The binary is written to: ```text # Linux executable produced by scripts/build-linux.sh. -dist/linux/pysentry-0.2.2-linux-amd64 +dist/linux/pysentry-0.2.3-linux-amd64 ``` Linux using Docker: @@ -118,7 +118,7 @@ The binary is copied to: ```text # Linux executable copied out of the Docker build image. -dist\linux\pysentry-0.2.2-linux-amd64 +dist\linux\pysentry-0.2.3-linux-amd64 ``` Release build from Linux: @@ -143,13 +143,13 @@ The binaries are copied to: ```text # Linux artifact. -dist/linux/pysentry-0.2.2-linux-amd64 +dist/linux/pysentry-0.2.3-linux-amd64 # Linux arm64 artifact. -dist/linux/pysentry-0.2.2-linux-arm64 +dist/linux/pysentry-0.2.3-linux-arm64 # Windows artifact cross-compiled from Linux. -dist/windows/pysentry-0.2.2-windows-amd64.exe +dist/windows/pysentry-0.2.3-windows-amd64.exe ``` ## Run From Source @@ -292,7 +292,7 @@ Linux: [Desktop Entry] Type=Application Name=PySentry -Exec=/opt/pysentry/pysentry-0.2.2-linux-amd64 +Exec=/opt/pysentry/pysentry-0.2.3-linux-amd64 Terminal=false ``` diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7882fd5..6193543 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -2,6 +2,15 @@ All notable PySentry changes are recorded in this file. +## 0.2.3 - 2026-06-15 + +- Changed History to use chronological ordering with new records appended at the bottom. +- Replaced the History list with a compact table. +- Added Time column sorting in both ascending and descending directions. +- Made History table columns user-resizable through the native Fyne table header. +- Shortened the Log column display to file names instead of full paths. +- Unified UI event timestamps with command run timestamps. + ## 0.2.2 - 2026-06-15 - Added Linux desktop integration that installs a user-level `.desktop` file and icon so taskbars can match the running window to the PySentry icon. diff --git a/src/core/version.go b/src/core/version.go index be2dfd6..4bd0fe2 100644 --- a/src/core/version.go +++ b/src/core/version.go @@ -3,4 +3,4 @@ package core // Version is the application version shown in the GUI and used by build // scripts in artifact names. It is a var rather than a const so release builds // can override it with Go ldflags when CI tags a build. -var Version = "0.2.2" +var Version = "0.2.3" diff --git a/src/gui/app.go b/src/gui/app.go index 07dedcb..2f352a8 100644 --- a/src/gui/app.go +++ b/src/gui/app.go @@ -620,9 +620,34 @@ func showJobDialog(w fyne.Window, title string, current job, onSave func(job)) { } func newHistoryView(events *[]event) *fyne.Container { + descending := false + headerText := func(id widget.TableCellID) string { + headers := []string{"Time", "Trigger", "Job", "State", "Detail", "Log"} + if id.Row < 0 && id.Col == 0 { + if descending { + return "Time desc" + } + return "Time asc" + } + if id.Row < 0 && id.Col >= 0 && id.Col < len(headers) { + return headers[id.Col] + } + return "" + } + sortedEvents := func() []event { + result := append([]event(nil), (*events)...) + sort.SliceStable(result, func(left int, right int) bool { + if descending { + return result[left].Time > result[right].Time + } + return result[left].Time < result[right].Time + }) + return result + } + table := widget.NewTable( func() (int, int) { - return len(*events) + 1, 6 + return len(*events), 6 }, func() fyne.CanvasObject { label := widget.NewLabel("") @@ -631,30 +656,44 @@ func newHistoryView(events *[]event) *fyne.Container { }, func(id widget.TableCellID, item fyne.CanvasObject) { label := item.(*widget.Label) - label.SetText(historyCellText(id, *events)) - label.TextStyle = fyne.TextStyle{Bold: id.Row == 0} + label.SetText(historyCellText(id, sortedEvents())) + label.TextStyle = fyne.TextStyle{} label.Refresh() }, ) + table.ShowHeaderRow = true + table.CreateHeader = func() fyne.CanvasObject { + label := widget.NewLabel("") + label.Wrapping = fyne.TextTruncate + return label + } + table.UpdateHeader = func(id widget.TableCellID, item fyne.CanvasObject) { + label := item.(*widget.Label) + label.SetText(headerText(id)) + label.TextStyle = fyne.TextStyle{Bold: true} + label.Refresh() + } + table.OnSelected = func(id widget.TableCellID) { + if id.Row < 0 && id.Col == 0 { + descending = !descending + table.Refresh() + } + table.Unselect(id) + } table.SetColumnWidth(0, 150) table.SetColumnWidth(1, 90) table.SetColumnWidth(2, 170) table.SetColumnWidth(3, 90) - table.SetColumnWidth(4, 360) - table.SetColumnWidth(5, 320) + table.SetColumnWidth(4, 260) + table.SetColumnWidth(5, 240) return container.NewPadded(table) } func historyCellText(id widget.TableCellID, events []event) string { - headers := []string{"Time", "Trigger", "Job", "State", "Detail", "Log"} - if id.Row == 0 { - return headers[id.Col] - } - eventIndex := id.Row - 1 - if eventIndex < 0 || eventIndex >= len(events) { + if id.Row < 0 || id.Row >= len(events) { return "" } - current := events[eventIndex] + current := events[id.Row] trigger := current.Trigger if trigger == "" { trigger = "Unknown" @@ -671,12 +710,24 @@ func historyCellText(id widget.TableCellID, events []event) string { case 4: return current.Detail case 5: - return current.LogFile + return logFileName(current.LogFile) default: return "" } } +func logFileName(path string) string { + path = strings.TrimSpace(path) + if path == "" { + return "" + } + path = strings.ReplaceAll(path, "\\", "/") + if slash := strings.LastIndex(path, "/"); slash >= 0 { + return path[slash+1:] + } + return path +} + func settingsView(w fyne.Window, store *core.Store, jobs *[]job) fyne.CanvasObject { startOnLogin := widget.NewCheck("Start PySentry when I sign in", nil) startOnLogin.SetChecked(store.Config.StartOnLogin)