@hyperframes/core 0.8.212 → 0.8.213

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.
@@ -99,6 +99,7 @@ export interface KindFeatures {
99
99
  syllables: number;
100
100
  /** Share of 50 ms frames within 20 dB of the loud end (95th percentile), 0–1: high for beds and speech, low for hits. */
101
101
  sustain: number;
102
+ voiced?: number;
102
103
  }
103
104
  export declare const signalDuration: (signal: AudioSignal) => number;
104
105
  /** Per-sample energy. Channels are squared first, so a reversed channel stays audible. */
@@ -112,10 +113,11 @@ export declare function silence(signal: AudioSignal, options?: SilenceOptions):
112
113
  export declare function kindFeatures(signal: AudioSignal): KindFeatures;
113
114
  /**
114
115
  * A first guess at what the audio is. Length decides most of it: beds run long, effects short. Long audio is voice
115
- * when its envelope moves at syllable rate without a beat; short audio is voice when it also holds its level.
116
+ * when its envelope moves at syllable rate without a beat; short audio also holds its level. Both need a voice pitch.
116
117
  */
117
118
  export declare function classify(signal: AudioSignal): KindGuess;
118
119
  export * from "./audioAnalysisLadder.js";
120
+ export * from "./audioPitch.js";
119
121
  export * from "./audioRhythm.js";
120
122
  export * from "./audioSections.js";
121
123
  export * from "./audioSnap.js";
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { finish } from "./audioSteps.js";
6
6
  import { fft } from "./fft.js";
7
+ import { voicedShare } from "./audioPitch.js";
7
8
  import { rhythm } from "./audioRhythm.js";
8
9
  import { sections } from "./audioSections.js";
9
10
  const DB_FLOOR = -120;
@@ -416,26 +417,33 @@ export function kindFeatures(signal) {
416
417
  sustain: round(sustainShare(signal)),
417
418
  };
418
419
  }
420
+ const VOICED_SPEECH = 0.15;
421
+ const VOICED_STEADY_SPEECH = 0.25;
419
422
  /**
420
423
  * A first guess at what the audio is. Length decides most of it: beds run long, effects short. Long audio is voice
421
- * when its envelope moves at syllable rate without a beat; short audio is voice when it also holds its level.
424
+ * when its envelope moves at syllable rate without a beat; short audio also holds its level. Both need a voice pitch.
422
425
  */
423
426
  export function classify(signal) {
424
427
  const features = kindFeatures(signal);
425
428
  const { duration, regularity, syllables, sustain } = features;
426
429
  const speechLike = syllables >= 0.35 && regularity < 0.35;
430
+ const voiced = () => (features.voiced ??= round(voicedShare(signal.channels, signal.sampleRate)));
431
+ const pitched = () => voiced() >= VOICED_SPEECH;
427
432
  if (duration >= 8) {
428
433
  if (sustain < 0.4)
429
434
  return { value: "sfx", confidence: 0.6, features };
430
- if (speechLike)
435
+ if (speechLike && pitched())
431
436
  return { value: "voice", confidence: round(Math.min(0.9, 0.4 + syllables)), features };
437
+ if (regularity < 0.2 && syllables >= 0.17 && voiced() >= VOICED_STEADY_SPEECH)
438
+ return { value: "voice", confidence: 0.6, features };
432
439
  return { value: "music", confidence: round(Math.min(0.9, 0.5 + regularity)), features };
433
440
  }
434
- if (duration >= 1.5 && syllables >= 0.17 && regularity < 0.3 && sustain >= 0.7)
441
+ if (duration >= 1.5 && syllables >= 0.17 && regularity < 0.3 && sustain >= 0.7 && pitched())
435
442
  return { value: "voice", confidence: speechLike ? 0.8 : 0.6, features };
436
443
  return { value: "sfx", confidence: duration <= 2 ? 0.85 : 0.65, features };
437
444
  }
438
445
  export * from "./audioAnalysisLadder.js";
446
+ export * from "./audioPitch.js";
439
447
  export * from "./audioRhythm.js";
440
448
  export * from "./audioSections.js";
441
449
  export * from "./audioSnap.js";
@@ -0,0 +1,12 @@
1
+ /** The rate pitch is tracked at: 8 kHz keeps the 70–500 Hz search cheap. */
2
+ export declare const PITCH_RATE = 8000;
3
+ export interface PitchPoint {
4
+ t: number;
5
+ hz: number;
6
+ }
7
+ /** The fundamental of each voiced 40 ms frame of 8 kHz mono, by YIN's cumulative mean normalized difference. */
8
+ export declare function pitchTrack(pcm: Float32Array): PitchPoint[];
9
+ /** Mono at {@link PITCH_RATE}: channels averaged, a box filter as wide as the step against aliasing, then sampled. */
10
+ export declare function atPitchRate(channels: readonly Float32Array[], sampleRate: number): Float32Array;
11
+ /** The share of 40 ms frames with a fundamental in the voice range; hits, whooshes and noise have none. */
12
+ export declare function voicedShare(channels: readonly Float32Array[], sampleRate: number): number;
@@ -0,0 +1,91 @@
1
+ /** The rate pitch is tracked at: 8 kHz keeps the 70–500 Hz search cheap. */
2
+ export const PITCH_RATE = 8000;
3
+ const FRAME = 320;
4
+ const HOP = 320;
5
+ const LOW_HZ = 70;
6
+ const HIGH_HZ = 500;
7
+ const VOICED_DB = -35;
8
+ const PERIODIC = 0.15;
9
+ const MIN_LAG = Math.floor(PITCH_RATE / HIGH_HZ);
10
+ const MAX_LAG = Math.ceil(PITCH_RATE / LOW_HZ);
11
+ function voiced(channels, start) {
12
+ let energy = 0;
13
+ for (const pcm of channels)
14
+ for (let i = 0; i < FRAME; i++)
15
+ energy += pcm[start + i] ** 2;
16
+ return 10 * Math.log10(energy / (FRAME * channels.length) + 1e-12) >= VOICED_DB;
17
+ }
18
+ function difference(channels, start, tau) {
19
+ let sum = 0;
20
+ for (const pcm of channels) {
21
+ for (let i = 0; i < FRAME; i++) {
22
+ const delta = pcm[start + i] - pcm[start + i + tau];
23
+ sum += delta * delta;
24
+ }
25
+ }
26
+ return sum;
27
+ }
28
+ /** The first lag whose normalized difference dips under the threshold, followed down to its minimum; -1 if none. */
29
+ function periodOf(channels, start, d) {
30
+ let running = 0;
31
+ let lag = -1;
32
+ for (let tau = 1; tau <= MAX_LAG; tau++) {
33
+ const sum = difference(channels, start, tau);
34
+ running += sum;
35
+ d[tau] = running > 0 ? (sum * tau) / running : 1;
36
+ if (lag === -1) {
37
+ if (tau >= MIN_LAG && d[tau] < PERIODIC)
38
+ lag = tau;
39
+ }
40
+ else if (d[tau] > d[tau - 1])
41
+ break;
42
+ else if (d[tau] < d[lag])
43
+ lag = tau;
44
+ }
45
+ return lag;
46
+ }
47
+ /** The fundamental of each voiced 40 ms frame of 8 kHz mono, by YIN's cumulative mean normalized difference. */
48
+ export function pitchTrack(pcm) {
49
+ return channelPitchTrack([pcm]);
50
+ }
51
+ function channelPitchTrack(channels) {
52
+ const length = channels[0]?.length ?? 0;
53
+ const out = [];
54
+ const d = new Float64Array(MAX_LAG + 1);
55
+ for (let start = 0; start + FRAME + MAX_LAG <= length; start += HOP) {
56
+ if (!voiced(channels, start))
57
+ continue;
58
+ const lag = periodOf(channels, start, d);
59
+ if (lag > 0)
60
+ out.push({ t: (start + FRAME / 2) / PITCH_RATE, hz: PITCH_RATE / lag });
61
+ }
62
+ return out;
63
+ }
64
+ /** Mono at {@link PITCH_RATE}: channels averaged, a box filter as wide as the step against aliasing, then sampled. */
65
+ export function atPitchRate(channels, sampleRate) {
66
+ const length = channels[0]?.length ?? 0;
67
+ if (!length || !channels.length)
68
+ return new Float32Array(0);
69
+ const step = sampleRate / PITCH_RATE;
70
+ const width = Math.max(1, Math.round(step));
71
+ const sums = new Float64Array(length + 1);
72
+ for (let i = 0; i < length; i++) {
73
+ let mixed = 0;
74
+ for (const channel of channels)
75
+ mixed += channel[i];
76
+ sums[i + 1] = sums[i] + mixed / channels.length;
77
+ }
78
+ const out = new Float32Array(Math.floor(length / step));
79
+ for (let n = 0; n < out.length; n++) {
80
+ const from = Math.floor(n * step);
81
+ const to = Math.min(length, from + width);
82
+ out[n] = (sums[to] - sums[from]) / (to - from);
83
+ }
84
+ return out;
85
+ }
86
+ /** The share of 40 ms frames with a fundamental in the voice range; hits, whooshes and noise have none. */
87
+ export function voicedShare(channels, sampleRate) {
88
+ const pitched = channels.map((channel) => atPitchRate([channel], sampleRate));
89
+ const frames = Math.floor((pitched[0]?.length ?? 0) / HOP);
90
+ return frames ? channelPitchTrack(pitched).length / frames : 0;
91
+ }
@@ -888,7 +888,11 @@ async function bundleProject(projectDir, options) {
888
888
  const rawHtml = readSource(mountedShell ?? entryHtml);
889
889
  const compiled = await compileHtml(rawHtml, sourceDir, options?.probeMediaDuration);
890
890
  if (options?.staticGuard !== false && !mountedShell) {
891
- const staticGuard = await validateHyperframeHtmlContract(compiled);
891
+ const entryRel = relative(projectDir, indexPath).replace(/\\/g, "/");
892
+ const staticGuard = await validateHyperframeHtmlContract(compiled, {
893
+ dir: projectDir,
894
+ ...(entryRel !== "index.html" && { compSrcPath: entryRel }),
895
+ });
892
896
  if (!staticGuard.isValid) {
893
897
  console.warn(`[StaticGuard] Invalid HyperFrame contract: ${staticGuard.missingKeys.join("; ")}`);
894
898
  }
@@ -4,4 +4,7 @@ export type HyperframeStaticGuardResult = {
4
4
  missingKeys: string[];
5
5
  failureReason: HyperframeStaticFailureReason | null;
6
6
  };
7
- export declare function validateHyperframeHtmlContract(html: string): Promise<HyperframeStaticGuardResult>;
7
+ export declare function validateHyperframeHtmlContract(html: string, project?: {
8
+ dir: string;
9
+ compSrcPath?: string;
10
+ }): Promise<HyperframeStaticGuardResult>;
@@ -1,6 +1,8 @@
1
- import { lintHyperframeHtml } from "@hyperframes/lint";
2
- export async function validateHyperframeHtmlContract(html) {
3
- const result = await lintHyperframeHtml(html);
1
+ import { collectExternalStyles, lintHyperframeHtml } from "@hyperframes/lint";
2
+ export async function validateHyperframeHtmlContract(html, project) {
3
+ const result = await lintHyperframeHtml(html, project && {
4
+ externalStyles: collectExternalStyles(project.dir, html, project.compSrcPath),
5
+ });
4
6
  const missingKeys = result.findings
5
7
  .filter((finding) => finding.severity === "error")
6
8
  .map((finding) => finding.message);
@@ -36,7 +36,7 @@ export declare function parseFontFamilyValue(value: string): string[];
36
36
  */
37
37
  export declare function normalizeSystemFontPrimaryFamilies(html: string, deterministicPrimary?: string): string;
38
38
  /** Surfaces font-family is declared on in served HTML. */
39
- export type FontFamilySurface = "font-family" | "data-font-family";
39
+ export type FontFamilySurface = "font-family" | "font" | "data-font-family";
40
40
  export type FontFamilyDeclaration = {
41
41
  surface: FontFamilySurface;
42
42
  declaration: string;
@@ -213,16 +213,30 @@ function collectCssCustomProperties(css, customProperties) {
213
213
  customProperties.set(decl.prop, decl.value);
214
214
  });
215
215
  }
216
+ const FONT_SIZE_KEYWORD = /^(?:xx-small|x-small|small|medium|large|x-large|xx-large|xxx-large|smaller|larger)$/i;
217
+ const FONT_SIZE_TOKEN = /^(?:\d*\.?\d+[a-z%]+|(?:calc|min|max|clamp|var)\(.*\))(?:\/\S+)?$/i;
218
+ function fontShorthandFamilies(value) {
219
+ for (const token of value.matchAll(/(?:[^\s(]|\([^)]*\))+/g)) {
220
+ const size = token[0].split("/")[0] ?? "";
221
+ if (FONT_SIZE_KEYWORD.test(size) || FONT_SIZE_TOKEN.test(token[0])) {
222
+ return value.slice(token.index + token[0].length).trim();
223
+ }
224
+ }
225
+ return "";
226
+ }
216
227
  function* iterateCssRootFontFamilyDeclarations(root) {
217
228
  const declarations = [];
218
229
  root.walkDecls((decl) => {
219
- if (decl.prop.toLowerCase() !== "font-family")
230
+ const prop = decl.prop.toLowerCase();
231
+ if (prop !== "font-family" && prop !== "font")
220
232
  return;
221
233
  if (isFontFaceDeclaration(decl))
222
234
  return;
223
- const declaration = decl.value;
235
+ const declaration = prop === "font" ? fontShorthandFamilies(decl.value) : decl.value;
236
+ if (!declaration)
237
+ return;
224
238
  declarations.push({
225
- surface: "font-family",
239
+ surface: prop,
226
240
  declaration,
227
241
  families: parseFontFamilyValue(declaration),
228
242
  });
@@ -230,7 +244,7 @@ function* iterateCssRootFontFamilyDeclarations(root) {
230
244
  yield* declarations;
231
245
  }
232
246
  function* iterateCssFontFamilyDeclarations(css) {
233
- if (!/font-family/i.test(css))
247
+ if (!/font(?:-family)?\s*:/i.test(css))
234
248
  return;
235
249
  const root = parseCssRoot(css);
236
250
  if (!root)
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/build-position-edits-render.ts; do not edit.
2
- const POSITION_EDITS_RENDER_IIFE = '"use strict";(()=>{function x(){return globalThis}function P(e,t){if(typeof window>"u")return;let o=x(),r=o.__hf?.onSwallowed;if(r)try{r({label:e,error:t})}catch(s){}(o.__hfDebug||o.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${e} swallowed:`,t)}var T=null;function R(e,t){if(T)try{T({source:"hf-preview",type:"analytics",event:e,properties:t??{}})}catch(o){P("runtime.analytics.site1",o)}}var M="http://www.w3.org/1999/xhtml";function N(e){return e!=null&&e.nodeType===1}function _(e){return N(e)&&(e.namespaceURI===M||e.namespaceURI==="http://www.w3.org/2000/svg")}var y="data-hf-edit-base-x",w="data-hf-edit-base-y",m="data-hf-edit-original-translate",k=e=>{let t=parseFloat(e??"");return Number.isFinite(t)?t:0},C=e=>{let t=[],o=0,r="";for(let s of e.trim())s==="("&&(o+=1),s===")"&&(o=Math.max(0,o-1)),/\\s/.test(s)&&o===0?(r&&t.push(r),r=""):r+=s;return r&&t.push(r),t},$=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,E=(e,t)=>$.test(e)&&$.test(t)?`${parseFloat(e)+parseFloat(t)}px`:`calc(${e} + ${t})`,O=(e,t,o)=>{if(!e||e==="none")return`${t} ${o}`;let[r,s,a]=C(e);if(r===void 0)return`${t} ${o}`;if(s===void 0)return`${E(r,t)} ${o}`;let i=a===void 0?"":` ${a}`;return`${E(r,t)} ${E(s,o)}${i}`},j=e=>{try{e.ownerDocument.defaultView?.gsap?.getProperty?.(e,"x")}catch{}},V=e=>{let t=e.style.getPropertyValue("translate").trim();if(t)return t==="none"?"":t;try{let o=e.ownerDocument.defaultView,r=o?o.getComputedStyle(e).getPropertyValue("translate").trim():"";return r==="none"?"":r}catch{return""}},b=new WeakMap;function B(e,t){let o=b.get(e);if(!t?.force&&o!==void 0&&e.style.getPropertyValue("translate")!==o){R("position_edit_fold_skipped",{hfId:e.getAttribute("data-hf-id")});return}let r=k(e.getAttribute("data-x"))-k(e.getAttribute(y)),s=k(e.getAttribute("data-y"))-k(e.getAttribute(w));e.hasAttribute(m)||e.setAttribute(m,V(e)),o===void 0&&j(e);let a=e.getAttribute(m)??"",i=O(a,`${r}px`,`${s}px`);e.style.setProperty("translate",i),b.set(e,e.style.getPropertyValue("translate"))}function h(e,t){let o=e.querySelectorAll(`[${m}]:not([${y}]):not([${w}])`);for(let a=0;a<o.length;a++){let i=o[a];if(i===void 0||!_(i))continue;let g=i.getAttribute(m)??"";g===""?i.style.removeProperty("translate"):i.style.setProperty("translate",g),i.removeAttribute(m),b.delete(i)}let r=e.querySelectorAll(`[${y}], [${w}]`),s=0;for(let a=0;a<r.length;a++){let i=r[a];i===void 0||!_(i)||(B(i,t),s+=1)}return s}var L="__hfPositionEditsSeekReapplyWrapped",D=new WeakSet,F=new WeakMap,H=new WeakMap;function W(e){let t=e,o=()=>{try{h(t.document)}catch{}},r=n=>typeof n=="function"&&(D.has(n)||!!n[L]),s=n=>{D.add(n);try{Object.defineProperty(n,L,{value:!0})}catch{}},a=n=>{if(typeof n!="function"||r(n))return n;let l=function(...u){let d=n.apply(this,u);return o(),d};return s(l),l},i=(n,l)=>{let u=F.get(n);if(u?.has(l))return!0;let d=Object.getOwnPropertyDescriptor(n,l);if(d?.configurable===!1){let c=n[l];return typeof c=="function"&&(n[l]=a(c),o()),!1}let p=n[l],f=d?.set;return Object.defineProperty(n,l,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>p,set:c=>{p=a(c),f?.call(n,c)}}),p=a(p),u??(u=new Set),u.add(l),F.set(n,u),o(),!0},g=(n,l)=>{let u=H.get(t),d=Object.getOwnPropertyDescriptor(t,n);if(!u?.has(n)){if(d?.configurable===!1){let c=t[n];return c?i(c,l):!1}let f=t[n];Object.defineProperty(t,n,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>f,set:c=>{f=c,f&&i(f,l)}}),u??(u=new Set),u.add(n),H.set(t,u)}let p=t[n];return p?i(p,l):!1},S=()=>{let n=g("__hf","seek"),l=g("__player","renderSeek");return n&&l};if(S())return;let v=120,A=t.setInterval(()=>{if(S()){t.clearInterval(A);return}v-=1,v<=0&&t.clearInterval(A)},50)}function I(){document.querySelector(`[${y}], [${w}]`)&&(h(document),W(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",I,{once:!0}):I();})();\n';
2
+ const POSITION_EDITS_RENDER_IIFE = '"use strict";(()=>{function x(){return globalThis}function T(e,t){if(typeof window>"u")return;let o=x(),r=o.__hf?.onSwallowed;if(r)try{r({label:e,error:t})}catch(s){}(o.__hfDebug||o.__HYPERFRAMES_DEBUG)&&console.debug(`[hyperframes] ${e} swallowed:`,t)}var P=null;function R(e,t){if(P)try{P({source:"hf-preview",type:"analytics",event:e,properties:t??{}})}catch(o){T("runtime.analytics.site1",o)}}var I="http://www.w3.org/1999/xhtml";function N(e){return e!=null&&e.nodeType===1}function _(e){return N(e)&&(e.namespaceURI===I||e.namespaceURI==="http://www.w3.org/2000/svg")}var y="data-hf-edit-base-x",w="data-hf-edit-base-y",m="data-hf-edit-original-translate",k=e=>{let t=parseFloat(e??"");return Number.isFinite(t)?t:0},C=e=>{let t=[],o=0,r="";for(let s of e.trim())s==="("&&(o+=1),s===")"&&(o=Math.max(0,o-1)),/\\s/.test(s)&&o===0?(r&&t.push(r),r=""):r+=s;return r&&t.push(r),t},$=/^-?(?:\\d+(?:\\.\\d+)?|\\.\\d+)px$/,E=(e,t)=>$.test(e)&&$.test(t)?`${parseFloat(e)+parseFloat(t)}px`:`calc(${e} + ${t})`,O=(e,t,o)=>{if(!e||e==="none")return`${t} ${o}`;let[r,s,a]=C(e);if(r===void 0)return`${t} ${o}`;if(s===void 0)return`${E(r,t)} ${o}`;let i=a===void 0?"":` ${a}`;return`${E(r,t)} ${E(s,o)}${i}`},j=e=>{try{e.ownerDocument.defaultView?.gsap?.getProperty?.(e,"x")}catch{}},V=e=>{let t=e.style.getPropertyValue("translate").trim();if(t)return t==="none"?"":t;try{let o=e.ownerDocument.defaultView,r=o?o.getComputedStyle(e).getPropertyValue("translate").trim():"";return r==="none"?"":r}catch{return""}},b=new WeakMap;function B(e,t){let o=b.get(e);if(!t?.force&&o!==void 0&&e.style.getPropertyValue("translate")!==o){R("position_edit_fold_skipped",{hfId:e.getAttribute("data-hf-id")});return}let r=k(e.getAttribute("data-x"))-k(e.getAttribute(y)),s=k(e.getAttribute("data-y"))-k(e.getAttribute(w));e.hasAttribute(m)||e.setAttribute(m,V(e)),o===void 0&&j(e);let a=e.getAttribute(m)??"",i=O(a,`${r}px`,`${s}px`);e.style.setProperty("translate",i),b.set(e,e.style.getPropertyValue("translate"))}function S(e,t){let o=e.querySelectorAll(`[${m}]:not([${y}]):not([${w}])`);for(let a=0;a<o.length;a++){let i=o[a];if(i===void 0||!_(i))continue;let g=i.getAttribute(m)??"";g===""?i.style.removeProperty("translate"):i.style.setProperty("translate",g),i.removeAttribute(m),b.delete(i)}let r=e.querySelectorAll(`[${y}], [${w}]`),s=0;for(let a=0;a<r.length;a++){let i=r[a];i===void 0||!_(i)||(B(i,t),s+=1)}return s}var L="__hfPositionEditsSeekReapplyWrapped",D=new WeakSet,F=new WeakMap,H=new WeakMap;function M(e){let t=e,o=()=>{try{S(t.document)}catch{}},r=n=>typeof n=="function"&&(D.has(n)||!!n[L]),s=n=>{D.add(n);try{Object.defineProperty(n,L,{value:!0})}catch{}},a=n=>{if(typeof n!="function"||r(n))return n;let l=function(...u){let d=n.apply(this,u);return o(),d};return s(l),l},i=(n,l)=>{let u=F.get(n);if(u?.has(l))return!0;let d=Object.getOwnPropertyDescriptor(n,l);if(d?.configurable===!1){let c=n[l];return typeof c=="function"&&(n[l]=a(c),o()),!1}let p=n[l],f=d?.set;return Object.defineProperty(n,l,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>p,set:c=>{p=a(c),f?.call(n,c)}}),p=a(p),u??(u=new Set),u.add(l),F.set(n,u),o(),!0},g=(n,l)=>{let u=H.get(t),d=Object.getOwnPropertyDescriptor(t,n);if(!u?.has(n)){if(d?.configurable===!1){let c=t[n];return c?i(c,l):!1}let f=t[n];Object.defineProperty(t,n,{configurable:!0,enumerable:d?.enumerable??!0,get:()=>f,set:c=>{f=c,f&&i(f,l)}}),u??(u=new Set),u.add(n),H.set(t,u)}let p=t[n];return p?i(p,l):!1},h=()=>{let n=g("__hf","seek"),l=g("__player","renderSeek");return n&&l};if(h())return;let v=120,A=t.setInterval(()=>{if(h()){t.clearInterval(A);return}v-=1,v<=0&&t.clearInterval(A)},50)}function W(){document.querySelector(`[${y}], [${w}]`)&&(S(document),M(window))}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",W,{once:!0}):W();})();\n';
3
3
  /** Returns the pre-built position-edits render IIFE as a string constant. */
4
4
  export function getPositionEditsRenderScript() {
5
5
  return POSITION_EDITS_RENDER_IIFE;