hyperframes 0.7.22 → 0.7.24

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.
@@ -1,4 +1,4 @@
1
- import{n as Qi}from"./index-B4h4u7eW.js";/*!
1
+ import{n as Qi}from"./index-CnZuUYe6.js";/*!
2
2
  * Copyright (c) 2026-present, Vanilagy and contributors
3
3
  *
4
4
  * This Source Code Form is subject to the terms of the Mozilla Public
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>HyperFrames Studio</title>
8
- <script type="module" crossorigin src="/assets/index-B4h4u7eW.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-CnZuUYe6.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-DmkOvZns.css">
10
10
  </head>
11
11
  <body>
@@ -1470,6 +1470,72 @@ function generateId() {
1470
1470
  return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1471
1471
  }
1472
1472
 
1473
+ // src/utils/safeStorage.ts
1474
+ function safeLocalStorage() {
1475
+ try {
1476
+ return typeof localStorage === "undefined" ? null : localStorage;
1477
+ } catch {
1478
+ return null;
1479
+ }
1480
+ }
1481
+ function safeSessionStorage() {
1482
+ try {
1483
+ return typeof sessionStorage === "undefined" ? null : sessionStorage;
1484
+ } catch {
1485
+ return null;
1486
+ }
1487
+ }
1488
+
1489
+ // src/telemetry/distinctId.ts
1490
+ var DISTINCT_ID_KEY = "hyperframes-studio:anonymousId";
1491
+ var LEGACY_STUDIO_ANON_ID_KEY = "hf-studio-anon-id";
1492
+ var cachedId = null;
1493
+ function getCliDistinctId() {
1494
+ try {
1495
+ const id = typeof window === "undefined" ? void 0 : window.__HF_CLI_DISTINCT_ID;
1496
+ return typeof id === "string" && id.length > 0 ? id : null;
1497
+ } catch {
1498
+ return null;
1499
+ }
1500
+ }
1501
+ function persist(ls, id) {
1502
+ for (const key of [DISTINCT_ID_KEY, LEGACY_STUDIO_ANON_ID_KEY]) {
1503
+ try {
1504
+ ls.setItem(key, id);
1505
+ } catch {
1506
+ }
1507
+ }
1508
+ }
1509
+ function resolveStudioDistinctId() {
1510
+ if (cachedId) return cachedId;
1511
+ const ls = safeLocalStorage();
1512
+ const cliId = getCliDistinctId();
1513
+ if (cliId) {
1514
+ cachedId = cliId;
1515
+ if (ls) persist(ls, cliId);
1516
+ return cliId;
1517
+ }
1518
+ if (ls) {
1519
+ let existing = null;
1520
+ try {
1521
+ existing = ls.getItem(DISTINCT_ID_KEY) ?? ls.getItem(LEGACY_STUDIO_ANON_ID_KEY);
1522
+ } catch {
1523
+ }
1524
+ if (existing) {
1525
+ cachedId = existing;
1526
+ persist(ls, existing);
1527
+ return existing;
1528
+ }
1529
+ } else {
1530
+ cachedId = "anonymous";
1531
+ return cachedId;
1532
+ }
1533
+ const id = generateId();
1534
+ cachedId = id;
1535
+ persist(ls, id);
1536
+ return id;
1537
+ }
1538
+
1473
1539
  // src/utils/studioTelemetry.ts
1474
1540
  var POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
1475
1541
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -1477,23 +1543,8 @@ var FLUSH_INTERVAL_MS = 3e4;
1477
1543
  var FLUSH_TIMEOUT_MS = 5e3;
1478
1544
  var queue = [];
1479
1545
  var flushTimer = null;
1480
- var distinctId = null;
1481
1546
  function getDistinctId() {
1482
- if (distinctId) return distinctId;
1483
- try {
1484
- const stored = localStorage.getItem("hf-studio-anon-id");
1485
- if (stored) {
1486
- distinctId = stored;
1487
- return stored;
1488
- }
1489
- } catch {
1490
- }
1491
- distinctId = generateId();
1492
- try {
1493
- localStorage.setItem("hf-studio-anon-id", distinctId);
1494
- } catch {
1495
- }
1496
- return distinctId;
1547
+ return resolveStudioDistinctId();
1497
1548
  }
1498
1549
  function isEnabled() {
1499
1550
  try {
@@ -2825,7 +2876,7 @@ function useMusicBeatAnalysis() {
2825
2876
  void io.writeProjectFile(beatPath, content).catch(() => {
2826
2877
  });
2827
2878
  };
2828
- const persist = () => {
2879
+ const persist2 = () => {
2829
2880
  const s = usePlayerStore.getState();
2830
2881
  const a = s.beatAnalysis;
2831
2882
  if (!a) return;
@@ -2837,7 +2888,7 @@ function useMusicBeatAnalysis() {
2837
2888
  flush2();
2838
2889
  }, PERSIST_DEBOUNCE_MS);
2839
2890
  };
2840
- setBeatPersist(persist);
2891
+ setBeatPersist(persist2);
2841
2892
  return () => {
2842
2893
  if (timer) clearTimeout(timer);
2843
2894
  flush2();
@@ -9469,30 +9520,10 @@ function useCompositionStack({
9469
9520
  }
9470
9521
 
9471
9522
  // src/telemetry/config.ts
9472
- var ANON_ID_KEY = "hyperframes-studio:anonymousId";
9473
9523
  var OPT_OUT_KEY = "hyperframes-studio:telemetryDisabled";
9474
9524
  var NOTICE_KEY = "hyperframes-studio:telemetryNoticeShown";
9475
- function safeLocalStorage() {
9476
- try {
9477
- return typeof localStorage === "undefined" ? null : localStorage;
9478
- } catch {
9479
- return null;
9480
- }
9481
- }
9482
- function newAnonymousId() {
9483
- return generateId();
9484
- }
9485
9525
  function getAnonymousId() {
9486
- const ls = safeLocalStorage();
9487
- if (!ls) return "anonymous";
9488
- const existing = ls.getItem(ANON_ID_KEY);
9489
- if (existing) return existing;
9490
- const id = newAnonymousId();
9491
- try {
9492
- ls.setItem(ANON_ID_KEY, id);
9493
- } catch {
9494
- }
9495
- return id;
9526
+ return resolveStudioDistinctId();
9496
9527
  }
9497
9528
  function isOptedOut() {
9498
9529
  return safeLocalStorage()?.getItem(OPT_OUT_KEY) === "1";
@@ -9507,13 +9538,6 @@ function markNoticeShown() {
9507
9538
  }
9508
9539
  }
9509
9540
  var SESSION_FIRED_KEY = "hyperframes-studio:sessionStartFired";
9510
- function safeSessionStorage() {
9511
- try {
9512
- return typeof sessionStorage === "undefined" ? null : sessionStorage;
9513
- } catch {
9514
- return null;
9515
- }
9516
- }
9517
9541
  function hasFiredSessionStart() {
9518
9542
  return safeSessionStorage()?.getItem(SESSION_FIRED_KEY) === "1";
9519
9543
  }
@@ -9608,12 +9632,12 @@ function trackEvent(event, properties = {}) {
9608
9632
  }
9609
9633
  function flush() {
9610
9634
  if (eventQueue.length === 0) return;
9611
- const distinctId2 = getAnonymousId();
9635
+ const distinctId = getAnonymousId();
9612
9636
  const batch = eventQueue.map((e) => ({
9613
9637
  event: e.event,
9614
9638
  // $ip: null tells PostHog to not record the request IP.
9615
9639
  properties: { ...e.properties, $ip: null },
9616
- distinct_id: distinctId2,
9640
+ distinct_id: distinctId,
9617
9641
  timestamp: e.timestamp
9618
9642
  }));
9619
9643
  eventQueue = [];
@@ -22418,32 +22442,59 @@ function runResolverShadow(session, hfId, ops, sourceContent) {
22418
22442
  const mismatches = sdkResolverShadowCheck(session, hfId, ops, sourceContent);
22419
22443
  if (mismatches.length === 0) return;
22420
22444
  const isElementNotFound = mismatches.some((m) => m.kind === "element_not_found");
22445
+ const strictCount = isElementNotFound && sourceContent !== void 0 ? countHfIdInSource(sourceContent, hfId) : void 0;
22421
22446
  trackStudioEvent("sdk_resolver_shadow", {
22422
22447
  hfId,
22423
22448
  // sessionElementCount > 0 + element_not_found = runtime-only element;
22424
22449
  // sessionElementCount === 0 = session is empty/broken (actionable).
22425
22450
  sessionElementCount: session.getElements().length,
22426
22451
  // Count of data-hf-id="<id>" occurrences in source for an emitted
22427
- // element_not_found (the runtime-node filter already dropped absent-from-
22428
- // source ids, so an emitted one is in source ≥1×). >1 = duplicate ids →
22429
- // resolver picked the wrong instance; =1 = single static node the SDK
22430
- // parse dropped (foreign-content exclusion / sub-comp inlining gap).
22431
- ...isElementNotFound && sourceContent !== void 0 ? { sourceHfIdCount: countHfIdInSource(sourceContent, hfId) } : {},
22452
+ // element_not_found. >1 = duplicate ids resolver picked the wrong
22453
+ // instance; =1 = single static node the SDK parse dropped (foreign-content
22454
+ // exclusion / sub-comp inlining gap); =0 = the runtime-node filter above
22455
+ // uses a loose substring match (biased toward keeping signal) while this
22456
+ // count uses a strict attribute match see sourceLooseMatchOnly below.
22457
+ ...strictCount !== void 0 ? { sourceHfIdCount: strictCount } : {},
22458
+ // Loose suppression check matched (kept this event) but the strict
22459
+ // attribute count came back 0 — see the sourceHfIdCount comment above.
22460
+ ...strictCount === 0 ? { sourceLooseMatchOnly: true } : {},
22432
22461
  mismatchCount: mismatches.length,
22433
22462
  mismatches: JSON.stringify(redactMismatches(mismatches))
22434
22463
  });
22435
22464
  } catch {
22436
22465
  }
22437
22466
  }
22438
- function recordResolverParity(session, hfId, opLabel) {
22467
+ async function recordResolverParity(session, hfId, opLabel, readSource) {
22439
22468
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22440
22469
  if (!session || !hfId) return;
22441
22470
  try {
22442
22471
  if (resolveSnapshot(session, hfId)) return;
22472
+ const sessionElementCount = session.getElements().length;
22473
+ let source;
22474
+ if (readSource) {
22475
+ try {
22476
+ source = await readSource();
22477
+ } catch {
22478
+ source = void 0;
22479
+ }
22480
+ }
22481
+ if (source !== void 0 && !source.includes(hfId)) return;
22482
+ const strictCount = source !== void 0 ? countHfIdInSource(source, hfId) : void 0;
22443
22483
  trackStudioEvent("sdk_resolver_shadow", {
22444
22484
  hfId,
22445
22485
  opLabel,
22446
- sessionElementCount: session.getElements().length,
22486
+ sessionElementCount,
22487
+ // sourceHfIdCount: strict data-hf-id="..." attribute count. Can be 0 even
22488
+ // on an emitted (non-suppressed) event — the suppression check above is a
22489
+ // loose substring match (biased toward keeping signal); see the longer
22490
+ // comment on this field in runResolverShadow for the full explanation.
22491
+ ...strictCount !== void 0 ? { sourceHfIdCount: strictCount } : {},
22492
+ // Loose suppression check matched (kept this event) but the strict
22493
+ // attribute count came back 0 — hfId appeared as plain text (class name,
22494
+ // comment, script string) but never as a data-hf-id="..." attribute.
22495
+ // Lets telemetry consumers filter this cohort without parsing the
22496
+ // sourceHfIdCount comment above.
22497
+ ...strictCount === 0 ? { sourceLooseMatchOnly: true } : {},
22447
22498
  mismatchCount: 1,
22448
22499
  mismatches: JSON.stringify([
22449
22500
  { kind: "element_not_found", hfId }
@@ -22559,7 +22610,7 @@ function isSafeAttributeValue(name, value) {
22559
22610
  return true;
22560
22611
  }
22561
22612
 
22562
- // src/utils/sdkCutover.ts
22613
+ // src/utils/sdkCutoverEligibility.ts
22563
22614
  var CUTOVER_OP_TYPES = /* @__PURE__ */ new Set([
22564
22615
  "inline-style",
22565
22616
  "text-content",
@@ -22619,6 +22670,8 @@ function shouldDeclineTextCutoverForTarget(target, ops) {
22619
22670
  function shouldUseSdkCutover(flagEnabled, hasSession, hfId, ops) {
22620
22671
  return flagEnabled && hasSession && !!hfId && ops.length > 0 && ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
22621
22672
  }
22673
+
22674
+ // src/utils/sdkCutover.ts
22622
22675
  async function captureOnDiskBefore(deps, targetPath, serializedFallback) {
22623
22676
  if (!deps.readProjectFile) return serializedFallback;
22624
22677
  try {
@@ -22674,7 +22727,13 @@ async function sdkCutoverPersist(selection, ops, originalContent, targetPath, sd
22674
22727
  }
22675
22728
  }
22676
22729
  async function sdkTimingPersist(hfId, targetPath, timingUpdate, sdkSession, deps, options) {
22677
- recordResolverParity(sdkSession, hfId, "setTiming");
22730
+ const timingSrc = deps.readProjectFile;
22731
+ void recordResolverParity(
22732
+ sdkSession,
22733
+ hfId,
22734
+ "setTiming",
22735
+ timingSrc ? () => timingSrc(targetPath) : void 0
22736
+ );
22678
22737
  if (!STUDIO_SDK_CUTOVER_ENABLED) return false;
22679
22738
  if (!sdkSession || !sdkSession.getElement(hfId)) return false;
22680
22739
  if (wrongCompositionFile(deps, targetPath)) return false;
@@ -22693,13 +22752,21 @@ async function sdkTimingPersist(hfId, targetPath, timingUpdate, sdkSession, deps
22693
22752
  }
22694
22753
  }
22695
22754
  function sdkGsapTweenPersist(targetPath, op, sdkSession, deps, options) {
22696
- if (op.kind === "add") recordResolverParity(sdkSession, op.target, "addGsapTween");
22697
- else
22755
+ if (op.kind === "add") {
22756
+ const gsapSrc = deps.readProjectFile;
22757
+ void recordResolverParity(
22758
+ sdkSession,
22759
+ op.target,
22760
+ "addGsapTween",
22761
+ gsapSrc ? () => gsapSrc(targetPath) : void 0
22762
+ );
22763
+ } else {
22698
22764
  recordAnimationResolverParity(
22699
22765
  sdkSession,
22700
22766
  op.animationId,
22701
22767
  op.kind === "set" ? "setGsapTween" : "removeGsapTween"
22702
22768
  );
22769
+ }
22703
22770
  if (!STUDIO_SDK_CUTOVER_ENABLED) return Promise.resolve(false);
22704
22771
  if (op.kind === "add" && sdkSession && !sdkSession.getElement(op.target))
22705
22772
  return Promise.resolve(false);
@@ -22840,7 +22907,12 @@ function sdkReplaceWithKeyframesPersist(targetPath, animationId, targetSelector,
22840
22907
  );
22841
22908
  }
22842
22909
  async function sdkDeletePersist(hfId, originalContent, targetPath, sdkSession, deps) {
22843
- recordResolverParity(sdkSession, hfId, "removeElement");
22910
+ void recordResolverParity(
22911
+ sdkSession,
22912
+ hfId,
22913
+ "removeElement",
22914
+ () => Promise.resolve(originalContent)
22915
+ );
22844
22916
  if (!STUDIO_SDK_CUTOVER_ENABLED) return false;
22845
22917
  if (!sdkSession || !sdkSession.getElement(hfId)) return false;
22846
22918
  if (wrongCompositionFile(deps, targetPath)) return false;
@@ -29769,7 +29841,7 @@ function useDomEditSession({
29769
29841
  showToast,
29770
29842
  refreshPreviewDocumentVersion,
29771
29843
  queueDomEditSave,
29772
- readProjectFile: _readProjectFile,
29844
+ readProjectFile,
29773
29845
  writeProjectFile,
29774
29846
  updateEditingFileContent,
29775
29847
  domEditSaveTimestampRef,
@@ -29793,7 +29865,6 @@ function useDomEditSession({
29793
29865
  forceReloadSdkSession
29794
29866
  }) {
29795
29867
  void _setRefreshKey;
29796
- void _readProjectFile;
29797
29868
  const {
29798
29869
  domEditSelection,
29799
29870
  domEditGroupSelections,
@@ -29952,7 +30023,10 @@ function useDomEditSession({
29952
30023
  // SDK persist), but the tripwire is decoupled from cutover — record whether
29953
30024
  // the SDK resolves each reordered element (the reorderElements op's targets).
29954
30025
  onReorderShadow: sdkSession ? (targets) => {
29955
- for (const target of targets) recordResolverParity(sdkSession, target, "reorderElements");
30026
+ let reorderSrcPromise;
30027
+ const reorderSrc = activeCompPath ? () => reorderSrcPromise ??= readProjectFile(activeCompPath) : void 0;
30028
+ for (const target of targets)
30029
+ void recordResolverParity(sdkSession, target, "reorderElements", reorderSrc);
29956
30030
  } : void 0
29957
30031
  });
29958
30032
  const { groupSelection, ungroupSelection } = useGroupCommits({
@@ -42664,9 +42738,9 @@ function makeSlideshowNotesController() {
42664
42738
  });
42665
42739
  };
42666
42740
  return {
42667
- schedule(manifest, persist, delayMs) {
42741
+ schedule(manifest, persist2, delayMs) {
42668
42742
  if (timer !== null) clearTimeout(timer);
42669
- pending = { manifest, persist };
42743
+ pending = { manifest, persist: persist2 };
42670
42744
  timer = setTimeout(() => {
42671
42745
  timer = null;
42672
42746
  drainPending();