diff --git a/docs/contributing.mdx b/docs/contributing.mdx index 51d544e74c..e4bab5dfed 100644 --- a/docs/contributing.mdx +++ b/docs/contributing.mdx @@ -89,16 +89,10 @@ bun run --filter '*' test | Package | Path | Description | |---------|------|-------------| | [`@hyperframes/core`](/packages/core) | `packages/core` | Types, HTML generation, runtime, linter | -| [`@hyperframes/sdk`](/packages/sdk) | `packages/sdk` | Headless composition editing engine | | [`@hyperframes/engine`](/packages/engine) | `packages/engine` | Seekable page-to-video capture engine | -| [`@hyperframes/player`](/packages/player) | `packages/player` | Embeddable composition player | | [`@hyperframes/producer`](/packages/producer) | `packages/producer` | Full rendering pipeline (capture + encode) | -| [`@hyperframes/shader-transitions`](/packages/shader-transitions) | `packages/shader-transitions` | WebGL shader transition engine | -| [`@hyperframes/aws-lambda`](/packages/aws-lambda) | `packages/aws-lambda` | AWS Lambda distributed rendering adapter | -| [`@hyperframes/gcp-cloud-run`](/packages/gcp-cloud-run) | `packages/gcp-cloud-run` | GCP Cloud Run distributed rendering adapter | | [`@hyperframes/studio`](/packages/studio) | `packages/studio` | Composition editor UI | | [`hyperframes`](/packages/cli) | `packages/cli` | CLI for creating, previewing, and rendering | -| `@hyperframes/sdk-playground` (private) | `packages/sdk-playground` | Local SDK playground app | ## What to Work On diff --git a/docs/docs.json b/docs/docs.json index 7e8ac25996..e0fb7919dc 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -302,13 +302,9 @@ "group": "Packages", "pages": [ "packages/core", - "packages/sdk", "packages/engine", "packages/player", "packages/producer", - "packages/shader-transitions", - "packages/aws-lambda", - "packages/gcp-cloud-run", "packages/studio", "packages/cli" ] diff --git a/docs/introduction.mdx b/docs/introduction.mdx index 671310dd51..72bb514cde 100644 --- a/docs/introduction.mdx +++ b/docs/introduction.mdx @@ -79,27 +79,12 @@ Run `npx hyperframes render --output demo.mp4` and this produces an MP4 with det Types, HTML parsing, runtime, and composition linter — the foundation everything else builds on. - - Headless composition editing engine for agents, custom editors, patch events, and persistence. - Seekable page-to-video capture engine. Loads HTML in headless Chrome and captures frame-by-frame. - - Embeddable web component for playing HyperFrames compositions in any web page. - Full rendering pipeline combining capture and FFmpeg encoding into a single API call. - - WebGL shader transitions for scene-to-scene motion and render-time compositing. - - - AWS Lambda and Step Functions adapter for distributed rendering. - - - Google Cloud Run and Workflows adapter for distributed rendering. - Visual composition editor UI for building and previewing timelines interactively. diff --git a/packages/core/src/parsers/gsapParser.ts b/packages/core/src/parsers/gsapParser.ts index a2f1047128..29f3c5c5cd 100644 --- a/packages/core/src/parsers/gsapParser.ts +++ b/packages/core/src/parsers/gsapParser.ts @@ -1243,13 +1243,17 @@ function applyEaseUpdate(varsArg: AstNode, ease: string): void { } } -function applyUpdatesToCall(call: TweenCallInfo, updates: Partial): void { +function applyUpdatesToCall( + call: TweenCallInfo, + updates: Partial & { easeEach?: string }, +): void { if (updates.properties) reconcileEditableProperties(call.varsArg, updates.properties); if (updates.fromProperties && call.method === "fromTo" && call.fromArg) { reconcileEditableProperties(call.fromArg, updates.fromProperties); } if (updates.duration !== undefined) setVarsKey(call.varsArg, "duration", updates.duration); - if (updates.ease !== undefined) applyEaseUpdate(call.varsArg, updates.ease); + if (updates.easeEach !== undefined) applyEaseUpdate(call.varsArg, updates.easeEach); + else if (updates.ease !== undefined) applyEaseUpdate(call.varsArg, updates.ease); if (updates.position !== undefined) { const posIdx = call.method === "fromTo" ? 3 : 2; call.node.arguments[posIdx] = parseExpr(valueToCode(updates.position)); @@ -1308,7 +1312,7 @@ function buildTweenStatementCode(timelineVar: string, anim: Omit, + updates: Partial & { easeEach?: string }, ): string { let parsed: ParsedGsapAst; try { @@ -1437,6 +1441,7 @@ export function addAnimationWithKeyframesToScript( auto?: boolean; }>, ease?: string, + easeEach?: string, ): { script: string; id: string } { let parsed: ParsedGsapAst; try { @@ -1450,7 +1455,7 @@ export function addAnimationWithKeyframesToScript( } const selector = JSON.stringify(targetSelector); - const kfCode = buildKeyframeObjectCode(keyframes); + const kfCode = buildKeyframeObjectCode(keyframes, easeEach ? { easeEach } : undefined); const varEntries = [`keyframes: ${kfCode}`, `duration: ${valueToCode(duration)}`]; if (ease) varEntries.push(`ease: ${JSON.stringify(ease)}`); const posCode = valueToCode(position); diff --git a/packages/core/src/parsers/gsapWriterAcorn.ts b/packages/core/src/parsers/gsapWriterAcorn.ts index 568bc255ab..c4ef2998aa 100644 --- a/packages/core/src/parsers/gsapWriterAcorn.ts +++ b/packages/core/src/parsers/gsapWriterAcorn.ts @@ -299,7 +299,7 @@ function findInsertionPoint(parsed: ParsedGsapAcornForWrite): number | null { export function updateAnimationInScript( script: string, animationId: string, - updates: Partial, + updates: Partial & { easeEach?: string }, ): string { if (!Object.keys(updates).length) return script; const parsed = parseGsapScriptAcornForWrite(script); @@ -324,13 +324,11 @@ export function updateAnimationInScript( if (updates.duration !== undefined) { upsertProp(ms, call.varsArg, "duration", updates.duration); } - if (updates.ease !== undefined) { - // For a keyframe tween, easing lives at keyframes.easeEach (per-keyframe), - // not a top-level ease. Writing top-level ease would leave the per-keyframe - // easing unchanged — the user's edit would silently do nothing. + const easeValue = updates.easeEach ?? updates.ease; + if (easeValue !== undefined) { const kfNode = keyframesObjectNode(call.varsArg); - if (kfNode) upsertProp(ms, kfNode, "easeEach", updates.ease); - else upsertProp(ms, call.varsArg, "ease", updates.ease); + if (kfNode) upsertProp(ms, kfNode, "easeEach", easeValue); + else upsertProp(ms, call.varsArg, "ease", easeValue); } if (updates.extras) { for (const [key, value] of Object.entries(updates.extras)) { @@ -1338,6 +1336,7 @@ export function addAnimationWithKeyframesToScript( auto?: boolean; }>, ease?: string, + easeEach?: string, ): { script: string; id: string } { const parsed = parseGsapScriptAcornForWrite(script); if (!parsed) return { script, id: "" }; @@ -1345,7 +1344,7 @@ export function addAnimationWithKeyframesToScript( if (insertionPoint === null) return { script, id: "" }; const sorted = [...keyframes].sort((a, b) => a.percentage - b.percentage); - const kfObjCode = buildKeyframeObjectCode(sorted); + const kfObjCode = buildKeyframeObjectCode(sorted, easeEach); const varParts = [`keyframes: ${kfObjCode}`, `duration: ${valueToCode(duration)}`]; if (ease) varParts.push(`ease: ${JSON.stringify(ease)}`); const stmtCode = `${parsed.timelineVar}.to(${JSON.stringify(targetSelector)}, { ${varParts.join(", ")} }, ${valueToCode(position)});`; diff --git a/packages/core/src/runtime/media.ts b/packages/core/src/runtime/media.ts index aa8e1b71da..bb67ab9d9b 100644 --- a/packages/core/src/runtime/media.ts +++ b/packages/core/src/runtime/media.ts @@ -290,33 +290,18 @@ export function syncRuntimeMedia(params: { } const forceSync = !isPlayingVideo && params.forceSync && drift > 0.02; if (hardSync || strictSync || forceSync) { - // Skip the per-tick seek (and the `el.load()` drift-recovery retry - // below) for `