Native Settings and the preference flow

What the user gets

One window, five pages (a tab strip; Ctrl+Tab, Ctrl+Shift+Tab and Ctrl+PageUp/PageDown also switch pages):

PageContents
Date and timePreset list (from clock_core::format::PRESETS, plus Custom format), multi-line format editor, live preview, token help
AppearancePrimary display only / All displays, font size 6–24 pt with spin buttons, clock theme System / Light / Dark
StartupThe opt-in login-startup checkbox (details). It is the Windows registration itself, not a stored preference, so nothing can drift from the OS-owned state
SupportStatus, Refresh status and Copy diagnostics (details)
TemplatesReview a shared link, share the current presentation, save/load/delete named local presets, open the website editor and enable/disable per-user template links (details)

Below every page, Settings shows the running app version, taken from the same Cargo workspace version as the executable’s file properties. GitHub Releases opens GitHub Releases in your default browser. Check now makes one bounded GitHub request on a worker, only when clicked. The disabled button shows “Checking…” while Settings remains usable. The status says “You have the latest version.”, “Version X.Y.Z is available”, or “Couldn’t check for updates.” An available result makes GitHub Releases open that release’s validated page, and explains updating: run the new setup, or exit and replace the portable exe. Nothing is downloaded or installed.

Check for updates automatically starts unchecked for new installs and upgrades. Apply or OK saves consent; Cancel discards it. There is no first-run prompt. When enabled, a one-shot timer waits at least three minutes after startup, resume or unlock, and checks at most once per 24 hours. Failed checks are quiet and wait for the next daily opportunity. A manual check also advances that deadline. One tray notification per newer version says “ElevenClock-rs X.Y.Z is available”; clicking opens the validated release page. Windows may suppress notifications (for example, Do Not Disturb); they are not retried.

Both buttons support Tab then Enter or Space; status changes are announced to screen readers. A release-opening failure appears in the Settings status. Closing Settings cancels a manual check; automatic checks continue without the dialog. Turning the option off, locking, suspending or pausing cancels automatic work and removes its wakeup; late results cannot notify. Exit never waits for the network. Requests time out after ten seconds overall, accept at most 64 KiB, never overlap, and respect server rate-limit backoff, persisted across restarts. Managed copies show “managed by your organization” beside the version, hide both buttons and the automatic checkbox and reject request dispatch, because their launcher handles updates. See ADR 0003 for data sent and GitHub retention.

Buttons: Exit ElevenClock-rs, Reset to defaults, OK, Cancel, Apply.

The window uses the Windows 11 text font (Segoe UI Variable) and the Mica material behind its title bar, through the documented DWMWA_SYSTEMBACKDROP_TYPE. The client area keeps standard themed controls, so it follows high contrast and screen readers unchanged; Windows builds without Mica simply show the plain title bar.

First launch

Flow

ActionResult
Edit anythingDraft only. Messages and the preview update as you type; nothing is live or saved. Apply enables once the draft differs from what is live
ApplyValidate, check fit, save, and only then tell the running clock (Input::Preferences). The clock changes without a restart; the window stays open and says “Settings applied.”
OKApply, then close. With no changes it simply closes
Cancel, Esc, the close button, Alt+CDiscard the draft and close. The clock keeps running
Reset to defaultsStage the defaults in the controls. Nothing is saved until Apply; Cancel drops them
Save fails“Settings were not saved. … Your previous settings are preserved.” The live clock is unchanged, the draft stays editable, the window stays open and Apply can simply be retried (capture)
Invalid draftApply refuses, says so, moves the keyboard to the first problem and selects the offending token (capture)
Damaged saved dataA notice at the bottom; defaults are shown. Apply asks for confirmation, keeps a copy of the damaged data, then saves (notice, confirmation). Declining changes nothing
Saved data from a newer versionEverything is disabled and the notice says saving is turned off; the data is never overwritten
First launchWelcome text; first-run completion is stored internally when the window is dismissed. Later launches show the normal text. A login-style start never opens Settings
Launch while running / tray or clock menu SettingsThe running instance shows or focuses the one window. An already-open window keeps its unsaved edits
ExitExplicit: removes the overlay, tray icon and helper. Closing never exits

Preview and year-boundary semantics

The preview always shows the current time and the documented examples, with the ISO week-year each belongs to, so the difference between %g (ISO week-year) and %y (calendar year) is visible instead of implied:

Sample (12:34:56)%g%V.%u (ISO week date)%y%V.%u (Calendar year + ISO week)
Tue 13 Oct 2026 (ISO week-year 2026, week 42): the fixture2642.22642.2
Mon 29 Dec 2025 (ISO week-year 2026, week 1)2601.12501.1
Fri 1 Jan 2027 (ISO week-year 2026, week 53)2653.52753.5
Mon 4 Jan 2027 (ISO week-year 2027, week 1)2701.12701.1

Calendar-year preset

The token help beside the editor lists every directive the core accepts (a test compares it with format::DIRECTIVES) and explains that %G/%g are the year the ISO week belongs to. Two-line formats show line 1 ↵ line 2.

Messages (always beside the control)

InputMessageWhere
%Q”“%Q” is not a supported token. The token list below shows what you can use.”Under the format editor; the token is selected on Apply
trailing %“A % must be followed by a token. Use %% to show a percent sign.”same
empty / empty line“Enter a format, or choose a preset.” / “Each line needs some content…”same
three lines, control characters, > 256 characters, > 512 renderedone sentence each (see settings_model::format_problem)same
font 30, 5, empty, abc“Font size must be between 6 and 24 pt.”Under the font size
does not fit“This does not fit the clock space on the primary taskbar (needs 94 × 40 px, 70 × 48 px available) and a secondary taskbar (…). Use a shorter format or a smaller font size.”Under the format and under the font size

Fit message

Fit is measured, not guessed: Runtime hands the draft to the same DirectWrite measurement the overlay surface uses (Renderer::plan_fit), against the clock slot discovered for each taskbar the selection would use, at that taskbar’s DPI, using the same worst-case lines (leap day, ISO week 53, AM/PM). If a taskbar has no proven clock slot, or nothing was discovered yet, there is nothing to measure and Apply is not blocked; the paint path still reports a later overflow, and Settings shows that as a notice when it is open.

Implementation notes

Fit-check caching

Settings retains one draft format’s sampled worst-case candidates. Repeated fit checks reuse successful measurements while rendered text, selected clock slots, DPI, font size and the runtime appearance/font revision are unchanged. A different format replaces the candidate cache; unavailable or failed measurements are never cached as successful fits. Both caches are bounded to the current draft/check. Operation-count guards for this cache and for the worst-case resampling are listed in contributing.

Action audit

Audited for issue #82: the existing small typed interaction::MenuCommand model already owns command IDs and the fixed menu. No additional action abstraction is needed for current behavior.

Entry pointsPolicy and dispatch ownerExisting evidence
Clock right click; tray mouse, keyboard and context-key activationQueue Interaction::Menu; native pump routes to UserAction::ShowMenu; interaction::menu_items and Runtime::command share Settings, Pause/Resume and Exittray decoder tests; runtime/tests.rs menu dispatch and tray coalescing tests
Clock click; screen-reader InvokeQueue activation; runtime checks the current overlay and activation gate before the native calendar actioninteraction press/gate/queue tests and runtime activation tests; hosted overlay harness for native input/UIA
Menu Settings; second-instance launchNative show request converges on SettingsWindow::show_or_focus; an existing draft is retainedSettings desktop harness for window/focus behavior
Menu Exit; Settings Exit; host close/session endMenu calls runtime quit; Settings/host queue Exit, pumped as Input::Quit; runtime owns teardownRuntime menu-exit, quit and end-session tests; lifecycle harness for native teardown
Apply/OK; Reset; Cancel/closeSettings controller owns validation/save/recovery; OK closes only after success, Reset stages a draft, Cancel discards itsettings_model and preferences_store portable tests; Settings desktop harness
GitHub Releases / Check nowSettings queues owned commands; managed policy guards both UI and native request dispatch. GitHub Releases opens the validated release URL; Check now starts one cancellable worker and posts a result wakeupPortable response, version, backoff and cancellation-policy tests; Windows-only WinHTTP loopback tests for response decoding, redirects, rate limits, streamed overflow, deadlines, cancellation and callback cleanup; desktop harness checks accessible names and tab order. Live GitHub/offline behavior and screen-reader announcements require Windows desktop verification
Startup checkbox; Support Refresh/CopySeparate typed startup/support policies, serviced outside callbacks; startup changes require explicit user actionStartup/support portable tests and native registry/clipboard tests

Pause/Resume is available through the shared menu; Settings does not duplicate it. Settings opening and Exit are unconditional requests, so their small native adapters contain no competing availability policy. Unsupported preference versions disable saving in the Settings model and remain protected at the store boundary. These layers enforce different boundaries rather than duplicating action policy. Revisit this audit when adding an entry point or conditional action; centralize proven shared availability/behavior in portable typed logic and test every affected source. Do not unify draft commands, OS registration and clock actions speculatively.