Skip to content

Add "Run at startup" toggle in Settings - #16

Merged
trsdn merged 4 commits into
mainfrom
trsdn-start-with-windows-toggle
Aug 19, 2026
Merged

trsdn merged 4 commits into
mainfrom
trsdn-start-with-windows-toggle

Conversation

@trsdn

@trsdn trsdn commented Aug 19, 2026 •

Copy link
Copy Markdown
Owner

Closes #15.

Summary

Adds an in-app Run at startup toggle (Settings → Startup) that works across every distribution channel — Inno installer, portable ZIP, and MSIX/Store — replacing the old install-time-only autostart that could never be changed from inside the app.

What changed

New module src/autostart.rs — an AutostartBackend trait (mirroring the existing CredentialStore pattern) with two mutually-exclusive Windows backends, plus enable_verified/disable_verified write-then-read-back helpers and a backend() factory that picks packaged vs unpackaged at runtime.

  • Unpackaged (installer + portable): reads/writes/deletes the HKCU\…\CurrentVersion\Run value OpenWritr (quoted current_exe, no args — an argless start is already silent tray mode). Includes:
    • stale-path repair — rewrites the value on read when the exe has moved (portable users relocating the folder);
    • OS-disabled detection by parsing the 12-byte StartupApproved\Run blob (first byte ≠ 2 ⇒ disabled) → surfaced as DisabledByOs; never re-enabled on launch;
    • one-time migration of the legacy {userstartup}\OpenWritr.lnk shortcut into the registry value.
  • MSIX/Store: WinRT StartupTask OpenWritrStartup (RequestEnableAsync/Disable), mapping DisabledByUser/DisabledByPolicy to DisabledByOs with a reason string.

UI (src/settings_ui.rs) — new "Startup" section with checkbox “Start OpenWritr automatically when I sign in to Windows” and an info button noting it starts silently in the tray. The toggle is deliberately not a Settings serde field: it's OS state, writes through immediately, is verified by read-back, never touches persist()/dirty/SettingsRevision, and re-reads real OS state each time the window opens (so external Task Manager / Settings → Apps → Startup changes show truthfully). When DisabledByOs the checkbox is disabled with an explanation; write failures render through inline_error (no silent no-op).

Installer — both installer/openwritr.iss and installer/openwritr-x64.iss drop the {userstartup} shortcut and instead write the same HKCU Run value (gated on the autostart task), and always remove the value on uninstall even if enabled later from the app. AppxManifest.template.xml stays Enabled="false" (the in-app toggle calls RequestEnableAsync).

Deletion of the legacy {userstartup}\OpenWritr.lnk shortcut is coupled to the autostart task ([InstallDelete] … Tasks: autostart): the shortcut is only removed when the replacement Run value is actually written in its place. If an existing user deselects the autostart task on upgrade (or in any silent/default run where the task is unset), the legacy shortcut is left intact so the app-side migration in autostart.rs can still pick it up — a previously active autostart is never silently dropped. An InitializeWizard [Code] hook additionally preselects the autostart task when a legacy shortcut or an existing Run value is detected (Inno otherwise remembers a prior deselection across upgrades).

Diagnostics — the diagnostics bundle (autostart-status.json) and --self-check now report the resolved backend and current state.

Docs — README installer + portable sections updated.

Validation

  • cargo fmt --check — clean (exit 0).
  • cargo clippy --all-targets — no new warnings from the changed files (pre-existing warnings only).
  • cargo test --bins — test result: ok. 283 passed; 0 failed; 8 ignored (main bin suite), plus the other bin suites green. Includes new unit tests: enable/disable/read round-trip, stale-path repair, StartupApproved disabled-blob parsing, legacy shortcut migration, and a settings_ui test asserting a write failure surfaces an error instead of silently flipping state.
  • Inno scripts — both openwritr.iss and openwritr-x64.iss compile with ISCC.exe at exit 0 (verified against a staged scratch tree).

Note: the .iss scripts are not covered by cargo test; an ISCC compile check in CI would close that gap, but that's left as a separate follow-up (out of scope for this PR).

Could NOT be verified automatically — please check manually

  • Real sign-out / sign-in autostart on installer and portable builds (silent tray start).
  • Disabling the entry in Task Manager → Startup showing up in-app as DisabledByOs.
  • The MSIX StartupTask path end-to-end (CI build here was arm64 desktop, not packaged).
  • Uninstall leaving no Run value and no leftover shortcut, including the enabled-later-in-app case.
  • Legacy .lnk → registry migration and the new task-preselect / gated-deletion behaviour on a machine upgrading from a prior install.

trsdn and others added 4 commits August 19, 2026 16:58
Add an in-app autostart toggle that works across all distribution
channels (Inno installer, portable ZIP, MSIX/Store).

New module src/autostart.rs defines an AutostartBackend trait with two
mutually-exclusive Windows backends:
- Unpackaged builds use the HKCU ...\CurrentVersion\Run registry value
  "OpenWritr" (quoted current_exe, argless = silent tray start). Includes
  stale-path repair on read, StartupApproved disabled-blob parsing for
  OS-disabled detection, and one-time migration of the legacy
  {userstartup}\OpenWritr.lnk shortcut.
- MSIX/Store builds use the WinRT StartupTask "OpenWritrStartup"
  (RequestEnableAsync/Disable), mapping DisabledByUser/DisabledByPolicy
  to a DisabledByOs state.

The toggle is OS state, not a Settings serde field: it writes through
immediately, is verified by read-back, never participates in persist()
or the dirty flag, and re-reads real OS state each time the window opens.
Failures surface through inline_error instead of silently no-oping.

Installer .iss files switched from the startup-folder shortcut to the
same HKCU Run value (gated on the autostart task), remove the legacy
shortcut, and always clean the value on uninstall. Diagnostics bundle
and --self-check now report the resolved backend and current state.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bdc56e7-31d0-49bb-ab08-fc1011f46419
The [InstallDelete] entry that removes the legacy {userstartup}\OpenWritr.lnk
ran unconditionally on install, while the replacement HKCU Run value is only
written with Tasks: autostart. An existing user who deselected the autostart
task on upgrade (or any silent/default run where the task is unset) would lose
their previously active autostart silently, and the app-side migration in
autostart.rs could no longer recover it because the shortcut was already gone.

Gate the shortcut deletion on Tasks: autostart in both .iss files, so the
legacy shortcut is only removed when the Run value is actually written in its
place; otherwise it is left intact for the app-side migration to handle. Also
add an [Code] InitializeWizard that preselects the autostart task when a legacy
shortcut or Run value already exists, so upgrades default to preserving state.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bdc56e7-31d0-49bb-ab08-fc1011f46419
The autostart task has no Flags: unchecked, so it is checked by default; the
old comment's "otherwise unchecked-by-default" aside was wrong. Explain the
real reason for the InitializeWizard preselect: Inno remembers the previous
task selection across upgrades, so a user who once deselected the task would
get it deselected again by default — the preselect restores it to checked when
prior autostart state (legacy shortcut or Run value) is detected.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bdc56e7-31d0-49bb-ab08-fc1011f46419
The [Code] block used a Pascal brace comment { ... } whose text contained the
Inno constant {userstartup}. Pascal brace comments do not nest, so the } of
{userstartup} closed the comment early (column 65) and the remainder was parsed
as code, failing both scripts with "'BEGIN' expected" (regression from 54611d1;
the earlier wording also contained {userstartup}). Switch to // line comments,
which have no such interaction with brace constants.

Verified: ISCC.exe compiles both openwritr.iss and openwritr-x64.iss with
exit 0 (they failed with exit 2 before this fix).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 1bdc56e7-31d0-49bb-ab08-fc1011f46419
@trsdn
trsdn merged commit 6b3daea into main Aug 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a "Run at startup" toggle in Settings

1 participant