From 4a8feb351e42e65d9124b24eb9f68e8dd8b23e0c Mon Sep 17 00:00:00 2001 From: mixeme Date: Mon, 15 Jun 2026 21:16:57 +0300 Subject: [PATCH] Release version 0.2.3 Improve the History tab by keeping records in chronological order, rendering them as a compact table, and allowing the Time column to toggle ascending or descending order. Use the native Fyne table header so users can resize columns, including Detail and Log, and show only the log file name instead of the full log path. Bump the application version to 0.2.3 and update README artifact examples plus docs/CHANGELOG.md. --- README.md | 14 ++++----- docs/CHANGELOG.md | 9 ++++++ src/core/version.go | 2 +- src/gui/app.go | 77 +++++++++++++++++++++++++++++++++++++-------- 4 files changed, 81 insertions(+), 21 deletions(-) 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)