Skip to content

Dependency upgrade (ESLint 9 / Vite 8 / Vitest 4 / TS 6 / Prettier 3) + react-hooks v7 fixes + autostart Run-key fix (0.0.49) - #54

Merged
gyng merged 4 commits into
mainfrom
chore/dependency-upgrades
Jul 3, 2026
Merged

Dependency upgrade (ESLint 9 / Vite 8 / Vitest 4 / TS 6 / Prettier 3) + react-hooks v7 fixes + autostart Run-key fix (0.0.49)#54
gyng merged 4 commits into
mainfrom
chore/dependency-upgrades

Conversation

@gyng

@gyng gyng commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Consolidated release branch for 0.0.49. Three areas, each independently verified; all gates green.

1. Autostart Run-key fix

auto-launch 0.5.0 wrote the …\Run\widgetsack value as a bare, unquoted path plus a trailing space (C:\…\widgetsack.exe ), which Windows' logon launcher silently refuses to execute — so the app never started at login even though the toggle, the durable pref, and Task Manager all reported it enabled. We now write the Run value ourselves (quoted, no trailing space), with a pure run_command() seam + regression tests.

2. Full dependency upgrade

  • Rust: cargo update (tauri 2.11.2→2.11.5 + ~50 transitive bumps).
  • Client: ESLint 8→9 (flat config; eslint 10 blocked ecosystem-wide by eslint-plugin-react/typescript-eslint peers), Vite 5→8, Vitest 2→4, TypeScript 5.3→6, Prettier 2→3, happy-dom 15→20, @vitejs/plugin-react 4→6, plus in-range bumps. Switched to the typescript-eslint umbrella; added @eslint/js, globals, and an explicit vite-node (dropped by vitest 4). npm audit: 0 vulnerabilities (was 7).
  • Test/type fixups for the bumps: happy-dom dialog stubs, vitest 4 Mock typing, TS 6 startViewTransition type, @types/node, downshift 9.4 combobox query.

3. react-hooks v7 (React Compiler) rules — all 115 sites fixed

Behavior-preserving: latest-ref writes → commit effects; effect sync-to-prop → store-previous render-phase guard; purity → lazy init/effect; immutability/memoization → dep-array & restructure. Canvas.tsx's ref-sync effect keeps 8 immutability sites under a scoped, documented disable (a latest-ref mirror can't satisfy both refs and immutability, and we don't run the compiler).

Verification

cargo test + cargo clippy · tsc · eslint . (0) · Prettier · 2762 unit tests · vite build · check:docs · 34 Playwright e2e.

🤖 Generated with Claude Code

gyng and others added 4 commits July 3, 2026 10:14
Refresh Cargo.lock within semver: tauri 2.11.2->2.11.5, tray-icon, openssl,
time, wasm-bindgen and ~50 transitive patch/minor bumps. cargo test + clippy
green. auto-launch stays 0.5.0 (pinned by tauri-plugin-autostart 2.5.1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Vitest 4, TS 6, Prettier 3)

Upgrade all client dependencies to their latest usable majors and satisfy the
newer lint/type rules. npm audit: 0 vulnerabilities (was 7).

Dependencies:
- eslint 8->9 with a flat config (eslint.config.js; .eslintrc.cjs + .eslintignore
  removed). eslint 10 is blocked ecosystem-wide by eslint-plugin-react /
  typescript-eslint peers. Switch @typescript-eslint/* to the typescript-eslint
  umbrella; add @eslint/js + globals.
- vite 5->8, @vitejs/plugin-react 4->6, vitest 2->4, @vitest/coverage-v8 2->4,
  happy-dom 15->20, typescript 5.3->6, prettier 2->3, plus in-range bumps.
- Add explicit vite-node (dropped by vitest 4; needed by gen:docs/check:docs).
- Add "node" to tsconfig types (was provided transitively before the bump).

react-hooks (React Compiler rules) - fix all 115 flagged sites, behavior-preserving:
- latest-ref writes (ref.current = x during render) -> commit effects
- effect sync-to-prop setState -> store-previous render-phase guard
- purity (Date.now/Math.random in render) -> lazy init / effect
- immutability + preserve-manual-memoization -> dep-array & restructure fixes
- Canvas.tsx ref-sync effect: 8 immutability sites use a scoped, documented
  disable - a latest-ref mirror cannot satisfy both refs and immutability, and
  this project does not run the React Compiler.

Test fixes for the upgrades:
- happy-dom 20 dropped window.prompt/confirm/alert -> stub in test-setup
- vitest 4 Mock<Procedure|Constructable> typing (BackgroundPanel, llm/source)
- TS 6 startViewTransition DOM type (viewTransition)
- downshift 9.4 combobox: open the sensor field by role, not the shared caret

Prettier 3 reformat across the tree. All gates green: check, lint (0 warnings),
2762 unit tests, build, check:docs, 34 e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… login

auto-launch 0.5.0's enable() formats the HKCU\...\Run value as
format!("{} {}", path, args.join(" ")); with our empty args that yields an
unquoted path plus a trailing space (C:\...\widgetsack.exe ). Windows' logon
Run-key launcher silently refuses that malformed command, so the app never
autostarted even though the toggle, the durable HKCU pref, and Task Manager's
StartupApproved list all reported it enabled -- the plugin's is_enabled() only
checks that the value exists, not its content.

Write the Run value ourselves (enable_autostart): quoted so an install path
with spaces still launches, and trailing-space-free, mirroring the
StartupApproved "enabled" marker. reconcile() and set_autostart_enabled() use it
for the enable path; the plugin's read/disable paths are unchanged. Add a
run_command() pure seam with regression tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Autostart Run-key fix + full dependency upgrade (Rust cargo update; client
toolchain to ESLint 9 flat config / Vite 8 / Vitest 4 / TS 6 / Prettier 3,
happy-dom 20, 0 vulnerabilities) + all 115 react-hooks (React Compiler) rule
fixes. Gates: cargo test+clippy, tsc, eslint(0), 2762 unit, build, check:docs,
34 e2e.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gyng
gyng merged commit dd6b0b9 into main Jul 3, 2026
4 checks passed
@gyng
gyng deleted the chore/dependency-upgrades branch July 3, 2026 02:24
gyng added a commit that referenced this pull request Jul 9, 2026
- typescript 6.0.3 -> 7.0.2 (native compiler): zero code changes needed;
  tsc --noEmit drops from ~8.0s to ~1.7s (fast mode; a bimodal ~6.3s mode —
  suspected Defender scanning the native binary — still beats TS6).
- typescript-eslint has no TS7 support (peer <6.1.0, crashes on the changed
  API), so lint moves to oxlint 1.73 (.oxlintrc.json): typescript/unicorn/oxc/
  react plugins, correctness category + explicit overrides mirroring the old
  recommended tiers. Zero-warnings gate unchanged (--max-warnings=0).
  Must-keeps preserved: typescript/no-explicit-any, react/rules-of-hooks,
  react/exhaustive-deps. KNOWN COVERAGE LOSS: 14 of 16 react-hooks v7
  (React-Compiler-era) rules have no oxlint equivalent yet — the #54 fixes
  stay in code but are no longer machine-enforced. Also lost: react/prop-types
  (moot under TS), react/no-deprecated.
- Formatting moves from Prettier to oxfmt 0.58 (.oxfmtrc.json via
  --migrate=prettier: tabs, single quotes, no trailing commas, width 100).
  Covers TS/TSX/JS/JSX/CSS/JSON/MD/HTML — no lost coverage; the one-time
  reformat touched 4 files, whitespace-only.
- eslint-disable comments renamed to oxlint-disable (tool-assisted, 11 files);
  legacy @typescript-eslint/* rule names inside them still resolve. A handful
  of newly-surfaced correctness findings fixed (useless spread fallbacks,
  new Array(n) holes, unsafe ?. in tests).
- Removed devDeps: eslint, @eslint/js, typescript-eslint, eslint-plugin-react,
  eslint-plugin-react-hooks, eslint-config-prettier, globals, prettier.
- AGENTS.md formatting/linting section updated.

Gates: tsc clean, oxfmt+oxlint clean, 2768 unit tests, build, check:docs,
34 Playwright e2e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gyng added a commit that referenced this pull request Jul 9, 2026
…int/oxfmt (0.0.50) (#55)

* fix(keepalive): survive zero windows — prevent last-window exit + respawn main

widgetsack is a tray app, but Tauri's default exits the process when the last
window closes. Zero windows is reachable BY DESIGN: an empty-primary `main`
self-destructs to reclaim its renderer (overlay.ts setMainWindowVisible), so a
layout whose widgets all sit on one secondary monitor lives on a single overlay
window — and any transient failure to spawn it (monitor not yet enumerated at
logon, WebView2 hiccup, monitor DDC-switched away) silently ended the process.
This was the "didn't autostart" failure of 2026-07-10.

- keepalive.rs (new): should_prevent_exit pure seam (+tests); on_zero_windows
  schedules a rate-limited (30s) respawn of the hidden `main`, whose own Canvas
  init re-runs the reconcile cycle — spawning overlays for whatever monitors
  exist NOW, then self-destroying again if the primary is still empty. Retries
  pace themselves off the event cycle (no polling); one attempt in flight;
  self-reschedules if window creation itself fails.
- main.rs: .run(context) → .build(context).run(handler); ExitRequested with
  code None is prevented. Tray Quit (app.exit(0) → code Some(0)) still exits.
- command.rs: respawn_main_hidden made pub(crate) + a `reason` log tag shared
  by the layout-watcher and keepalive recovery paths.

Runtime-verified with a debug --multi instance on a layout whose only populated
monitor key matches no connected display: the app survives repeated zero-window
cycles (keepalive → respawn → self-destroy) and recovers the overlay as soon as
the layout regains a real monitor. cargo test (200 incl. 2 new), clippy, fmt
all clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(reliability): overlay/log hardening + instant display recovery (0.0.50)

Follow-ups to the zero-window keepalive, from the post-incident review:

- log_client command (command.rs) + overlay.ts logClient helper: overlay
  lifecycle failures (reconcile, spawn, self-fit, main destroy/recreate) now
  land in the persistent rotating log file as target "client" instead of dying
  with the webview console — the 2026-07-10 postmortem cost hours because these
  paths were console-only.
- populatedMonitorKeys returns null on a load_layout/parse failure and
  reconcileOverlays skips the pass entirely — "couldn't read the layout" is no
  longer treated as "layout is empty" (which closed every working overlay on a
  transient IPC hiccup).
- reconcileOverlays is single-flight with a trailing rerun (new pure
  core/singleFlight.ts + tests): concurrent passes (init, layout_changed,
  topology watcher) no longer race to create the same overlay label.
- displaywatch.rs: hidden-window WM_DISPLAYCHANGE watcher respawns `main` the
  moment a monitor returns while the app is at zero windows — instant recovery
  instead of keepalive's 30s retry. (Deliberately a hidden top-level window,
  not HWND_MESSAGE: message-only windows don't receive broadcast messages.)
- GSMTC SessionManager failure now degrades gracefully (media disabled, app
  starts) instead of a startup .expect crash; the rx consumer became
  `while let` so a closed channel can't hot-spin.
- cargo update (24 crates within semver; no tauri/wry/softbuffer bumps were
  available — the upstream softbuffer !dc.is_null() panic remains open).
- Version 0.0.50.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore(toolchain): TypeScript 7 + oxlint/oxfmt replace eslint+prettier

- typescript 6.0.3 -> 7.0.2 (native compiler): zero code changes needed;
  tsc --noEmit drops from ~8.0s to ~1.7s (fast mode; a bimodal ~6.3s mode —
  suspected Defender scanning the native binary — still beats TS6).
- typescript-eslint has no TS7 support (peer <6.1.0, crashes on the changed
  API), so lint moves to oxlint 1.73 (.oxlintrc.json): typescript/unicorn/oxc/
  react plugins, correctness category + explicit overrides mirroring the old
  recommended tiers. Zero-warnings gate unchanged (--max-warnings=0).
  Must-keeps preserved: typescript/no-explicit-any, react/rules-of-hooks,
  react/exhaustive-deps. KNOWN COVERAGE LOSS: 14 of 16 react-hooks v7
  (React-Compiler-era) rules have no oxlint equivalent yet — the #54 fixes
  stay in code but are no longer machine-enforced. Also lost: react/prop-types
  (moot under TS), react/no-deprecated.
- Formatting moves from Prettier to oxfmt 0.58 (.oxfmtrc.json via
  --migrate=prettier: tabs, single quotes, no trailing commas, width 100).
  Covers TS/TSX/JS/JSX/CSS/JSON/MD/HTML — no lost coverage; the one-time
  reformat touched 4 files, whitespace-only.
- eslint-disable comments renamed to oxlint-disable (tool-assisted, 11 files);
  legacy @typescript-eslint/* rule names inside them still resolve. A handful
  of newly-surfaced correctness findings fixed (useless spread fallbacks,
  new Array(n) holes, unsafe ?. in tests).
- Removed devDeps: eslint, @eslint/js, typescript-eslint, eslint-plugin-react,
  eslint-plugin-react-hooks, eslint-config-prettier, globals, prettier.
- AGENTS.md formatting/linting section updated.

Gates: tsc clean, oxfmt+oxlint clean, 2768 unit tests, build, check:docs,
34 Playwright e2e.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
gyng added a commit that referenced this pull request Jul 9, 2026
Conflict resolution notes:
- NowPlaying.tsx was binary-diffed on BOTH sides: the trackKey line embedded
  literal NUL (0x00) separator bytes in the source. Converted them to the \0
  escape sequence (identical string value) so the file is text again and
  future merges work.
- Re-expressed the branch's hasMedia fix in main''s post-#54 (react-hooks v7)
  structure: the layer-drop lives in the adjust-during-render (now keyed on
  hasSession && hasMedia), the crossfade effect early-outs via a
  commit-effect-written hasMediaRef (deps stay [artKey, hasSession]), and the
  thumb stack renders only while a track is present.
- The branch''s regression test passes against the adaptation (2769 unit tests).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gyng added a commit that referenced this pull request Jul 9, 2026
…#57)

Coverage had slid to 97.69/93.45/96.39/98.89 unnoticed (Vitest 2->4 remap
changes in #54 plus untested-code drift) because CI ran test:unit, never the
thresholds. This restores it above the original v0.0.48 baseline and makes
regression impossible to miss:

- ~400 new tests across 5 parallel clusters (Inspector/editor panels, meters,
  plugins+llm+formula, lib/core, canvas+widgets misc): 2769 -> 3151 tests.
  Inspector.tsx alone went 81.1% -> 99.7% stmts. All test-only; no source
  changes, no coverage-ignore pragmas (standing policy). Residue is
  documented unreachable defensive arms / library-internal branches, each
  justified in the PR notes.
- CI client job now runs test:coverage instead of test:unit, so the
  vite.config ratchet gates every PR.
- Thresholds re-ratcheted: statements 99->99.25, lines 99->99.5,
  functions 96->99.5, branches 95->98.5 (~0.4pp slack for cross-platform
  v8 remap jitter).
- Also hardened one pre-existing flaky test (MonitorSourcesEditor rescan
  raced a disabled button under parallel load).

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant