Skip to content

Standardize the in-app update dialog across Studio apps #36

Description

@ElbertePlinio

Goal

Give every shipping Pickforge Studio desktop app one branded, accessible update prompt that appears after the visible main window opens, then downloads, installs, and relaunches through Tauri.

Current state

  • PickForge has a custom updater store, titlebar badge, and Settings action, but no startup dialog.
  • PickScribe and PickGauge call window.confirm(...); wording, release-note handling, and behavior differ.
  • All three already use Tauri updater/process plugins and @pickforge/brand.
  • PickScribe and PickGauge already defer checks until the main window is visible; this must remain true for hidden login-start and floating/tray windows.
  • None of the three updater flows has focused behavior or visual coverage.

Scope means the currently shipping GUI auto-updating apps: PickForge, PickScribe, and PickGauge. PickLab is CLI/MCP-only; PickArena and web-only products do not self-update through a desktop dialog. Future Tauri apps should adopt the shared package.

Decision

Build @pickforge/tauri-updater in pickforge-platform as one framework-neutral Web Component plus a headless, injected update controller. SolidJS and Svelte apps mount the same element; no parallel framework-specific implementations.

The package will:

  • expose a testable state machine: idle → checking → available → downloading → installing → restarting, plus dismissed and recoverable error;
  • provide the Tauri adapter over check(), downloadAndInstall(event => …), and relaunch();
  • enforce one check per app process, packaged-build-only execution, main-window eligibility, and visible/focused-window deferral;
  • render update version, optional release notes, download progress, retryable errors, Update & restart, and Later;
  • treat startup network/update-feed failures as silent and non-blocking;
  • dismiss Later only for the current process so a still-available release is offered again on the next app launch;
  • retain PickForge’s manual Settings check and optional titlebar availability badge through the same controller;
  • accept injected adapters/metadata for deterministic tests and VRT, never production mocks or fallback behavior.

Design contract

Status: locked
Design lead: GPT-5.6 Sol — issue-planning session
Mode: targeted / preserve

Outcome

  • User and job: a desktop user opening an outdated app who needs to understand, trust, and apply the update without leaving the app.
  • Desired result: the same unmistakably Pickforge prompt in all three products, with one clear primary action and no browser-native confirm UI.
  • Success evidence: matched rendered captures in all three apps, keyboard/screen-reader checks, and signed packaged updater smoke tests.

Direction

  • Concept: a compact release card lifted above the app canvas.
  • Hierarchy: product mark + mono UPDATE AVAILABLE eyebrow → current/new version rail → concise release notes → one ember primary action.
  • Composition: centered native <dialog>, max-width: 560px, restrained glass-strong surface, 14px radius, hairline border, overlay shadow, generous but desktop-efficient spacing.
  • Type/color/shape: existing Geist/Geist Mono and --pf-* tokens; off-white text; muted metadata; one ember focal point on the primary action/progress; no app-specific recoloring.
  • Motion: 140–180ms opacity + 8px lift for orientation only; progress transitions without layout jumps; no motion under prefers-reduced-motion.
  • Take: existing Pickforge titlebar language, mono eyebrow/tick motif, glass-strong modal treatment, ember one-primary rule.
  • Reject: window.confirm, OS-native mismatched chrome, giant gradients, celebratory confetti, full release-page density, multiple competing CTAs.

Required states

  1. Available — version and optional release notes; Update & restart + Later.
  2. Downloading/installing — determinate progress when content length exists, otherwise honest indeterminate status; dismissal disabled once installation starts.
  3. Error — short inline error, Retry primary, Later secondary; app remains usable.
  4. Restarting — completion confirmation and protected terminal state.
  5. No update/check failure — no startup dialog.

Accessibility and interaction

  • Native <dialog> semantics with labelled title/description, initial focus on Update & restart, trapped focus, focus restoration, visible focus rings, and polite live progress.
  • Escape equals Later only before download begins; backdrop click does not dismiss.
  • Release notes are plain text, bounded/scrollable, and must not execute markup.
  • Works at each app’s configured minimum window: PickForge 880×600, PickScribe 780×560, PickGauge 820×580.
  • Respects light/dark/system themes and reduced motion.

Design deviations require an issue update and design-lead approval. Final visual acceptance returns to GPT-5.6 Sol after representative rendered captures exist.

Rollout flag

This spans multiple PRs/releases, so gate integrations with studioUpdateDialog from @pickforge/flags, default off. Keep the legacy updater UI active while the flag is off; remove the legacy path only after the new dialog ships enabled.

  • merged behind flag studioUpdateDialog
  • tested on main with the flag on in all three apps
  • enabled in the next PickForge, PickScribe, and PickGauge releases
  • flag and legacy updater paths removed from all three apps

PR plan

  • PR 1 — Canonical dialog visual spec (branding-visual)
    • Depends on: none
    • Touches: component guidance/tokens only where the locked contract exposes a real missing canonical rule
    • Validation: token/reference audit and rendered reference at minimum desktop size
    • Link: pending
  • PR 2 — Shared updater package (pickforge-platform)
    • Depends on: PR 1 contract
    • Touches: new packages/tauri-updater, workspace exports/build/publish configuration
    • Validation: controller/DOM unit tests, typecheck, package build, standalone visual fixture
    • Link: pending
  • PR 3 — PickForge integration (pickforge)
    • Depends on: published PR 2 package
    • Replaces: app-local updater state machine while preserving Settings check and titlebar badge behavior
    • Validation: focused unit tests, build, VRT at 1280×820 and 880×600, packaged signed-update smoke
    • Link: pending
  • PR 4 — PickScribe integration (pickscribe)
    • Depends on: published PR 2 package
    • Replaces: window.confirm updater while preserving visible-main-window deferral and hidden autostart behavior
    • Validation: focused unit tests, Svelte check/build, VRT at 1020×720 and 780×560, hidden-start smoke, packaged signed-update smoke
    • Link: pending
  • PR 5 — PickGauge integration (pickgauge)
    • Depends on: published PR 2 package
    • Replaces: window.confirm updater while excluding the floating capsule and hidden main window
    • Validation: focused unit tests, Svelte check/build, VRT at 1000×700 and 820×580, floating/hidden-window smoke, packaged signed-update smoke
    • Link: pending
  • PR 6+ — Enable, release, then remove the flag (one narrow PR per app as release timing requires)
    • Depends on: app integration accepted with flag on
    • Validation: release artifact/latest.json checks, one real open → prompt → download → install → relaunch flow per app, then clean-cutover tests after flag removal
    • Link: pending

PRs 3–5 are independent after PR 2 publishes and may run in parallel in separate repos/worktrees. Use Refs pickforge/pickforge-platform#36 for partial PRs; only the final flag-removal PR closes this issue.

Acceptance criteria

  • PickForge, PickScribe, and PickGauge mount the same shared dialog implementation and CSS; no duplicated app-local modal markup.
  • Opening an outdated packaged app shows the prompt once, only after the visible main window is ready.
  • Hidden login-start, tray, and floating windows never surface or duplicate the prompt.
  • Later, Escape, progress, retry, install, and relaunch behavior match the contract.
  • Release notes remain safe plain text and long notes do not overflow minimum-size windows.
  • Startup check failure never blocks launch or opens an error dialog.
  • Keyboard, focus restoration/trap, screen-reader naming/live progress, contrast, light/dark/system theme, and reduced-motion checks pass.
  • Deterministic controller and DOM tests cover no-update, available, dismissal, single-flight, progress with/without content length, install failure/retry, and relaunch.
  • Each app has representative normal/minimum-size screenshots accepted by the named design lead.
  • Each app passes a signed packaged update smoke from old build to staged newer build.
  • Legacy window.confirm and obsolete app-local updater implementations are removed after rollout.

Validation references

Tauri v2 supports check(), update.downloadAndInstall(event => …) with Started/Progress/Finished events, and relaunch() through the process plugin. Existing signed endpoint/public-key configuration remains app-owned and unchanged.

Current status: Planned
Next: land the canonical visual spec, then build and publish the shared package.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestflaggedFeature merged dark pending release enablement and flag removal

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions