hyperframes 0.8.49 → 0.8.50

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.
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.49" : "0.0.0-dev";
53
+ VERSION = true ? "0.8.50" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -65197,7 +65197,7 @@ function buildChromeArgs(options, config) {
65197
65197
  // frame-capture loop then blocks waiting for it, deadlocking the render.
65198
65198
  "--autoplay-policy=no-user-gesture-required"
65199
65199
  ];
65200
- if (browserGpuMode !== "software") {
65200
+ if (browserGpuMode !== "software" || options.requiresWebGpu) {
65201
65201
  chromeArgs.push(WEBGPU_FLAG);
65202
65202
  }
65203
65203
  if (browserGpuMode === "software") {
@@ -65221,6 +65221,28 @@ function buildChromeArgs(options, config) {
65221
65221
  }
65222
65222
  return chromeArgs;
65223
65223
  }
65224
+ function compositionRequiresWebGpu(html) {
65225
+ for (const [tag] of html.matchAll(/<(?:[^<>"']|"[^"]*"|'[^']*')*>/g)) {
65226
+ if (/\bdata-composition-id\b/i.test(tag)) return /\bdata-requires-webgpu(?:\s|=|>)/i.test(tag);
65227
+ }
65228
+ return false;
65229
+ }
65230
+ async function assertWebGpuAdapterAvailable(page, requiresWebGpu) {
65231
+ if (!requiresWebGpu) return;
65232
+ const hasUsableAdapter = await page.evaluate(async (timeoutMs) => {
65233
+ if (typeof navigator === "undefined" || !navigator.gpu) return false;
65234
+ try {
65235
+ const adapter2 = await Promise.race([
65236
+ navigator.gpu.requestAdapter(),
65237
+ new Promise((resolve84) => setTimeout(() => resolve84(null), timeoutMs))
65238
+ ]);
65239
+ return !!adapter2 && !adapter2.info.isFallbackAdapter;
65240
+ } catch {
65241
+ return false;
65242
+ }
65243
+ }, WEBGPU_ADAPTER_PROBE_TIMEOUT_MS);
65244
+ if (!hasUsableAdapter) throw new WebGpuUnavailableError();
65245
+ }
65224
65246
  function getBrowserGpuArgs(mode, platform11) {
65225
65247
  if (mode === "software") {
65226
65248
  return ["--use-gl=angle", "--use-angle=swiftshader", "--enable-unsafe-swiftshader"];
@@ -65245,7 +65267,7 @@ function getBrowserGpuArgs(mode, platform11) {
65245
65267
  return ["--enable-gpu-rasterization"];
65246
65268
  }
65247
65269
  }
65248
- var _puppeteer, CACHED_HEADLESS_SHELL_EXECUTABLES, ENABLE_BROWSER_POOL, BEGINFRAME_ONLY_FLAGS, BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS, BEGINFRAME_PROBE_TIMEOUT_MS, BEGINFRAME_PROBE_CLEANUP_TIMEOUT_MS, _autoBrowserGpuModeCache, _unverifiedHardwareGpuWarned, browserLeasePool, _cachedVramMb, CANVAS_DRAW_ELEMENT_FEATURE_FLAG, WEBGPU_FLAG;
65270
+ var _puppeteer, CACHED_HEADLESS_SHELL_EXECUTABLES, ENABLE_BROWSER_POOL, BEGINFRAME_ONLY_FLAGS, BEGINFRAME_SCREENSHOT_PROBE_ATTEMPTS, BEGINFRAME_PROBE_TIMEOUT_MS, BEGINFRAME_PROBE_CLEANUP_TIMEOUT_MS, _autoBrowserGpuModeCache, _unverifiedHardwareGpuWarned, browserLeasePool, _cachedVramMb, CANVAS_DRAW_ELEMENT_FEATURE_FLAG, WEBGPU_FLAG, WebGpuUnavailableError, WEBGPU_ADAPTER_PROBE_TIMEOUT_MS;
65249
65271
  var init_browserManager = __esm({
65250
65272
  "../engine/src/services/browserManager.ts"() {
65251
65273
  "use strict";
@@ -65284,6 +65306,15 @@ var init_browserManager = __esm({
65284
65306
  _cachedVramMb = null;
65285
65307
  CANVAS_DRAW_ELEMENT_FEATURE_FLAG = "--enable-features=CanvasDrawElement";
65286
65308
  WEBGPU_FLAG = "--enable-unsafe-webgpu";
65309
+ WebGpuUnavailableError = class extends Error {
65310
+ constructor() {
65311
+ super(
65312
+ "This composition declares data-requires-webgpu, but no hardware WebGPU adapter could be obtained on this browser launch (a software fallback adapter such as swiftshader reports one but cannot render it). Run on a host with a GPU, or remove data-requires-webgpu."
65313
+ );
65314
+ this.name = "WebGpuUnavailableError";
65315
+ }
65316
+ };
65317
+ WEBGPU_ADAPTER_PROBE_TIMEOUT_MS = 1e4;
65287
65318
  }
65288
65319
  });
65289
65320
 
@@ -68894,8 +68925,9 @@ async function createCaptureSession(serverUrl, outputDir, options, onBeforeCaptu
68894
68925
  config
68895
68926
  );
68896
68927
  const preMode = headlessShell && isLinux && !effectiveForceScreenshot && !supersampling && !drawElementTransparent ? "beginframe" : "screenshot";
68928
+ const requiresWebGpu = options.requiresWebGpu ?? await fetch(`${serverUrl}/index.html`, { signal: AbortSignal.timeout(5e3) }).then((res) => res.text()).then(compositionRequiresWebGpu).catch(() => false);
68897
68929
  const chromeArgs = buildChromeArgs(
68898
- { width: options.width, height: options.height, captureMode: preMode },
68930
+ { width: options.width, height: options.height, captureMode: preMode, requiresWebGpu },
68899
68931
  { ...config, browserGpuMode: resolvedGpuMode }
68900
68932
  );
68901
68933
  const browserLease = await acquireBrowser(chromeArgs, config);
@@ -68906,7 +68938,8 @@ async function createCaptureSession(serverUrl, outputDir, options, onBeforeCaptu
68906
68938
  options,
68907
68939
  onBeforeCapture,
68908
68940
  config,
68909
- useDrawElement
68941
+ useDrawElement,
68942
+ requiresWebGpu
68910
68943
  });
68911
68944
  }
68912
68945
  async function constructCaptureSessionWithRollback(input2) {
@@ -68955,6 +68988,7 @@ async function constructCaptureSession(input2) {
68955
68988
  onBeforeCapture,
68956
68989
  config,
68957
68990
  useDrawElement,
68991
+ requiresWebGpu,
68958
68992
  onPageCreated
68959
68993
  } = input2;
68960
68994
  const { browser, captureMode } = browserLease;
@@ -69017,6 +69051,7 @@ async function constructCaptureSession(input2) {
69017
69051
  serverUrl,
69018
69052
  outputDir,
69019
69053
  onBeforeCapture,
69054
+ requiresWebGpu,
69020
69055
  isInitialized: false,
69021
69056
  browserConsoleBuffer: [],
69022
69057
  scriptLoadFailures: [],
@@ -69470,6 +69505,7 @@ async function initializeSession(session) {
69470
69505
  );
69471
69506
  throw error;
69472
69507
  }
69508
+ await assertWebGpuAdapterAvailable(page, session.requiresWebGpu ?? false);
69473
69509
  };
69474
69510
  if (session.captureMode === "screenshot") {
69475
69511
  await gotoEntryPage();
@@ -74333,6 +74369,37 @@ var init_htmlCompiler = __esm({
74333
74369
  }
74334
74370
  });
74335
74371
 
74372
+ // ../core/dist/compiler/scriptRuns.js
74373
+ function isClassicInline(el) {
74374
+ const type = (el.getAttribute("type") || "").trim().toLowerCase();
74375
+ return !type || type === "text/javascript" || type === "application/javascript";
74376
+ }
74377
+ function isSeparateExecution(el, isPinned) {
74378
+ return el.hasAttribute("src") || isPinned(el) || (el.getAttribute("type") || "").trim().toLowerCase() === "module";
74379
+ }
74380
+ function inlineScriptRuns(scripts, isPinned = () => false) {
74381
+ const runs = [];
74382
+ let members = [];
74383
+ for (const el of scripts) {
74384
+ if (isSeparateExecution(el, isPinned)) {
74385
+ if (members.length === 0)
74386
+ continue;
74387
+ runs.push({ members, anchor: el });
74388
+ members = [];
74389
+ } else if (isClassicInline(el)) {
74390
+ members.push(el);
74391
+ }
74392
+ }
74393
+ if (members.length > 0)
74394
+ runs.push({ members, anchor: null });
74395
+ return runs;
74396
+ }
74397
+ var init_scriptRuns = __esm({
74398
+ "../core/dist/compiler/scriptRuns.js"() {
74399
+ "use strict";
74400
+ }
74401
+ });
74402
+
74336
74403
  // ../core/dist/compiler/externalScripts.js
74337
74404
  function readExternalScriptAttributes(el) {
74338
74405
  const attributes2 = {};
@@ -78762,6 +78829,46 @@ var require_dist = __commonJS({
78762
78829
  }
78763
78830
  });
78764
78831
 
78832
+ // ../parsers/dist/topLevelElements.js
78833
+ function isSubCompositionHost(node) {
78834
+ return node.attrs["data-composition-src"] !== void 0 || node.attrs["data-composition-id"] !== void 0;
78835
+ }
78836
+ function isTimedElement(node) {
78837
+ const { attrs: attrs2 } = node;
78838
+ return attrs2["data-start"] !== void 0 || attrs2["data-track-index"] !== void 0 && attrs2["data-duration"] !== void 0 || MEDIA_TAGS.has(node.tag.toLowerCase());
78839
+ }
78840
+ function topLevelElements(root) {
78841
+ const rows = [];
78842
+ const visit = (node) => {
78843
+ if (NON_RENDERED_TAGS.has(node.tag.toLowerCase())) return;
78844
+ if (isSubCompositionHost(node) || isTimedElement(node)) rows.push(node);
78845
+ else node.children.forEach(visit);
78846
+ };
78847
+ root.children.forEach(visit);
78848
+ return rows;
78849
+ }
78850
+ function trackKindOf(node) {
78851
+ const explicit = node.attrs["data-track-kind"]?.trim().toLowerCase();
78852
+ if (explicit && TRACK_KINDS.includes(explicit))
78853
+ return { kind: explicit, source: "attribute" };
78854
+ const tag = node.tag.toLowerCase();
78855
+ if (tag === "video" || tag === "audio") return { kind: tag, source: "tag" };
78856
+ if (isSubCompositionHost(node)) {
78857
+ const legacy = node.attrs["data-composition-id"] === "captions" || /(?:^|\s)caption[-_]/.test(node.attrs.class ?? "");
78858
+ return legacy ? { kind: "captions", source: "legacy-captions" } : { kind: "graphics", source: "sub-composition" };
78859
+ }
78860
+ return { kind: "graphics", source: "tag" };
78861
+ }
78862
+ var NON_RENDERED_TAGS, MEDIA_TAGS, TRACK_KINDS;
78863
+ var init_topLevelElements = __esm({
78864
+ "../parsers/dist/topLevelElements.js"() {
78865
+ "use strict";
78866
+ NON_RENDERED_TAGS = /* @__PURE__ */ new Set(["script", "style", "template", "noscript", "link", "meta"]);
78867
+ MEDIA_TAGS = /* @__PURE__ */ new Set(["video", "audio", "img"]);
78868
+ TRACK_KINDS = ["video", "audio", "captions", "graphics"];
78869
+ }
78870
+ });
78871
+
78765
78872
  // ../parsers/dist/assetResolution.js
78766
78873
  import { existsSync as existsSync10 } from "fs";
78767
78874
  import { isAbsolute as isAbsolute2, posix as posix2, relative as relative2, resolve as resolve7 } from "path";
@@ -83002,6 +83109,94 @@ function extractScenesFromClips(ctx) {
83002
83109
  collectCompositionIdScenes(ctx, seen, scenes);
83003
83110
  return scenes;
83004
83111
  }
83112
+ function toNode(open3) {
83113
+ const attrs2 = {};
83114
+ for (const name of NODE_ATTRS) attrs2[name] = readDecodedAttr(open3.raw, name) ?? void 0;
83115
+ return { tag: open3.name, attrs: attrs2, children: [], open: open3 };
83116
+ }
83117
+ function enclosing(stack, root, index) {
83118
+ let top = stack.at(-1);
83119
+ while (top && stack.length > 1 && index >= top.end) {
83120
+ stack.pop();
83121
+ top = stack.at(-1);
83122
+ }
83123
+ return top?.node ?? root;
83124
+ }
83125
+ function buildTree(ctx) {
83126
+ const { rootTag } = ctx;
83127
+ const rootEnd = rootTag?.closeIndex;
83128
+ if (!rootTag || rootEnd == null) return null;
83129
+ const root = toNode(rootTag);
83130
+ const stack = [{ node: root, end: rootEnd }];
83131
+ for (const tag of ctx.tags.filter((t2) => t2.index > rootTag.index && t2.index < rootEnd)) {
83132
+ const node = toNode(tag);
83133
+ enclosing(stack, root, tag.index).children.push(node);
83134
+ if (tag.closeIndex != null) stack.push({ node, end: tag.closeIndex });
83135
+ }
83136
+ return root;
83137
+ }
83138
+ function hasNestedStructure(node) {
83139
+ if (OPAQUE_TAGS2.has(node.tag)) return null;
83140
+ for (const child of node.children) {
83141
+ if (NON_LAYOUT_TAGS.has(child.tag)) continue;
83142
+ if (!INLINE_TEXT_TAGS.has(child.tag)) return child;
83143
+ const deeper = hasNestedStructure(child);
83144
+ if (deeper) return deeper;
83145
+ }
83146
+ return null;
83147
+ }
83148
+ function nestedStructureFindings(rows, severity) {
83149
+ return rows.flatMap((row) => {
83150
+ const nested = isSubCompositionHost(row) ? null : hasNestedStructure(row);
83151
+ if (!nested) return [];
83152
+ return [
83153
+ {
83154
+ code: "nested_structure_needs_subcomposition",
83155
+ severity,
83156
+ message: `${describe(row)} is a timeline element that contains nested ${describe(nested)}. The timeline shows one row per top-level element, and the root composition is built only from sub-compositions.`,
83157
+ elementId: row.attrs.id,
83158
+ fixHint: `Move ${describe(row)} and its contents into a sub-composition file and mount it with data-composition-src.`
83159
+ }
83160
+ ];
83161
+ });
83162
+ }
83163
+ function missingDurationFindings(rows, severity) {
83164
+ const intendedClip = (row) => !MEDIA_TAGS2.has(row.tag) || row.attrs["data-start"] !== void 0 || row.attrs["data-track-index"] !== void 0;
83165
+ return rows.filter(
83166
+ (row) => row.attrs["data-duration"] === void 0 && row.attrs["data-end"] === void 0 && !isSubCompositionHost(row) && intendedClip(row)
83167
+ ).map((row) => {
83168
+ const isMedia = MEDIA_TAGS2.has(row.tag);
83169
+ return {
83170
+ code: isMedia ? "media_missing_duration" : "timeline_element_missing_timing",
83171
+ severity,
83172
+ message: isMedia ? `${describe(row)} is media on the timeline without data-duration, so its clip has no length.` : `${describe(row)} is a timeline element without data-duration, so the timeline cannot draw where it ends.`,
83173
+ elementId: row.attrs.id,
83174
+ fixHint: `Add data-duration (in seconds) to ${describe(row)}.`
83175
+ };
83176
+ });
83177
+ }
83178
+ function captionFindings(rows, severity) {
83179
+ const captionRows = rows.filter((row) => trackKindOf(row).kind === "captions");
83180
+ const findings = captionRows.filter((row) => trackKindOf(row).source === "legacy-captions").map((row) => ({
83181
+ code: "caption_track_kind_missing",
83182
+ severity,
83183
+ message: `${describe(row)} looks like captions but is not marked as such, so the timeline cannot group it on the caption track.`,
83184
+ elementId: row.attrs.id,
83185
+ fixHint: `Add data-track-kind="captions" to ${describe(row)}.`
83186
+ }));
83187
+ const lanes = new Set(
83188
+ captionRows.flatMap((row) => row.attrs["data-track-index"] ?? row.attrs["data-track"] ?? [])
83189
+ );
83190
+ if (lanes.size > 1) {
83191
+ findings.push({
83192
+ code: "multiple_caption_tracks",
83193
+ severity,
83194
+ message: `Captions are spread across ${lanes.size} timeline tracks. Keep all captions on one track.`,
83195
+ fixHint: "Give every caption element the same data-track-index."
83196
+ });
83197
+ }
83198
+ return findings;
83199
+ }
83005
83200
  function dedupeKeyFor(finding) {
83006
83201
  return [
83007
83202
  finding.code,
@@ -83416,7 +83611,7 @@ function resolveCssAssetCandidates(projectDir, url, htmlCompSrcPath, cssRootRela
83416
83611
  }
83417
83612
  return resolveLocalAssetCandidates(projectDir, url);
83418
83613
  }
83419
- async function lintProject(projectDir, entryFile) {
83614
+ async function lintProject(projectDir, entryFile, hostOptions = {}) {
83420
83615
  const indexPath2 = entryFile ? resolve8(entryFile) : resolve8(projectDir, "index.html");
83421
83616
  if (entryFile && !isWithinProjectRoot(projectDir, indexPath2)) {
83422
83617
  throw new Error(`Explicit lint entry is outside the project directory: ${entryFile}`);
@@ -83429,6 +83624,7 @@ async function lintProject(projectDir, entryFile) {
83429
83624
  let totalInfos = 0;
83430
83625
  const rootHtml = readFileSync5(indexPath2, "utf-8");
83431
83626
  const rootResult = await lintHyperframeHtml(rootHtml, {
83627
+ ...hostOptions,
83432
83628
  filePath: indexPath2,
83433
83629
  externalStyles: collectExternalStyles(projectDir, rootHtml, rootCompSrcPath)
83434
83630
  });
@@ -83460,6 +83656,7 @@ async function lintProject(projectDir, entryFile) {
83460
83656
  allHtmlSources.push({ html, compSrcPath });
83461
83657
  if (isSnippetFragment(html)) continue;
83462
83658
  const result = await lintHyperframeHtml(html, {
83659
+ ...hostOptions,
83463
83660
  filePath,
83464
83661
  isSubComposition: true,
83465
83662
  externalStyles: collectExternalStyles(projectDir, html, compSrcPath)
@@ -83770,7 +83967,7 @@ function isSnippetFragment(html) {
83770
83967
  if (!firstTag) return false;
83771
83968
  return /\bdata-hf-snippet\b/.test(firstTag[0]);
83772
83969
  }
83773
- 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, 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, compositionRules, 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, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, PROBE_TIMEOUT_MS, PROBE_CONCURRENCY, AUDIO_EXTENSIONS, MASK_IMAGE_URL_RE;
83970
+ 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, 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, compositionRules, 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_TAGS2, NON_LAYOUT_TAGS, MEDIA_TAGS2, NODE_ATTRS, describe, structureRules, RULE_GROUPS, LINT_RULE_COUNT, LINT_RULE_GROUP_COUNTS, PROBE_TIMEOUT_MS, PROBE_CONCURRENCY, AUDIO_EXTENSIONS, MASK_IMAGE_URL_RE;
83774
83971
  var init_dist4 = __esm({
83775
83972
  "../lint/dist/index.js"() {
83776
83973
  "use strict";
@@ -83784,6 +83981,7 @@ var init_dist4 = __esm({
83784
83981
  init_compositionContract();
83785
83982
  init_composition();
83786
83983
  init_slideshow();
83984
+ init_topLevelElements();
83787
83985
  init_assets();
83788
83986
  init_subCompositionValidity();
83789
83987
  init_esm10();
@@ -86866,6 +87064,64 @@ ${other.raw}`)
86866
87064
  return findings;
86867
87065
  }
86868
87066
  ];
87067
+ INLINE_TEXT_TAGS = /* @__PURE__ */ new Set([
87068
+ "br",
87069
+ "wbr",
87070
+ "b",
87071
+ "i",
87072
+ "u",
87073
+ "s",
87074
+ "em",
87075
+ "strong",
87076
+ "small",
87077
+ "sub",
87078
+ "sup",
87079
+ "mark",
87080
+ "span",
87081
+ "a",
87082
+ "code",
87083
+ "abbr"
87084
+ ]);
87085
+ OPAQUE_TAGS2 = /* @__PURE__ */ new Set([
87086
+ "svg",
87087
+ "canvas",
87088
+ "picture",
87089
+ "video",
87090
+ "audio",
87091
+ "select",
87092
+ "textarea",
87093
+ "iframe",
87094
+ "object"
87095
+ ]);
87096
+ NON_LAYOUT_TAGS = /* @__PURE__ */ new Set(["style", "script", "template", "noscript"]);
87097
+ MEDIA_TAGS2 = /* @__PURE__ */ new Set(["img", "video", "audio"]);
87098
+ NODE_ATTRS = [
87099
+ "id",
87100
+ "class",
87101
+ "data-start",
87102
+ "data-duration",
87103
+ "data-end",
87104
+ "data-track",
87105
+ "data-track-index",
87106
+ "data-track-kind",
87107
+ "data-composition-id",
87108
+ "data-composition-src"
87109
+ ];
87110
+ describe = (node) => `<${node.tag}${node.attrs.id ? ` id="${node.attrs.id}"` : ""}>`;
87111
+ structureRules = [
87112
+ (ctx) => {
87113
+ if (ctx.options.isSubComposition) return [];
87114
+ const root = buildTree(ctx);
87115
+ if (!root) return [];
87116
+ const severity = ctx.options.host === "studio" ? "error" : "warning";
87117
+ const rows = topLevelElements(root);
87118
+ return [
87119
+ ...nestedStructureFindings(rows, severity),
87120
+ ...missingDurationFindings(rows, severity),
87121
+ ...captionFindings(rows, severity)
87122
+ ];
87123
+ }
87124
+ ];
86869
87125
  RULE_GROUPS = [
86870
87126
  { group: "core", rules: coreRules },
86871
87127
  { group: "media", rules: mediaRules },
@@ -86875,7 +87131,8 @@ ${other.raw}`)
86875
87131
  { group: "adapters", rules: adapterRules },
86876
87132
  { group: "textures", rules: textureRules },
86877
87133
  { group: "fonts", rules: fontRules },
86878
- { group: "slideshow", rules: slideshowRules }
87134
+ { group: "slideshow", rules: slideshowRules },
87135
+ { group: "structure", rules: structureRules }
86879
87136
  ];
86880
87137
  LINT_RULE_COUNT = RULE_GROUPS.reduce((n2, g) => n2 + g.rules.length, 0);
86881
87138
  LINT_RULE_GROUP_COUNTS = Object.fromEntries(
@@ -87699,22 +87956,19 @@ function coalesceHeadStylesAndBodyScripts(document2) {
87699
87956
  headStyleEls[i2].remove();
87700
87957
  }
87701
87958
  }
87702
- const bodyInlineScripts = [...document2.querySelectorAll("body script")].filter((el) => {
87703
- if (el.hasAttribute(RUNTIME_BOOTSTRAP_ATTR) || el.hasAttribute("src"))
87704
- return false;
87705
- const type = (el.getAttribute("type") || "").trim().toLowerCase();
87706
- return !type || type === "text/javascript" || type === "application/javascript";
87707
- });
87708
- if (bodyInlineScripts.length > 0) {
87709
- const mergedJs = joinJsChunks(bodyInlineScripts.map((el) => el.textContent || ""));
87710
- for (const el of bodyInlineScripts)
87959
+ const isPinned = (el) => el.hasAttribute(RUNTIME_BOOTSTRAP_ATTR);
87960
+ for (const { members, anchor } of inlineScriptRuns([...document2.querySelectorAll("body script")], isPinned)) {
87961
+ const mergedJs = joinJsChunks(members.map((el) => el.textContent || ""));
87962
+ for (const el of members)
87711
87963
  el.remove();
87712
- if (mergedJs) {
87713
- const stripped = stripJsCommentsParserSafe(mergedJs);
87714
- const inlineScript = document2.createElement("script");
87715
- inlineScript.textContent = stripped;
87964
+ if (!mergedJs)
87965
+ continue;
87966
+ const inlineScript = document2.createElement("script");
87967
+ inlineScript.textContent = stripJsCommentsParserSafe(mergedJs);
87968
+ if (anchor)
87969
+ anchor.before(inlineScript);
87970
+ else
87716
87971
  document2.body.appendChild(inlineScript);
87717
- }
87718
87972
  }
87719
87973
  }
87720
87974
  function injectTextRenderingRule(document2) {
@@ -88185,6 +88439,7 @@ var DEFAULT_RUNTIME_SCRIPT_URL, CSS_IMPORT_RE, CSS_COMMENT_RE, INLINE_MIME, MAX_
88185
88439
  var init_htmlBundler = __esm({
88186
88440
  "../core/dist/compiler/htmlBundler.js"() {
88187
88441
  "use strict";
88442
+ init_scriptRuns();
88188
88443
  init_externalScripts();
88189
88444
  init_flattenedRoot();
88190
88445
  init_flattenedRoot();
@@ -88362,6 +88617,7 @@ __export(compiler_exports, {
88362
88617
  injectScriptsAtHeadStart: () => injectScriptsAtHeadStart,
88363
88618
  injectScriptsIntoHtml: () => injectScriptsIntoHtml,
88364
88619
  injectTagsAtHeadStart: () => injectTagsAtHeadStart,
88620
+ inlineScriptRuns: () => inlineScriptRuns,
88365
88621
  inlineSubCompositions: () => inlineSubCompositions,
88366
88622
  isNonRelativeUrl: () => isNonRelativeUrl,
88367
88623
  isPathInside: () => isPathInside,
@@ -88387,6 +88643,7 @@ var init_compiler = __esm({
88387
88643
  init_getVariables();
88388
88644
  init_htmlParityContract();
88389
88645
  init_htmlDocument();
88646
+ init_scriptRuns();
88390
88647
  init_staticGuard();
88391
88648
  init_compositionScoping();
88392
88649
  init_inlineSubCompositions();
@@ -95315,6 +95572,7 @@ __export(src_exports, {
95315
95572
  TRANSITIONS: () => TRANSITIONS,
95316
95573
  VIDEO_FRAME_FORMATS: () => VIDEO_FRAME_FORMATS,
95317
95574
  VideoSourceExtractionError: () => VideoSourceExtractionError,
95575
+ WebGpuUnavailableError: () => WebGpuUnavailableError,
95318
95576
  acquireBrowser: () => acquireBrowser,
95319
95577
  analyzeClipMediaFit: () => analyzeClipMediaFit,
95320
95578
  analyzeCompositionHdr: () => analyzeCompositionHdr,
@@ -95327,6 +95585,7 @@ __export(src_exports, {
95327
95585
  assertMediaPayload: () => assertMediaPayload,
95328
95586
  assertPublicHttpsUrl: () => assertPublicHttpsUrl,
95329
95587
  assertSwiftShader: () => assertSwiftShader,
95588
+ assertWebGpuAdapterAvailable: () => assertWebGpuAdapterAvailable,
95330
95589
  augmentPageNavigationTimeoutError: () => augmentPageNavigationTimeoutError,
95331
95590
  augmentProtocolTimeoutError: () => augmentProtocolTimeoutError,
95332
95591
  beginFrameCapture: () => beginFrameCapture,
@@ -95350,6 +95609,7 @@ __export(src_exports, {
95350
95609
  cloneCaptureWarnings: () => cloneCaptureWarnings,
95351
95610
  closeCaptureSession: () => closeCaptureSession,
95352
95611
  completeDeferredDrawElementInit: () => completeDeferredDrawElementInit,
95612
+ compositionRequiresWebGpu: () => compositionRequiresWebGpu,
95353
95613
  computeWorkerSizing: () => computeWorkerSizing,
95354
95614
  convertTransfer: () => convertTransfer,
95355
95615
  createCaptureSession: () => createCaptureSession,
@@ -112044,7 +112304,7 @@ var init_previewLifecycleOutput = __esm({
112044
112304
  // src/browser/gpuPolicy.ts
112045
112305
  var gpuPolicy_exports = {};
112046
112306
  __export(gpuPolicy_exports, {
112047
- assertWebGpuRequirement: () => assertWebGpuRequirement,
112307
+ assertWebGpuAdapterAvailable: () => assertWebGpuAdapterAvailable,
112048
112308
  compositionRequiresWebGpu: () => compositionRequiresWebGpu,
112049
112309
  resolveCaptureBrowserGpuMode: () => resolveCaptureBrowserGpuMode,
112050
112310
  resolveLocalBrowserGpuMode: () => resolveLocalBrowserGpuMode
@@ -112059,23 +112319,10 @@ async function resolveCaptureBrowserGpuMode(requestedMode, chromePath) {
112059
112319
  const { resolveBrowserGpuMode: resolveBrowserGpuMode2 } = await Promise.resolve().then(() => (init_src(), src_exports));
112060
112320
  return resolveBrowserGpuMode2(requestedMode, { chromePath });
112061
112321
  }
112062
- function compositionRequiresWebGpu(html) {
112063
- const compositionRoot2 = html.match(
112064
- /<[^>]*\bdata-composition-id(?:\s*=\s*("[^"]*"|'[^']*'|[^\s>]+))?[^>]*>/i
112065
- );
112066
- return compositionRoot2 ? /\bdata-requires-webgpu(?:\s|=|>)/i.test(compositionRoot2[0]) : false;
112067
- }
112068
- function assertWebGpuRequirement(html, requestedMode, resolvedMode) {
112069
- if (requestedMode !== "auto" || resolvedMode !== "software" || !compositionRequiresWebGpu(html)) {
112070
- return;
112071
- }
112072
- throw new Error(
112073
- "This composition declares data-requires-webgpu, but browser GPU auto-detection found no hardware GPU. Run on a WebGPU-capable host or set PRODUCER_BROWSER_GPU_MODE=hardware (or pass --browser-gpu on commands that support it) to require hardware explicitly; use --no-browser-gpu only when intentionally testing the composition's software fallback."
112074
- );
112075
- }
112076
112322
  var init_gpuPolicy = __esm({
112077
112323
  "src/browser/gpuPolicy.ts"() {
112078
112324
  "use strict";
112325
+ init_src();
112079
112326
  }
112080
112327
  });
112081
112328
 
@@ -123431,7 +123678,7 @@ function promoteTemplateCompositionId(rawComp, body) {
123431
123678
  }
123432
123679
  if (!templateCompositionId) return;
123433
123680
  if (body.querySelector("[data-composition-id]")) return;
123434
- const root = Array.from(body.children).find((el) => !NON_RENDERED_TAGS.has(el.tagName));
123681
+ const root = Array.from(body.children).find((el) => !NON_RENDERED_TAGS2.has(el.tagName));
123435
123682
  root?.setAttribute("data-composition-id", templateCompositionId);
123436
123683
  }
123437
123684
  function tagRootCompositionFile(bodyHtml, compPath) {
@@ -123981,7 +124228,7 @@ async function lintUncoveredHtml(adapter2, projectDir, htmlFiles, coveredFiles)
123981
124228
  for (const file of htmlFiles) {
123982
124229
  if (coveredFiles.has(file)) continue;
123983
124230
  const content = readFileSync92(join92(projectDir, file), "utf-8");
123984
- const result = await adapter2.lint(content, { filePath: file });
124231
+ const result = await adapter2.lint(content, { filePath: file, isSubComposition: true });
123985
124232
  for (const finding of result?.findings ?? []) {
123986
124233
  findings.push({ ...finding, file });
123987
124234
  }
@@ -124955,7 +125202,7 @@ function updateBackgroundRemovalProgress(state, event) {
124955
125202
  state.framesProcessed = event.index;
124956
125203
  state.avgMsPerFrame = event.avgMsPerFrame;
124957
125204
  }
124958
- var IGNORE_DIRS, SIGNATURE_TEXT_EXTENSIONS, SIGNATURE_EXCLUDED_DIRS, MAX_SIGNATURE_TEXT_BYTES, STUDIO_SIGNATURE_MANIFEST_PATHS, projectSignatureCache, COMPOSITION_ID_RE, MIME_TYPES2, SAMPLE_RATE, PEAK_COUNT, WAVEFORM_CACHE_VERSION, VIDEO_EXT2, AUDIO_EXT2, DEFAULT_KEEP_PER_FILE, RECEIPT_TTL_MS, receipts, consumeFileWriteReceipt, CompositionInsertionError, differential, behavioral, GSAP_MUTATION_CAPABILITIES, GSAP_WRITER_MIGRATION, atomicCutTail, HOLD_SYNC_MUTATION_TYPES, REGEXP_SPECIALS, NON_RENDERED_TAGS, VARIABLES_PAYLOAD_ERROR, PROJECT_SIGNATURE_META, GSAP_CDN_VERSION, GSAP_CDN_SCRIPT, GSAP_CUSTOM_EASE_CDN_SCRIPT, GSAP_MOTION_PATH_CDN_SCRIPT, GSAP_CDN_FALLBACK_SCRIPT, VALID_RESOLUTIONS, ThumbnailGenerationCoordinator, thumbnailGenerationCoordinator, THUMBNAIL_CACHE_VERSION, THUMBNAIL_MAX_OUTPUT_WIDTH, THUMBNAIL_MAX_OUTPUT_HEIGHT, STORYBOARD_MAX_OUTPUT_DIMENSION, THUMBNAIL_CACHE_MAX_BYTES, THUMBNAIL_CACHE_MAX_AGE_MS, prunedCacheDirs, MAX_FONT_RESULTS, GOOGLE_FONTS_METADATA_URL, GOOGLE_FONTS_FETCH_TIMEOUT_MS, cachedFonts, cachedGoogleFonts, GOOGLE_FONT_FALLBACKS, VIDEO_EXTENSIONS2, IMAGE_EXTENSIONS, VIDEO_OUTPUT_EXTENSIONS, QUALITIES, DEVICES;
125205
+ var IGNORE_DIRS, SIGNATURE_TEXT_EXTENSIONS, SIGNATURE_EXCLUDED_DIRS, MAX_SIGNATURE_TEXT_BYTES, STUDIO_SIGNATURE_MANIFEST_PATHS, projectSignatureCache, COMPOSITION_ID_RE, MIME_TYPES2, SAMPLE_RATE, PEAK_COUNT, WAVEFORM_CACHE_VERSION, VIDEO_EXT2, AUDIO_EXT2, DEFAULT_KEEP_PER_FILE, RECEIPT_TTL_MS, receipts, consumeFileWriteReceipt, CompositionInsertionError, differential, behavioral, GSAP_MUTATION_CAPABILITIES, GSAP_WRITER_MIGRATION, atomicCutTail, HOLD_SYNC_MUTATION_TYPES, REGEXP_SPECIALS, NON_RENDERED_TAGS2, VARIABLES_PAYLOAD_ERROR, PROJECT_SIGNATURE_META, GSAP_CDN_VERSION, GSAP_CDN_SCRIPT, GSAP_CUSTOM_EASE_CDN_SCRIPT, GSAP_MOTION_PATH_CDN_SCRIPT, GSAP_CDN_FALLBACK_SCRIPT, VALID_RESOLUTIONS, ThumbnailGenerationCoordinator, thumbnailGenerationCoordinator, THUMBNAIL_CACHE_VERSION, THUMBNAIL_MAX_OUTPUT_WIDTH, THUMBNAIL_MAX_OUTPUT_HEIGHT, STORYBOARD_MAX_OUTPUT_DIMENSION, THUMBNAIL_CACHE_MAX_BYTES, THUMBNAIL_CACHE_MAX_AGE_MS, prunedCacheDirs, MAX_FONT_RESULTS, GOOGLE_FONTS_METADATA_URL, GOOGLE_FONTS_FETCH_TIMEOUT_MS, cachedFonts, cachedGoogleFonts, GOOGLE_FONT_FALLBACKS, VIDEO_EXTENSIONS2, IMAGE_EXTENSIONS, VIDEO_OUTPUT_EXTENSIONS, QUALITIES, DEVICES;
124959
125206
  var init_dist9 = __esm({
124960
125207
  "../studio-server/dist/index.js"() {
124961
125208
  "use strict";
@@ -125169,7 +125416,7 @@ var init_dist9 = __esm({
125169
125416
  "delete-all-for-selector"
125170
125417
  ]);
125171
125418
  REGEXP_SPECIALS = /[.*+?^${}()|[\]\\]/g;
125172
- NON_RENDERED_TAGS = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "LINK", "META", "TEMPLATE", "NOSCRIPT"]);
125419
+ NON_RENDERED_TAGS2 = /* @__PURE__ */ new Set(["SCRIPT", "STYLE", "LINK", "META", "TEMPLATE", "NOSCRIPT"]);
125173
125420
  VARIABLES_PAYLOAD_ERROR = "variables must be a JSON object of {variableId: value}";
125174
125421
  PROJECT_SIGNATURE_META = "hyperframes-project-signature";
125175
125422
  GSAP_CDN_VERSION = "3.15.0";
@@ -126715,14 +126962,14 @@ async function openSettledCompositionPage(html, url, options) {
126715
126962
  const puppeteer = await import("puppeteer-core");
126716
126963
  const { buildChromeArgs: buildChromeArgs2 } = await Promise.resolve().then(() => (init_src(), src_exports));
126717
126964
  const requestedGpuMode = options.browserGpuMode ?? resolveCliChromeGpuMode();
126965
+ const requiresWebGpu = compositionRequiresWebGpu(html);
126718
126966
  const launch = async (executablePath) => {
126719
126967
  const resolvedGpuMode = await resolveCaptureBrowserGpuMode(requestedGpuMode, executablePath);
126720
- assertWebGpuRequirement(html, requestedGpuMode, resolvedGpuMode);
126721
126968
  return puppeteer.default.launch({
126722
126969
  headless: true,
126723
126970
  executablePath,
126724
126971
  args: buildChromeArgs2(
126725
- { ...viewport, captureMode: "screenshot" },
126972
+ { ...viewport, captureMode: "screenshot", requiresWebGpu },
126726
126973
  { browserGpuMode: resolvedGpuMode }
126727
126974
  )
126728
126975
  });
@@ -126763,6 +127010,7 @@ ${remediation}`,
126763
127010
  waitUntil: "domcontentloaded",
126764
127011
  timeout: resolveDiagnosticNavigationTimeoutMs(process.env, options.navigationTimeoutMs)
126765
127012
  });
127013
+ await assertWebGpuAdapterAvailable(page, requiresWebGpu);
126766
127014
  const renderReadyTimedOut = !await waitForCompositionSettle(page, options);
126767
127015
  return { browser: chromeBrowser, page, renderReadyTimedOut };
126768
127016
  } catch (err) {
@@ -135113,22 +135361,16 @@ function coalesceHeadStylesAndBodyScripts2(html) {
135113
135361
  }
135114
135362
  }
135115
135363
  if (body) {
135116
- const bodyScripts = Array.from(body.querySelectorAll("script")).filter((el) => {
135117
- const src = (el.getAttribute("src") || "").trim();
135118
- if (src) return false;
135119
- const type = (el.getAttribute("type") || "").trim().toLowerCase();
135120
- return !type || type === "text/javascript" || type === "application/javascript";
135121
- });
135122
- if (bodyScripts.length > 0) {
135123
- const mergedJs = bodyScripts.map((el) => (el.textContent || "").trim()).filter(Boolean).join("\n;\n").trim();
135124
- for (const el of bodyScripts) {
135125
- el.remove();
135126
- }
135127
- if (mergedJs) {
135128
- const script = document2.createElement("script");
135129
- script.textContent = mergedJs;
135130
- body.appendChild(script);
135131
- }
135364
+ for (const { members, anchor } of inlineScriptRuns(
135365
+ Array.from(body.querySelectorAll("script"))
135366
+ )) {
135367
+ const mergedJs = members.map((el) => (el.textContent || "").trim()).filter(Boolean).join("\n;\n").trim();
135368
+ for (const el of members) el.remove();
135369
+ if (!mergedJs) continue;
135370
+ const script = document2.createElement("script");
135371
+ script.textContent = mergedJs;
135372
+ if (anchor) anchor.before(script);
135373
+ else body.appendChild(script);
135132
135374
  }
135133
135375
  }
135134
135376
  return document2.toString();
@@ -136584,7 +136826,8 @@ async function runProbeStage(input2) {
136584
136826
  quality: captureFormat === "png" ? void 0 : 80,
136585
136827
  variables: job.config.variables,
136586
136828
  deviceScaleFactor,
136587
- motionBlur
136829
+ motionBlur,
136830
+ requiresWebGpu: compositionRequiresWebGpu(compiled.html)
136588
136831
  };
136589
136832
  const PROBE_MAX_ATTEMPTS = 2;
136590
136833
  for (let attempt = 1; attempt <= PROBE_MAX_ATTEMPTS; attempt++) {
@@ -142435,7 +142678,8 @@ async function executeRenderPipeline(input2) {
142435
142678
  skipReadinessVideoIds: videoReadinessSkipIds,
142436
142679
  // Probe-resolved duration: drawElement self-verification derives its
142437
142680
  // sample frame indices from this so they land inside the drained range.
142438
- compositionDurationSeconds: job.duration
142681
+ compositionDurationSeconds: job.duration,
142682
+ requiresWebGpu: compositionRequiresWebGpu(compiled.html)
142439
142683
  });
142440
142684
  const createRenderVideoFrameInjector = () => createVideoFrameInjector(frameLookup, {
142441
142685
  frameDataUriCacheLimit: cfg.frameDataUriCacheLimit,
@@ -147649,7 +147893,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
147649
147893
  ...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
147650
147894
  // lock the BeginFrame warmup loop to a fixed iteration count so
147651
147895
  // `beginFrameTimeTicks` is host-independent. Only chunks ever set this.
147652
- lockWarmupTicks: true
147896
+ lockWarmupTicks: true,
147897
+ requiresWebGpu: compositionRequiresWebGpu(
147898
+ readFileSync44(join81(compiledDir, "index.html"), "utf-8")
147899
+ )
147653
147900
  };
147654
147901
  const chunkWorkerCount = calculateOptimalWorkers(framesInChunk, void 0, cfg);
147655
147902
  let session = null;
@@ -149297,11 +149544,11 @@ function createStudioServer(options) {
149297
149544
  },
149298
149545
  async lint(html, opts) {
149299
149546
  const { lintHyperframeHtml: lintHyperframeHtml2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports2));
149300
- return await lintHyperframeHtml2(html, opts);
149547
+ return await lintHyperframeHtml2(html, { ...opts, host: "studio" });
149301
149548
  },
149302
149549
  async lintProject(dir) {
149303
149550
  const { lintProject: lintProject2 } = await Promise.resolve().then(() => (init_dist4(), dist_exports2));
149304
- return await lintProject2(dir);
149551
+ return await lintProject2(dir, void 0, { host: "studio" });
149305
149552
  },
149306
149553
  runtimeUrl: "/api/runtime.js",
149307
149554
  rendersDir: () => join86(projectDir, "renders"),
@@ -149410,13 +149657,7 @@ function createStudioServer(options) {
149410
149657
  return null;
149411
149658
  }
149412
149659
  const sourcePath = join86(opts.project.dir, opts.compPath);
149413
- if (existsSync70(sourcePath)) {
149414
- assertWebGpuRequirement(
149415
- readFileSync46(sourcePath, "utf-8"),
149416
- session.requestedGpuMode,
149417
- session.resolvedGpuMode
149418
- );
149419
- }
149660
+ const requiresWebGpu = existsSync70(sourcePath) ? compositionRequiresWebGpu(readFileSync46(sourcePath, "utf-8")) : false;
149420
149661
  let page = null;
149421
149662
  const closePage = () => void page?.close().catch(() => {
149422
149663
  });
@@ -149432,6 +149673,7 @@ function createStudioServer(options) {
149432
149673
  deviceScaleFactor: thumbnailDeviceScaleFactor(opts)
149433
149674
  });
149434
149675
  await page.goto(opts.previewUrl, { waitUntil: "domcontentloaded", timeout: 1e4 });
149676
+ await assertWebGpuAdapterAvailable(page, requiresWebGpu);
149435
149677
  await page.waitForFunction(
149436
149678
  () => {
149437
149679
  const w3 = window;
@@ -160227,7 +160469,12 @@ async function runLayoutAudit(projectDir, opts) {
160227
160469
  const { ensureBrowser: ensureBrowser2 } = await Promise.resolve().then(() => (init_manager2(), manager_exports2));
160228
160470
  const puppeteer = await import("puppeteer-core");
160229
160471
  const { buildChromeArgs: buildChromeArgs2 } = await Promise.resolve().then(() => (init_src(), src_exports));
160230
- const { assertWebGpuRequirement: assertWebGpuRequirement2, resolveCaptureBrowserGpuMode: resolveCaptureBrowserGpuMode2, resolveLocalBrowserGpuMode: resolveLocalBrowserGpuMode2 } = await Promise.resolve().then(() => (init_gpuPolicy(), gpuPolicy_exports));
160472
+ const {
160473
+ assertWebGpuAdapterAvailable: assertWebGpuAdapterAvailable2,
160474
+ compositionRequiresWebGpu: compositionRequiresWebGpu2,
160475
+ resolveCaptureBrowserGpuMode: resolveCaptureBrowserGpuMode2,
160476
+ resolveLocalBrowserGpuMode: resolveLocalBrowserGpuMode2
160477
+ } = await Promise.resolve().then(() => (init_gpuPolicy(), gpuPolicy_exports));
160231
160478
  const html = await bundleProjectHtml(projectDir);
160232
160479
  const server = await serveStaticProjectHtml(
160233
160480
  projectDir,
@@ -160242,12 +160489,12 @@ async function runLayoutAudit(projectDir, opts) {
160242
160489
  requestedGpuMode,
160243
160490
  browser.executablePath
160244
160491
  );
160245
- assertWebGpuRequirement2(html, requestedGpuMode, resolvedGpuMode);
160492
+ const requiresWebGpu = compositionRequiresWebGpu2(html);
160246
160493
  chromeBrowser = await puppeteer.default.launch({
160247
160494
  headless: true,
160248
160495
  executablePath: browser.executablePath,
160249
160496
  args: buildChromeArgs2(
160250
- { width: 1920, height: 1080, captureMode: "screenshot" },
160497
+ { width: 1920, height: 1080, captureMode: "screenshot", requiresWebGpu },
160251
160498
  { browserGpuMode: resolvedGpuMode }
160252
160499
  )
160253
160500
  });
@@ -160258,6 +160505,7 @@ async function runLayoutAudit(projectDir, opts) {
160258
160505
  waitUntil: "domcontentloaded",
160259
160506
  timeout: resolveDiagnosticNavigationTimeoutMs()
160260
160507
  });
160508
+ await assertWebGpuAdapterAvailable2(page, requiresWebGpu);
160261
160509
  await alignViewportToComposition(page, server.url);
160262
160510
  await page.waitForFunction(() => !!window.__timelines, {
160263
160511
  timeout: opts.timeout
@@ -160964,17 +161212,21 @@ async function validateInBrowser(project, opts) {
160964
161212
  const puppeteer = await import("puppeteer-core");
160965
161213
  const { buildChromeArgs: buildChromeArgs2, analyzeClipMediaFit: analyzeClipMediaFit2 } = await Promise.resolve().then(() => (init_src(), src_exports));
160966
161214
  const requestedGpuMode = resolveCliChromeGpuMode();
160967
- const { assertWebGpuRequirement: assertWebGpuRequirement2, resolveCaptureBrowserGpuMode: resolveCaptureBrowserGpuMode2 } = await Promise.resolve().then(() => (init_gpuPolicy(), gpuPolicy_exports));
161215
+ const {
161216
+ assertWebGpuAdapterAvailable: assertWebGpuAdapterAvailable2,
161217
+ compositionRequiresWebGpu: compositionRequiresWebGpu2,
161218
+ resolveCaptureBrowserGpuMode: resolveCaptureBrowserGpuMode2
161219
+ } = await Promise.resolve().then(() => (init_gpuPolicy(), gpuPolicy_exports));
160968
161220
  const resolvedGpuMode = await resolveCaptureBrowserGpuMode2(
160969
161221
  requestedGpuMode,
160970
161222
  browser.executablePath
160971
161223
  );
160972
- assertWebGpuRequirement2(html, requestedGpuMode, resolvedGpuMode);
161224
+ const requiresWebGpu = compositionRequiresWebGpu2(html);
160973
161225
  const chromeBrowser = await puppeteer.default.launch({
160974
161226
  headless: true,
160975
161227
  executablePath: browser.executablePath,
160976
161228
  args: buildChromeArgs2(
160977
- { ...viewport, captureMode: "screenshot" },
161229
+ { ...viewport, captureMode: "screenshot", requiresWebGpu },
160978
161230
  { browserGpuMode: resolvedGpuMode }
160979
161231
  )
160980
161232
  });
@@ -161026,6 +161278,7 @@ async function validateInBrowser(project, opts) {
161026
161278
  if (hinted) throw hinted;
161027
161279
  throw err;
161028
161280
  }
161281
+ await assertWebGpuAdapterAvailable2(page, requiresWebGpu);
161029
161282
  await new Promise((r2) => setTimeout(r2, opts.timeout ?? 3e3));
161030
161283
  for (const w3 of await auditClipDurations(page, analyzeClipMediaFit2, opts.timeout ?? 3e3)) {
161031
161284
  warnings.push(w3);
@@ -166619,12 +166872,12 @@ async function openCompositionPage(html, url, executablePath) {
166619
166872
  const size = resolveCompositionViewportFromHtml(html);
166620
166873
  const requestedGpuMode = resolveLocalBrowserGpuMode();
166621
166874
  const resolvedGpuMode = await resolveCaptureBrowserGpuMode(requestedGpuMode, executablePath);
166622
- assertWebGpuRequirement(html, requestedGpuMode, resolvedGpuMode);
166875
+ const requiresWebGpu = compositionRequiresWebGpu(html);
166623
166876
  const browser = await puppeteer.default.launch({
166624
166877
  headless: true,
166625
166878
  executablePath,
166626
166879
  args: buildChromeArgs2(
166627
- { ...size, captureMode: "screenshot" },
166880
+ { ...size, captureMode: "screenshot", requiresWebGpu },
166628
166881
  { browserGpuMode: resolvedGpuMode }
166629
166882
  )
166630
166883
  });
@@ -166632,6 +166885,7 @@ async function openCompositionPage(html, url, executablePath) {
166632
166885
  const navigationTimeout = resolveDiagnosticNavigationTimeoutMs();
166633
166886
  await page.setViewport(size);
166634
166887
  await page.goto(url, { waitUntil: "domcontentloaded", timeout: navigationTimeout });
166888
+ await assertWebGpuAdapterAvailable(page, requiresWebGpu);
166635
166889
  await page.waitForFunction(() => !!window.__timelines, {
166636
166890
  timeout: 1e4
166637
166891
  }).catch(() => {
@@ -167784,7 +168038,7 @@ import { existsSync as existsSync94, readFileSync as readFileSync74 } from "fs";
167784
168038
  import { resolve as resolve64, dirname as dirname53 } from "path";
167785
168039
  function countRenderableDescendants(root) {
167786
168040
  return Array.from(root.querySelectorAll("*")).filter(
167787
- (el) => !NON_RENDERED_TAGS2.has(el.tagName.toLowerCase())
168041
+ (el) => !NON_RENDERED_TAGS3.has(el.tagName.toLowerCase())
167788
168042
  ).length;
167789
168043
  }
167790
168044
  function estimateDurationFromScripts(root) {
@@ -167944,7 +168198,7 @@ function parseSubComposition(html, fallbackId, fallbackWidth, fallbackHeight) {
167944
168198
  }
167945
168199
  return { id, duration, width, height, elementCount };
167946
168200
  }
167947
- var examples18, NON_RENDERED_TAGS2, compositions_default;
168201
+ var examples18, NON_RENDERED_TAGS3, compositions_default;
167948
168202
  var init_compositions = __esm({
167949
168203
  "src/commands/compositions.ts"() {
167950
168204
  "use strict";
@@ -167958,7 +168212,7 @@ var init_compositions = __esm({
167958
168212
  ["List compositions in the current project", "hyperframes compositions"],
167959
168213
  ["Output as JSON", "hyperframes compositions --json"]
167960
168214
  ];
167961
- NON_RENDERED_TAGS2 = /* @__PURE__ */ new Set(["script", "style", "link", "meta", "template"]);
168215
+ NON_RENDERED_TAGS3 = /* @__PURE__ */ new Set(["script", "style", "link", "meta", "template"]);
167962
168216
  compositions_default = defineCommand({
167963
168217
  meta: { name: "compositions", description: "List all compositions in a project" },
167964
168218
  args: {