feat(ui): shared UI primitives + npm update notification#58
Merged
Conversation
Add Button, Code-with-copy, Dialog under template/src/shared/ui/ and a modalManager service under template/src/shared/services/modal/ so widgets can open dialogs without per-call mounting. ModalRoot is mounted once in +layout.svelte. Add /api/update-check endpoint that probes registry.npmjs.org for the latest @forgeplan/web (5-min server-process cache, 5-second timeout, GET only, never throws). VersionFooter polls it once at mount and every 30 minutes; when hasUpdate, an UpdateButton appears above the footer and opens UpdateDialog (current → latest + copyable manual command). Auto-update is intentionally out of scope: running `npx @forgeplan/web update` from the running server would rmSync the very files serving the request. Dialog explains this and offers the manual path only. Rule 22 amended to allow exactly one non-forgeplan endpoint hitting the literal URL https://registry.npmjs.org/@forgeplan/web/latest. Refs: PRD-013, RFC-012, EVID-017 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`npx @forgeplan/web update` has two interactive failure modes: - if the package is not cached, npx prompts "Ok to proceed? (y)" and blocks waiting for Enter — bad UX when the user pasted the command; - if a stale version is cached, npx silently runs the old copy, which copies the OLD dist/ into .forgeplan-web/ — the update is a no-op. Switch the dialog command to `npx -y @forgeplan/web@latest update`: `-y` auto-confirms the install prompt; `@latest` forces npx to fetch the newest tarball. Add a one-line footnote explaining both flags. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After `update` runs, the host process keeps serving the OLD code: - macOS/Linux: the running Node process holds the old .forgeplan-web/ files open via open inodes, so rmSync from the new bin doesn't kill the request loop. The browser sees the old version until the user manually restarts `node .forgeplan-web/index.js`. - Windows: the rm step itself can fail with EBUSY because Windows locks open files; update may partially fail, but again the running server is unaffected. Either way the user has to stop+start the server. HMR is not an option here — adapter-node has no dev-server hooks at runtime. So the dialog now: - enumerates the four steps (Stop, Update, Restart, Reload); - pings /api/version every 5 s while open; - shows a "Server now serves vX.Y.Z" banner with a Reload button when the polled web version differs from the dialog's `current`; - shows an "offline" banner with a `npx @forgeplan/web start` hint when the ping fails (process killed but not yet restarted). Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The dialog already detects the server-side version change. Reloading is the inevitable next step — adding a click between "we saw the new version" and "you see the new version" is friction without value (the user explicitly opened the update dialog, intent is clear). So when the polled /api/version differs from the dialog's `current`: - show a banner that the server now serves the new version + a fading "reloading…" hint; - arm a 1.5 s setTimeout that calls window.location.reload(); - offer a Cancel button to stop the timer (preserves any in-tab state the user wants to keep) and a "Reload now" button to skip the wait; - guard against the timer firing twice if the poller produces multiple hits before the page unloads (only the first detection arms it). Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reload alone won't pick up the new version: Node holds the old ES modules in memory after the rmSync — open inodes keep them alive, and new requests are served from the in-memory module cache. The process itself has to be restarted before browser reload becomes meaningful. Add a warn-styled line right under the step list to make the chain explicit (Stop → Update → Restart → Reload) and prevent the common "I just refreshed but nothing changed" support question. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per request: keep manual update only. The dialog now shows the current → latest header, the four-step manual recipe, and the copyable command. Everything else — the /api/version pinger, serverDown banner, detected-version banner, auto-reload setTimeout, Cancel/Reload-now buttons — is removed. Manual update is the only supported path. The user runs the command, restarts the server themselves, and reloads the tab on their own. Refs: PRD-013 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
explosivebit
added a commit
that referenced
this pull request
May 6, 2026
… light theme (#65) ## Summary Bundles three feature streams from develop into v0.1.12: - **F18 Time-travel slider — SINGLE mode** (PR #63) — `/api/snapshot?at=ISO`, `/api/timeline-events`, `widgets/timeline/`, canvas snapshot hydration. PRD-008 / RFC-007 / EVID-020 active. - **`init --experimental` flag** (PR #60) — opt-in to esbuild single-file bundle, ~9× smaller `dist/`. PRD-014 / RFC-013 / EVID-018 active. Closes the verification ask in #59. - **Multi-graph mosaic dashboard** (PR #64) — split-pane layout with persistent layout, addresses #61. - **Version display footer** (PR #57) — `getForgeplanVersion()` + `compareSemver`. PRD-012 / RFC-011 / EVID-016 active. - **Shared UI primitives + npm update notification** (PR #58) — modal manager + update checker. PRD-013 / RFC-012 / EVID-017 active. - **Light theme** (PR #62) — dual-token CSS + `data-theme` attribute. PRD-015 / RFC-014 / EVID-019 active. COMPARE mode for time-travel (Alt-drag two scrubbers + diff overlay) intentionally deferred — endpoint returns 501 with TODO marker. ## Why Five feature PRs accumulated on develop since v0.1.11 (May 6). Cutting them as a single minor-patch release rather than gating on the v0.2.0 milestone (full proactive-surfacer arc) keeps the cadence steady and ships the bundle-size win + multi-graph layout to users now. ## Test plan - [x] `npm run check` — 0 errors / 0 warnings across 486 files - [x] `npx vitest run` — 14 files / 127 tests pass - [x] PR #63 CI matrix (3 OS, node 22) — green before merge - [ ] release/v0.1.12 CI matrix — pending on this PR - [ ] Manual smoke after tag → GitHub Release fires `release.yml` workflow ## Refs PRDs activated since v0.1.11: PRD-008, PRD-012, PRD-013, PRD-014, PRD-015. RFCs activated: RFC-007, RFC-011, RFC-012, RFC-013, RFC-014. Evidence: EVID-016 / EVID-017 / EVID-018 / EVID-019 / EVID-020. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Button, Code-with-copy, Dialog under template/src/shared/ui/ and a modalManager service under template/src/shared/services/modal/ so widgets can open dialogs without per-call mounting. ModalRoot is mounted once in +layout.svelte.
Add /api/update-check endpoint that probes registry.npmjs.org for the latest @forgeplan/web (5-min server-process cache, 5-second timeout, GET only, never throws). VersionFooter polls it once at mount and every 30 minutes; when hasUpdate, an UpdateButton appears above the footer and opens UpdateDialog (current → latest + copyable manual command).
Auto-update is intentionally out of scope: running
npx @forgeplan/web updatefrom the running server would rmSync the very files serving the request. Dialog explains this and offers the manual path only.Rule 22 amended to allow exactly one non-forgeplan endpoint hitting the literal URL https://registry.npmjs.org/@forgeplan/web/latest.
Refs: PRD-013, RFC-012, EVID-017