hyperframes 0.8.80 → 0.8.82

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 (43) hide show
  1. package/dist/backgroundChecksWorker.js +890 -0
  2. package/dist/cli.js +3682 -2418
  3. package/dist/commands/layout-audit.browser.js +55 -25
  4. package/dist/fontLocalizeCli.js +575 -174
  5. package/dist/hyperframe-runtime.js +53 -53
  6. package/dist/hyperframe.manifest.json +1 -1
  7. package/dist/hyperframe.runtime.iife.js +53 -53
  8. package/dist/hyperframes-player.global.js +3 -3
  9. package/dist/registry/localSemantic.js +7 -3
  10. package/dist/renderSetupWorker.js +62 -32
  11. package/dist/sherpaWorker.js +179 -0
  12. package/dist/skills/hyperframes/SKILL.md +2 -0
  13. package/dist/skills/hyperframes/references/plugin-installation.md +43 -0
  14. package/dist/skills/hyperframes/references/skill-lifecycle.md +2 -0
  15. package/dist/skills/hyperframes/scripts/plugin-cli.mjs +88 -0
  16. package/dist/skills/hyperframes/scripts/plugin-cli.test.mjs +117 -0
  17. package/dist/skills/hyperframes-cli/SKILL.md +2 -0
  18. package/dist/skills/hyperframes-cli/references/upgrade-info-misc.md +1 -0
  19. package/dist/skills/media-use/SKILL.md +2 -0
  20. package/dist/skills/media-use/references/operations.md +7 -5
  21. package/dist/skills/media-use/references/setup-providers.md +9 -9
  22. package/dist/skills/media-use/registry/localSemantic.js +7 -3
  23. package/dist/skills/media-use/scripts/lib/ff-binaries.mjs +37 -0
  24. package/dist/skills/media-use/scripts/lib/probe.mjs +3 -1
  25. package/dist/skills/media-use/scripts/lib/probe.test.mjs +118 -1
  26. package/dist/skills/media-use/scripts/lib/test-home.test.mjs +10 -0
  27. package/dist/skills/media-use/scripts/lib/tts-local-provider.mjs +5 -3
  28. package/dist/skills/media-use/scripts/resolve-plugin.test.mjs +62 -0
  29. package/dist/skills/media-use/scripts/resolve.mjs +45 -19
  30. package/dist/skills/media-use/scripts/transcribe.mjs +39 -25
  31. package/dist/skills/media-use/scripts/transcribe.test.mjs +112 -0
  32. package/dist/skills/registry/localSemantic.js +7 -3
  33. package/dist/studio/assets/{hyperframes-player-CgnoxDoD.js → hyperframes-player-zGIbZhLJ.js} +1 -1
  34. package/dist/studio/assets/{index-J9HXe7mU.js → index-BiLCTunu.js} +1 -1
  35. package/dist/studio/assets/{index-CsG24Jub.js → index-DSazIt7R.js} +198 -198
  36. package/dist/studio/assets/{index-DZGVvqDT.js → index-DStmfnpZ.js} +1 -1
  37. package/dist/studio/index.d.ts +172 -7
  38. package/dist/studio/index.html +1 -1
  39. package/dist/studio/index.js +2567 -2314
  40. package/dist/studio/index.js.map +1 -1
  41. package/dist/templates/_shared/AGENTS.md +5 -1
  42. package/dist/templates/_shared/CLAUDE.md +5 -1
  43. package/package.json +1 -1
@@ -52868,17 +52868,22 @@ function escapeCaseInsensitiveToken(source, token, replacement) {
52868
52868
  function inlineScriptTags(scripts) {
52869
52869
  return scripts.map((source) => `<script>${escapeInlineScriptSource(source)}</script>`).join("\n");
52870
52870
  }
52871
- function findDocumentTag(html, tag) {
52872
- const lowered = lowerAscii(html);
52871
+ function* markupStarts(lowered) {
52873
52872
  const unclosedRawText = /* @__PURE__ */ new Set();
52874
52873
  let cursor = 0;
52875
52874
  while (cursor !== -1) {
52876
52875
  const open = lowered.indexOf("<", cursor);
52877
52876
  if (open === -1)
52878
- return -1;
52877
+ return;
52878
+ yield open;
52879
+ cursor = skipMarkup(lowered, open, unclosedRawText);
52880
+ }
52881
+ }
52882
+ function findDocumentTag(html, tag) {
52883
+ const lowered = lowerAscii(html);
52884
+ for (const open of markupStarts(lowered)) {
52879
52885
  if (isTagAt(lowered, open, tag))
52880
52886
  return open;
52881
- cursor = skipMarkup(lowered, open, unclosedRawText);
52882
52887
  }
52883
52888
  return -1;
52884
52889
  }
@@ -53069,7 +53074,7 @@ function parseApplicationExtension(bytes, offset3) {
53069
53074
  }
53070
53075
  return null;
53071
53076
  }
53072
- function parseAnimatedGifMetadata(bytes) {
53077
+ function parseAnimatedGifMetadata(bytes, onFrame) {
53073
53078
  if (bytes.length < 13)
53074
53079
  return null;
53075
53080
  const signature = readAscii(bytes, 0, 6);
@@ -53085,6 +53090,7 @@ function parseAnimatedGifMetadata(bytes) {
53085
53090
  pos += colorTableByteLength(packed);
53086
53091
  }
53087
53092
  let frameCount = 0;
53093
+ let controlPackedOffset = null;
53088
53094
  const delaysCentiseconds = [];
53089
53095
  let loopCount = null;
53090
53096
  while (pos < bytes.length) {
@@ -53107,6 +53113,7 @@ function parseAnimatedGifMetadata(bytes) {
53107
53113
  if (delay == null)
53108
53114
  return null;
53109
53115
  delaysCentiseconds.push(normalizeDelayCentiseconds(delay));
53116
+ controlPackedOffset = pos + 1;
53110
53117
  pos += 1 + blockSize;
53111
53118
  if (bytes[pos] !== 0)
53112
53119
  return null;
@@ -53122,6 +53129,8 @@ function parseAnimatedGifMetadata(bytes) {
53122
53129
  pos = parsed.nextOffset;
53123
53130
  continue;
53124
53131
  }
53132
+ if (label === PLAIN_TEXT_LABEL)
53133
+ controlPackedOffset = null;
53125
53134
  const next = skipSubBlocks(bytes, pos);
53126
53135
  if (next == null)
53127
53136
  return null;
@@ -53132,8 +53141,11 @@ function parseAnimatedGifMetadata(bytes) {
53132
53141
  if (pos + 9 > bytes.length)
53133
53142
  return null;
53134
53143
  const imagePacked = bytes[pos + 8] ?? 0;
53144
+ const localColorTable = (imagePacked & 128) !== 0;
53145
+ onFrame?.({ controlPackedOffset, localColorTable });
53146
+ controlPackedOffset = null;
53135
53147
  pos += 9;
53136
- if ((imagePacked & 128) !== 0) {
53148
+ if (localColorTable) {
53137
53149
  pos += colorTableByteLength(imagePacked);
53138
53150
  }
53139
53151
  if (pos >= bytes.length)
@@ -53159,11 +53171,59 @@ function parseAnimatedGifMetadata(bytes) {
53159
53171
  animated: frameCount > 1
53160
53172
  };
53161
53173
  }
53162
- var BROWSER_MIN_DELAY_CENTISECONDS;
53174
+ function frameLayouts(bytes) {
53175
+ const frames = [];
53176
+ return parseAnimatedGifMetadata(bytes, (frame) => frames.push(frame)) === null ? null : frames;
53177
+ }
53178
+ function disposalOf(bytes, frame) {
53179
+ return frame.controlPackedOffset === null ? null : (bytes[frame.controlPackedOffset] ?? 0) & DISPOSAL_BITS;
53180
+ }
53181
+ function gifClearsAfterLeavingFrameInPlace(bytes) {
53182
+ const frames = frameLayouts(bytes);
53183
+ if (frames === null)
53184
+ return null;
53185
+ let sawOpaque = false;
53186
+ for (const frame of frames) {
53187
+ const disposal = disposalOf(bytes, frame);
53188
+ if (disposal === LEAVE_IN_PLACE)
53189
+ sawOpaque = true;
53190
+ else if (disposal === DISPOSE_TO_BACKGROUND && sawOpaque)
53191
+ return true;
53192
+ }
53193
+ return false;
53194
+ }
53195
+ function clearGifFramesBeforeNext(bytes) {
53196
+ const frames = frameLayouts(bytes);
53197
+ if (frames === null)
53198
+ return false;
53199
+ const controls = frames.flatMap((f) => f.controlPackedOffset === null ? [] : [f.controlPackedOffset]);
53200
+ const shared = frames.flatMap((f) => f.controlPackedOffset === null || f.localColorTable ? [] : [f.controlPackedOffset]);
53201
+ const hasIndex = (at2) => ((bytes[at2] ?? 0) & HAS_TRANSPARENT_INDEX) !== 0;
53202
+ const source = shared.find(hasIndex);
53203
+ for (const at2 of controls) {
53204
+ bytes[at2] = (bytes[at2] ?? 0) & ~DISPOSAL_BITS | DISPOSE_TO_BACKGROUND;
53205
+ }
53206
+ if (source === void 0)
53207
+ return true;
53208
+ for (const at2 of shared) {
53209
+ if (hasIndex(at2))
53210
+ continue;
53211
+ bytes[at2] = (bytes[at2] ?? 0) | HAS_TRANSPARENT_INDEX;
53212
+ bytes[at2 + TRANSPARENT_INDEX_AFTER_PACKED] = bytes[source + TRANSPARENT_INDEX_AFTER_PACKED] ?? 0;
53213
+ }
53214
+ return true;
53215
+ }
53216
+ var BROWSER_MIN_DELAY_CENTISECONDS, PLAIN_TEXT_LABEL, DISPOSAL_BITS, LEAVE_IN_PLACE, DISPOSE_TO_BACKGROUND, HAS_TRANSPARENT_INDEX, TRANSPARENT_INDEX_AFTER_PACKED;
53163
53217
  var init_gif = __esm({
53164
53218
  "../core/dist/media/gif.js"() {
53165
53219
  "use strict";
53166
53220
  BROWSER_MIN_DELAY_CENTISECONDS = 10;
53221
+ PLAIN_TEXT_LABEL = 1;
53222
+ DISPOSAL_BITS = 28;
53223
+ LEAVE_IN_PLACE = 1 << 2;
53224
+ DISPOSE_TO_BACKGROUND = 2 << 2;
53225
+ HAS_TRANSPARENT_INDEX = 1;
53226
+ TRANSPARENT_INDEX_AFTER_PACKED = 3;
53167
53227
  }
53168
53228
  });
53169
53229
 
@@ -54212,6 +54272,13 @@ var init_safePath = __esm({
54212
54272
  }
54213
54273
  });
54214
54274
 
54275
+ // ../core/dist/projectRule.js
54276
+ var init_projectRule = __esm({
54277
+ "../core/dist/projectRule.js"() {
54278
+ "use strict";
54279
+ }
54280
+ });
54281
+
54215
54282
  // ../core/dist/adapters/gsap.js
54216
54283
  var init_gsap = __esm({
54217
54284
  "../core/dist/adapters/gsap.js"() {
@@ -55334,6 +55401,7 @@ var init_dist2 = __esm({
55334
55401
  init_parityContract();
55335
55402
  init_telemetryRedaction();
55336
55403
  init_safePath();
55404
+ init_projectRule();
55337
55405
  init_gsap();
55338
55406
  init_text2();
55339
55407
  init_renderOutputTimestamp();
@@ -63231,6 +63299,16 @@ var init_evenDimensions = __esm({
63231
63299
  }
63232
63300
  });
63233
63301
 
63302
+ // ../engine/src/utils/sdrCaptureColor.ts
63303
+ var SDR_RGB_TO_BT709_FILTER, SDR_CAPTURE_TO_BT709_FILTER;
63304
+ var init_sdrCaptureColor = __esm({
63305
+ "../engine/src/utils/sdrCaptureColor.ts"() {
63306
+ "use strict";
63307
+ SDR_RGB_TO_BT709_FILTER = "scale=out_color_matrix=bt709:out_range=tv:flags=neighbor";
63308
+ SDR_CAPTURE_TO_BT709_FILTER = `scale=in_color_matrix=bt601:in_range=pc:flags=neighbor,format=gbrp,${SDR_RGB_TO_BT709_FILTER}`;
63309
+ }
63310
+ });
63311
+
63234
63312
  // ../engine/src/utils/runFfmpeg.ts
63235
63313
  import { spawn as spawn2 } from "child_process";
63236
63314
  function isExternalFfmpegInterruption(result) {
@@ -63267,21 +63345,20 @@ ${tail}` : windowsMessage;
63267
63345
  ffmpeg stderr (tail):
63268
63346
  ${tail}` : `FFmpeg exited with code ${exitCode}`;
63269
63347
  }
63270
- async function runFfmpeg(args, opts) {
63271
- const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
63348
+ function spawnFfmpeg(args) {
63272
63349
  const ffmpeg = spawn2(getFfmpegBinary(), args, { windowsHide: true });
63273
63350
  trackChildProcess(ffmpeg);
63274
- const managed = new ManagedChildProcess(ffmpeg, {
63275
- signal: opts?.signal,
63276
- deadlineAtMs: Date.now() + timeout,
63277
- onStderr: opts?.onStderr
63278
- });
63279
- const outcome = await managed.wait();
63351
+ return ffmpeg;
63352
+ }
63353
+ function succeeded(outcome) {
63354
+ return outcome.reason === "exit" && outcome.exitCode === 0;
63355
+ }
63356
+ function toResult(outcome, stderr = outcome.stderr) {
63280
63357
  const result = {
63281
- success: outcome.reason === "exit" && outcome.exitCode === 0,
63358
+ success: succeeded(outcome),
63282
63359
  exitCode: outcome.exitCode,
63283
63360
  signal: outcome.signal,
63284
- stderr: outcome.stderr,
63361
+ stderr,
63285
63362
  durationMs: outcome.durationMs,
63286
63363
  terminationReason: outcome.reason,
63287
63364
  error: outcome.error
@@ -63291,6 +63368,43 @@ async function runFfmpeg(args, opts) {
63291
63368
  }
63292
63369
  return result;
63293
63370
  }
63371
+ async function runFfmpeg(args, opts) {
63372
+ const timeout = opts?.timeout ?? DEFAULT_TIMEOUT;
63373
+ const managed = new ManagedChildProcess(spawnFfmpeg(args), {
63374
+ signal: opts?.signal,
63375
+ deadlineAtMs: Date.now() + timeout,
63376
+ onStderr: opts?.onStderr
63377
+ });
63378
+ return toResult(await managed.wait());
63379
+ }
63380
+ async function runFfmpegPipeline(producerArgs, consumerArgs, opts) {
63381
+ const deadlineAtMs = Date.now() + (opts?.timeout ?? DEFAULT_TIMEOUT);
63382
+ const producer = spawnFfmpeg(producerArgs);
63383
+ const consumer = spawnFfmpeg(consumerArgs);
63384
+ consumer.stdin?.on("error", () => {
63385
+ });
63386
+ for (const event of ["close", "error"]) {
63387
+ consumer.once(event, () => producer.stdout?.destroy());
63388
+ producer.once(event, () => consumer.stdin?.end());
63389
+ }
63390
+ if (consumer.stdin) producer.stdout?.pipe(consumer.stdin);
63391
+ const settleOrder = [];
63392
+ const outcomes = await Promise.all(
63393
+ [producer, consumer].map(
63394
+ (child) => new ManagedChildProcess(child, {
63395
+ signal: opts?.signal,
63396
+ deadlineAtMs,
63397
+ onStderr: opts?.onStderr
63398
+ }).wait().then((outcome) => {
63399
+ settleOrder.push(outcome);
63400
+ return outcome;
63401
+ })
63402
+ )
63403
+ );
63404
+ const reported = settleOrder.find((outcome) => !succeeded(outcome)) ?? outcomes[1];
63405
+ const stderr = [...outcomes.filter((outcome) => outcome !== reported), reported].map((outcome) => outcome.stderr).join("");
63406
+ return toResult(reported, stderr);
63407
+ }
63294
63408
  var FFMPEG_SIGTERM_EXIT_LINE, DEFAULT_TIMEOUT, DEFAULT_STDERR_TAIL_LINES;
63295
63409
  var init_runFfmpeg = __esm({
63296
63410
  "../engine/src/utils/runFfmpeg.ts"() {
@@ -63696,6 +63810,8 @@ async function extractMediaMetadata(filePath) {
63696
63810
  isVFR,
63697
63811
  hasAlpha,
63698
63812
  colorSpace,
63813
+ colorRange: videoStream.color_range,
63814
+ pixelFormat,
63699
63815
  frames
63700
63816
  };
63701
63817
  })();
@@ -64239,23 +64355,20 @@ function buildEncoderArgs(options, inputArgs, outputPath, gpuEncoder = null) {
64239
64355
  "tv"
64240
64356
  );
64241
64357
  }
64358
+ const sdrFilter = options.hdr ? void 0 : SDR_CAPTURE_TO_BT709_FILTER;
64359
+ const captureFilter = sdrFilter ?? "scale=in_range=pc:out_range=tv";
64242
64360
  if (gpuEncoder === "vaapi") {
64243
64361
  const vfIdx = args.indexOf("-vf");
64244
64362
  if (vfIdx !== -1) {
64245
- args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
64363
+ args[vfIdx + 1] = `${captureFilter},${args[vfIdx + 1]}`;
64246
64364
  }
64247
64365
  } else if (shouldUseGpu) {
64248
- const vf = withEvenDimensionPad("", pixelFormat, options.width, options.height);
64366
+ const vf = withEvenDimensionPad(sdrFilter ?? "", pixelFormat, options.width, options.height);
64249
64367
  if (vf) args.push("-vf", vf);
64250
64368
  } else {
64251
64369
  args.push(
64252
64370
  "-vf",
64253
- withEvenDimensionPad(
64254
- "scale=in_range=pc:out_range=tv",
64255
- pixelFormat,
64256
- options.width,
64257
- options.height
64258
- )
64371
+ withEvenDimensionPad(captureFilter, pixelFormat, options.width, options.height)
64259
64372
  );
64260
64373
  }
64261
64374
  args.push("-video_track_timescale", "90000");
@@ -64625,6 +64738,7 @@ var init_chunkEncoder = __esm({
64625
64738
  init_gpuEncoder();
64626
64739
  init_hdr();
64627
64740
  init_evenDimensions();
64741
+ init_sdrCaptureColor();
64628
64742
  init_runFfmpeg();
64629
64743
  init_ffprobe();
64630
64744
  init_dist2();
@@ -64917,25 +65031,15 @@ function buildStreamingArgs(options, outputPath, gpuEncoder = null) {
64917
65031
  "tv"
64918
65032
  );
64919
65033
  }
64920
- if (options.rawInputFormat) {
65034
+ const sdrFilter = options.rawInputFormat ? SDR_RGB_TO_BT709_FILTER : SDR_CAPTURE_TO_BT709_FILTER;
65035
+ if (options.rawInputFormat && options.hdr) {
64921
65036
  } else if (gpuEncoder === "vaapi") {
64922
65037
  const vfIdx = args.indexOf("-vf");
64923
65038
  if (vfIdx !== -1) {
64924
- args[vfIdx + 1] = `scale=in_range=pc:out_range=tv,${args[vfIdx + 1]}`;
65039
+ args[vfIdx + 1] = `${sdrFilter},${args[vfIdx + 1]}`;
64925
65040
  }
64926
- } else if (shouldUseGpu) {
64927
- const vf = withEvenDimensionPad("", pixelFormat, options.width, options.height);
64928
- if (vf) args.push("-vf", vf);
64929
65041
  } else {
64930
- args.push(
64931
- "-vf",
64932
- withEvenDimensionPad(
64933
- "scale=in_range=pc:out_range=tv",
64934
- pixelFormat,
64935
- options.width,
64936
- options.height
64937
- )
64938
- );
65042
+ args.push("-vf", withEvenDimensionPad(sdrFilter, pixelFormat, options.width, options.height));
64939
65043
  }
64940
65044
  args.push("-video_track_timescale", "90000");
64941
65045
  }
@@ -65100,6 +65204,7 @@ var init_streamingEncoder = __esm({
65100
65204
  init_ffmpegBinaries();
65101
65205
  init_hdr();
65102
65206
  init_evenDimensions();
65207
+ init_sdrCaptureColor();
65103
65208
  init_config();
65104
65209
  init_dist2();
65105
65210
  init_vp9Options();
@@ -70877,10 +70982,11 @@ function resolveCompositionDuration(input) {
70877
70982
  reason: pendingClips > 0 ? "every clip's length is still pending" : "no authored duration and no clip with a length"
70878
70983
  };
70879
70984
  }
70880
- var isPositiveFinite;
70985
+ var TIMING_TOLERANCE_SECONDS, isPositiveFinite;
70881
70986
  var init_compositionDuration = __esm({
70882
70987
  "../parsers/dist/compositionDuration.js"() {
70883
70988
  "use strict";
70989
+ TIMING_TOLERANCE_SECONDS = 0.05;
70884
70990
  isPositiveFinite = (value) => value !== null && Number.isFinite(value) && value > 0;
70885
70991
  }
70886
70992
  });
@@ -75560,6 +75666,26 @@ function captionFindings(rows, severity) {
75560
75666
  }
75561
75667
  return findings;
75562
75668
  }
75669
+ function clipsPastRootFindings(root, rows) {
75670
+ if (root.attrs["data-composition-id"] === void 0) return [];
75671
+ const rootDuration = readDataDurationSeconds((name) => root.attrs[name]);
75672
+ if (rootDuration === null) return [];
75673
+ const limit = hundredths(rootDuration);
75674
+ return rows.flatMap((row) => {
75675
+ const { start, end } = readClipTiming({ getAttribute: (name) => row.attrs[name] ?? null });
75676
+ if (start === null || end === null || end <= rootDuration + TIMING_TOLERANCE_SECONDS) return [];
75677
+ return [
75678
+ {
75679
+ code: "clip_ends_past_root_duration",
75680
+ severity: "warning",
75681
+ message: `${describe(row)} runs from ${hundredths(start)}s to ${hundredths(end)}s, past the root composition's data-duration of ${limit}s, so it is cut off in previews, posters and renders.`,
75682
+ elementId: row.attrs.id,
75683
+ fixHint: `Extend the root data-duration to ${hundredths(end)}, or make ${describe(row)} end at or before ${limit}s.`,
75684
+ snippet: truncateSnippet(row.open.raw)
75685
+ }
75686
+ ];
75687
+ });
75688
+ }
75563
75689
  function dedupeKeyFor(finding) {
75564
75690
  return [
75565
75691
  finding.code,
@@ -75628,7 +75754,7 @@ async function lintHyperframeHtml(html, options = {}) {
75628
75754
  timings: { totalMs: performance.now() - startedAt, ...timings }
75629
75755
  };
75630
75756
  }
75631
- var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, callIndexes, INSPECTION_VIEWPORT_CAP, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, ZOOM_DECLARATION, NEGATIVE_Z_INDEX, compositionRules, CLIP_MEDIA_TAGS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, INLINE_TEXT_TAGS, OPAQUE_TAGS, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
75757
+ var import_postcss_selector_parser, COMPOSITION_ID_IN_CSS_PATTERN, TIMELINE_REGISTRY_INIT_PATTERN, TIMELINE_REGISTRY_OBJECT_LITERAL_PATTERN, TIMELINE_REGISTRY_ASSIGN_PATTERN, WINDOW_TIMELINE_ASSIGN_PATTERN, INVALID_SCRIPT_CLOSE_PATTERN, TIMELINE_REGISTRY_KEY_PATTERN, TIMELINE_REGISTRY_OBJECT_OPEN_PATTERN, TIMELINE_REGISTRY_OBJECT_ENTRY_PATTERN, REGEX_ALLOWED_BEFORE, REGEX_ALLOWED_KEYWORDS, WORD_CHAR, CodeContext, VISIBLE_MARKUP_COMMENT_PATTERN, VISIBLE_MARKUP_COMMENT_PROTECTED_BLOCK_PATTERN, coreRules, IMAGE_SRC_EXT, VIDEO_SRC_EXT, AUDIO_SRC_EXT, SRC_KIND_NOUN, mediaRules, AUDIO_GROUP_TIMING_ATTRS, callIndexes, INSPECTION_VIEWPORT_CAP, MAX_COMPOSITION_LINES, MAX_TIMED_ELEMENTS_PER_TRACK, TRACK_DENSITY_EXEMPT_TAGS, CAPTION_CUE_TOKEN, HEAVY_OVERLAY_ELEMENT_COUNT_WARN, HEAVY_OVERLAY_EXEMPT_TAGS, HEAVY_OVERLAY_CSS_PATTERN, INLINE_STYLE_DISPLAY_NONE_PATTERN, ZOOM_DECLARATION, NEGATIVE_Z_INDEX, compositionRules, CLIP_MEDIA_TAGS, SCENE_BOUNDARY_EPSILON_SECONDS, UNRESOLVED_TARGET, gsapWindowsCache, CONFLICTING_TRANSLATE_PROPS, CONFLICTING_SCALE_PROPS, RELATIVE_TWEEN_VALUE, TRANSFORM_SENSITIVE_READ, TRANSFORM_INVARIANT_READ, CALLBACK_MEASUREMENT_PATTERN, NUMBER_METHODS, gsapRules, captionRules, adapterRules, TEXTURE_BASE_CLASS, TEXTURE_CLASS_PREFIX, textureRules, GENERIC_FAMILIES, fontRules, slideshowRules, INLINE_TEXT_TAGS, OPAQUE_TAGS, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, hundredths, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS;
75632
75758
  var init_dist3 = __esm({
75633
75759
  "../lint/dist/index.js"() {
75634
75760
  "use strict";
@@ -75649,6 +75775,9 @@ var init_dist3 = __esm({
75649
75775
  init_composition();
75650
75776
  init_slideshow();
75651
75777
  init_topLevelElements();
75778
+ init_compositionContract();
75779
+ init_mediaDuration();
75780
+ init_compositionDuration();
75652
75781
  init_esm10();
75653
75782
  init_esm10();
75654
75783
  COMPOSITION_ID_IN_CSS_PATTERN = /\[data-composition-id=["']([^"']+)["']\]/g;
@@ -78865,6 +78994,7 @@ ${other.raw}`)
78865
78994
  "data-composition-src"
78866
78995
  ];
78867
78996
  describe = (node) => `<${node.tag}${node.attrs.id ? ` id="${node.attrs.id}"` : ""}>`;
78997
+ hundredths = (seconds) => Math.round(seconds * 100) / 100;
78868
78998
  structureRules = [
78869
78999
  (ctx) => {
78870
79000
  if (ctx.options.isSubComposition) return [];
@@ -78875,7 +79005,8 @@ ${other.raw}`)
78875
79005
  return [
78876
79006
  ...nestedStructureFindings(rows, severity),
78877
79007
  ...missingDurationFindings(rows, severity),
78878
- ...captionFindings(rows, severity)
79008
+ ...captionFindings(rows, severity),
79009
+ ...clipsPastRootFindings(root, rows)
78879
79010
  ];
78880
79011
  }
78881
79012
  ];
@@ -79783,7 +79914,7 @@ var init_extractionCache = __esm({
79783
79914
  init_extractedFrameIndex();
79784
79915
  COMPLETE_SENTINEL = ".hf-complete";
79785
79916
  GC_MARKER = ".hf-last-gc";
79786
- SCHEMA_PREFIX = "hfcache-v4-";
79917
+ SCHEMA_PREFIX = "hfcache-v6-";
79787
79918
  KEY_HEX_CHARS = 16;
79788
79919
  CACHE_GENERATION_PREFIX = "hfcache-v";
79789
79920
  }
@@ -80131,6 +80262,21 @@ function parseImageElements(html) {
80131
80262
  }
80132
80263
  return images;
80133
80264
  }
80265
+ function chromeGuessForUntaggedMatrix(metadata) {
80266
+ const matrix = metadata.colorSpace?.colorSpace;
80267
+ if (matrix && matrix !== "unknown") return [];
80268
+ const hd = metadata.height >= 720 && !CHROME_BT601_UNTAGGED_CODECS.has(metadata.videoCodec);
80269
+ return [`setparams=colorspace=${hd ? "bt709" : "smpte170m"}`];
80270
+ }
80271
+ function restoreSourceColourFilter(metadata) {
80272
+ const tags = [
80273
+ ["range", metadata.colorRange],
80274
+ ["colorspace", metadata.colorSpace?.colorSpace],
80275
+ ["color_primaries", metadata.colorSpace?.colorPrimaries],
80276
+ ["color_trc", metadata.colorSpace?.colorTransfer]
80277
+ ].filter(([, value]) => value && value !== "unknown" && value !== "reserved");
80278
+ return tags.length > 0 ? [`setparams=${tags.map(([key2, value]) => `${key2}=${value}`).join(":")}`] : [];
80279
+ }
80134
80280
  async function extractVideoFramesRange(videoPath, videoId, startTime, duration, options, signal, config, outputDirOverride) {
80135
80281
  const ffmpegProcessTimeout = config?.ffmpegProcessTimeout ?? DEFAULT_CONFIG.ffmpegProcessTimeout;
80136
80282
  const { outputDir, quality = 95 } = options;
@@ -80167,6 +80313,7 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
80167
80313
  const outputPattern = join13(videoOutputDir, framePattern);
80168
80314
  const isHdr = isHdrColorSpace(metadata.colorSpace);
80169
80315
  const isMacOS = process.platform === "darwin";
80316
+ const sampleCfrAtOutputFps = !options.finalFrameOnly && !metadata.isVFR;
80170
80317
  const args = [];
80171
80318
  if (isHdr && isMacOS) {
80172
80319
  args.push("-hwaccel", "videotoolbox");
@@ -80177,14 +80324,19 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
80177
80324
  if (options.finalFrameOnly) {
80178
80325
  args.push("-i", videoPath, "-ss", String(startTime), "-frames:v", "1");
80179
80326
  } else {
80327
+ if (sampleCfrAtOutputFps) args.push("-noaccurate_seek");
80180
80328
  args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
80181
80329
  }
80182
80330
  const vfFilters = [];
80183
80331
  if (isHdr && isMacOS) {
80184
80332
  vfFilters.push("format=nv12");
80185
80333
  }
80186
- if (!options.finalFrameOnly && !metadata.isVFR) {
80187
- vfFilters.push(`fps=${ffmpegFps}`);
80334
+ if (sampleCfrAtOutputFps) {
80335
+ vfFilters.push(
80336
+ "settb=intb/2",
80337
+ "setpts=PTS-lte(TB\\,0.001)",
80338
+ `fps=${ffmpegFps}:start_time=0:round=up`
80339
+ );
80188
80340
  }
80189
80341
  if (options.sdrToHdrTransfer) {
80190
80342
  vfFilters.push(SDR_TO_HDR_COLORSPACE_FILTER);
@@ -80192,14 +80344,37 @@ async function extractVideoFramesRange(videoPath, videoId, startTime, duration,
80192
80344
  if (options.toneMapHdrToSdr && isHdr && !isMacOS) {
80193
80345
  vfFilters.push(HDR_TO_SDR_TONEMAP_FILTER);
80194
80346
  }
80195
- if (vfFilters.length > 0) args.push("-vf", vfFilters.join(","));
80196
- if (!options.finalFrameOnly && metadata.isVFR) {
80197
- args.push("-fps_mode", "cfr", "-r", ffmpegFps);
80347
+ if (!isHdr && !options.sdrToHdrTransfer) {
80348
+ vfFilters.push(...chromeGuessForUntaggedMatrix(metadata), SDR_CANVAS_PASSTHROUGH_FILTER);
80349
+ if (format === "jpg") vfFilters.push(SDR_JPEG_AS_BT601_FULL_RANGE_FILTER);
80350
+ }
80351
+ const encodeArgs = ["-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0"];
80352
+ if (format === "png") encodeArgs.push("-compression_level", "1");
80353
+ encodeArgs.push("-y", outputPattern);
80354
+ const runOptions = { signal, timeout: ffmpegProcessTimeout };
80355
+ const resampleVfrToCfr = !options.finalFrameOnly && metadata.isVFR;
80356
+ if (resampleVfrToCfr) args.push("-fps_mode", "cfr", "-r", ffmpegFps);
80357
+ let processResult;
80358
+ if (resampleVfrToCfr && vfFilters.length > 0 && !isHdr && NUT_RAW_PIXEL_FORMATS.has(metadata.pixelFormat ?? "")) {
80359
+ processResult = await runFfmpegPipeline(
80360
+ [...args, "-an", "-sn", "-dn", "-c:v", "rawvideo", "-f", "nut", "pipe:1"],
80361
+ [
80362
+ "-f",
80363
+ "nut",
80364
+ "-i",
80365
+ "pipe:0",
80366
+ "-vf",
80367
+ [...restoreSourceColourFilter(metadata), ...vfFilters].join(","),
80368
+ "-fps_mode",
80369
+ "passthrough",
80370
+ ...encodeArgs
80371
+ ],
80372
+ runOptions
80373
+ );
80374
+ } else {
80375
+ if (vfFilters.length > 0) args.push("-vf", vfFilters.join(","));
80376
+ processResult = await runFfmpeg([...args, ...encodeArgs], runOptions);
80198
80377
  }
80199
- args.push("-q:v", format === "jpg" ? String(Math.ceil((100 - quality) / 3)) : "0");
80200
- if (format === "png") args.push("-compression_level", "1");
80201
- args.push("-y", outputPattern);
80202
- const processResult = await runFfmpeg(args, { signal, timeout: ffmpegProcessTimeout });
80203
80378
  if (processResult.failureReason === "external_interruption") {
80204
80379
  throw new VideoSourceExtractionError(
80205
80380
  "external_interruption",
@@ -81162,7 +81337,7 @@ function createFrameLookupTable(videos, extracted, fps) {
81162
81337
  }
81163
81338
  return table;
81164
81339
  }
81165
- var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, HDR_TO_SDR_TONEMAP_FILTER, HDR_TO_SDR_TRANSFORM_KEY, VideoSourceExtractionError, TRANSIENT_FFMPEG_SPAWN_CODES, FINAL_FRAME_LOGICAL_DURATION_SECONDS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
81340
+ var VIDEO_FRAME_FORMATS, EXTRACT_CACHE_MIN_AGE_MS, GC_STALENESS_MS, SDR_TO_HDR_COLORSPACE_FILTER, HDR_TO_SDR_TONEMAP_FILTER, HDR_TO_SDR_TRANSFORM_KEY, SDR_CANVAS_PASSTHROUGH_FILTER, SDR_JPEG_AS_BT601_FULL_RANGE_FILTER, VideoSourceExtractionError, CHROME_BT601_UNTAGGED_CODECS, NUT_RAW_PIXEL_FORMATS, TRANSIENT_FFMPEG_SPAWN_CODES, FINAL_FRAME_LOGICAL_DURATION_SECONDS, ALPHA_CAPABLE_CODECS, FrameLookupTable;
81166
81341
  var init_videoFrameExtractor = __esm({
81167
81342
  "../engine/src/services/videoFrameExtractor.ts"() {
81168
81343
  "use strict";
@@ -81185,6 +81360,8 @@ var init_videoFrameExtractor = __esm({
81185
81360
  SDR_TO_HDR_COLORSPACE_FILTER = "colorspace=all=bt2020:iall=bt709:range=tv";
81186
81361
  HDR_TO_SDR_TONEMAP_FILTER = "zscale=t=linear:npl=100,tonemap=hable:desat=0,zscale=p=bt709:t=bt709:m=bt709:r=tv";
81187
81362
  HDR_TO_SDR_TRANSFORM_KEY = "hdr2sdr-hable-bt709";
81363
+ SDR_CANVAS_PASSTHROUGH_FILTER = "setparams=color_primaries=bt709:color_trc=iec61966-2-1";
81364
+ SDR_JPEG_AS_BT601_FULL_RANGE_FILTER = "scale=flags=neighbor,format=gbrp,scale=out_color_matrix=bt601:out_range=pc:flags=neighbor,format=yuv420p";
81188
81365
  VideoSourceExtractionError = class extends Error {
81189
81366
  constructor(kind, retryable, message, diagnostic = message) {
81190
81367
  super(message);
@@ -81198,6 +81375,47 @@ var init_videoFrameExtractor = __esm({
81198
81375
  diagnostic;
81199
81376
  hyperframesVideoSourceExtractionError = true;
81200
81377
  };
81378
+ CHROME_BT601_UNTAGGED_CODECS = /* @__PURE__ */ new Set(["vp9", "av1"]);
81379
+ NUT_RAW_PIXEL_FORMATS = /* @__PURE__ */ new Set([
81380
+ "yuv420p",
81381
+ "yuvj420p",
81382
+ "yuv422p",
81383
+ "yuvj422p",
81384
+ "yuv444p",
81385
+ "yuvj444p",
81386
+ "yuva420p",
81387
+ "yuva444p",
81388
+ "yuv440p",
81389
+ "yuv411p",
81390
+ "yuv410p",
81391
+ "yuva422p",
81392
+ "yuv420p10le",
81393
+ "yuv422p10le",
81394
+ "yuv444p10le",
81395
+ "yuva420p10le",
81396
+ "yuva444p10le",
81397
+ "yuv420p12le",
81398
+ "nv12",
81399
+ "nv21",
81400
+ "yuyv422",
81401
+ "uyvy422",
81402
+ "gray",
81403
+ "gray10le",
81404
+ "ya8",
81405
+ "gbrp",
81406
+ "gbrap",
81407
+ "gbrp10le",
81408
+ "gbrp12le",
81409
+ "gbrap10le",
81410
+ "rgb24",
81411
+ "bgr24",
81412
+ "rgba",
81413
+ "bgra",
81414
+ "argb",
81415
+ "abgr",
81416
+ "rgb48le",
81417
+ "rgba64le"
81418
+ ]);
81201
81419
  TRANSIENT_FFMPEG_SPAWN_CODES = /* @__PURE__ */ new Set(["EAGAIN", "EMFILE", "ENFILE"]);
81202
81420
  FINAL_FRAME_LOGICAL_DURATION_SECONDS = 1e-6;
81203
81421
  ALPHA_CAPABLE_CODECS = /* @__PURE__ */ new Set(["vp9", "vp8", "prores"]);
@@ -88889,11 +89107,11 @@ import {
88889
89107
  existsSync as existsSync33,
88890
89108
  mkdirSync as mkdirSync19,
88891
89109
  mkdtempSync as mkdtempSync11,
88892
- readFileSync as readFileSync15,
89110
+ readFileSync as readFileSync16,
88893
89111
  readdirSync as readdirSync12,
88894
89112
  rmSync as rmSync15,
88895
89113
  statSync as statSync12,
88896
- writeFileSync as writeFileSync11,
89114
+ writeFileSync as writeFileSync12,
88897
89115
  copyFileSync as copyFileSync6,
88898
89116
  appendFileSync
88899
89117
  } from "fs";
@@ -94151,7 +94369,80 @@ function dedupeVariants(variants) {
94151
94369
  init_esm10();
94152
94370
  init_fontData_generated();
94153
94371
  init_fontCompression();
94372
+ import postcss5 from "postcss";
94373
+
94374
+ // ../producer/src/services/authoredGoogleFonts.ts
94375
+ init_esm10();
94154
94376
  import postcss4 from "postcss";
94377
+ function screenStylesheet(media) {
94378
+ return !media?.trim() || media.split(",").some((query2) => /^(all|screen)$/i.test(query2.trim()));
94379
+ }
94380
+ function googleStylesheetFamilies(urlText) {
94381
+ let parsed;
94382
+ try {
94383
+ parsed = new URL(urlText);
94384
+ } catch {
94385
+ return [];
94386
+ }
94387
+ if (parsed.protocol !== "https:" || parsed.hostname !== "fonts.googleapis.com") return [];
94388
+ return parsed.searchParams.getAll("family").flatMap((family) => family.split("|")).map((family) => family.split(":")[0]?.trim().toLowerCase() ?? "").filter(Boolean);
94389
+ }
94390
+ function importUrls(css) {
94391
+ let root;
94392
+ try {
94393
+ root = postcss4.parse(css);
94394
+ } catch {
94395
+ return [];
94396
+ }
94397
+ const urls = [];
94398
+ for (const node of root.nodes) {
94399
+ if (node.type !== "atrule" || node.name.toLowerCase() !== "import") continue;
94400
+ const url = unconditionalImportUrl(node.params);
94401
+ if (url) urls.push(url);
94402
+ }
94403
+ return urls;
94404
+ }
94405
+ function unconditionalImportUrl(params) {
94406
+ const match = /^(?:url\(\s*['"]?([^'")\s]+)['"]?\s*\)|['"]([^'"]+)['"])\s*(.*)$/i.exec(params);
94407
+ if (!match || !screenStylesheet(match[3] ?? null)) return void 0;
94408
+ return match[1] ?? match[2];
94409
+ }
94410
+ function activeStylesheet(element) {
94411
+ if (element.hasAttribute("disabled") || !screenStylesheet(element.getAttribute("media")))
94412
+ return false;
94413
+ const type = element.getAttribute("type")?.trim().toLowerCase();
94414
+ return !type || type === "text/css";
94415
+ }
94416
+ function stylesheetUrls(element) {
94417
+ if (!activeStylesheet(element)) return [];
94418
+ if (element.localName !== "link") return importUrls(element.textContent ?? "");
94419
+ const rel = (element.getAttribute("rel") ?? "").toLowerCase().split(/\s+/);
94420
+ if (!rel.includes("stylesheet") || rel.includes("alternate")) return [];
94421
+ const href = element.getAttribute("href");
94422
+ return href ? [href] : [];
94423
+ }
94424
+ function authoredGoogleFontStylesheets(html) {
94425
+ const byFamily = /* @__PURE__ */ new Map();
94426
+ const { document: document2 } = parseHTML(html);
94427
+ for (const element of document2.querySelectorAll("link, style")) {
94428
+ for (const url of stylesheetUrls(element)) {
94429
+ for (const family of googleStylesheetFamilies(url)) {
94430
+ const previous = byFamily.get(family) ?? [];
94431
+ byFamily.set(family, [...previous.filter((existing) => existing !== url), url]);
94432
+ }
94433
+ }
94434
+ }
94435
+ return byFamily;
94436
+ }
94437
+ function withPageText(url, fontText) {
94438
+ const parsed = new URL(url);
94439
+ if (!fontText || parsed.searchParams.has("text")) return url;
94440
+ const withoutFragment = url.split("#")[0] ?? url;
94441
+ const joiner = parsed.search ? "&" : "?";
94442
+ return `${withoutFragment}${joiner}text=${encodeURIComponent(fontText)}${parsed.hash}`;
94443
+ }
94444
+
94445
+ // ../producer/src/services/deterministicFonts.ts
94155
94446
  var GENERIC_FAMILIES2 = /* @__PURE__ */ new Set([
94156
94447
  "sans-serif",
94157
94448
  "serif",
@@ -94168,6 +94459,13 @@ var GENERIC_FAMILIES2 = /* @__PURE__ */ new Set([
94168
94459
  "-apple-system",
94169
94460
  "blinkmacsystemfont"
94170
94461
  ]);
94462
+ var CSS_WIDE_KEYWORDS = /* @__PURE__ */ new Set([
94463
+ "inherit",
94464
+ "initial",
94465
+ "unset",
94466
+ "revert",
94467
+ "revert-layer"
94468
+ ]);
94171
94469
  function parseFontFamilyValue(value) {
94172
94470
  const pieces = [];
94173
94471
  let start = 0;
@@ -94202,7 +94500,17 @@ function parseFontFamilyValue(value) {
94202
94500
  pieces.push(value.slice(start));
94203
94501
  return pieces.map((piece) => piece.trim().replace(/^['"]/, "").replace(/['"]$/, "").trim()).filter((piece) => piece.length > 0);
94204
94502
  }
94205
- function systemPrimaryReplacement(value, deterministicPrimary) {
94503
+ function systemPrimaryReplacement(value, deterministicPrimary, customProperties) {
94504
+ const variable = primaryCssVariable(value);
94505
+ if (variable) {
94506
+ if (customProperties.get(variable.name) || variable.fallback === null) return null;
94507
+ const fallback = systemPrimaryReplacement(
94508
+ variable.fallback,
94509
+ deterministicPrimary,
94510
+ customProperties
94511
+ );
94512
+ return fallback ? `var(${variable.name}, ${fallback})${variable.rest}` : null;
94513
+ }
94206
94514
  const families = parseFontFamilyValue(value);
94207
94515
  if (families.length === 0) return null;
94208
94516
  if (!GENERIC_FAMILIES2.has(normalizeFamilyName(families[0]))) return null;
@@ -94210,7 +94518,7 @@ function systemPrimaryReplacement(value, deterministicPrimary) {
94210
94518
  }
94211
94519
  function parseCssRoot(css) {
94212
94520
  try {
94213
- return postcss4.parse(css);
94521
+ return postcss5.parse(css);
94214
94522
  } catch {
94215
94523
  return null;
94216
94524
  }
@@ -94219,11 +94527,11 @@ function isFontFaceDeclaration(decl) {
94219
94527
  const parent = decl.parent;
94220
94528
  return parent?.type === "atrule" && parent.name.toLowerCase() === "font-face";
94221
94529
  }
94222
- function normalizeCssDeclarations(root, deterministicPrimary) {
94530
+ function normalizeCssDeclarations(root, replacePrimary) {
94223
94531
  let changed = false;
94224
94532
  root.walkDecls((decl) => {
94225
94533
  if (decl.prop.startsWith("--")) {
94226
- const replacement2 = systemPrimaryReplacement(decl.value, deterministicPrimary);
94534
+ const replacement2 = replacePrimary(decl.value);
94227
94535
  if (!replacement2) return;
94228
94536
  decl.value = replacement2;
94229
94537
  changed = true;
@@ -94233,50 +94541,52 @@ function normalizeCssDeclarations(root, deterministicPrimary) {
94233
94541
  if (isFontFaceDeclaration(decl)) {
94234
94542
  return;
94235
94543
  }
94236
- const replacement = systemPrimaryReplacement(decl.value, deterministicPrimary);
94544
+ const replacement = replacePrimary(decl.value);
94237
94545
  if (!replacement) return;
94238
94546
  decl.value = replacement;
94239
94547
  changed = true;
94240
94548
  });
94241
94549
  return changed;
94242
94550
  }
94243
- function normalizeCssFontFamilyDeclarations(css, deterministicPrimary) {
94551
+ function normalizeCssFontFamilyDeclarations(css, replacePrimary) {
94244
94552
  const root = parseCssRoot(css);
94245
94553
  if (!root) return css;
94246
- const changed = normalizeCssDeclarations(root, deterministicPrimary);
94554
+ const changed = normalizeCssDeclarations(root, replacePrimary);
94247
94555
  return changed ? root.toString() : css;
94248
94556
  }
94249
- function normalizeInlineStyleAttribute(style, deterministicPrimary) {
94557
+ function normalizeInlineStyleAttribute(style, replacePrimary) {
94250
94558
  const root = parseCssRoot(`*{${style}}`);
94251
94559
  if (!root) return style;
94252
94560
  const rule = root.first;
94253
94561
  if (rule?.type !== "rule") return style;
94254
94562
  const before2 = rule.toString();
94255
- normalizeCssDeclarations(root, deterministicPrimary);
94563
+ normalizeCssDeclarations(root, replacePrimary);
94256
94564
  if (rule.toString() === before2) return style;
94257
94565
  const serialized = (rule.nodes ?? []).map((node) => node.toString()).join("; ");
94258
94566
  return serialized.endsWith(";") ? serialized : `${serialized};`;
94259
94567
  }
94260
94568
  function normalizeSystemFontPrimaryFamilies(html, deterministicPrimary = "Inter") {
94261
94569
  const { document: document2 } = parseHTML(html);
94570
+ const customProperties = collectFontFamilyCustomProperties(html);
94571
+ const replacePrimary = (value) => systemPrimaryReplacement(value, deterministicPrimary, customProperties);
94262
94572
  let changed = false;
94263
94573
  for (const styleEl of Array.from(document2.querySelectorAll("style"))) {
94264
94574
  const current2 = styleEl.textContent ?? "";
94265
- const next = normalizeCssFontFamilyDeclarations(current2, deterministicPrimary);
94575
+ const next = normalizeCssFontFamilyDeclarations(current2, replacePrimary);
94266
94576
  if (next === current2) continue;
94267
94577
  styleEl.textContent = next;
94268
94578
  changed = true;
94269
94579
  }
94270
94580
  for (const el of Array.from(document2.querySelectorAll("[style]"))) {
94271
94581
  const current2 = el.getAttribute("style") ?? "";
94272
- const next = normalizeInlineStyleAttribute(current2, deterministicPrimary);
94582
+ const next = normalizeInlineStyleAttribute(current2, replacePrimary);
94273
94583
  if (next === current2) continue;
94274
94584
  el.setAttribute("style", next);
94275
94585
  changed = true;
94276
94586
  }
94277
94587
  for (const el of Array.from(document2.querySelectorAll("[data-font-family]"))) {
94278
94588
  const current2 = el.getAttribute("data-font-family") ?? "";
94279
- const next = systemPrimaryReplacement(current2, deterministicPrimary);
94589
+ const next = replacePrimary(current2);
94280
94590
  if (!next) continue;
94281
94591
  el.setAttribute("data-font-family", next);
94282
94592
  changed = true;
@@ -94326,7 +94636,7 @@ function collectFontFamilyCustomProperties(html) {
94326
94636
  }
94327
94637
  return customProperties;
94328
94638
  }
94329
- function primaryCssVariableName(value) {
94639
+ function primaryCssVariable(value) {
94330
94640
  const trimmed = value.trim();
94331
94641
  if (!trimmed.toLowerCase().startsWith("var(")) return null;
94332
94642
  let depth = 0;
@@ -94339,21 +94649,35 @@ function primaryCssVariableName(value) {
94339
94649
  if (char !== ")") continue;
94340
94650
  depth -= 1;
94341
94651
  if (depth !== 0) continue;
94342
- const varExpression = trimmed.slice(0, index + 1);
94343
- const inner = varExpression.slice(4, -1).trim();
94652
+ const inner = trimmed.slice(4, index).trim();
94344
94653
  const commaIndex = inner.indexOf(",");
94345
- const variableName = (commaIndex === -1 ? inner : inner.slice(0, commaIndex)).trim();
94346
- return /^--[A-Za-z0-9_-]+$/.test(variableName) ? variableName : null;
94654
+ const name = (commaIndex === -1 ? inner : inner.slice(0, commaIndex)).trim();
94655
+ if (!/^--[A-Za-z0-9_-]+$/.test(name)) return null;
94656
+ return {
94657
+ name,
94658
+ fallback: commaIndex === -1 ? null : inner.slice(commaIndex + 1),
94659
+ rest: trimmed.slice(index + 1)
94660
+ };
94347
94661
  }
94348
94662
  return null;
94349
94663
  }
94350
- function resolveFontFamilyDeclarationFamilies(declaration, customProperties) {
94664
+ var MAX_CSS_VARIABLE_DEPTH = 8;
94665
+ function resolveDeclaredFamilies(declaration, customProperties, optional = false, depth = 0) {
94666
+ const tag = (families2) => families2.map((family) => ({ family, optional }));
94351
94667
  const families = parseFontFamilyValue(declaration);
94352
- const variableName = primaryCssVariableName(declaration);
94353
- if (!variableName) return families;
94354
- const resolved = customProperties.get(variableName);
94355
- if (!resolved) return families;
94356
- return [...parseFontFamilyValue(resolved), ...families.slice(1)];
94668
+ const variable = depth < MAX_CSS_VARIABLE_DEPTH ? primaryCssVariable(declaration) : null;
94669
+ if (!variable) return tag(families);
94670
+ const resolved = customProperties.get(variable.name);
94671
+ const source = resolved || variable.fallback;
94672
+ if (source === null) return tag(families);
94673
+ const primary = resolveDeclaredFamilies(
94674
+ source,
94675
+ customProperties,
94676
+ !resolved || depth > 0,
94677
+ depth + 1
94678
+ );
94679
+ if (primary.length === 0) return [];
94680
+ return [...primary, ...tag(families.slice(1))];
94357
94681
  }
94358
94682
  function* iterateFontFamilyDeclarations(html) {
94359
94683
  const { document: document2 } = parseHTML(html);
@@ -94478,18 +94802,22 @@ function extractExistingFontFaces(html) {
94478
94802
  }
94479
94803
  return families;
94480
94804
  }
94805
+ function isFetchableFamilyName(normalized2) {
94806
+ if (!normalized2 || normalized2.startsWith("var(")) return false;
94807
+ return !GENERIC_FAMILIES2.has(normalized2) && !CSS_WIDE_KEYWORDS.has(normalized2);
94808
+ }
94481
94809
  function extractRequestedFontFamilies(html) {
94482
94810
  const requested = /* @__PURE__ */ new Map();
94483
94811
  const customProperties = collectFontFamilyCustomProperties(html);
94484
94812
  for (const { declaration } of iterateFontFamilyDeclarations(html)) {
94485
- for (const originalCase of resolveFontFamilyDeclarationFamilies(
94486
- declaration,
94487
- customProperties
94488
- )) {
94489
- const normalized2 = originalCase.toLowerCase();
94490
- if (!normalized2 || GENERIC_FAMILIES2.has(normalized2)) continue;
94491
- if (normalized2.startsWith("var(")) continue;
94492
- if (!requested.has(normalized2)) requested.set(normalized2, originalCase);
94813
+ for (const { family, optional } of resolveDeclaredFamilies(declaration, customProperties)) {
94814
+ const normalized2 = family.toLowerCase();
94815
+ if (!isFetchableFamilyName(normalized2)) continue;
94816
+ const seen = requested.get(normalized2);
94817
+ requested.set(normalized2, {
94818
+ family: seen?.family ?? family,
94819
+ optional: (seen?.optional ?? true) && optional
94820
+ });
94493
94821
  }
94494
94822
  }
94495
94823
  return requested;
@@ -94567,7 +94895,10 @@ function partitionWeightRuns(faces, coveredWeights) {
94567
94895
  async function buildFontFaceCss(requestedFamilies, options, fontText) {
94568
94896
  const rules = [];
94569
94897
  const unresolved = [];
94570
- for (const [normalizedFamily, originalCaseFamily] of requestedFamilies) {
94898
+ const requiredFirst = [...requestedFamilies].sort(
94899
+ ([, a], [, b]) => Number(a.optional) - Number(b.optional)
94900
+ );
94901
+ for (const [normalizedFamily, { family: originalCaseFamily, optional }] of requiredFirst) {
94571
94902
  const canonicalKey = FONT_ALIASES[normalizedFamily];
94572
94903
  if (canonicalKey) {
94573
94904
  const canonical = CANONICAL_FONTS[canonicalKey];
@@ -94589,7 +94920,7 @@ async function buildFontFaceCss(requestedFamilies, options, fontText) {
94589
94920
  coveredWeights.add(coverageKey(face.weight, style));
94590
94921
  }
94591
94922
  const canonicalFamily = resolveAliasDisplayName(normalizedFamily);
94592
- const googleFaces2 = canonicalFamily ? await fetchGoogleFont(canonicalFamily, options, fontText) : [];
94923
+ const googleFaces2 = canonicalFamily ? await fetchFamilyFaces(canonicalFamily, optional, options, fontText) : [];
94593
94924
  const supplementary = googleFaces2.filter(
94594
94925
  (face) => !coveredWeights.has(coverageKey(face.weight, face.style)) || !isBundledSubsetRange(face.unicodeRange)
94595
94926
  );
@@ -94615,7 +94946,7 @@ async function buildFontFaceCss(requestedFamilies, options, fontText) {
94615
94946
  rules.push(...bundledRules);
94616
94947
  continue;
94617
94948
  }
94618
- const googleFaces = await fetchGoogleFont(originalCaseFamily, options, fontText);
94949
+ const googleFaces = await fetchFamilyFaces(originalCaseFamily, optional, options, fontText);
94619
94950
  if (googleFaces.length > 0) {
94620
94951
  for (const face of googleFaces) {
94621
94952
  rules.push(
@@ -94630,7 +94961,7 @@ async function buildFontFaceCss(requestedFamilies, options, fontText) {
94630
94961
  }
94631
94962
  continue;
94632
94963
  }
94633
- if (options.allowSystemFontCapture) {
94964
+ if (options.allowSystemFontCapture && !pageNamedThisFile(originalCaseFamily, options)) {
94634
94965
  const variants = locateSystemFontVariants(originalCaseFamily);
94635
94966
  if (variants.length > 0) {
94636
94967
  let totalBytes = 0;
@@ -94880,7 +95211,8 @@ function raceAgainstAbort(promise, signal) {
94880
95211
  });
94881
95212
  }
94882
95213
  function fetchGoogleFontCss(url, familyName, options) {
94883
- const key2 = `${options.failClosedFontFetch ? "closed" : "open"}:${url}`;
95214
+ const mode = options.failClosedFontFetch ? `closed${options.retryPolicy.maxAttempts}` : "open";
95215
+ const key2 = `${mode}:${url}`;
94884
95216
  let shared = googleFontCssCache.get(key2);
94885
95217
  if (!shared) {
94886
95218
  shared = fetchFontResource(
@@ -94900,35 +95232,66 @@ function fetchGoogleFontCss(url, familyName, options) {
94900
95232
  }
94901
95233
  return raceAgainstAbort(shared, options.abortSignal);
94902
95234
  }
95235
+ function pageNamedThisFile(familyName, options) {
95236
+ return options.authoredStylesheets.has(normalizeFamilyName(familyName.replace(/\+/g, " ")));
95237
+ }
95238
+ function declaredFaceFamily(block) {
95239
+ const found = /font-family:\s*['"]([^'"]+)['"]/i.exec(block);
95240
+ return found?.[1];
95241
+ }
94903
95242
  async function fetchGoogleFont(familyName, options, fontText) {
94904
- const slug = fontSlug(familyName);
94905
95243
  const googleFamilyName = familyName.replace(/\+/g, " ");
94906
95244
  const encodedFamily = encodeURIComponent(googleFamilyName);
94907
95245
  const textParam = fontText ? `&text=${encodeURIComponent(fontText)}` : "";
94908
- const url = `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700${textParam}`;
94909
- let cssText;
95246
+ const normalizedFamily = normalizeFamilyName(googleFamilyName);
95247
+ const authoredStylesheet = FONT_ALIASES[normalizedFamily] ? void 0 : options.authoredStylesheets.get(normalizedFamily);
95248
+ const urls = authoredStylesheet ? authoredStylesheet.map((url) => withPageText(url, fontText)) : [
95249
+ `https://fonts.googleapis.com/css2?family=${encodedFamily}:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,700${textParam}`
95250
+ ];
95251
+ const faces = [];
95252
+ for (const url of urls) {
95253
+ faces.push(...await fetchGoogleFontStylesheet(googleFamilyName, url, options));
95254
+ }
95255
+ return faces;
95256
+ }
95257
+ async function readGoogleFontStylesheet(familyName, url, options) {
94910
95258
  try {
94911
95259
  const cssResult = await fetchGoogleFontCss(url, familyName, options);
94912
95260
  if (!cssResult.ok) {
94913
- return [];
95261
+ return null;
94914
95262
  }
94915
- cssText = cssResult.body;
95263
+ return cssResult.body;
94916
95264
  } catch (err) {
94917
95265
  throwIfCallerAborted(options.abortSignal);
94918
95266
  if (err instanceof FontFetchError) throw err;
94919
95267
  if (options.failClosedFontFetch) {
94920
95268
  throw fontFetchError(familyName, url, "Google Fonts CSS", { error: err });
94921
95269
  }
94922
- return [];
95270
+ return null;
94923
95271
  }
94924
- const faceRegex = /@font-face\s*\{[^}]*font-style:\s*(normal|italic)[^}]*font-weight:\s*(\d+)[^}]*src:\s*url\(([^)]+)\)\s*format\(['"]woff2['"]\)(?:[^}]*?unicode-range:\s*([^;}]+))?[^}]*\}/gi;
95272
+ }
95273
+ function parseGoogleFontSource(match, familyName) {
95274
+ const declared = declaredFaceFamily(match[0]);
95275
+ if (declared && normalizeFamilyName(declared) !== normalizeFamilyName(familyName)) return null;
95276
+ const url = match[3];
95277
+ if (!url) return null;
95278
+ return {
95279
+ style: match[1] || "normal",
95280
+ weight: (match[2] || "400").replace(/\s+/g, " "),
95281
+ url,
95282
+ unicodeRange: match[4]?.trim() || void 0
95283
+ };
95284
+ }
95285
+ async function fetchGoogleFontStylesheet(familyName, url, options) {
95286
+ const slug = fontSlug(familyName);
95287
+ const cssText = await readGoogleFontStylesheet(familyName, url, options);
95288
+ if (!cssText) return [];
95289
+ const faceRegex = /@font-face\s*\{[^}]*font-style:\s*(normal|italic|oblique(?:\s+-?\d+(?:\.\d+)?deg){0,2})\s*;[^}]*font-weight:\s*(\d+(?:\s+\d+)?)[^}]*src:\s*url\(([^)]+)\)\s*format\(['"]woff2['"]\)(?:[^}]*?unicode-range:\s*([^;}]+))?[^}]*\}/gi;
94925
95290
  const faces = [];
94926
95291
  for (const match of cssText.matchAll(faceRegex)) {
94927
- const style = match[1] || "normal";
94928
- const weight = match[2] || "400";
94929
- const woff2Url = match[3] || "";
94930
- const unicodeRange = match[4]?.trim() || void 0;
94931
- if (!woff2Url) continue;
95292
+ const source = parseGoogleFontSource(match, familyName);
95293
+ if (!source) continue;
95294
+ const { weight, style, unicodeRange, url: woff2Url } = source;
94932
95295
  const cachePath = cachedWoff2Path(slug, weight, style, subsetToken(woff2Url));
94933
95296
  const dataUri = await ensureWoff2DataUri(
94934
95297
  cachePath,
@@ -94947,6 +95310,23 @@ async function fetchGoogleFont(familyName, options, fontText) {
94947
95310
  }
94948
95311
  return faces;
94949
95312
  }
95313
+ async function fetchFamilyFaces(familyName, optional, options, fontText) {
95314
+ if (!optional) return fetchGoogleFont(familyName, options, fontText);
95315
+ const maxAttempts = Math.min(options.retryPolicy.maxAttempts, 2);
95316
+ try {
95317
+ return await fetchGoogleFont(
95318
+ familyName,
95319
+ { ...options, retryPolicy: { ...options.retryPolicy, maxAttempts } },
95320
+ fontText
95321
+ );
95322
+ } catch (err) {
95323
+ if (!(err instanceof FontFetchUnavailableError)) throw err;
95324
+ defaultLogger.warn(
95325
+ `[Compiler] Optional font "${familyName}" is unavailable, rendering its fallback: ${err.message}`
95326
+ );
95327
+ return [];
95328
+ }
95329
+ }
94950
95330
  var GOOGLE_FONTS_TEXT_MAX_ENCODED_LENGTH = 1700;
94951
95331
  var SMALL_TO_FULL_KANA = /* @__PURE__ */ new Map([
94952
95332
  ["\u3041", "\u3042"],
@@ -95090,14 +95470,15 @@ async function injectDeterministicFontFaces(html, options = {}) {
95090
95470
  allowSystemFontCapture,
95091
95471
  abortSignal: options.abortSignal,
95092
95472
  retryPolicy,
95093
- retryDeadlineMs: Date.now() + retryPolicy.maxElapsedMs
95473
+ retryDeadlineMs: Date.now() + retryPolicy.maxElapsedMs,
95474
+ authoredStylesheets: authoredGoogleFontStylesheets(html)
95094
95475
  };
95095
95476
  const existingFaces = extractExistingFontFaces(html);
95096
95477
  const requestedFamilies = extractRequestedFontFamilies(html);
95097
95478
  const pendingFamilies = /* @__PURE__ */ new Map();
95098
- for (const [normalizedFamily, originalCaseFamily] of requestedFamilies) {
95479
+ for (const [normalizedFamily, requested] of requestedFamilies) {
95099
95480
  if (!existingFaces.has(normalizedFamily)) {
95100
- pendingFamilies.set(normalizedFamily, originalCaseFamily);
95481
+ pendingFamilies.set(normalizedFamily, requested);
95101
95482
  }
95102
95483
  }
95103
95484
  if (pendingFamilies.size === 0) {
@@ -95108,11 +95489,14 @@ async function injectDeterministicFontFaces(html, options = {}) {
95108
95489
  fetchOptions,
95109
95490
  extractGoogleFontsText(html)
95110
95491
  );
95111
- if (unresolved.length > 0 && options.failClosedFontFetch) {
95492
+ const required = unresolved.filter(
95493
+ (family) => !pendingFamilies.get(family.toLowerCase())?.optional
95494
+ );
95495
+ if (required.length > 0 && options.failClosedFontFetch) {
95112
95496
  throw new FontFetchError(
95113
- unresolved.join(", "),
95497
+ required.join(", "),
95114
95498
  "",
95115
- `[Compiler] Unresolved fonts in fail-closed mode: ${unresolved.join(", ")}. Distributed renders require all fonts to be resolvable.`
95499
+ `[Compiler] Unresolved fonts in fail-closed mode: ${required.join(", ")}. Distributed renders require all fonts to be resolvable.`
95116
95500
  );
95117
95501
  }
95118
95502
  if (!css) {
@@ -99935,7 +100319,17 @@ async function runCaptureHdrStage(input) {
99935
100319
 
99936
100320
  // ../producer/src/services/render/stages/encodeStage.ts
99937
100321
  init_src();
99938
- import { copyFileSync as copyFileSync5, existsSync as existsSync32, mkdirSync as mkdirSync18, readdirSync as readdirSync11, rmSync as rmSync13, statSync as statSync11 } from "fs";
100322
+ init_dist2();
100323
+ import {
100324
+ copyFileSync as copyFileSync5,
100325
+ existsSync as existsSync32,
100326
+ mkdirSync as mkdirSync18,
100327
+ readdirSync as readdirSync11,
100328
+ readFileSync as readFileSync15,
100329
+ rmSync as rmSync13,
100330
+ statSync as statSync11,
100331
+ writeFileSync as writeFileSync11
100332
+ } from "fs";
99939
100333
  import { dirname as dirname18, join as join46 } from "path";
99940
100334
 
99941
100335
  // ../producer/src/services/render/stages/gifEncodeArgs.ts
@@ -99943,15 +100337,23 @@ import { join as join45 } from "path";
99943
100337
  function fpsToFfmpegArg2(fps) {
99944
100338
  return fps.den === 1 ? String(fps.num) : `${fps.num}/${fps.den}`;
99945
100339
  }
100340
+ var KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA = ["-reinit_filter", "0"];
100341
+ var WRITE_EVERY_FRAME_WHOLE = ["-gifflags", "0"];
100342
+ function framesInput(input, fpsArg) {
100343
+ return [
100344
+ "-framerate",
100345
+ fpsArg,
100346
+ ...KEEP_FILTER_GRAPH_WHEN_FRAMES_DROP_ALPHA,
100347
+ "-i",
100348
+ join45(input.framesDir, input.framePattern)
100349
+ ];
100350
+ }
99946
100351
  function buildGifPalettegenArgs(input) {
99947
100352
  const fpsArg = fpsToFfmpegArg2(input.fps);
99948
100353
  const transparency = input.preserveAlpha ? ":reserve_transparent=1" : "";
99949
100354
  return [
99950
100355
  "-y",
99951
- "-framerate",
99952
- fpsArg,
99953
- "-i",
99954
- join45(input.framesDir, input.framePattern),
100356
+ ...framesInput(input, fpsArg),
99955
100357
  "-vf",
99956
100358
  `fps=${fpsArg},palettegen=stats_mode=diff${transparency}`,
99957
100359
  input.palettePath
@@ -99962,16 +100364,14 @@ function buildGifPaletteuseArgs(input) {
99962
100364
  const transparency = input.preserveAlpha ? ":alpha_threshold=128" : "";
99963
100365
  return [
99964
100366
  "-y",
99965
- "-framerate",
99966
- fpsArg,
99967
- "-i",
99968
- join45(input.framesDir, input.framePattern),
100367
+ ...framesInput(input, fpsArg),
99969
100368
  "-i",
99970
100369
  input.palettePath,
99971
100370
  "-lavfi",
99972
100371
  `fps=${fpsArg} [x]; [x][1:v] paletteuse=dither=sierra2_4a${transparency}`,
99973
100372
  "-loop",
99974
100373
  String(input.loop),
100374
+ ...input.wholeFrames ? WRITE_EVERY_FRAME_WHOLE : [],
99975
100375
  input.outputPath
99976
100376
  ];
99977
100377
  }
@@ -99985,19 +100385,38 @@ function resolveGifLoop(loop) {
99985
100385
  }
99986
100386
  return resolved;
99987
100387
  }
100388
+ function gifEncodeFailure(startTime, outputPath, error, failureReason) {
100389
+ return {
100390
+ success: false,
100391
+ outputPath,
100392
+ durationMs: Date.now() - startTime,
100393
+ framesEncoded: 0,
100394
+ fileSize: 0,
100395
+ error,
100396
+ failureReason
100397
+ };
100398
+ }
100399
+ async function reencodeWithClearedWholeFrames(argsInput, run, startTime) {
100400
+ const { outputPath } = argsInput;
100401
+ const result = await runFfmpeg(buildGifPaletteuseArgs({ ...argsInput, wholeFrames: true }), run);
100402
+ if (!result.success) {
100403
+ const error = formatFfmpegError(result.exitCode, result.stderr);
100404
+ return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
100405
+ }
100406
+ const gif = readFileSync15(outputPath);
100407
+ if (!clearGifFramesBeforeNext(gif)) {
100408
+ const error = "[FFmpeg] GIF output could not be parsed to clear its frames";
100409
+ return gifEncodeFailure(startTime, outputPath, error);
100410
+ }
100411
+ writeFileSync11(outputPath, gif);
100412
+ return null;
100413
+ }
99988
100414
  async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
99989
100415
  const startTime = Date.now();
99990
100416
  const files = readdirSync11(framesDir).filter((file) => file.match(/\.(jpg|jpeg|png)$/i));
99991
100417
  const frameCount = files.length;
99992
100418
  if (frameCount === 0) {
99993
- return {
99994
- success: false,
99995
- outputPath,
99996
- durationMs: Date.now() - startTime,
99997
- framesEncoded: 0,
99998
- fileSize: 0,
99999
- error: "[FFmpeg] No frame files found in directory"
100000
- };
100419
+ return gifEncodeFailure(startTime, outputPath, "[FFmpeg] No frame files found in directory");
100001
100420
  }
100002
100421
  const argsInput = {
100003
100422
  framesDir,
@@ -100009,35 +100428,17 @@ async function encodeGifFromDir(framesDir, framePattern, outputPath, input) {
100009
100428
  preserveAlpha: input.preserveAlpha
100010
100429
  };
100011
100430
  try {
100012
- const paletteResult = await runFfmpeg(buildGifPalettegenArgs(argsInput), {
100013
- signal: input.signal,
100014
- timeout: input.timeout
100015
- });
100016
- if (!paletteResult.success) {
100017
- return {
100018
- success: false,
100019
- outputPath,
100020
- durationMs: Date.now() - startTime,
100021
- framesEncoded: 0,
100022
- fileSize: 0,
100023
- error: formatFfmpegError(paletteResult.exitCode, paletteResult.stderr),
100024
- failureReason: paletteResult.failureReason
100025
- };
100431
+ const run = { signal: input.signal, timeout: input.timeout };
100432
+ for (const args of [buildGifPalettegenArgs(argsInput), buildGifPaletteuseArgs(argsInput)]) {
100433
+ const result = await runFfmpeg(args, run);
100434
+ if (!result.success) {
100435
+ const error = formatFfmpegError(result.exitCode, result.stderr);
100436
+ return gifEncodeFailure(startTime, outputPath, error, result.failureReason);
100437
+ }
100026
100438
  }
100027
- const gifResult = await runFfmpeg(buildGifPaletteuseArgs(argsInput), {
100028
- signal: input.signal,
100029
- timeout: input.timeout
100030
- });
100031
- if (!gifResult.success) {
100032
- return {
100033
- success: false,
100034
- outputPath,
100035
- durationMs: Date.now() - startTime,
100036
- framesEncoded: 0,
100037
- fileSize: 0,
100038
- error: formatFfmpegError(gifResult.exitCode, gifResult.stderr),
100039
- failureReason: gifResult.failureReason
100040
- };
100439
+ if (input.preserveAlpha && gifClearsAfterLeavingFrameInPlace(readFileSync15(outputPath)) !== false) {
100440
+ const failure = await reencodeWithClearedWholeFrames(argsInput, run, startTime);
100441
+ if (failure) return failure;
100041
100442
  }
100042
100443
  const fileSize = existsSync32(outputPath) ? statSync11(outputPath).size : 0;
100043
100444
  return {
@@ -101723,7 +102124,7 @@ async function executeRenderPipeline(input) {
101723
102124
  throw new Error(`Entry file not found: ${htmlPath}`);
101724
102125
  }
101725
102126
  assertNotAborted();
101726
- const rawEntry = readFileSync15(htmlPath, "utf-8");
102127
+ const rawEntry = readFileSync16(htmlPath, "utf-8");
101727
102128
  if (entryFile !== "index.html" && rawEntry.trimStart().startsWith("<template")) {
101728
102129
  const wrapperPath = join48(workDir, "standalone-entry.html");
101729
102130
  const projectIndexPath = join48(projectDir, "index.html");
@@ -101733,7 +102134,7 @@ async function executeRenderPipeline(input) {
101733
102134
  );
101734
102135
  }
101735
102136
  const standaloneHtml = extractStandaloneEntryFromIndex(
101736
- readFileSync15(projectIndexPath, "utf-8"),
102137
+ readFileSync16(projectIndexPath, "utf-8"),
101737
102138
  entryFile,
101738
102139
  rawEntry
101739
102140
  );
@@ -101742,7 +102143,7 @@ async function executeRenderPipeline(input) {
101742
102143
  `Entry file "${entryFile}" is not mounted from index.html via data-composition-src, so it cannot be rendered independently.`
101743
102144
  );
101744
102145
  }
101745
- writeFileSync11(wrapperPath, standaloneHtml, "utf-8");
102146
+ writeFileSync12(wrapperPath, standaloneHtml, "utf-8");
101746
102147
  htmlPath = wrapperPath;
101747
102148
  log.info("Extracted standalone entry from index.html host context", {
101748
102149
  entryFile
@@ -103216,7 +103617,7 @@ async function executeRenderPipeline(input) {
103216
103617
  job.perfSummary = perfSummary;
103217
103618
  if (job.config.debug) {
103218
103619
  try {
103219
- writeFileSync11(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
103620
+ writeFileSync12(perfOutputPath, JSON.stringify(perfSummary, null, 2), "utf-8");
103220
103621
  } catch (err) {
103221
103622
  log.debug("Failed to write perf summary", {
103222
103623
  perfOutputPath,
@@ -103569,7 +103970,7 @@ import {
103569
103970
  mkdirSync as mkdirSync20,
103570
103971
  statSync as statSync14,
103571
103972
  mkdtempSync as mkdtempSync12,
103572
- writeFileSync as writeFileSync12,
103973
+ writeFileSync as writeFileSync13,
103573
103974
  rmSync as rmSync16,
103574
103975
  createReadStream as createReadStream5
103575
103976
  } from "fs";
@@ -103585,7 +103986,7 @@ import { serve as serve3 } from "@hono/node-server";
103585
103986
  init_dist3();
103586
103987
  import {
103587
103988
  existsSync as existsSync34,
103588
- readFileSync as readFileSync16,
103989
+ readFileSync as readFileSync17,
103589
103990
  statSync as statSync13,
103590
103991
  openSync as openSync8,
103591
103992
  fstatSync as fstatSync5,
@@ -103630,7 +104031,7 @@ function readEntryHtml(filePath) {
103630
104031
  }
103631
104032
  try {
103632
104033
  if (!fstatSync5(fd).isFile()) return null;
103633
- return readFileSync16(fd, "utf-8");
104034
+ return readFileSync17(fd, "utf-8");
103634
104035
  } finally {
103635
104036
  closeSync9(fd);
103636
104037
  }
@@ -103984,7 +104385,7 @@ async function resolveInlineRenderHtml(body) {
103984
104385
  function materializeInlineProject(html, options) {
103985
104386
  const tempRoot = process.env.PRODUCER_TMP_PROJECT_DIR || tmpdir8();
103986
104387
  const tempProjectDir = mkdtempSync12(join51(tempRoot, "producer-project-"));
103987
- writeFileSync12(join51(tempProjectDir, "index.html"), html, "utf-8");
104388
+ writeFileSync13(join51(tempProjectDir, "index.html"), html, "utf-8");
103988
104389
  return {
103989
104390
  prepared: {
103990
104391
  input: { projectDir: tempProjectDir, ...options },
@@ -104434,11 +104835,11 @@ init_src();
104434
104835
  // ../producer/src/services/distributed/plan.ts
104435
104836
  init_dist2();
104436
104837
  init_src();
104437
- import { cpSync as cpSync2, existsSync as existsSync39, mkdirSync as mkdirSync22, renameSync as renameSync8, rmSync as rmSync17, writeFileSync as writeFileSync14 } from "fs";
104838
+ import { cpSync as cpSync2, existsSync as existsSync39, mkdirSync as mkdirSync22, renameSync as renameSync8, rmSync as rmSync17, writeFileSync as writeFileSync15 } from "fs";
104438
104839
  import { join as join55, relative as relative8, sep as sep5 } from "path";
104439
104840
 
104440
104841
  // ../producer/src/services/render/stages/freezePlan.ts
104441
- import { existsSync as existsSync37, mkdirSync as mkdirSync21, readFileSync as readFileSync17, readdirSync as readdirSync13, writeFileSync as writeFileSync13 } from "fs";
104842
+ import { existsSync as existsSync37, mkdirSync as mkdirSync21, readFileSync as readFileSync18, readdirSync as readdirSync13, writeFileSync as writeFileSync14 } from "fs";
104442
104843
  import { join as join52, relative as relative6, resolve as resolve16 } from "path";
104443
104844
 
104444
104845
  // ../producer/src/services/distributed/planProtocol.ts
@@ -104482,7 +104883,7 @@ var FIELD_DELIMITER = Buffer.from([0]);
104482
104883
  init_src();
104483
104884
  import { execFile as execFileCallback } from "child_process";
104484
104885
  import { dirname as dirname23, join as join53 } from "path";
104485
- import { existsSync as existsSync38, readFileSync as readFileSync18 } from "fs";
104886
+ import { existsSync as existsSync38, readFileSync as readFileSync19 } from "fs";
104486
104887
  import { fileURLToPath as fileURLToPath8 } from "url";
104487
104888
  import { promisify as promisify5 } from "util";
104488
104889
  var PLAN_AUDIO_RELATIVE_PATH = MIXED_AUDIO_FILENAME;
@@ -104518,13 +104919,13 @@ import {
104518
104919
  mkdirSync as mkdirSync24,
104519
104920
  mkdtempSync as mkdtempSync14,
104520
104921
  openSync as openSync9,
104521
- readFileSync as readFileSync19,
104922
+ readFileSync as readFileSync20,
104522
104923
  readSync as readSync3,
104523
104924
  readdirSync as readdirSync15,
104524
104925
  renameSync as renameSync10,
104525
104926
  rmSync as rmSync19,
104526
104927
  statSync as statSync16,
104527
- writeFileSync as writeFileSync16
104928
+ writeFileSync as writeFileSync17
104528
104929
  } from "fs";
104529
104930
  import { createHash as createHash14 } from "crypto";
104530
104931
  import { tmpdir as tmpdir9 } from "os";
@@ -104540,7 +104941,7 @@ import {
104540
104941
  renameSync as renameSync9,
104541
104942
  rmSync as rmSync18,
104542
104943
  statSync as statSync15,
104543
- writeFileSync as writeFileSync15
104944
+ writeFileSync as writeFileSync16
104544
104945
  } from "fs";
104545
104946
  import { dirname as dirname24, join as join57 } from "path";
104546
104947
 
@@ -104560,23 +104961,23 @@ import {
104560
104961
  existsSync as existsSync42,
104561
104962
  mkdirSync as mkdirSync25,
104562
104963
  mkdtempSync as mkdtempSync15,
104563
- readFileSync as readFileSync20,
104964
+ readFileSync as readFileSync21,
104564
104965
  readdirSync as readdirSync16,
104565
104966
  rmSync as rmSync20,
104566
104967
  statSync as statSync17,
104567
- writeFileSync as writeFileSync17
104968
+ writeFileSync as writeFileSync18
104568
104969
  } from "fs";
104569
104970
  import { dirname as dirname26, join as join59 } from "path";
104570
104971
 
104571
104972
  // ../producer/src/services/distributed/renderChunk.ts
104572
104973
  init_src();
104573
104974
  import { randomBytes } from "crypto";
104574
- import { existsSync as existsSync43, mkdirSync as mkdirSync26, readFileSync as readFileSync21, readdirSync as readdirSync17, rmSync as rmSync21, writeFileSync as writeFileSync18 } from "fs";
104975
+ import { existsSync as existsSync43, mkdirSync as mkdirSync26, readFileSync as readFileSync22, readdirSync as readdirSync17, rmSync as rmSync21, writeFileSync as writeFileSync19 } from "fs";
104575
104976
  import { extname as extname8, join as join60 } from "path";
104576
104977
  init_src();
104577
104978
 
104578
104979
  // ../producer/src/services/distributed/projectHash.ts
104579
- import { readdirSync as readdirSync18, readFileSync as readFileSync22 } from "fs";
104980
+ import { readdirSync as readdirSync18, readFileSync as readFileSync23 } from "fs";
104580
104981
  import { createHash as createHash15 } from "crypto";
104581
104982
  import { join as join62, relative as relative10 } from "path";
104582
104983
 
@@ -104619,8 +105020,8 @@ async function runFontLocalize(io, localize) {
104619
105020
  }
104620
105021
 
104621
105022
  // src/version.ts
104622
- var VERSION = true ? "0.8.80" : "0.0.0-dev";
104623
- var PRODUCER_VERSION = true ? "0.8.80" : "0.0.0-dev";
105023
+ var VERSION = true ? "0.8.82" : "0.0.0-dev";
105024
+ var PRODUCER_VERSION = true ? "0.8.82" : "0.0.0-dev";
104624
105025
 
104625
105026
  // src/fontLocalizeCli.ts
104626
105027
  async function readStdin() {