mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-01 19:42:03 +00:00
sync/hyperframes-codegen-0975ac7d
17
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
efc2e1964a |
fix(skills): require user confirmation before skill updates (#3295)
Replace "run silently, don't ask" with explicit confirmation guidance in ten workflow SKILL.md files so agents do not auto-run npx updates without the user. Regenerate skills-manifest.json. Refs heygen-com/hyperframes#2613 |
||
|
|
9da422fd7f |
feat(cli): run a managed background preview in every launch mode (#3310)
`--background` was rejected outside the embedded server. It now re-execs the CLI in foreground, which makes it mode-agnostic by construction: whichever server the child resolves to serves the config endpoint the readiness probe looks for. `--foreground` is its counterpart, for a non-interactive shell that wants to stay attached, and a bare launch keeps the same promise — attached in an interactive terminal, managed in an agent session. That generalization exposed an existing hole. Local-studio mode runs Vite with the studio package as its cwd and needs that package's own Vite config, which the published tarball does not carry, but resolving the package was treated as proof the mode was usable. An npm-installed studio therefore took a path that can never come up — previously a clear error, now a ten-second silent timeout. The predicate becomes "can this studio actually be served", so a published install falls back to embedded mode, which works. Over the 1k line budget at ~1.3k. The overage is one command file and its tests carrying one invariant, and the seam that would split it further is inside a single request-handling function — a split there would produce two PRs neither of which starts a preview on its own. |
||
|
|
d3a4e90370 |
fix(skills): stop embedded-captions shipping author-only paths; dedupe slideshow (#3225)
* fix(skills): stop embedded-captions shipping author-only paths The skill is distributed via `hyperframes skills` (--copy, so the installed bundle matches the published tree), but three shipped files pointed at directories that only exist on the original author's machine. - references/test-set.md: deleted. Nothing in the skill referenced it (zero inbound links across all 140 files), and its corpus lives at ~/Downloads/heygen_relevant_videos/, so it was neither reachable nor runnable. - SKILL.md / dna/README.md: the retired 54-template archive was stated as living at a ~/Downloads path. Keeps the fact, drops the false location. - themes/PORTING.md: marked maintainer-only. It authors new theme DNAs rather than using the skill, nothing links it, and its inputs (the cap_fx3 demos, the frame corpora, CONTRACT.md) are not distributed. The paths stay as the author's original layout, now explicitly labelled as names for the inputs. The 10 references to ~/Downloads/hyperframes are untouched: those are the documented last resort in a real chain (HYPERFRAMES_ROOT -> in-repo -> that path), and every file carrying it also carries the env var. Regenerates skills-manifest.json, which hashes the whole skill directory. Closes #3219 — Rames Jusso (James's assistant) * fix(skills): delete themes/PORTING.md instead of marking it internal Nothing in the skill referenced it, its inputs are not distributed, and git history keeps it recoverable. Marking a dead procedure still ships the dead procedure. — Rames Jusso (James's assistant) * fix(skills): remove duplicated media-cleanup block in slideshow The media-cleanup and global-nav-mute sections appeared twice in skills/slideshow/SKILL.md. The two copies were not identical: the later one was missing the paragraph requiring custom media controls to sync through media events. Deletes the incomplete copy, keeps the complete one. Originally authored in #3218. Carried here re-signed because the org requires signed commits and both changes regenerate skills-manifest.json, which would otherwise conflict between the two PRs. Co-authored-by: Lance Curtis <69400491+imprimisxo@users.noreply.github.com> * chore(skills): regenerate manifest for slideshow + embedded-captions — Rames Jusso (James's assistant) * chore(skills): record the two embedded-captions deletions in the guard check-no-main-deletions.mjs deliberately has no blanket override, so each intentional deletion is named with its reason and shows up in review. — Rames Jusso (James's assistant) --------- Co-authored-by: Lance Curtis <69400491+imprimisxo@users.noreply.github.com> |
||
|
|
b9be0b2625 |
feat(skills,studio,media-use): the intent layer, review loop, and user memory — BRIEF.md, companion mode, recipes; /website-to-video folds into /product-launch-video (#2133)
* feat(studio,cli): per-frame board comments, self-refreshing storyboard, status-aware preview landing Per-frame comment boxes on the storyboard board batch into .hyperframes/frame-comments.json (a resubmit wins per frame; unconsumed comments on other frames are kept). Submitted-but-unconsumed comments stay visible — a toolbar banner plus a per-tile echo — until the agent consumes the file; the banner also says what to do next (reply anything in the agent chat). The board keeps itself current: GET /projects/:id/signature exposes the watcher-cached project signature, the storyboard payload carries the signature it was derived from, and the view polls at 2s (hidden tabs skipped, re-checked on visibility), refetching in place with no loading flash. Posters bake the signature into their URL so tiles fill in as sketches land and a poster that failed mid-write retries on the next version; the empty state upgrades itself when STORYBOARD.md appears, and its handoff prompt now points the agent at the review loop and uses the parser's real status vocabulary (outline, not planned). preview lands the browser on the storyboard view while the board is the review surface — any frame built, or pure planning (srcs declared, none on disk yet) — and on the timeline once the video is assembled. * feat(skills): the review loop — plan, sketch, build as one shared process hyperframes-core/references/review-loop.md is the single source for the three-pass collaborative review: the plan proposed on a live board (§ 1), wireframe sketches marked built with one layout question (§ 2 — real words on plain blocks, run no CLI; a confirmed board is itself a valid deliverable when the user asked for a storyboard, not a video), the build dressing confirmed layouts (§ 3, worker or inline), and the final look (§ 4). Autonomous runs skip every gate and keep one question before render. The three narrative workflows' Steps 3/4/6 collapse to references plus their sketch stand-ins (captured-asset blocks for product-launch-video, plain code panels for pr-to-video); the confirmed-sketch handoff stays in each frame-worker prompt. general-video plans on a board for multi-scene narrative pieces in collaborative mode — its sketch pass is layout-before-animation with the user watching. The router treats "I want a storyboard" as a process request rather than a route, and closes exploratory intake by recommending a route plus how the run will review. The supporting contracts land next door: the comments channel (silent submit, one reply picks it up, check the file before the words) in brief-contract § 1; the sidecar schema and the built status rung in storyboard-format; the mode question asked first and alone in the three workflows' Step 0. * feat(media-use): user memory — remembered preferences and frozen recipes Two tiers of memory on media-use's existing two-tier storage split. Preferences (lightweight): confirmed brief answers — destination, aspect, language, mode, voice, style preset — recorded to the project's .media/preferences.json (committed, the team inherits it) and promoted to the personal ~/.media/preferences.json once the same value is confirmed in two different projects (a sightings ledger accumulates the cross-project evidence user-side, since project files can't see each other). prefs.mjs get/record; merge reads project-over-user; a changed value restarts its provenance. Recipes (heavyweight): one approved run frozen as a named, versioned bundle — frame.md, the storyboard skeleton (structure kept: durations, transitions, srcs, Video direction; statuses reset to outline; content blanked to per-frame fill-ins naming the beat's role), and the confirmed brief values. Named folders, not content hashes: re-freezing bumps version and archives <name>@v<N>; a freeze is already confirmed, so it promotes to the user tier immediately. recipe.mjs freeze/list/use, plus resolve --type recipe --entity <name> delegating like grade/lut. 16 new node --test cases; the media-use lib suite is 168/168. * feat(skills): wire user memory into the brief and the review loop brief-contract § 2 gains Remembered defaults: read the merged preferences before Round 2 and let a remembered value become the recommended option with a receipt naming its source project. Memory changes the default, never the question — every ask-marked field still gets asked, and what the request says this time beats what was picked last time. Record only what the user actually confirmed (a defaulted voice nobody chose is not an answer; a "go" that accepts the recommended defaults is). The first record announces itself once; after that the receipts carry the reminder. In autonomous mode a remembered value becomes the decided value, receipt included. The three narrative workflows read the remembered defaults before Round 2, record the confirmed answers at the Step 0 gate, record the chosen preset at the Step 2 gate (pr-to-video excepted — its preset is fixed), and fall back to the remembered voice when the request names none. general-video's discovery reads the same defaults. Recipes wire in at both ends: Step 0 checks for a matching recipe before the mode question — one question, plural-aware, and adopting one fills the brief, skips the design step, and drafts the storyboard from the frozen skeleton while every review gate still runs. The review loop's final look (§ 4) offers the freeze once after approval, and the confirmation teaches the recall phrase — the name is something the system reminds the user of, never something they must remember. The router recognizes a named recipe or "like last time" as a route. * docs(skills): the sketch pass names check, not the deprecated validate * feat(skills): intent-layer references — process, route briefs, capability menu, BRIEF.md format * feat(media-use): brief skeleton as the recipe's fourth artifact; flow/storyboard preference keys * feat(skills): the intent layer conducts every brief — workflows execute BRIEF.md * feat(skills): retire the mode preference key; sync catalog surfaces for intent layer * refactor(skills): dedupe router vs intent-layer guidance — one owner per rule * feat(skills): the design ask — own spec, pick by eye from showcases, or defer * docs(skills): the design ask says the honest line on capture routes * feat(skills): product-launch-video absorbs website-to-video as the tour angle * refactor(skills): keep product-launch-video pristine — a tour is brief intent, not a pipeline branch * feat(skills): production loop + genre lenses; general-video goes freeform (route yours, laws hold) * refactor(skills): /hyperframes is the front door - route tables and scope lists leave the workflows * docs(skills): review-loop pass across skill catalog * fix(cli): pass project dir to openStudioBrowser in background-server path * feat(skills): add pitch-round reference - verbalized sampling concept gate * feat(skills): wire pitch round into intent layer - completeness triage + route eligibility * feat(skills): editorial capability recommendations, handoff disciplines, menu-probe split * feat(skills): pitches carry their machinery; source-only-formed requests pitch the telling * feat(skills): companion goes director - ceiling treatment plus blueprint/rule citation discipline * fix(scripts): sandbox npx-leak guard - private npm global prefix keeps npx on the branch CLI * chore(skills): resync manifest hash after formatter pass reflowed general-video tables * fix(skills): recipe freeze reads workflow from BRIEF.md; style_preset records require workflow scope Two holes found by a live companion-run freeze: the agent-supplied --workflow contradicted the run's actual workflow (recipe.json said faceless-explainer, brief-skeleton said general-video), and the style_preset lookup missed because the preference had been recorded under the bare key. - freezeRecipe resolves the workflow from BRIEF.md frontmatter; the flag is a fallback for briefless projects and a contradicting flag is ignored (noted). - recordPreference refuses a bare style_preset — the scoped key is the only writable shape; freeze tolerates legacy bare records via read fallback. - review-loop § 4 / media-use SKILL / brief-format wording follow the machinery. |
||
|
|
cf7c1d7609 |
docs(cli,skills): teach check as the canonical verification gate
Scaffolded projects' npm run check now invokes the single check command instead of chaining lint, validate, and inspect (three Chrome boots become one). The CLI skill, its correctness reference, the entry skill's capability map, README/docs catalog rows, the Mintlify CLI page (new check section, deprecation banner on inspect), template CLAUDE/AGENTS (byte-identical), root CLAUDE/AGENTS, and every creation-workflow skill that taught the old sequence all point at check. snapshot keeps its standalone sections; validate/inspect stay documented as deprecated aliases with their check equivalents. |
||
|
|
d13c96d470 |
fix(skills): force figma.com sources through /figma, not raw MCP tools
Two real incidents this week had agents skip /figma entirely and drive Figma via raw MCP tools (get_metadata/get_screenshot/get_design_context) when a figma.com URL landed inside a creation-workflow skill. Root cause: none of the creation workflows mention Figma at all, and the only routing table that does (/hyperframes) is skipped whenever a workflow is invoked directly rather than through the entry router — which is the common path. Going raw loses real infrastructure the CLI/skill guarantees: sanitizeSvg() before freezing (raw-fetched SVGs are unsanitized), .media/manifest.jsonl provenance (no cache-hit, no version tracking), and brand-token var() binding (colors bake as literals, so a later Figma brand change can't propagate without a full re-import). Added a "figma source" callout to every creation workflow that could plausibly receive a figma.com link (product-launch-video, website-to-video, general-video, motion-graphics, slideshow), plus a defense-in-depth line in /hyperframes's own routing checklist. The fix lives in the workflows themselves so it doesn't depend on the entry router being consulted. Co-Authored-By: Claude Opus <noreply@anthropic.com> |
||
|
|
17b852784b |
feat(skills): mode-first briefs, value-first storyboards, and destination defaults across creation workflows (#2058)
* feat(skills): add brief contract — interaction modes + shared intake fields across workflows New hyperframes-core/references/brief-contract.md, the shared intake contract every creation workflow now runs its brief against: - §1 interaction mode: collaborative (default) vs autonomous, ongoing vs one-time signals, mode set once and carried forward, and a gate taxonomy (preference / checkpoint / quality / routing) — autonomous skips waiting, never verification - §2 field registry: destination→aspect derivation (feed → 1:1, Shorts/TikTok → 9:16, else 16:9), message, angle, length, audience, language, narration — each workflow binds fields as ask or state - §3 question rules: one round with one question per asked field (native question UI mandatory when available, recommended option first with a receipt), never drop a question as inferable, and a mode legend advertised in the intro text instead of asked Wired into the surfaces: - hyperframes router: detect mode at entry, derive aspect from destination instead of stating 16:9 - product-launch-video / pr-to-video / faceless-explainer: ask/state binding tables at Step 0; Step 3/6 checkpoint-gate branches (autonomous posts a heads-up with a preview hint before render) - website-to-video: local mode definition now defers to the contract - music-to-video, general-video, embedded-captions, talking-head-recut, slideshow, motion-graphics: mode semantics wired per gate type - storyboard-format: new optional 'mode' frontmatter key - pr-to-video: length tier is a ceiling, not a floor — a one-headline PR recommends inside the 30–90s sweet spot regardless of diff size * feat(skills): story spine + mode-first brief across creation workflows Story — the reverse-iceberg feedback: - New hyperframes-creative/references/story-spine.md, three rules for the narrated workflows: the hook speaks the viewer's outcome language, the value claim lands by beat 2 (implementation is the footnote of the story, not the spine), and the storyboard is presented as a proposal — 'This video tells [audience] that [message]' plus a per-frame why: drawn from narrativeRole - pr-to-video: feature-reveal reordered promise-first (impact leads, diff/mechanism follow as evidence); hooks ban file/function names; fix-explainer, refactor-walkthrough, changelog unchanged - product-launch-video / faceless-explainer hook rules aligned to the spine; website-to-video's beat summary gains the echo line + why:; general-video points at the spine from its plan step Brief — hardened after live-test drift: - Mode is now the first question (Collaborative recommended vs Autonomous), its own round, skipped when the request carries a signal; autonomous asks nothing further until one final preview-or-render question before render - Step 0 rewritten as a literal two-round question script in each shot-sequence workflow (website-to-video's editorial register, channel-agnostic); brief-contract.md §3 reduced to invariants so the procedure lives in exactly one place * feat(skills): split type minimums by viewing context typography.md: full-screen viewing keeps body 20px / headline 60px; in-feed destinations (X / LinkedIn / Instagram — brief-contract's destination field) scale to body >=32px, headline >=90px, data labels >=24px. First-pass values, to be calibrated against real renders. * feat(skills): storyboard proposal as a table + credits close by default - story-spine § 3: the proposal presents frames as a markdown table (frame · beat · on screen · why) instead of dense paragraphs; the three shot-sequence workflows and website-to-video's beat summary reference the same shape - pr-to-video: the credits close is now the default ending — every PR video ends on a contributors frame (committers by commit count, 1-6 avatars), with no taste judgment; the only skip is when no avatar was fetched, and the user can cut the frame in the proposal * fix(skills): address review nits on the brief/story contracts - embedded-captions: the identity procedure now states both sides of the preference gate inline (user picks; autonomous picks with a stated why) - website-to-video step-2-brief: note that its mode section is the workflow's application of brief-contract.md, not a second definition - brief-contract: resuming a project reads mode from STORYBOARD.md frontmatter — a recorded mode counts as set, closing the write-only gap * docs(skills): add a non-code receipts example to the brief contract Review nit (jrusso1020, #2058): the receipts example in § 3 was PR-video-shaped only. A destination-shaped example joins it so the rule reads as workflow-neutral. |
||
|
|
81884a7495 |
fix(cli,skills): install workflow skills on demand instead of re-pulling the full set (#2012)
* fix(cli,skills): install workflow skills on demand instead of re-pulling the full set Users report every init re-pulls all 21 skills into ~/.agents/skills whenever anything is stale or missing - heavy, noisy, and it re-expands deliberate partial installs. Split the set into two tiers: - core: the /hyperframes router + hyperframes-* domain skills + media-use, which every workflow references structurally. init and bare 'skills update' keep these (plus anything already installed) fresh, and never expand the install. - on demand: the end-user workflow skills (and figma). They install at trigger time via 'skills update <name...>' - positional names are the only way update expands an install: one targeted 'skills add --skill <name>' covering only stale/missing targets, a fast no-op when current, presence-verified after install, exit 1 on unknown names, and a presence-only degrade when GitHub is unreachable. The /hyperframes router now runs 'skills update <workflow>' after routing and before reading the workflow skill, so a routed workflow is guaranteed present even on a machine that only has the core set. Each on-demand skill also opens with the same self-maintenance step (run 'npx hyperframes skills update <name>' silently), so a workflow triggered directly - without the router - still refreshes itself and restores any missing core skill before relying on it. When the manifest is unreachable (offline / rate-limited) the engine degrades honestly instead of claiming success: named runs presence-check the request plus a pinned fallback core list (unit-pinned to skills/) and blind-install whatever is absent; a bare strict update fails loudly so the 'check || update' chain can't pass while everything stays stale; init reports the skipped freshness check. --json emits structured errors on failure paths. skills check still lists every skill, but exits non-zero only for stale installed skills, an incomplete core set, or removed leftovers - workflow skills not yet installed are reported as available on demand. Bare 'hyperframes skills' (and 'skills add --all') remain the explicit full-set installs. Verified end-to-end with a sandboxed $HOME: fresh init installs the 9 core skills only; 'skills update slideshow' adds exactly that skill (no-op on re-run, exit 1 on unknown names); bare update refreshes without expanding; a live Claude Code run routed PR-to-video, executed the router's update step, and the workflow skill appeared before use; and a second live run triggered an installed workflow directly, whose opening maintenance step restored a deliberately removed core skill. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(skills): clarify update-engine contracts + document lazy-install model - skills.ts: note the UpdateSkillsResult.unknown strict-mode contract, verifyInstalled's non-strict (warn-not-throw) intent, and that a partial install stays "refreshed but never expanded" (review nits). - docs/guides/skills.mdx: add a "Keeping skills current" section covering the core-eager / workflow-on-demand model and the skills check|update commands, per the repo's catalog-maintenance rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: kiritowoo <295860553+kiritowoo@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Miao Yang <miao.yang@heygen.com> |
||
|
|
306a291dea |
fix(skills): audit descriptions — trim routing prose, fix stale facts, add missing triggers (#1990)
* fix(skills): audit descriptions — trim routing prose, fix stale facts, add missing triggers Descriptions are the always-loaded routing tier; this audit rebuilds them on one principle: discriminate by input shape, not pipeline internals. - Trim creation-workflow descriptions to positive trigger + nearest-neighbor disambiguation + /hyperframes escape hatch; full routing prose already lives in each skill body's route-confirm block and the router - Codify the workflow-vs-domain split as ownership (owns the end-to-end deliverable vs capability layer pulled in mid-flight) in /hyperframes, and widen "make me a video" framing to deck / composition port - Fix stale facts: embedded-captions identity count (desc 32, body 17 → actual 36 = 10 classic + 26 themed), six→ten visual languages, retired RVM/Standard wording in router details, figma shader transport (MCP → MCP source / native export), keyframes "cursor demos" (no backing content), hyperframes-media scripts/audio.mjs leak - Register missing capabilities: motion-graphics maps category (was in categories/ but absent from its own table, description, and router), asset-fusion + news triggers, slideshow page-to-deck + presenter mode, general-video editing, talking-head-recut 16:9/9:16/4:5 canvas, cli feedback + lambda sites, product demos, mood-brief BGM generation - website-to-video: relabel promo-shaped video types to keep the promo boundary with /product-launch-video; drop headless-Chrome wording - music-to-video: lyric timing via /hyperframes-media transcription or user-supplied lyrics, placed on the beat grid - Sync catalogs in lockstep (CLAUDE.md, AGENTS.md, README, docs/guides/skills.mdx, CLI project templates): add music-to-video + slideshow entries, complete the domain-skill lists, and extend the catalog-maintenance rule to cover AGENTS.md and the templates Validated with a 35-case description-only routing eval: 35/35 both before and after the rewrite (including new maps / asset-fusion / news probes). * fix(skills): post-media-v2 consistency — stale media ref, router figma wording, catalog rows - music-to-video: lyric transcription now routes to /media-use (the retired /hyperframes-media was still referenced) - router capability map: figma row gains the shaders fact (MCP source / native export), matching the SKILL.md source of truth - media-use catalog rows (CLAUDE.md, README, docs/guides/skills.mdx): add image models + captioning, aligning with the v2 description - catalog rule #1: root AGENTS.md carries the workflow list only (it has no domain-skill section) — rule wording now says so |
||
|
|
a7c3cc7d68 |
fix(slideshow): make presenter mode work over Google Meet/Zoom screen share
Fix slideshow presenter mode for screen-share workflows by opening the audience view as a regular noopener tab, preserving audience query construction across fragments, and keeping iframe keyboard forwarding diagnosable. |
||
|
|
a4303137cb |
fix: storyboard-angle review follow-ups (M1 bg-on-clip, B3 slideshow, parser guard, CLI fixes) (#1791)
* fix(skills): storyboard review — bg-on-clip rule, slideshow output, parser parity guard Addresses the storyboard-angle review (jrusso1020): - M1 (invisible text): frame-worker.md (x3) + SKILL.md Step 5 (x3) now require a frame's full-bleed background on a class=clip layer, never the #root / data-composition-id element (the root is clip-gated to its scene window, so a background on it is not a dependable ground and dark text can land on the black host body). The assembler already paints frame.md's canvas onto index #root as the base ground; the per-frame clip rides on top. - B3 (slideshow truncates to slide 1): slideshow/SKILL.md gains an Output section (decks render via 'present'; 'render index.html' captures only the first composition; linear main-line MP4 export is deferred). - Parser drift: vendoredParity.test.ts guards the three vendored storyboard.mjs copies (byte-identical + parse-parity with @hyperframes/core). - skills-manifest.json regenerated for the edited SKILL.md files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(cli): storyboard review — lint, validate help, snapshot, inspect, capture, render Addresses the CLI findings from the storyboard-angle review (jrusso1020): - lint (@hyperframes/lint): accept vendor-prefixed system-font keywords -apple-system / BlinkMacSystemFont so a system stack with a generic fallback no longer trips font_family_without_font_face (+ test). - help: list 'validate' under Project in 'hyperframes --help' (was runnable but undocumented). - snapshot: honor -o/--output (the flag did not exist; output was hardcoded to snapshots/). The dir is resolved once and threaded through capture + contact sheet + Gemini. - snapshot: split font status into loaded / error / unused with a one-line summary; only a real 'error' is reported as FAILED (an unrequested @font-face is 'unused', not a contradiction with 'loaded'). - inspect: suppress text_occluded across a scene-to-scene crossfade (occluder in a different data-composition-id mount while a scene is mid-fade); a same-scene or two-settled-scenes overlap still flags. - inspect: suppress content_overlap between in-flow siblings governed by the same flex/grid container (tight stacks / number lockups are layout slop). - capture: record source resolution (videoWidth/Height) in video-manifest.json alongside the DOM display box; consumers size off the source dims. - render: warn when the target carries a slideshow island (render captures only the first scene, so the MP4 is truncated to slide 1; use 'present'). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
7517f6ac86 |
feat(slideshow): per-slide autoplay (manual-advance, opt-in) (#1708)
* feat(slideshow): per-slide autoplay (manual-advance, opt-in) Adds an opt-in `autoplay` flag to slideshow slides: when the presenter lands on a video slide, its `<video>` plays from the start. The slideshow still holds and never auto-advances — the presenter clicks Next when ready. This covers compositions whose own controls can't be clicked (the player renders the composition pointer-events:none). Plumbing (done, tested): - core: `SlideRef.autoplay?: boolean`, parsed + validated in parseSlideshow (a non-boolean autoplay rejects the manifest); carried through resolve. - controller: optional `PlayerPort.playSceneMedia(sceneId)`, fired only on forward `enterSlide` for autoplay slides (not resume/back/sync, so the audience — which mirrors the presenter's media events — isn't double-driven). - component: `playSceneDocumentMedia` reaches the same-origin composition iframe, finds the scene's `<video>`, and asserts playback; `stopMedia` (already wired on slide change) resets it. An autoplay token cancels a pending start when the slide changes. - tests: controller autoplay behavior + parser flag round-trip/validation (131 player + 22 core slideshow tests pass). KNOWN LIMITATION — runtime media-start needs the player media model (@vance): On current main the clip<->timeline binding from #1601 keeps every clip synced and *paused* to the held timeline frame, which wins against playSceneMedia's play() — so the clip does not actually start on main yet (it does on the pre-#1601 player). The correct fix is a sanctioned "let this clip free-run while the timeline holds" path in the player/runtime media controller. Flagging for Vance to wire the start into the #1601 media model (or rebase onto it) when back. The plumbing above is the stable surface that hook plugs into. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(slideshow): address code-review on per-slide autoplay - guard playSceneDocumentMedia behind resolveMode() !== "audience": the audience mirrors the presenter's media events, so it must not independently drive its own copy of the clip. - drop the per-enter window pointerdown/keydown "gesture retry" listeners, which leaked when muted autoplay succeeded without a gesture. The poll already re-asserts play(), so a gesture within the window is picked up next tick. - stop polling once the clip is advancing across two ticks (was re-asserting play() for the full window even after playback was confirmed). - cancel any in-flight autoplay loop on disconnectedCallback (bump the token). - split the poll into findSceneVideo + stepAutoplay helpers (keeps each small). - fix the enterSlide comment: autoplay fires from enterSlide (next/prev/ goToSlide), not resumeSlide (back/backToMain/syncTo). - parser: isOptionalBoolean type guard instead of a one-off helper; drop `as` assertions in the new controller test. 131 player + 22 core slideshow tests pass; lint/format/typecheck/fallow clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(slideshow): autoplay skill guidance + address review nits Addresses review feedback on #1708: - skill: document per-slide `autoplay` in the slideshow standalone-harness reference — when to use it (video is the slide's primary content, its end is the advance cue) vs not (background/ambient loops, footage talked over), per Vance's guidance, before merge. - play() rejection is no longer blanket-swallowed: AbortError (timeline-sync seek interrupt) and NotAllowedError (gesture-gated autoplay) are expected and ignored; any other rejection is surfaced once via console.warn (Via nit 1). - clarify in the SlideRef.autoplay doc that it plays the scene's FIRST <video> (Via nit 2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
64eaad7d69 |
feat(slideshow): auto-set interactive on inner player (#1712)
* feat(slideshow): auto-set interactive on inner player The slideshow now sets the `interactive` attribute on its inner <hyperframes-player> instances at mount time, so pointer events reach the composition iframe automatically. Removes the agent-compliance burden of having to remember to add `interactive` on every player tag inside a slideshow. Idempotent: an author-supplied `interactive` attribute (any value, including `interactive="false"`) is preserved. A MutationObserver also picks up players inserted dynamically after the initial mount. Standalone player usage outside a slideshow still requires the explicit attribute — that surface is unchanged. Skill guidance at skills/slideshow/SKILL.md updated to reflect the automatic behavior. * docs(slideshow): clarify interactive attribute semantics Per Rames R1 review feedback: the test comment implied `interactive="false"` is an author opt-out, but `:host([interactive])` is presence-matching per HTML boolean-attribute convention — so any value (including "false") enables pointer events at runtime. The slideshow's mechanical wire-up preserves any author-supplied value verbatim for DOM hygiene, not as a runtime opt-out. |
||
|
|
341e65aea2 | fix(slideshow): harden media controls in present decks (#1619) | ||
|
|
f0c4dee705 |
fix(slideshow): present media controls (#1601)
* fix(slideshow): harden media controls in present decks
* refactor(slideshow): clear Fallow audit findings
Decompose flagged high-CRAP functions and extract production-code
duplications so the audit gate clears.
- core/runtime/bridge.ts handler — replace the 14-branch if-chain with a
CONTROL_HANDLERS dispatch table; flash-elements payload handling moves
to its own helper. Behavior preserved (all existing bridge.test.ts
cases hit the same dispatchers via the public installRuntimeControlBridge
API).
- player/slideshow/SlideshowController syncTo — split into
isValidSyncTarget / isCrossSlide / rerootStackTo helpers. The
stopSlideMedia decision and the stack re-rooting are now individually
named; the public method is a 4-line orchestrator.
- cli/commands/validate.ts run — extract emitJsonReport / emitTextReport
so the orchestrator no longer carries the dual JSON/text branches.
Cuts the cyclomatic complexity flagged by fallow after the
shouldIgnoreRequestFailure signature expansion shifted the fingerprint.
- player/hyperframes-player.ts — _setIframeMediaMuted and _stopIframeMedia
shared a `try { iframeDoc = contentDocument } catch { return }` preamble
(clone group 15). Extract _getSameOriginIframeDocument(): Document | null
and have both call sites consume it.
- studio/panels/SlideshowPanel.tsx — the notes controller's debounce-tail
and explicit flush() shared the pending-drain pattern (clone group 16).
Extract a drainPending() closure both call.
- player/hyperframes-player.test.ts — collapse the new stopMedia / muted
tests' repeated Object.defineProperty(iframe, "contentDocument", { get })
shape behind a stubIframeContentDocument helper.
No behavior changes — refactor only. Existing tests cover the affected
paths unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* refactor(validate): split run further; ignore test dup parity
Second Fallow pass surfaced two minor follow-ups after the first cut:
- packages/cli/src/commands/validate.ts run + emitTextReport still
carried minor CRAP findings (43.1 / 37.1, threshold 30). Extract
printValidationResult / formatConsoleEntry / formatTotals /
emitFailureReport so run becomes a try/catch + delegation, well
below the threshold; emitTextReport drops the inline format loops.
- .fallowrc.jsonc duplicates.ignore: add hyperframes-player.test.ts
alongside the existing SlideshowPanel.test.ts entry. Same reasoning
documented there — parallel arrange/act/assert test cases are
intentionally self-contained for readability; collapsing them under
shared fixtures would couple unrelated scenarios (same-origin vs
realm media, audio-locked permutations, seek bridge variants).
No behavior changes — refactor + config-policy parity only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
cc2220e59e |
fix(slideshow): address code-review findings #1580-1584 (#1585)
* fix(slideshow): address code-review findings #1580-1584
- player: bundle @hyperframes/core into the IIFE/global build (noExternal)
- player: resolve audience mode from ?mode=audience URL query, not just attr
- player: event-driven waitForScenes + loud failure when no slides resolve
- player: scope window keydown so Space/Backspace don't hijack the host page
- player: audience mirrors full position (branch + fragment) via syncTo
- player: next() reveals remaining fragments even at slide end; enterBranch ignores empty sequences
- core: harden extractScenes against null/non-object scene entries
- core: strict manifest validation; error on inverted ranges & empty hotspot targets; dedup fragments
- core/lint: accept data-end/timeline-derived scene durations (match runtime)
- core+studio: share ISLAND_TYPE + island regex from @hyperframes/core/slideshow
- studio: SlideList reflects manifest slide order; branch-slide authoring (notes/fragments/hotspots)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(player): slideshow fullscreen + presenter-view rework
- fullscreen toggle in the nav chrome (button + 'F' key); standard Fullscreen
API on the <hyperframes-slideshow> element, icon reflects state
- presenter console: live slide on top, speaker-notes panel below, with the nav
controls shown in-view; Present button hides once presenting (harness)
- audience (viewer) window: chrome reduced to a fullscreen-only control, no nav
- fix: audience / back() / backToMain() mirror stayed frozen on the first frame —
a bare paused seek does not repaint some compositions. resumeSlide now plays a
brief render-nudge (RENDER_NUDGE) past the target so the composition paints,
then onTime pauses at the hold
- refactor: extract reusable buildNavCluster() + wireChromeButtons(); rework
buildPresenterLayout into the bottom notes panel
- example: airbnb-deck presenter-test.html harness (Present button + 'F')
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(player): slideshow no auto-progress + presenter slide fits/pins
- navigation jumps to a static frame instead of auto-playing the timeline:
playTo() seeks to the hold (+ a brief RENDER_NUDGE to repaint) rather than
sustaining playback, so slides hold until the user advances
- presenter view: pin the live slide to the top and confine the player to the
region above the notes panel, so the player CONTAINS the composition — the
full slide stays visible (letterboxed) at any width and re-fits on resize;
its bottom is no longer cut off by the notes panel
- tests: seek targets updated for the render-nudge offset
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(slideshow): presenter nav flash, slide-1 boundary, branch buttons
Three presenter-mode fixes from testing the airbnb deck: (1) navigation flash — seek to the exact target then play forward to repaint, instead of seeking backward (t-0.2) which painted the previous scene at boundaries; split hold into holdTarget (logical) and holdAt (target+nudge, clamped to slide.end). (2) slide-1 boundary — no-fragment slides rest at the slide midpoint, not slide.end. (3) presenter branch buttons — surface hotspots as buttons in the presenter console (the on-slide pill is lost in the letterboxed view). Also extract paintChrome() to dedupe the three chrome-render sites.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(slideshow): stop presenter nav buttons flickering / dropping clicks
The presenter elapsed clock called render() every second, which rebuilt the
entire chrome (innerHTML) including the nav buttons — they flickered and any
click landing mid-rebuild was lost. The 1s tick now updates only the elapsed
text node; the nav buttons are rebuilt only on actual navigation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(slideshow): CSP-safe nav hover, UUID editor ids, manifest version
Addresses review feedback on the split stack:
- CSP: replace the 8 inline onmouseover/onmouseout handlers on the nav
buttons with a [data-hf-nav-cluster] button:hover CSS rule (injected once
per document). No inline event handlers → works under strict CSP.
- IDs: studio sequence/hotspot id generation used Date.now() (sub-ms
collision on rapid clicks) — now crypto.randomUUID().
- Versioning: stamp version on the persisted manifest island (preserving an
existing one); add the optional version field + SLIDESHOW_MANIFEST_VERSION
to the core schema so future schema changes can migrate older islands.
These live on the review-fixes tip (consistent with the stack's fixup-on-tip
model); the touched code belongs to ss-player-b (#1590), ss-studio-a/b
(#1591/#1592), and ss-core (#1580).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(ci): fix format + fallow gates for slideshow stack
- .prettierignore: exclude generated demo compositions (registry/examples/**/*.html)
from oxfmt — large video-pipeline output (GSAP/Three/WebGL), not hand-authored
source. Was failing 'Format' repo-wide (pre-existing on main via #1584).
- .fallowrc: exempt SlideshowPanel.tsx (health/complexity — section fan-out) and
the slideshowPanelHelpers.ts / SlideshowPanel.test.ts parallel-structure clones
(duplicates.ignore). File-level config, not inline comments — inline shifts line
numbers and breaks fallow's inherited-finding fingerprint (per existing rc note).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(slideshow): address PR review + CodeQL findings
- CodeQL #638 (parseSlideshow): complete the regex metachar escape in
slideshowIslandRegex (was missing backslash); add JSDoc on the factory +
lastIndex caveat (reviewer 5a/16).
- CodeQL #639/#640 + review items 13/17: remove registry/examples/airbnb-deck/
presenter-test.html — a generated test harness (postMessage w/o origin check,
proto-pollution) that was scope-creep into a fix PR and a 3rd duplicate island.
Regenerate locally via the scratchpad script when testing.
- Review item 15 (docs drift in skills/slideshow/SKILL.md): lint resolves scenes
by data-composition-id only (not .clip[id]); fragments are valid INCLUSIVE of
[start,end], not 'strictly inside'.
IIFE bundles core confirmed (0 external @hyperframes/core refs in the slideshow
global build). format/lint/fallow green.
* feat(cli): add 'present' command — serve a deck in presenter mode
hyperframes present [dir] starts a lightweight HTTP server, wraps the
composition in <hyperframes-slideshow> with its island inlined, and opens
the browser. A real HTTP origin is required for presenter mode: present()
opens the audience window via window.open(?mode=audience) and the two sync
over BroadcastChannel — neither works from file://.
- New utils/compositionServer.ts factors the server scaffolding shared with
'play' (resolve runtime/player/slideshow bundles, inject runtime, asset
content-types, bind to a free port); play.ts now uses it too.
- Errors clearly if the deck has no slideshow island.
- .fallowrc: exempt the play/present command entrypoints (validation + server
wiring) and the per-command startup/logging block from the complexity /
duplication gates.
Verified end-to-end against registry/examples/airbnb-deck: server serves the
wrapper + assets, the component binds and renders (counter 1 / 11).
* fix(cli): present renders the deck (player sizing + self-driving serve)
Two bugs caused a black slide area:
- The <hyperframes-player> had no positioning, so its iframe collapsed to
zero size — the (absolutely-positioned) chrome showed but the composition
didn't. Add position:absolute; inset:0 (matches demo.html).
- The composition was served with the engine runtime injected, which leaves
its timelines engine-paused (blank). Slideshow decks self-drive their own
timelines (like demo.html / the standalone harness), so serve them raw.
Verified end-to-end on registry/examples/airbnb-deck: cover renders, Next
advances 1/11 -> 2/11 and slide 2 paints.
* fix(cli): present plays slideshow sound effects
The composition (in the player's sandboxed iframe) posts
{ type: 'hf-sfx', name } to the parent on nav, but the iframe is
autoplay-blocked — audio must play in the parent that owns the user gesture.
Add the parent-side hf-sfx handler (the 4 standard clips advance/fragment/
branch-enter/back, served from the deck's sfx/ under /composition/sfx/),
gesture-unlocked and mute-aware, in both presenter and audience windows.
Verified: sfx serve 200 (audio/mpeg) and Next delivers [advance, fragment]
to the parent handler.
* feat(examples): softer mellow slideshow sfx for airbnb-deck
Replace the aggressive percussive pops with gentle sine-tone cues (warm
pitches C5/G4/E5/F4, 12ms attack + exponential decay, lowpassed) — advance/
fragment/branch-enter/back. Much lighter; fragment is the most subtle.
* feat(examples): whoosh + sparkle slideshow sfx for airbnb-deck
Replace the sine-tone cues with airy, designed sounds:
- advance: a soft whoosh (band-limited pink noise, bell-shaped swell)
- back: that whoosh reversed and darkened
- fragment: a light sparkle (staggered high chime blips)
- branch-enter: whoosh + a trailing sparkle (magical entry)
* feat(examples): directional whoosh + richer branch-enter cue (airbnb-deck)
- Going backward a slide now plays the reverse whoosh (back), not advance —
the sfx logic detects nav direction by scene order instead of firing advance
for every scene change.
- branch-enter is now a more interesting magical cue: a faint whoosh + an
ascending C5-E5-G5-C6 chime arpeggio + a trailing sparkle.
Verified: next then prev fires [advance, fragment, back]; no page errors.
* fix(cli): harden present sfx handler + mute-hover affordance (R2 review)
Addresses Rames R2 items 19-21:
- 20: the present audio handler reintroduced the CodeQL classes removed with
presenter-test.html — add an origin check (same-origin composition iframe)
and an own-property guard so a 'name' like __proto__ can't resolve to and
mutate Object.prototype.
- 21: assetContentType used a bare index lookup (ext='__proto__' -> prototype);
guard with Object.hasOwn.
- 19: the CSP hover rule erased the speaker button's muted color; add a
higher-specificity [data-hf-muted] [data-hf-mute]:hover override.
Verified: hf-sfx origin matches location.origin (guard passes), advance/fragment
still fire, deck renders + advances. Items 14/18/22 deferred (minor, pre-existing).
* fix(slideshow): address remaining R2 items (14/18/22) + re-remove harness
- 14: resumeSlide now mirrors enterSlide — a no-fragment slide resumes at its
midpoint (visible-at-rest), not frame-0; fragmented slides still resume to the
saved fragment or slide.start. Added a dedicated test naming the heuristic.
- 18: fullscreenchange swaps only the fullscreen glyph + aria (hoisted SVGs to
module consts) instead of re-rendering the whole chrome.
- 22: .prettierignore lists the specific generated demo compositions instead of
blanket registry/examples/**/*.html, so hand-authored example HTML still formats.
- presenter-test.html: a stray
|
||
|
|
b7a1163753 |
docs(skill): slideshow authoring guidance + standalone harness reference (#1583)
* feat(player): slideshow controller state machine Stack-split from the original ss-player PR (#1581): the SlideshowController state machine (stack-based slide/fragment/branch navigation) and its tests. The <hyperframes-slideshow> web component follows in the next PR. * feat(player): <hyperframes-slideshow> web component + presenter Stack-split from the original ss-player PR (#1581): the <hyperframes-slideshow> custom element (wraps <hyperframes-player>, drives the controller), presenter/audience BroadcastChannel sync, nav chrome, and the player scenes hook. * feat(studio): slideshow manifest persistence + panel helpers Stack-split from the original ss-studio PR (#1582): the data layer — setSlideshowManifest, the useSlideshowPersist hook, and panel helpers. The editor panel UI follows in the next PR. * feat(studio): slideshow branching editor panel UI Stack-split from the original ss-studio PR (#1582): the SlideshowPanel / SlideshowSubPanels editor UI, right-panel wiring, and app integration. * docs(skill): slideshow authoring guidance + standalone harness reference New /slideshow skill (island schema, slide rules, fragments, branching, validation) + a standalone-harness reference doc, and a router entry in the /hyperframes skill. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |