hyperframes 0.8.37 → 0.8.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/cli.js +136 -148
  2. package/dist/docs/examples.md +1 -1
  3. package/dist/fontLocalizeCli.js +18 -95
  4. package/dist/hyperframes-player.global.js +1 -1
  5. package/dist/skills/hyperframes/SKILL.md +1 -1
  6. package/dist/skills/hyperframes/references/brief-contract.md +108 -0
  7. package/dist/skills/hyperframes/references/brief-format.md +72 -0
  8. package/dist/skills/hyperframes/references/capability-menu.md +1 -1
  9. package/dist/skills/hyperframes/references/frame-worker-core.md +80 -0
  10. package/dist/skills/hyperframes/references/intent-interview.md +8 -8
  11. package/dist/skills/hyperframes/references/production-loop.md +26 -0
  12. package/dist/skills/hyperframes/references/review-loop.md +41 -0
  13. package/dist/skills/hyperframes/references/route-briefs.md +1 -1
  14. package/dist/skills/hyperframes/references/script-format.md +52 -0
  15. package/dist/skills/hyperframes/references/storyboard-format.md +130 -0
  16. package/dist/skills/hyperframes/references/subagent-dispatch.md +35 -0
  17. package/dist/skills/hyperframes/scripts/lib/frame-packets-core.mjs +212 -0
  18. package/dist/skills/hyperframes-cli/SKILL.md +9 -8
  19. package/dist/skills/hyperframes-cli/references/init-and-scaffold.md +6 -6
  20. package/dist/skills/hyperframes-cli/references/preview-render.md +6 -3
  21. package/dist/studio/assets/{hyperframes-player-DrGk-lf5.js → hyperframes-player-BBFfad14.js} +1 -1
  22. package/dist/studio/assets/{index-DMsdtB-j.js → index-3XC5KvGR.js} +4 -4
  23. package/dist/studio/assets/{index-xK1YGiPD.js → index-CtYBUtk8.js} +1 -1
  24. package/dist/studio/assets/{index-BtGF7Kl-.js → index-De_8OqJf.js} +1 -1
  25. package/dist/studio/index.html +1 -1
  26. package/dist/studio/index.js +1 -1
  27. package/dist/studio/index.js.map +1 -1
  28. package/dist/templates/blank/index.html +17 -37
  29. package/dist/templates/from-file/index.html +74 -0
  30. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -50,7 +50,7 @@ var VERSION;
50
50
  var init_version = __esm({
51
51
  "src/version.ts"() {
52
52
  "use strict";
53
- VERSION = true ? "0.8.37" : "0.0.0-dev";
53
+ VERSION = true ? "0.8.39" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -79691,12 +79691,24 @@ function parseHtmlStructure(source) {
79691
79691
  const blocks = { script: [], style: [] };
79692
79692
  const openTagsByName = /* @__PURE__ */ new Map();
79693
79693
  const openBlocks = [];
79694
+ let explicitOpenTag = null;
79694
79695
  const parser = new Parser3(
79695
79696
  {
79696
- onopentag(name) {
79697
- const index = parser.startIndex;
79697
+ onopentagname(name) {
79698
+ let tokenStart = parser.endIndex - 1;
79699
+ while (tokenStart >= parser.startIndex && !/[\t\n\f\r />]/.test(source.charAt(tokenStart))) {
79700
+ tokenStart -= 1;
79701
+ }
79702
+ const index = source.indexOf("<", tokenStart + 1);
79703
+ explicitOpenTag = index >= 0 && index < parser.endIndex && source.slice(index + 1, parser.endIndex).toLowerCase() === name ? { index, nameEnd: parser.endIndex } : null;
79704
+ },
79705
+ onopentag(name, _attrs, isImplied) {
79706
+ const origin = !isImplied ? explicitOpenTag : null;
79707
+ const index = origin?.index ?? parser.startIndex;
79708
+ explicitOpenTag = null;
79698
79709
  const raw = source.slice(index, parser.endIndex + 1);
79699
- const attrs2 = raw.slice(name.length + 1, -1).replace(/\s*\/$/, "");
79710
+ const rawAttrs = origin ? source.slice(origin.nameEnd, parser.endIndex) : raw.slice(name.length + 1, -1);
79711
+ const attrs2 = rawAttrs.replace(/\s*\/$/, "");
79700
79712
  const tag = { raw, name, attrs: attrs2, index };
79701
79713
  tags.push(tag);
79702
79714
  const sameNameStack = openTagsByName.get(name) ?? [];
@@ -80970,10 +80982,6 @@ function targetHasNoStableIdentity(selector, identity) {
80970
80982
  if (identity) return false;
80971
80983
  return selector === UNRESOLVED_TARGET || selector === "dwell/hold" || selector.startsWith("proxy \u2192 ");
80972
80984
  }
80973
- function readRegisteredTimelineCompositionId(script) {
80974
- const match = script.match(WINDOW_TIMELINE_ASSIGN_PATTERN);
80975
- return match?.[1] || match?.[2] || null;
80976
- }
80977
80985
  function unwrapRaw(value) {
80978
80986
  if (typeof value === "number") return value;
80979
80987
  if (typeof value !== "string") return void 0;
@@ -81140,21 +81148,6 @@ function makesOverlayVisible(win) {
81140
81148
  if (win.method === "from" && isHiddenGsapState(win.propertyValues)) return true;
81141
81149
  return isVisibleGsapState(win.propertyValues);
81142
81150
  }
81143
- function isSceneBoundaryExit(win) {
81144
- if (win.end <= win.position) return false;
81145
- if (win.method !== "to" && win.method !== "fromTo") return false;
81146
- return isHiddenGsapState(win.propertyValues);
81147
- }
81148
- function isHardKillSet(win, selector, boundary) {
81149
- return win.method === "set" && win.targetSelector === selector && Math.abs(win.position - boundary) <= SCENE_BOUNDARY_EPSILON_SECONDS && isHiddenGsapState(win.propertyValues);
81150
- }
81151
- function hiddenStateLiteral(values) {
81152
- if (zeroValue(values.autoAlpha)) return "{ autoAlpha: 0 }";
81153
- if (zeroValue(values.opacity)) return "{ opacity: 0 }";
81154
- if (stringValue(values.visibility)?.toLowerCase() === "hidden") return '{ visibility: "hidden" }';
81155
- if (stringValue(values.display)?.toLowerCase() === "none") return '{ display: "none" }';
81156
- return "{ opacity: 0 }";
81157
- }
81158
81151
  function findTagEnd2(source, tag) {
81159
81152
  const escapedTagName = tag.name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
81160
81153
  const pattern = new RegExp(`<\\/?${escapedTagName}\\b[^>]*>`, "gi");
@@ -81171,56 +81164,11 @@ function findTagEnd2(source, tag) {
81171
81164
  }
81172
81165
  return source.length;
81173
81166
  }
81174
- function collectCompositionRanges(source, tags) {
81175
- return tags.map((tag) => {
81176
- const id = readDecodedAttr(tag.raw, "data-composition-id");
81177
- if (!id) return null;
81178
- return {
81179
- id,
81180
- start: tag.index,
81181
- end: findTagEnd2(source, tag)
81182
- };
81183
- }).filter((range) => range !== null);
81184
- }
81185
- function findContainingCompositionId(tag, ranges) {
81186
- let match = null;
81187
- for (const range of ranges) {
81188
- if (tag.index < range.start || tag.index >= range.end) continue;
81189
- if (!match || range.start >= match.start) match = range;
81190
- }
81191
- return match?.id || null;
81192
- }
81193
81167
  function getClipTagClasses(tag) {
81194
81168
  const classAttr = readAttr(tag.raw, "class") || "";
81195
81169
  const classes = classAttr.split(/\s+/).filter(Boolean);
81196
81170
  return classes.includes("clip") ? { classAttr, classes } : null;
81197
81171
  }
81198
- function collectClipStartBoundariesByComposition(source, tags) {
81199
- const ranges = collectCompositionRanges(source, tags);
81200
- const boundaries = /* @__PURE__ */ new Map();
81201
- for (const tag of tags) {
81202
- if (!getClipTagClasses(tag)) continue;
81203
- const compositionId = findContainingCompositionId(tag, ranges);
81204
- if (!compositionId) continue;
81205
- const start = numberValue(readAttr(tag.raw, "data-start") ?? void 0);
81206
- if (start == null || start <= 0) continue;
81207
- const compositionBoundaries = boundaries.get(compositionId) ?? /* @__PURE__ */ new Set();
81208
- compositionBoundaries.add(start);
81209
- boundaries.set(compositionId, compositionBoundaries);
81210
- }
81211
- return new Map(
81212
- [...boundaries.entries()].map(([compositionId, values]) => [
81213
- compositionId,
81214
- [...values].sort((a, b2) => a - b2)
81215
- ])
81216
- );
81217
- }
81218
- function findMatchingSceneBoundary(time, boundaries) {
81219
- for (const boundary of boundaries) {
81220
- if (Math.abs(time - boundary) <= SCENE_BOUNDARY_EPSILON_SECONDS) return boundary;
81221
- }
81222
- return null;
81223
- }
81224
81172
  function readStyleProperty(style, property) {
81225
81173
  const escapedProperty = property.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
81226
81174
  const match = style.match(new RegExp(`(?:^|;)\\s*${escapedProperty}\\s*:\\s*([^;]+)`, "i"));
@@ -84143,7 +84091,7 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
84143
84091
  },
84144
84092
  // overlapping_gsap_tweens + gsap_animates_clip_element
84145
84093
  // fallow-ignore-next-line complexity
84146
- async ({ source, tags, scripts, styles, rootCompositionId }) => {
84094
+ async ({ tags, scripts, styles }) => {
84147
84095
  const findings = [];
84148
84096
  const authoredHiddenSelectors = new Set(
84149
84097
  scripts.flatMap((script) => [...extractStandaloneHiddenSelectors(script.content)])
@@ -84164,13 +84112,10 @@ ${allInlineStyles}`.replace(/\/\*[\s\S]*?\*\//g, "");
84164
84112
  if (cls !== "clip") clipClasses.set(`.${cls}`, info);
84165
84113
  }
84166
84114
  }
84167
- const clipStartBoundariesByComposition = collectClipStartBoundariesByComposition(source, tags);
84168
84115
  const styleRules = collectSimpleStyleRules(styles);
84169
84116
  const reportedVisibleOverlayKeys = /* @__PURE__ */ new Set();
84170
84117
  for (const script of scripts) {
84171
- const localTimelineCompId = readRegisteredTimelineCompositionId(script.content);
84172
84118
  const gsapWindows = await cachedExtractGsapWindows(script.content);
84173
- const clipStartBoundaries = clipStartBoundariesByComposition.get(localTimelineCompId || rootCompositionId || "") ?? [];
84174
84119
  for (let i2 = 0; i2 < gsapWindows.length; i2++) {
84175
84120
  const left = gsapWindows[i2];
84176
84121
  if (!left) continue;
@@ -84232,28 +84177,6 @@ ${right.raw}`)
84232
84177
  snippet: truncateSnippet(fromToWindows.map((win) => win.raw).join("\n"))
84233
84178
  });
84234
84179
  }
84235
- if (clipStartBoundaries.length > 0) {
84236
- for (const win of gsapWindows) {
84237
- if (win.targetSelector === UNRESOLVED_TARGET) continue;
84238
- if (!isSceneBoundaryExit(win)) continue;
84239
- const boundary = findMatchingSceneBoundary(win.end, clipStartBoundaries);
84240
- if (boundary == null) continue;
84241
- const hasHardKill = gsapWindows.some(
84242
- (candidate) => isHardKillSet(candidate, win.targetSelector, boundary)
84243
- );
84244
- if (hasHardKill) continue;
84245
- const exitClipInfo = clipIds.get(win.targetSelector) || clipClasses.get(win.targetSelector);
84246
- const fixHint = exitClipInfo ? `"${win.targetSelector}" is a clip element \u2014 the framework already manages its visibility. Wrap the scene's content in an inner non-clip <div>, move the exit tween and the hard kill (\`tl.set("<inner-selector>", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\`) onto that wrapper instead.` : `Add \`tl.set("${win.targetSelector}", ${hiddenStateLiteral(win.propertyValues)}, ${boundary.toFixed(2)})\` after the exit tween.`;
84247
- findings.push({
84248
- code: "gsap_exit_missing_hard_kill",
84249
- severity: "error",
84250
- message: `GSAP exit on "${win.targetSelector}" ends at the ${boundary.toFixed(2)}s clip start boundary without a matching tl.set hard kill. Non-linear seeking can land after the fade and leave stale visibility state.`,
84251
- selector: win.targetSelector,
84252
- fixHint,
84253
- snippet: truncateSnippet(win.raw)
84254
- });
84255
- }
84256
- }
84257
84180
  for (const tag of tags) {
84258
84181
  const selectors = tagSimpleSelectors(tag);
84259
84182
  if (selectors.length === 0) continue;
@@ -106332,7 +106255,7 @@ var init_generators = __esm({
106332
106255
  {
106333
106256
  id: "blank",
106334
106257
  label: "Blank",
106335
- hint: "Empty composition \u2014 just the scaffolding",
106258
+ hint: "Centered Inter stage, paused timeline",
106336
106259
  source: "bundled"
106337
106260
  }
106338
106261
  ];
@@ -107673,12 +107596,12 @@ function loadTranscript(filePath) {
107673
107596
  }
107674
107597
  const parsed = JSON.parse(content);
107675
107598
  const format = detectJsonFormat(parsed);
107676
- const words = format === "whisper-cpp" ? parseWhisperCpp(parsed) : format === "openai" ? parseOpenAI(parsed) : parsed.map((w3) => ({
107677
- id: w3.id ?? "",
107599
+ const words = (format === "whisper-cpp" ? parseWhisperCpp(parsed) : format === "openai" ? parseOpenAI(parsed) : parsed.map((w3) => ({
107600
+ id: w3.id,
107678
107601
  text: w3.text.trim(),
107679
107602
  start: round3(w3.start),
107680
107603
  end: round3(w3.end)
107681
- }));
107604
+ }))).map((w3, i2) => ({ ...w3, id: w3.id || `w${i2}` }));
107682
107605
  return { words, format };
107683
107606
  }
107684
107607
  function stripBeforeOnset(words, onsetSeconds) {
@@ -110272,6 +110195,27 @@ function formatRenderSummaryDetail(input2) {
110272
110195
  const renderTime = `rendered in ${formatDuration(input2.elapsedMs)}`;
110273
110196
  return [middle, renderTime].filter(Boolean).join(" \xB7 ");
110274
110197
  }
110198
+ function resolvePrintedCaptureMode(sessionMode, observabilityCaptureMode) {
110199
+ if (sessionMode && sessionMode !== "unknown") return sessionMode;
110200
+ return observabilityCaptureMode;
110201
+ }
110202
+ function formatRenderPipelineDetail(input2) {
110203
+ const parts = [];
110204
+ if (input2.captureMode) parts.push(`${input2.captureMode} capture`);
110205
+ if (input2.browserGpuMode) parts.push(`${input2.browserGpuMode} gpu`);
110206
+ for (const [key2, stageLabel] of PIPELINE_STAGES) {
110207
+ const ms = input2.stages[key2];
110208
+ if (ms == null) continue;
110209
+ const label2 = key2 === "encodeMs" && input2.streamingEncode ? "encode (during capture)" : stageLabel;
110210
+ parts.push(`${label2} ${formatDuration(ms)}`);
110211
+ }
110212
+ return parts.length > 0 ? parts.join(" \xB7 ") : void 0;
110213
+ }
110214
+ function formatScreenshotFallbackHint(input2) {
110215
+ if (input2.platform !== "linux" || input2.requestedGpuMode !== "auto") return void 0;
110216
+ if (input2.captureMode !== "screenshot" || input2.browserGpuMode !== "software") return void 0;
110217
+ return "Screenshot capture (slower): BeginFrame did not run. Needs chrome-headless-shell and no --resolution upscale. Heavy compositions can stall on software GL.";
110218
+ }
110275
110219
  function label(name, value) {
110276
110220
  const pad4 = 14 - name.length;
110277
110221
  return ` ${c.dim(name)}${" ".repeat(Math.max(1, pad4))}${c.bold(value)}`;
@@ -110287,10 +110231,21 @@ ${c.dim(indented)}`);
110287
110231
  if (suggestion) console.error(` ${c.accent(suggestion)}`);
110288
110232
  console.error();
110289
110233
  }
110234
+ var PIPELINE_STAGES;
110290
110235
  var init_format = __esm({
110291
110236
  "src/ui/format.ts"() {
110292
110237
  "use strict";
110293
110238
  init_colors();
110239
+ PIPELINE_STAGES = [
110240
+ ["compileMs", "compile"],
110241
+ ["videoExtractMs", "extract"],
110242
+ ["audioProcessMs", "audio"],
110243
+ ["browserProbeMs", "probe"],
110244
+ ["captureSetupMs", "setup"],
110245
+ ["captureFrameMs", "capture"],
110246
+ ["encodeMs", "encode"],
110247
+ ["assembleMs", "assemble"]
110248
+ ];
110294
110249
  }
110295
110250
  });
110296
110251
 
@@ -149098,6 +149053,12 @@ import {
149098
149053
  import { resolve as resolve43, basename as basename16, join as join87, dirname as dirname37 } from "path";
149099
149054
  import { fileURLToPath as fileURLToPath9 } from "url";
149100
149055
  import { execFileSync as execFileSync10, spawn as spawn14 } from "child_process";
149056
+ function resolveScaffoldTemplateId(exampleFlag, hasMediaFile) {
149057
+ const example = exampleFlag === "agent" ? "blank" : exampleFlag;
149058
+ if (example && example !== "blank") return example;
149059
+ if (hasMediaFile) return "from-file";
149060
+ return "blank";
149061
+ }
149101
149062
  function resolveVideoDurationSeconds({
149102
149063
  streamDuration,
149103
149064
  frameDuration,
@@ -149534,14 +149495,11 @@ var init_init = __esm({
149534
149495
  ["Scaffold a portrait video", "hyperframes init my-video --resolution portrait"],
149535
149496
  ["Start from an existing video file", "hyperframes init my-video --video clip.mp4"],
149536
149497
  ["Start from an audio file", "hyperframes init my-video --audio track.mp3"],
149537
- ["Scaffold with Tailwind CSS", "hyperframes init my-video --example blank --tailwind"],
149538
- [
149539
- "Non-interactive mode (for CI or AI agents)",
149540
- "hyperframes init my-video --example blank --non-interactive"
149541
- ],
149498
+ ["Scaffold with Tailwind CSS", "hyperframes init my-video --tailwind"],
149499
+ ["Non-interactive mode (for CI or AI agents)", "hyperframes init my-video --non-interactive"],
149542
149500
  [
149543
149501
  "Opt out of the GitHub skills check (CI/tests only)",
149544
- "HYPERFRAMES_SKIP_SKILLS=1 hyperframes init my-video --example blank --non-interactive"
149502
+ "HYPERFRAMES_SKIP_SKILLS=1 hyperframes init my-video --non-interactive"
149545
149503
  ]
149546
149504
  ];
149547
149505
  WEB_CODECS = /* @__PURE__ */ new Set(["h264", "vp8", "vp9", "av1", "theora"]);
@@ -149611,6 +149569,11 @@ var init_init = __esm({
149611
149569
  type: "boolean",
149612
149570
  description: "Disable interactive prompts (for CI/agents)"
149613
149571
  },
149572
+ agent: {
149573
+ type: "boolean",
149574
+ hidden: true,
149575
+ description: "Deprecated alias; default init is the centered blank"
149576
+ },
149614
149577
  "skip-skills": {
149615
149578
  type: "boolean",
149616
149579
  description: "[temporarily ignored] init always checks AI skills against GitHub while the skills.sh registry catches up; set HYPERFRAMES_SKIP_SKILLS=1 to opt out (CI/tests)"
@@ -149658,7 +149621,7 @@ var init_init = __esm({
149658
149621
  const skipSkills = process.env.HYPERFRAMES_SKIP_SKILLS === "1";
149659
149622
  const skipSkillsFlagIgnored = args["skip-skills"] === true && !skipSkills;
149660
149623
  const tailwind = args.tailwind === true;
149661
- const nonInteractive = args["non-interactive"] === true;
149624
+ const nonInteractive = args["non-interactive"] === true || args.agent === true;
149662
149625
  const modelFlag = args.model;
149663
149626
  const languageFlag = args.language;
149664
149627
  const initialTranscriptionModel = initialModelForLanguage(
@@ -149686,15 +149649,7 @@ var init_init = __esm({
149686
149649
  }
149687
149650
  }
149688
149651
  if (!interactive) {
149689
- if (!exampleFlag && !videoFlag && !audioFlag) {
149690
- console.error(
149691
- c.error(
149692
- "Non-interactive init requires --example, --video, or --audio. For an empty starter project, pass --example blank explicitly."
149693
- )
149694
- );
149695
- failCommand();
149696
- }
149697
- const templateId2 = exampleFlag ?? "blank";
149652
+ const templateId2 = resolveScaffoldTemplateId(exampleFlag, Boolean(videoFlag || audioFlag));
149698
149653
  const name2 = args.name ?? "my-video";
149699
149654
  const destDir2 = resolve43(name2);
149700
149655
  if (existsSync71(destDir2) && readdirSync27(destDir2).length > 0) {
@@ -149916,8 +149871,8 @@ var init_init = __esm({
149916
149871
  }
149917
149872
  }
149918
149873
  let templateId;
149919
- if (exampleFlag) {
149920
- templateId = exampleFlag;
149874
+ if (exampleFlag || videoFlag || audioFlag) {
149875
+ templateId = resolveScaffoldTemplateId(exampleFlag, Boolean(videoFlag || audioFlag));
149921
149876
  } else {
149922
149877
  const allTemplates = await resolveTemplateList();
149923
149878
  const defaultTemplate = "blank";
@@ -154352,12 +154307,16 @@ function createRenderPlan(args, now = /* @__PURE__ */ new Date()) {
154352
154307
  failUsage();
154353
154308
  }
154354
154309
  let fps = fpsParse.value;
154355
- const qualityRaw = args.quality ?? "standard";
154356
- if (!VALID_QUALITY.has(qualityRaw)) {
154357
- errorBox("Invalid quality", `Got "${qualityRaw}". Must be draft, standard, or high.`);
154310
+ const qualityRaw = args.quality ?? "looks";
154311
+ if (!(qualityRaw in QUALITY_ALIASES)) {
154312
+ errorBox(
154313
+ "Invalid quality",
154314
+ `Got "${qualityRaw}". Must be draft, looks, delivery, standard, or high.`
154315
+ );
154358
154316
  failUsage();
154359
154317
  }
154360
- const quality = qualityRaw;
154318
+ const qualityAlias = QUALITY_ALIASES[qualityRaw];
154319
+ const quality = qualityAlias.quality;
154361
154320
  const flagSkill = normalizeSkillSlug(args.skill);
154362
154321
  const authoringSkill = flagSkill ?? loadProjectConfig(project.dir).authoringSkill;
154363
154322
  const invalidAuthoringSkill = typeof args.skill === "string" && args.skill.trim() !== "" && !flagSkill ? args.skill : void 0;
@@ -154505,7 +154464,7 @@ function createRenderPlan(args, now = /* @__PURE__ */ new Date()) {
154505
154464
  "Invalid crf",
154506
154465
  `Got "${args.crf}". Must be a non-negative integer.`,
154507
154466
  0
154508
- ) : void 0;
154467
+ ) : format === "mov" || args["video-bitrate"] ? void 0 : "crf" in qualityAlias ? qualityAlias.crf : void 0;
154509
154468
  let vp9CpuUsed;
154510
154469
  if (args["vp9-cpu-used"] != null) {
154511
154470
  const parsed = Number(args["vp9-cpu-used"]);
@@ -154596,7 +154555,7 @@ function resolveBrowserGpuForCli(useDocker, browserGpuArg, envMode = process.env
154596
154555
  if (envMode === "hardware" || envMode === "software" || envMode === "auto") return envMode;
154597
154556
  return "auto";
154598
154557
  }
154599
- var VALID_QUALITY, RENDER_FORMATS, VALID_FORMAT, RENDER_FORMAT_LABEL, FORMAT_EXT;
154558
+ var QUALITY_ALIASES, RENDER_FORMATS, VALID_FORMAT, RENDER_FORMAT_LABEL, FORMAT_EXT;
154600
154559
  var init_plan2 = __esm({
154601
154560
  "src/commands/render/plan.ts"() {
154602
154561
  "use strict";
@@ -154609,7 +154568,13 @@ var init_plan2 = __esm({
154609
154568
  init_skill();
154610
154569
  init_projectConfig();
154611
154570
  init_catalogUsage();
154612
- VALID_QUALITY = /* @__PURE__ */ new Set(["draft", "standard", "high"]);
154571
+ QUALITY_ALIASES = {
154572
+ draft: { quality: "draft" },
154573
+ standard: { quality: "standard" },
154574
+ high: { quality: "high" },
154575
+ looks: { quality: "standard", crf: 16 },
154576
+ delivery: { quality: "high" }
154577
+ };
154613
154578
  RENDER_FORMATS = ["mp4", "webm", "mov", "png-sequence", "gif"];
154614
154579
  VALID_FORMAT = new Set(RENDER_FORMATS);
154615
154580
  RENDER_FORMAT_LABEL = "mp4, webm, mov, png-sequence, or gif";
@@ -156032,6 +155997,9 @@ async function renderLocal(projectDir, outputPath, options) {
156032
155997
  );
156033
155998
  const engineConfig = producer.resolveConfig({
156034
155999
  browserGpuMode: options.browserGpuMode ?? "software",
156000
+ // Local auto opts out of the software-GPU screenshot clamp. Docker and
156001
+ // --no-browser-gpu request software; --resolution supersamples via screenshot.
156002
+ ...options.browserGpuMode === "auto" && options.outputResolution == null && process.env.PRODUCER_FORCE_SCREENSHOT !== "true" ? { forceScreenshot: false } : {},
156035
156003
  ...options.pageNavigationTimeoutMs != null ? { pageNavigationTimeout: options.pageNavigationTimeoutMs } : {},
156036
156004
  ...options.protocolTimeout != null && { protocolTimeout: options.protocolTimeout },
156037
156005
  ...options.playerReadyTimeout != null && { playerReadyTimeout: options.playerReadyTimeout },
@@ -156094,8 +156062,8 @@ async function renderLocal(projectDir, outputPath, options) {
156094
156062
  outputPath,
156095
156063
  elapsed,
156096
156064
  options.quiet,
156097
- job.perfSummary?.compositionDurationSeconds,
156098
- job.perfSummary?.totalFrames
156065
+ job.perfSummary,
156066
+ options.browserGpuMode
156099
156067
  )
156100
156068
  );
156101
156069
  runPostRenderStep(
@@ -156446,37 +156414,57 @@ function trackRenderMetrics(job, elapsedMs, options, docker) {
156446
156414
  ...getMemorySnapshot()
156447
156415
  });
156448
156416
  }
156449
- function printRenderComplete(outputPath, elapsedMs, quiet, outputDurationSeconds, frameCount) {
156450
- if (quiet) return;
156451
- let fileSize = "unknown";
156452
- let isDirectory = false;
156417
+ function readOutputFootprint(outputPath) {
156453
156418
  try {
156454
156419
  const stat3 = statSync31(outputPath);
156455
- isDirectory = stat3.isDirectory();
156456
- if (stat3.isDirectory()) {
156457
- let total = 0;
156458
- for (const entry of readdirSync29(outputPath, { withFileTypes: true })) {
156459
- if (!entry.isFile()) continue;
156460
- try {
156461
- total += statSync31(join97(outputPath, entry.name)).size;
156462
- } catch {
156463
- }
156420
+ if (!stat3.isDirectory()) return { fileSize: formatBytes(stat3.size), isDirectory: false };
156421
+ let total = 0;
156422
+ for (const entry of readdirSync29(outputPath, { withFileTypes: true })) {
156423
+ if (!entry.isFile()) continue;
156424
+ try {
156425
+ total += statSync31(join97(outputPath, entry.name)).size;
156426
+ } catch {
156464
156427
  }
156465
- fileSize = formatBytes(total);
156466
- } else {
156467
- fileSize = formatBytes(stat3.size);
156468
156428
  }
156429
+ return { fileSize: formatBytes(total), isDirectory: true };
156469
156430
  } catch {
156431
+ return { fileSize: "unknown", isDirectory: false };
156470
156432
  }
156433
+ }
156434
+ function printRenderComplete(outputPath, elapsedMs, quiet, perf, requestedGpuMode) {
156435
+ if (quiet) return;
156436
+ const { fileSize, isDirectory } = readOutputFootprint(outputPath);
156471
156437
  const detail = formatRenderSummaryDetail({
156472
156438
  elapsedMs,
156473
- outputDurationSeconds,
156439
+ outputDurationSeconds: perf?.compositionDurationSeconds,
156474
156440
  isDirectory,
156475
- frameCount
156441
+ frameCount: perf?.totalFrames
156476
156442
  });
156477
156443
  console.log("");
156478
156444
  console.log(c.success("\u25C7") + " " + c.accent(outputPath));
156479
156445
  console.log(" " + c.bold(fileSize) + c.dim(" \xB7 " + detail));
156446
+ if (perf) printRenderPipeline(perf, requestedGpuMode);
156447
+ }
156448
+ function printRenderPipeline(perf, requestedGpuMode) {
156449
+ const capture2 = {
156450
+ captureMode: resolvePrintedCaptureMode(
156451
+ perf.drawElement?.mode,
156452
+ perf.observability?.capture.captureMode
156453
+ ),
156454
+ browserGpuMode: perf.observability?.capture.browserGpuMode
156455
+ };
156456
+ const pipeline5 = formatRenderPipelineDetail({
156457
+ ...capture2,
156458
+ streamingEncode: perf.observability?.capture.useStreamingEncode,
156459
+ stages: perf.stages
156460
+ });
156461
+ if (pipeline5) console.log(" " + c.dim(pipeline5));
156462
+ const hint2 = formatScreenshotFallbackHint({
156463
+ ...capture2,
156464
+ requestedGpuMode,
156465
+ platform: process.platform
156466
+ });
156467
+ if (hint2) console.log(" " + c.dim(hint2));
156480
156468
  }
156481
156469
  var examples9, render_default, DOCKER_IMAGE_PREFIX, deParallelRouterUserManaged, deParallelRouterUserManagedResolved, deParallelRouterBreakerTrippedThisProcess;
156482
156470
  var init_render = __esm({
@@ -156583,8 +156571,8 @@ var init_render = __esm({
156583
156571
  quality: {
156584
156572
  type: "string",
156585
156573
  alias: "q",
156586
- description: "Quality: draft, standard, high. MOV always uses the fixed alpha-preserving ProRes 4444 profile.",
156587
- default: "standard"
156574
+ description: "Quality: draft, looks, delivery, or standard/high. looks is the default (CRF 16). delivery is high. MOV always uses the fixed alpha-preserving ProRes 4444 profile.",
156575
+ default: "looks"
156588
156576
  },
156589
156577
  skill: {
156590
156578
  type: "string",
@@ -219968,7 +219956,7 @@ async function streamVideo(url, destPath, asJson) {
219968
219956
  failCommand();
219969
219957
  }
219970
219958
  }
219971
- var VALID_QUALITY2, VALID_FORMAT2, VALID_RESOLUTION, VALID_ASPECT_RATIO, DIRECT_UPLOAD_LIMIT_BYTES, ARCHIVE_DIAGNOSTIC_THRESHOLD_BYTES, LARGEST_FILE_COUNT, FORMAT_EXT2, examples36, render_default2, IDEMPOTENCY_KEY_RE, ASPECT_FALLBACK_HINT;
219959
+ var VALID_QUALITY, VALID_FORMAT2, VALID_RESOLUTION, VALID_ASPECT_RATIO, DIRECT_UPLOAD_LIMIT_BYTES, ARCHIVE_DIAGNOSTIC_THRESHOLD_BYTES, LARGEST_FILE_COUNT, FORMAT_EXT2, examples36, render_default2, IDEMPOTENCY_KEY_RE, ASPECT_FALLBACK_HINT;
219972
219960
  var init_render3 = __esm({
219973
219961
  "src/commands/cloud/render.ts"() {
219974
219962
  "use strict";
@@ -219987,7 +219975,7 @@ var init_render3 = __esm({
219987
219975
  init_parsing();
219988
219976
  init_upload();
219989
219977
  init_statusColor();
219990
- VALID_QUALITY2 = ["draft", "standard", "high"];
219978
+ VALID_QUALITY = ["draft", "standard", "high"];
219991
219979
  VALID_FORMAT2 = ["mp4", "webm", "mov"];
219992
219980
  VALID_RESOLUTION = ["1080p", "4k"];
219993
219981
  VALID_ASPECT_RATIO = ["16:9", "9:16", "1:1"];
@@ -220109,7 +220097,7 @@ var init_render3 = __esm({
220109
220097
  async run({ args }) {
220110
220098
  const asJson = Boolean(args.json);
220111
220099
  const fps = parseIntFlag3(args.fps, { flag: "--fps", min: 1, max: 240 });
220112
- const quality = parseEnumFlag(args.quality, VALID_QUALITY2, { flag: "--quality" });
220100
+ const quality = parseEnumFlag(args.quality, VALID_QUALITY, { flag: "--quality" });
220113
220101
  const format = parseEnumFlag(args.format, VALID_FORMAT2, { flag: "--format" });
220114
220102
  const resolution = parseEnumFlag(args.resolution, VALID_RESOLUTION, {
220115
220103
  flag: "--resolution"
@@ -4,7 +4,7 @@ Built-in templates available via `npx hyperframes init --example <name>`.
4
4
 
5
5
  ## blank
6
6
 
7
- Empty 1920x1080 composition with GSAP timeline wired up. Start from scratch.
7
+ Centered Inter stage, paused GSAP timeline. Default `hyperframes init` scaffold.
8
8
 
9
9
  ## title-card
10
10