Stage 9 of the GUI layout plan: the roadmap item the review was raised under is closed, so the plan and the findings document go with it — what they established now lives in STANDARDS and the CHANGELOG. STANDARDS gains the rule the review produced: a size that must follow the theme is measured at build time, not written as a pixel constant, because a hand-tuned number is only correct for the theme it was tuned against. rowOverlap, captionColumnWidth, textColumnWidth, activityRowsHeight and initialSplitOffset are the worked examples. The CHANGELOG entry keeps to what the user can see: the window opens at the size it asks for and drags smaller, the Jobs divider is draggable, History columns hold their content on a scaled UI, and the Settings button row and block spacing are as their layouts intended. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6.8 KiB
Roadmap
This file tracks planned GoSentry work that is larger than a single bug fix. Completed work is recorded in CHANGELOG.md, not here.
Open Items
Update check from GitHub releases
Releases are published as GitHub Releases (tags like v0.12.0, built by
.github/workflows/release.yml), but the app never tells the user a newer
version exists — they have to check the releases page by hand.
Add an update check that queries the GitHub Releases API
(GET /repos/mixeme/gosentry/releases/latest) for the latest published tag,
strips the leading v, and compares it against app.Version. When a newer
version is available, surface it non-intrusively — an "Update available"
line in Settings (next to the existing version/build info) with a hyperlink
to the release page, not a modal on launch.
Design notes / open questions:
- Opt-in and offline-safe. The check makes a network request, so it must be off by default (or clearly consented) and never block startup. Failures (offline, rate-limited, API change) should be silent — no error dialogs for a best-effort convenience feature.
- Version comparison. Compare semantic versions, not strings, so
0.12.0reads as newer than0.9.0. A tiny semver comparator inapp(or a small dependency) avoids lexical bugs. - Where the check lives. Keep it in the
applayer behind the Service so the UI only renders the result, and cache the last check so opening Settings repeatedly does not spam the API (unauthenticated GitHub allows 60 req/h). - Repo coordinates. The primary remote is Gitea; the GitHub repo used for
releases is
mixeme/gosentryand must be wired in explicitly (constant or build-time value) rather than derived fromorigin. - No auto-download. Scope is detection and notification only; installing the update stays a manual click-through to the release page.
Import/export jobs as a cron table
Jobs can only be moved between machines by copying jobs.json by hand. Add
"Import" / "Export" actions (Settings tab, file dialogs) that read and write a
crontab-style text file, so a job list can be shared, version-controlled, or
seeded from an existing Unix crontab.
Export writes one line per job — schedule fields, then command and arguments —
and import parses the same format back into domain.Job values.
Design notes / open questions:
- The job model is wider than a crontab line.
Name,Folder,StartOnly,OverlapPolicy,TimeoutSeconds, andEnabledhave no cron equivalent. Either accept a lossy export (schedule + command only) or carry the extra fields in a structured comment above each line (# gosentry: name=… folder=… timeout=…), which keeps the file readable by real cron while making the round-trip lossless. The comment form is preferred; decide the exact key set before implementing. - Disabled jobs.
Enabled: falsemaps naturally to a commented-out line, but then a disabled job is indistinguishable from a user's own comment unless the# gosentry:marker is present. Pick one representation and document it. @everyis not crontab. GoSentry accepts@every 10s(seedomain.Parse), which no cron implementation understands. Exporting it produces a file that is not a valid crontab; exporting it as an approximation would silently change the schedule. Keep the raw string and flag the file as GoSentry-flavoured, rather than converting.- Command vs arguments. Crontab has a single command string; GoSentry splits
CommandandArguments. Import must split the line the same way the runner would (seerunner/invocation*.go, which differs per OS), and export must join them back without changing quoting. - What to skip on import. Environment assignments (
SHELL=,PATH=,MAILTO=), six-field (seconds) crontabs, and@rebootare outside whatdomain.Parseaccepts. Skip them, and report which lines were skipped and why — a partial import that silently drops rows is worse than a failed one. - Merge semantics. Import must decide between replacing the job list and appending to it, and must assign fresh IDs rather than trusting the file. Appending with a confirmation dialog is the safer default; replacing needs an explicit "this deletes N jobs" confirmation.
- Where it lives. Encoding/decoding is pure text handling and belongs in
domain(or a smallstoragecodec) with unit tests over round-trips; the Service exposes import/export operations; the UI only picks the file and shows the outcome.
Window size persistence (frozen)
Window size is currently not saved on quit or close. Saving was disabled
because w.Canvas().Size() returns the maximized dimensions when the window is
maximized, which would corrupt the stored size on the next launch.
Re-enabling requires a cross-platform way to detect the maximized state before
saving. Fyne v2.x has no API for this; it needs per-OS native calls:
IsZoomed (Windows), _NET_WM_STATE (X11/Linux), NSWindow.isZoomed
(macOS). Unfreeze once that detection is in place.
Disadvantages of a platform-specific approach:
- Three separate implementations. Windows, macOS, and Linux each need their own file guarded by a build tag. Each adds CGO bindings or raw syscall wrappers that must be kept in sync as OS APIs evolve.
- Linux is not one target. X11 and Wayland have completely different window
state models.
_NET_WM_STATEis X11-only; under Wayland the compositor controls window decorations and there is no stable client-side API to query the maximized state. A singlelinuxbuild tag cannot cover both correctly. - Native window handle is not exposed. Fyne does not surface the underlying
HWND/NSWindow/XIDthrough its public API. Obtaining it requires either enumerating OS-level windows by PID (fragile, finds wrong windows when dialogs are open) or reaching into Fyne/GLFW internals (breaks on Fyne upgrades). - Thread-safety constraints. Win32 and GLFW both require their calls to be made from the OS main thread. Tray-menu callbacks run on a separate goroutine, so any native call must be marshalled back to the main thread, adding synchronisation complexity.
- Test coverage gap. Maximized-state detection cannot be exercised by Fyne's headless test driver; it requires a real display and manual or screen-capture automation per platform.
History tab — column filters (Trigger / Job / State)
Add dropdown filters above the History table so the user can narrow rows by
trigger source, job name, or run state. Blocked on Fyne native support: the
current widget.Table has no built-in filter API, and a filter bar built from
widget.Select widgets above the table feels visually out-of-place. Revisit
when Fyne adds first-class column filtering or a composable data-grid widget.