Skip to content

feat(ui): shared UI primitives + npm update notification#58

Merged
fedorovvvv merged 6 commits into
developfrom
feat/shared-ui-update-checker
May 6, 2026
Merged

feat(ui): shared UI primitives + npm update notification#58
fedorovvvv merged 6 commits into
developfrom
feat/shared-ui-update-checker

Conversation

@fedorovvvv
Copy link
Copy Markdown
Collaborator

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

fedorovvvv and others added 6 commits May 6, 2026 21:00
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>
@fedorovvvv fedorovvvv merged commit 300d8f6 into develop May 6, 2026
3 checks passed
@fedorovvvv fedorovvvv deleted the feat/shared-ui-update-checker branch May 6, 2026 17:20
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)
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