Repository navigation
feat(studio): the desktop-app button says Install the desktop app and never dead-ends - #5854
Conversation
…p app and never dead-ends The button now reads "Install the desktop app" beside Framey drawn as the app draws it. Where the app is installed, a press opens this project in it; a folder preview serves from a bare index.html gets its hyperframes.json first, so the app no longer refuses it. Without the app, the button links straight to hyperframes.dev's download, and a failed open lands there too. Platforms with no build of the app show no button. Telemetry now records eligibility, one impression, the click and the press outcome, with OS, app installed and download available.
… click and outcome
… and the app hand-off
Edit accuracy: accurate 271 (base branch 271), smooth 189 of thoseThe gate passes. Quarantined, measured but not gated (0) Unstable (3)
|
…d, install and open Without the app, the header button opens a card where Framey walks three steps: the download opens in a new tab, the install is found by asking the preview server while the card is open, and the project opens through the existing hand-off. A failed hand-off shows the folder to copy and Try again. With the app ready the press opens the project directly. Each step change is recorded through trackStudioEvent; a download is never counted as an install.
terencecho
left a comment
There was a problem hiding this comment.
Reviewed immutable HEAD 2f73a0a52b82ab111178800888de9c9de1136f7d against effective base a971c2bb257cf3f11feddab36c2b888b0e08a1cc (API base b46ebedeae959d3060ca4ece244ea6861d0f2352). No blocking introduced defect found in the inspected source.
What holds up
- The GET response separates installation from hand-off capability, while POST retains its same-origin checks and bounds the optional correlation ID (
packages/cli/src/server/desktopRoutes.ts:36–61). - Bare-index marker preparation uses the existing no-overwrite publication path:
desktopApp.ts:138–147→projectConfig.ts:191–197→writeNewFile.ts:5–17; an existing destination is not replaced by that operation. - The card derives its three steps from one progress value, keeps an actionable download retry, and disables automatic opening when hand-off is unavailable (
packages/studio/src/components/DesktopGuideCard.tsx:164–265). The production provider/header path supplies the project folder.
Nonblocking follow-up
packages/studio/src/components/OpenInDesktopButton.tsx:180–189: install_detected is not guaranteed once per page as the PR body promises. On one page: detect installation → remove the app → Open returns not-installed (lines 240–241) → reinstall → the restarted watcher detects it again. Both calls enter the telemetry queue, which has no deduplication (studioTelemetry.ts:115–141), with the same page ID. A page-lifetime detection latch and a repeated-cycle test would enforce that contract. This is a low-priority measurement issue, not a blocked hand-off; I did not query live analytics.
Scope and limits
I fully read all nine existing changed effective-base files before all ten changed HEAD files, inspected applicable standards and the pinned review rubric, and adjudicated both independent reviewers' final reports. Supporting production paths and workflow trigger excerpts were inspected selectively, not exhaustively. Exact-head check metadata includes successful build, lint, typecheck, Studio tests and CLI smoke checks, as well as skipped and historical cancelled rows; this is not a claim that I ran them or certified their checkout trees.
This is source approval only: I did not run tests, builds, a browser, native hand-off or render QA, and do not certify visual/layout behavior or end-to-end installer attribution. The maintainer's rendered-result QA before merge remains required. This review does not clear that hold or authorize a merge. The ending snapshot has no native auto-merge request, and the inspected repository workflow triggers do not run on review approval; this does not certify external automation.
— Review by tai (pr-review)
Verdict: APPROVE
Reasoning: No source-proven blocking regression survived verification. The telemetry-contract follow-up is nonblocking, and the separate maintainer render-QA-before-merge condition remains in force.
The app removed and found again on the same page recorded install_detected twice; a page latch keeps it to one, as the telemetry contract says.
|
Review follow-up on |
What
Studio's header button that leads to the HyperFrames desktop app:
index.htmlgets itshyperframes.jsonfirst (created only when missing), so the app no longer refuses it with "No HyperFrames project at …".GET /api/open-in-desktopevery 2 s; closing the card stops it. After a minute without the app it asks whether the download finished. Meanwhile the button reads "Finish installing" with "2 of 3".Why
Users of the local preview did not find the desktop app: "Edit with Framey" did not say what it does, and a press could end in the app's "No HyperFrames project" dialog for a project the preview had just opened. The app opens only folders that pass core's
isHyperframesProject(index.htmlplushyperframes.json,meta.jsonorproject.json), while preview serves any folder with anindex.html. Sending people to a download and leaving them there also lost them between the download and the app: the card keeps the next step in view until the project is open.How
openInDesktop(every hand-off goes through it: this button andhyperframes open) addshyperframes.jsonwith the existing no-overwritecreateProjectConfigwhen the folder hasindex.htmlbut fails core's project rule.GET /api/open-in-desktopsayshandoff: trueonly where the app is installed and can take a project, and addsosandinstalled. The card's install step reads the same route; nothing new on the server.OpenInDesktopButton.tsxowns the route, telemetry, the install check and the hand-off;DesktopGuideCard.tsxdraws the card from oneProgressvalue (label, Framey's mood and each step's state derive from it). Both use Studio'sPopover,Buttonand tokens.FrameyGlyphtakes an optionalmood; the moods' motion is incomponents.css, with a reduced-motion path.trackStudioEventonly.Telemetry contract
Every event also carries the session properties
trackStudioEventalready adds, includingstudio_versionandtab_id(a random id per page load).Context properties on every event below:
os(the preview server's platform:darwin/linux/win32),app_installed(boolean),download_available(boolean).studio:desktop_cta_eligibilityeligibility:open_in_app/download/no_app_for_platformstudio:desktop_cta_impressioneligibilitystudio:desktop_cta_guidestep:opened/closed/download_opened/install_detectedstudio:toolbar_action(existing)action:get_desktop_app/open_in_desktopstudio:desktop_cta_outcomeoutcome:opened/sent_to_download/failed;reasonwhen known:not-installed,open-failed,handoff-unavailable,download_tab_blocked,preview_server_unreachableA download is never counted as an install:
download_openedandsent_to_downloadmean a new tab opened on the download page, nothing more.install_detectedfires only when the preview server reports the app installed, once per page, even if the app is removed and found again.Correlation field: the page's
tab_id(a random UUID, no personal data), passed on ashf_cta:https://hyperframes.dev/studio/download?…&hf_cta=<tab_id>.POST /api/open-in-desktopwith body{ "cta": "<tab_id>" }. Only[A-Za-z0-9-]{1,64}is accepted. When the app took the folder, the CLI writes.hyperframes/desktop-cta.json={ "cta": "<tab_id>", "at": <ms> }in the project.Unmatched today: nothing in the app reads
desktop-cta.jsonyet, and hyperframes.dev does not yet carryhf_ctainto the installer.No layout shift
The real Studio build from this branch, headless Chrome at 1440x900, a fixture project. The preview server's answers were staged per scenario so every state could be driven without an app on the test machine. A probe recorded the boxes of the Compositions, Preview, Inspector and Timeline panels and of the preview stage on every animation frame, from the settled page through each press:
Every frame of every scenario has the same boxes (x,y,w×h): Compositions 3,43,364×494 · Preview 373,43,655×494 · Inspector 1034,43,403×494 · Timeline 3,543,1434×354 · preview stage 395,116,611×344.
Test plan
desktopApp.test.tsanddesktopRoutes.test.ts:index.htmlfolder passesisHyperframesProjectat the moment of hand-off, and a project's own marker is left alone.ctaid is forwarded only when valid and recorded only when the app took the folder.OpenInDesktopButton.dom.test.tsx(16 of 20 fail on main):install_detectedper page when the app is found, removed and found again (review finding; fails without the page latch).hyperframes previewon a fixture, every state above driven in headless Chrome; captures below. The browser's analytics requests were blocked, so nothing reached analytics.Before
main, no app on the machine: the button, then the press.
After
The button, no app on the machine:
The press opens the guide at step 1:
The download opened; the card waits for the install, and the button says what is left:
The app turned up; step 3 is ready:
A failed hand-off: the folder to copy and Try again:
After a minute without the app:
Linux with the app installed: the folder to drop in:
Windows: no button.