Skip to content

fix(cli): ship player + slideshow bundles so present/play work from npm#1706

Merged
jrusso1020 merged 1 commit into
mainfrom
fix/cli-ship-player-globals
Jun 24, 2026
Merged

fix(cli): ship player + slideshow bundles so present/play work from npm#1706
jrusso1020 merged 1 commit into
mainfrom
fix/cli-ship-player-globals

Conversation

@jrusso1020

Copy link
Copy Markdown
Collaborator

Problem

npx hyperframes present (and play) always fail with:

@hyperframes/player not found. Run `bun run --cwd packages/player build` first.

The only workaround has been to run the presenter from a monorepo checkout — which is exactly the "BIG GOTCHA" people writing decks have had to document and pass around.

Root cause

present/play render compositions in the standalone browser player and resolve the player + slideshow IIFE bundles at runtime via resolvePlayerPath / resolveSlideshowPath (packages/cli/src/utils/compositionServer.ts). Those resolvers check, in order:

  1. monorepo-dev path (../../../player/dist/...)
  2. alongside the built CLI (dist/hyperframes-player.global.js, dist/hyperframes-slideshow.global.js)

In an npm install only #2 can ever hit — but build-copy.mjs never staged those two bundles into dist/, so the published package ships present/play with no player to load. (The runtime bundle is already handled by build:runtime, which is why only the player/slideshow globals were missing.)

Fix

Copy both player globals from packages/player/dist into the CLI dist/ during build:copy, existsSync-guarded with a warn-and-skip, matching the surrounding pattern in the file.

Verification

  • npm pack --dry-run now includes dist/hyperframes-player.global.js and dist/hyperframes-slideshow.global.js.
  • node dist/cli.js present <deck> starts the presenter cleanly (HTTP 200) with no "@hyperframes/player not found".
  • Confirmed end-to-end against a real 21-slide deck: slides render and step correctly.

🤖 Generated with Claude Code

`present` and `play` render compositions in the standalone browser player,
resolving the player/slideshow IIFE bundles via resolvePlayerPath /
resolveSlideshowPath. Those resolvers look for the bundles alongside the built
CLI (dist/hyperframes-player.global.js, dist/hyperframes-slideshow.global.js),
but build-copy.mjs never staged them into dist/. The remaining candidate paths
are monorepo-dev only, so an npm install has nothing to resolve.

Result: `npx hyperframes present` always failed with
"@hyperframes/player not found", forcing users to run the presenter from a
monorepo checkout.

Copy both player globals from packages/player/dist into the CLI dist during
build:copy (existsSync-guarded + warn, matching the surrounding pattern). The
runtime bundle is already handled by build:runtime. Verified: the globals now
appear in `npm pack`, and `node dist/cli.js present` starts without the
player-not-found error.

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

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Tight, well-scoped fix for a real usability pain point.

The change is exactly right: resolvePlayerPath / resolveSlideshowPath in compositionServer.ts already look for the globals alongside the built CLI (dist/<name>.global.js), but build-copy.mjs never staged them there — so npx hyperframes present always fails from an npm install. This plugs that gap.

Good patterns:

  • existsSync-guarded with warn-and-skip matches the existing motionSampleScript pattern directly above. A missing bundle at build time downgrades to a warning instead of failing the whole build — correct, since a contributor who hasn't built the player package might still want to iterate on other CLI changes.
  • Source paths are correct: hyperframes-player.global.js at packages/player/dist/ and hyperframes-slideshow.global.js at packages/player/dist/slideshow/ — these match the player package's build output structure.
  • The flat dist/ destination matches what resolvePlayerPath / resolveSlideshowPath expect for the non-monorepo (npm install) resolution path.

No issues. CI still running.

Review by Miga

@vanceingalls vanceingalls left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small, surgical build-script fix: stages hyperframes-player.global.js and hyperframes-slideshow.global.js into packages/cli/dist/ during build:copy so npx hyperframes present / play actually find the player when the CLI is installed from npm. Production delta is 21 lines, single file (packages/cli/scripts/build-copy.mjs), pattern matches the surrounding existsSync-guarded warn-and-skip blocks. Customer-facing pain point (the "BIG GOTCHA" people writing decks had to work around).

Verdict: LGTM

Findings (numbered, with severity tag):

None. Read both the resolver and the build script at HEAD and the contract is exact:

  • resolvePlayerPath at packages/cli/src/utils/compositionServer.ts:35-43 looks for hyperframes-player.global.js at resolve(d, "..", "..", "..", "player", "dist", ...) (monorepo dev), then resolve(d, "hyperframes-player.global.js") and resolve(d, "..", "hyperframes-player.global.js") (alongside CLI dist root or one level up — covers dist/cli.js and dist/commands/*.js callsites).
  • resolveSlideshowPath (compositionServer.ts:45-53) — same three-candidate shape for the slideshow global.
  • The fix copies to flat DIST = packages/cli/dist/ root, which hits candidates 2 and 3 in both resolvers from any callsite under dist/.
  • Source paths (packages/player/dist/hyperframes-player.global.js and packages/player/dist/slideshow/hyperframes-slideshow.global.js) match the player's package.json exports[*].script entries exactly.

Verified (band-aid sweep clean):

  • Pattern #1 duplicate guardsexistsSync-loop reuses the same warn-and-skip shape as the skills loop above (build-copy.mjs:84-90) and the standalone motionSampleScript block (build-copy.mjs:107-109). No new helper needed at this scale.
  • Pattern #3 silent scope gap — PR body claims present AND play; grep confirms only present.ts and play.ts call the resolvers (present.ts:77-78, play.ts:99). preview.ts does not use them, so it's correctly out of scope. No third command silently affected.
  • Pattern #6 title/comment vs code drift — title says "ship player + slideshow bundles so present/play work from npm"; diff ships exactly those two bundles, used by exactly those two commands. The inline comment block re-states the same explanation.
  • Pattern #10 decorative gate — the gate (existsSync(src)) has a real populate path: source files come from packages/player/dist/, which is built before CLI in the root package.json build script (bun run --filter '...,player,...' build && bun run --filter @hyperframes/cli build). In the release pipeline the sources will always exist; the skip-with-warn only fires for a contributor iterating on CLI without prebuilding player — same intentional behavior as the existing skill/script blocks.
  • Build pipeline ordering — root package.json builds @hyperframes/player before @hyperframes/cli, so build:copy runs after both, finds the player globals, and copies them. Confirmed.
  • npm-pack inclusion — CLI package.json files: ["dist"] ships everything under dist/, so the copied globals will be in the published tarball.
  • Cross-PR family non-regression — orthogonal to HF #1671 (skills install path), #1670 (engine fast-screenshot), #1702/#1703 (runtime + engine fixes). None touch build-copy.mjs or the resolver path.
  • CLI smoke coverageCLI smoke (required), CLI: npx shim (ubuntu/macos/windows), Smoke: global install, SDK: unit + contract + smoke, Studio: load smoke all SUCCESS on this SHA — exactly the right cohort for a dist-staging fix.

CI state at HEAD 109f8f5a27d3f9a618fe813d3883df5b587a7d31: All required checks green (CI Build/Test/Typecheck/Lint/Fallow/Format, CLI smoke required, CLI npx shim ubuntu/macos/windows, Smoke global install, preview-regression, regression, player-perf, SDK + Studio smoke). Two orthogonal Windows render-verification jobs still IN_PROGRESS (Render / Tests on windows-latest) — unrelated to a dist/ copy fix.

Prior reviewer state: Miga LGTM (COMMENTED, 2026-06-24T23:45:49Z) + Miguel APPROVED (2026-06-24T23:48:48Z, no body — silent stamp). Converged. Miga's coverage focused on the in-component contract (resolver paths, existsSync pattern parity, flat-dist destination correctness). This review adds the complementary cross-package read: scope-vs-claim (present+play only, preview correctly untouched), root build-orchestration ordering (player builds before CLI), files: ["dist"] ship-list inclusion, and CI-cohort verification (all CLI smoke variants green).

Review by Via

@jrusso1020 jrusso1020 merged commit 1c38998 into main Jun 24, 2026
39 checks passed
@jrusso1020 jrusso1020 deleted the fix/cli-ship-player-globals branch June 24, 2026 23:57
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.

4 participants