Say when the tray is signed out, and draw the sparkline so it reads as a chart #14

Merged
geusebio merged 2 commits from fix/tray-signed-out-and-sparkline into main 2026-08-18 20:18:52 +00:00
Owner

Two menu-visible defects the tray's own capture suite found, landed together because both are about what a row says and both re-bless the same captures.

Signed out — platform #838

Before: the tray refused to start, printed a line to stderr, exited 1. The whole user-visible state was an empty panel — the old capture for this scene was, honestly, a photograph of nothing.

After:

The tray with no credential

The stderr line is still printed for whoever started it from a terminal. Both halves are asserted now — the golden holds the menu and the stderr.

Decision Why
The endpoint is named in the row "Not signed in" alone is half a sentence with one tray on staging and another on production
No Refresh The credential is read once at startup, so a running tray cannot notice sc login in a terminal beside it. A Refresh that could only ever fail is worse than a sentence saying a restart is what picks it up — which is why the hint names the restart
Sign in… opens the plain /login signedInURL trades a path for a one-shot signed-in link, and that is exactly what a tray with no credential has nothing to ask for it with. The device flow is still waiting on /cli/auth/*

The sparkline — platform #839

Braille packed two samples per cell, which is why it was chosen. At menu font size DejaVu draws it small and low in the line box, so it came out as a dotted leader rather than a chart.

Before — ⣀⣀⣠⣴⣷⣦⣄⣀⣀⣀⣀⣀:

Braille, reading as a dotted leader

After — ▂▂▄▆█▆▄▃▂▂▂▂:

Block elements, reading as a chart

Same row, same 12-cell width, and now legibly a quiet night, a working day and a tail-off. Block elements are full-height, sit on the baseline, and are in every desktop font.

The cost is density — one sample per cell instead of two — and it is paid by bucketing rather than widening a row that already carries a name, a balance and a runway. A series longer than 12 reduces to 12 columns taking the largest reading in each bucket: the glance is looking for the hour something expensive ran, and averaging it against quiet neighbours is how a spike disappears. is reserved for zero, so an all-zero day draws a flat baseline and the smallest non-zero reading draws one step above it.

Harness changes

expects: exit is retired — it existed for one scene, and no scene now expects the tray not to run. Replacing it: capture.sh branches on the scene having no token (env -u SC_TOKEN, not SC_TOKEN=, which is not the same thing to any reader of it), and a new log walk step appends the process's stderr to the dump.

How it was verified

  • hack/screenshot/run.shevery scene re-captured, all three themes, then check.sh --bless. The re-blessed diff is the review: outside the signed-out scene, the only change in any golden is the sparkline.
  • check.sh and manual.py check green afterwards; docs/manual.md regenerated from the run.
  • go vet, go test ./..., hack/lint.sh (golangci-lint v2.6.1, 0 issues), shellcheck on capture.sh.
  • hack/coverage.sh green — see below.

coverage.floor for internal/tray drops 38.3 → 37.6, with the reason recorded in the file. onReadySignedOut and Run's new branch are systray wiring — the "OS-drawn menu callback" hack/coverage.sh's own header names as the thing a unit test cannot exercise. The wording is covered instead (signedOutTitle, signedOutHint, signInURL in label_test.go), and the rows themselves end to end by the signed-out golden. Measured on Linux as well as darwin — both 37.6%, so this is CI's figure and not a laptop's.

Closes #838 and #839 in the platform repo, which needs a submodule pin bump once this merges.

Two menu-visible defects the tray's own capture suite found, landed together because both are about what a row *says* and both re-bless the same captures. ## Signed out — [platform #838](https://git.grey.ooo/Grey.ooo/Someones.Computer/issues/838) **Before:** the tray refused to start, printed a line to stderr, exited 1. The whole user-visible state was an empty panel — the old capture for this scene was, honestly, a photograph of nothing. **After:** ![The tray with no credential](https://git.grey.ooo/Grey.ooo/someones.computer_tray/raw/commit/f3e23855d9bd2ca36e1027a506953ab0dc71eb25/docs/images/linux/signed-out/menu.png) The stderr line is still printed for whoever started it from a terminal. Both halves are asserted now — the golden holds the menu *and* the stderr. | Decision | Why | |---|---| | The endpoint is named in the row | "Not signed in" alone is half a sentence with one tray on staging and another on production | | No **Refresh** | The credential is read once at startup, so a running tray cannot notice `sc login` in a terminal beside it. A Refresh that could only ever fail is worse than a sentence saying a restart is what picks it up — which is why the hint names the restart | | **Sign in…** opens the plain `/login` | `signedInURL` trades a path for a one-shot signed-in link, and that is exactly what a tray with no credential has nothing to ask for it with. The device flow is still waiting on `/cli/auth/*` | ## The sparkline — [platform #839](https://git.grey.ooo/Grey.ooo/Someones.Computer/issues/839) Braille packed two samples per cell, which is why it was chosen. At menu font size DejaVu draws it small and low in the line box, so it came out as a **dotted leader** rather than a chart. **Before — `⣀⣀⣠⣴⣷⣦⣄⣀⣀⣀⣀⣀`:** ![Braille, reading as a dotted leader](https://git.grey.ooo/Grey.ooo/someones.computer_tray/raw/commit/3d394e9/docs/images/linux/applications/menu.png) **After — `▂▂▄▆█▆▄▃▂▂▂▂`:** ![Block elements, reading as a chart](https://git.grey.ooo/Grey.ooo/someones.computer_tray/raw/commit/f3e23855d9bd2ca36e1027a506953ab0dc71eb25/docs/images/linux/applications/menu.png) Same row, same 12-cell width, and now legibly a quiet night, a working day and a tail-off. Block elements are full-height, sit on the baseline, and are in every desktop font. The cost is density — one sample per cell instead of two — and it is paid by **bucketing rather than widening** a row that already carries a name, a balance and a runway. A series longer than 12 reduces to 12 columns taking the **largest** reading in each bucket: the glance is looking for the hour something expensive ran, and averaging it against quiet neighbours is how a spike disappears. `▁` is reserved for zero, so an all-zero day draws a flat baseline and the smallest non-zero reading draws one step above it. ## Harness changes `expects: exit` is **retired** — it existed for one scene, and no scene now expects the tray not to run. Replacing it: `capture.sh` branches on the scene having no token (`env -u SC_TOKEN`, not `SC_TOKEN=`, which is not the same thing to any reader of it), and a new `log` walk step appends the process's stderr to the dump. ## How it was verified - `hack/screenshot/run.sh` — **every scene re-captured**, all three themes, then `check.sh --bless`. The re-blessed diff is the review: outside the signed-out scene, the only change in any golden is the sparkline. - `check.sh` and `manual.py check` green afterwards; `docs/manual.md` regenerated from the run. - `go vet`, `go test ./...`, `hack/lint.sh` (golangci-lint v2.6.1, **0 issues**), `shellcheck` on `capture.sh`. - `hack/coverage.sh` green — see below. **`coverage.floor` for `internal/tray` drops 38.3 → 37.6**, with the reason recorded in the file. `onReadySignedOut` and `Run`'s new branch are systray wiring — the "OS-drawn menu callback" `hack/coverage.sh`'s own header names as the thing a unit test cannot exercise. The wording is covered instead (`signedOutTitle`, `signedOutHint`, `signInURL` in `label_test.go`), and the rows themselves end to end by the signed-out golden. Measured on Linux as well as darwin — both 37.6%, so this is CI's figure and not a laptop's. Closes #838 and #839 in the platform repo, which needs a submodule pin bump once this merges.
Braille was chosen because two samples share a cell, which fits a day of
hourly readings into twelve characters. Rendered in a GTK menu at menu font
size, DejaVu draws braille patterns small and low in the line box, so the
series came out as a dotted leader — the row of dots a table of contents uses
to join a title to a page number — with the peak a pixel or two above the
rest. A reader saw punctuation, or a rendering fault, not "a quiet night, a
working day, a tail-off".

Block elements are full-height, sit on the baseline the way a bar chart wants
to, and are in every desktop font. The captured menu now reads
`▂▂▄▆█▆▄▃▂▂▂▂` where it read `⣀⣀⣠⣴⣷⣦⣄⣀⣀⣀⣀⣀`.

The cost is density: one sample per cell instead of two. That is paid by
bucketing rather than by widening a menu row that already carries a name, a
balance and a runway — a series longer than twelve is reduced to twelve
columns, taking the *largest* reading in each bucket. Max, not mean: the
glance is looking for the hour something expensive was running, and averaging
that hour against its quiet neighbours is exactly how a spike disappears.

▁ is reserved for a reading of zero, so an all-zero day draws a flat baseline
(which is what it means) and the smallest non-zero reading draws one step
above it (so the hours something ran are never indistinguishable from the
hours it did not).

The goldens and the captures are re-blessed from a full `hack/screenshot/
run.sh`; the only diff in the ones not about the signed-out scene is the
sparkline itself.

Closes #839
Draw a signed-out menu, instead of exiting to a stderr nobody reads
All checks were successful
CI / build (pull_request) Successful in 2m9s
CI / captures (pull_request) Successful in 8m52s
f3e23855d9
sc-tray refused to start without a credential: it printed why and exited 1.
Half of that reasoning holds — a tray icon sitting dim and silent tells you
nothing — and half of it was never true. Nobody running a desktop app is
reading its stderr. Launched from a .desktop file, a login item or a Windows
shortcut, that sentence goes nowhere, and the whole user-visible state is an
empty panel: indistinguishable from a crash, a missing dependency, or the app
never having been started.

It now starts anyway and says so where it can be seen:

    Not signed in — https://someones.computer   [disabled]
    Sign in…
    ───
    run `sc login`, then restart sc-tray        [disabled]
    Quit

The stderr line is still printed, for whoever did start it from a terminal.
Both halves are now asserted: the golden holds the menu *and* the stderr.

Three things worth saying about the shape:

- **The endpoint is named.** "Not signed in" alone is half a sentence with one
  tray on staging and another on production, which is the arrangement the
  identity row exists for.
- **There is no Refresh.** A credential is read once at startup, so a running
  tray cannot notice `sc login` happening in a terminal beside it; a Refresh
  that could only ever fail would be worse than saying plainly that a restart
  is what picks it up. That is also why the hint names the restart.
- **Sign in… opens the plain login page**, not signedInURL — that trades a
  path for a one-shot signed-in link, and a one-shot link is precisely what a
  tray with no credential has nothing to ask for it with. The device flow that
  would make this a real sign-in is still waiting on /cli/auth/*.

The capture harness had a whole mode for this scene — `expects: exit`, whose
capture was an exit code and a log — and it is retired, because there is no
longer a scene that expects the tray not to run. What replaces it is smaller:
capture.sh branches on the scene having no token (unsetting SC_TOKEN rather
than passing an empty one, which is not the same thing to any reader of it),
and a new `log` walk step appends the process's stderr to the dump.

coverage.floor for internal/tray drops 38.3 -> 37.6, with the reason recorded
in the file: onReadySignedOut and Run's new branch are systray wiring — the
"OS-drawn menu callback" hack/coverage.sh names as the thing a unit test
cannot exercise. Its wording is covered instead, as signedOutTitle,
signedOutHint and signInURL in label_test.go, and the rows themselves are
asserted end to end by the signed-out golden.

Closes #838
geusebio changed title from Say when the tray is signed out, and draw the sparkline so it reads as a chart to WIP: Say when the tray is signed out, and draw the sparkline so it reads as a chart 2026-08-18 15:05:11 +00:00
geusebio changed title from WIP: Say when the tray is signed out, and draw the sparkline so it reads as a chart to Say when the tray is signed out, and draw the sparkline so it reads as a chart 2026-08-18 15:14:10 +00:00
geusebio deleted branch fix/tray-signed-out-and-sparkline 2026-08-18 20:18:54 +00:00
Sign in to join this conversation.
No reviewers
No labels
in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/someones.computer_tray!14
No description provided.