hyperframes 0.7.21 → 0.7.23

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.
@@ -4,6 +4,7 @@ import {
4
4
  buildDomEditStylePatchOperation,
5
5
  buildDomEditTextPatchOperation,
6
6
  collectDomEditLayerItems,
7
+ domEditSelectionToFacts,
7
8
  findElementForSelection,
8
9
  findElementForTimelineElement,
9
10
  getDomEditLayerKey,
@@ -17,7 +18,7 @@ import {
17
18
  resolveDomEditSelection,
18
19
  serializeDomEditTextFields,
19
20
  setCompositionSourceMap
20
- } from "./chunk-SBGXX7WY.js";
21
+ } from "./chunk-AN2EWWK3.js";
21
22
 
22
23
  // src/components/nle/NLELayout.tsx
23
24
  import {
@@ -1469,6 +1470,72 @@ function generateId() {
1469
1470
  return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1470
1471
  }
1471
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
+
1472
1539
  // src/utils/studioTelemetry.ts
1473
1540
  var POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
1474
1541
  var POSTHOG_HOST = "https://us.i.posthog.com";
@@ -1476,23 +1543,8 @@ var FLUSH_INTERVAL_MS = 3e4;
1476
1543
  var FLUSH_TIMEOUT_MS = 5e3;
1477
1544
  var queue = [];
1478
1545
  var flushTimer = null;
1479
- var distinctId = null;
1480
1546
  function getDistinctId() {
1481
- if (distinctId) return distinctId;
1482
- try {
1483
- const stored = localStorage.getItem("hf-studio-anon-id");
1484
- if (stored) {
1485
- distinctId = stored;
1486
- return stored;
1487
- }
1488
- } catch {
1489
- }
1490
- distinctId = generateId();
1491
- try {
1492
- localStorage.setItem("hf-studio-anon-id", distinctId);
1493
- } catch {
1494
- }
1495
- return distinctId;
1547
+ return resolveStudioDistinctId();
1496
1548
  }
1497
1549
  function isEnabled() {
1498
1550
  try {
@@ -2824,7 +2876,7 @@ function useMusicBeatAnalysis() {
2824
2876
  void io.writeProjectFile(beatPath, content).catch(() => {
2825
2877
  });
2826
2878
  };
2827
- const persist = () => {
2879
+ const persist2 = () => {
2828
2880
  const s = usePlayerStore.getState();
2829
2881
  const a = s.beatAnalysis;
2830
2882
  if (!a) return;
@@ -2836,7 +2888,7 @@ function useMusicBeatAnalysis() {
2836
2888
  flush2();
2837
2889
  }, PERSIST_DEBOUNCE_MS);
2838
2890
  };
2839
- setBeatPersist(persist);
2891
+ setBeatPersist(persist2);
2840
2892
  return () => {
2841
2893
  if (timer) clearTimeout(timer);
2842
2894
  flush2();
@@ -5196,11 +5248,6 @@ var STUDIO_RAZOR_TOOL_ENABLED = resolveStudioBooleanEnvFlag(
5196
5248
  ["VITE_STUDIO_ENABLE_RAZOR_TOOL", "VITE_STUDIO_RAZOR_TOOL_ENABLED"],
5197
5249
  true
5198
5250
  );
5199
- var STUDIO_STORYBOARD_ENABLED = resolveStudioBooleanEnvFlag(
5200
- env,
5201
- ["VITE_STUDIO_ENABLE_STORYBOARD", "VITE_STUDIO_STORYBOARD_ENABLED"],
5202
- false
5203
- );
5204
5251
  var STUDIO_PREVIEW_SELECTION_ENABLED = STUDIO_INSPECTOR_PANELS_ENABLED;
5205
5252
  var STUDIO_SDK_CUTOVER_ENABLED = resolveStudioBooleanEnvFlag(
5206
5253
  env,
@@ -9473,30 +9520,10 @@ function useCompositionStack({
9473
9520
  }
9474
9521
 
9475
9522
  // src/telemetry/config.ts
9476
- var ANON_ID_KEY = "hyperframes-studio:anonymousId";
9477
9523
  var OPT_OUT_KEY = "hyperframes-studio:telemetryDisabled";
9478
9524
  var NOTICE_KEY = "hyperframes-studio:telemetryNoticeShown";
9479
- function safeLocalStorage() {
9480
- try {
9481
- return typeof localStorage === "undefined" ? null : localStorage;
9482
- } catch {
9483
- return null;
9484
- }
9485
- }
9486
- function newAnonymousId() {
9487
- return generateId();
9488
- }
9489
9525
  function getAnonymousId() {
9490
- const ls = safeLocalStorage();
9491
- if (!ls) return "anonymous";
9492
- const existing = ls.getItem(ANON_ID_KEY);
9493
- if (existing) return existing;
9494
- const id = newAnonymousId();
9495
- try {
9496
- ls.setItem(ANON_ID_KEY, id);
9497
- } catch {
9498
- }
9499
- return id;
9526
+ return resolveStudioDistinctId();
9500
9527
  }
9501
9528
  function isOptedOut() {
9502
9529
  return safeLocalStorage()?.getItem(OPT_OUT_KEY) === "1";
@@ -9511,13 +9538,6 @@ function markNoticeShown() {
9511
9538
  }
9512
9539
  }
9513
9540
  var SESSION_FIRED_KEY = "hyperframes-studio:sessionStartFired";
9514
- function safeSessionStorage() {
9515
- try {
9516
- return typeof sessionStorage === "undefined" ? null : sessionStorage;
9517
- } catch {
9518
- return null;
9519
- }
9520
- }
9521
9541
  function hasFiredSessionStart() {
9522
9542
  return safeSessionStorage()?.getItem(SESSION_FIRED_KEY) === "1";
9523
9543
  }
@@ -9612,12 +9632,12 @@ function trackEvent(event, properties = {}) {
9612
9632
  }
9613
9633
  function flush() {
9614
9634
  if (eventQueue.length === 0) return;
9615
- const distinctId2 = getAnonymousId();
9635
+ const distinctId = getAnonymousId();
9616
9636
  const batch = eventQueue.map((e) => ({
9617
9637
  event: e.event,
9618
9638
  // $ip: null tells PostHog to not record the request IP.
9619
9639
  properties: { ...e.properties, $ip: null },
9620
- distinct_id: distinctId2,
9640
+ distinct_id: distinctId,
9621
9641
  timestamp: e.timestamp
9622
9642
  }));
9623
9643
  eventQueue = [];
@@ -12819,14 +12839,11 @@ function createTransformCommitHandlers({
12819
12839
 
12820
12840
  // src/components/editor/PropertyPanel.tsx
12821
12841
  import { classifyPropertyGroup } from "@hyperframes/core/gsap-parser";
12842
+ import { resolveEditingSections } from "@hyperframes/core/editing";
12822
12843
 
12823
12844
  // src/components/editor/propertyPanelMediaSection.tsx
12824
12845
  import { useState as useState20 } from "react";
12825
12846
  import { Fragment as Fragment10, jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
12826
- var MEDIA_TAGS = /* @__PURE__ */ new Set(["video", "audio"]);
12827
- function isMediaElement(element) {
12828
- return MEDIA_TAGS.has(element.tagName);
12829
- }
12830
12847
  function MediaSection({
12831
12848
  projectDir,
12832
12849
  element,
@@ -13500,9 +13517,6 @@ var DEFAULT_COLOR_GRADING = {
13500
13517
  colorSpace: HF_COLOR_GRADING_COLOR_SPACE
13501
13518
  };
13502
13519
  var COLOR_GRADING_DATA_KEY = HF_COLOR_GRADING_ATTR.replace(/^data-/, "");
13503
- function isColorGradingCapableElement(element) {
13504
- return element.tagName === "video" || element.tagName === "img";
13505
- }
13506
13520
  function readColorGradingFromElement(element) {
13507
13521
  const grading = normalizeHfColorGrading2(element.dataAttributes[COLOR_GRADING_DATA_KEY]) ?? DEFAULT_COLOR_GRADING;
13508
13522
  return { ...grading, intensity: 1 };
@@ -18711,6 +18725,7 @@ var PropertyPanel = memo22(function PropertyPanel2({
18711
18725
  const manualRotationEditingDisabled = !element.capabilities.canApplyManualRotation;
18712
18726
  const sourceLabel = element.id ? `#${element.id}` : element.selector;
18713
18727
  const showEditableSections = element.capabilities.canEditStyles;
18728
+ const sections = resolveEditingSections(domEditSelectionToFacts(element, gsapAnimations.length));
18714
18729
  const manualOffset = readStudioPathOffset(element.element);
18715
18730
  const manualSize = readStudioBoxSize(element.element);
18716
18731
  const resolvedWidth = manualSize.width > 0 ? manualSize.width : parsePxMetricValue(styles.width ?? "") ?? element.boundingBox.width;
@@ -18833,7 +18848,7 @@ var PropertyPanel = memo22(function PropertyPanel2({
18833
18848
  onRemoveTextField
18834
18849
  }
18835
18850
  ),
18836
- (element.dataAttributes.start != null || gsapAnimations.length > 0) && // Render whenever there's an authored clip range OR animations to infer
18851
+ sections.timing && // Render whenever there's an authored clip range OR animations to infer
18837
18852
  // one from — a pure-GSAP element with no data-start still gets a Timing
18838
18853
  // range (TimingSection derives it from its tweens).
18839
18854
  /* @__PURE__ */ jsx54(
@@ -18844,7 +18859,7 @@ var PropertyPanel = memo22(function PropertyPanel2({
18844
18859
  onSetAttribute
18845
18860
  }
18846
18861
  ),
18847
- isMediaElement(element) && /* @__PURE__ */ jsx54(
18862
+ sections.media && /* @__PURE__ */ jsx54(
18848
18863
  MediaSection,
18849
18864
  {
18850
18865
  projectDir,
@@ -18855,7 +18870,7 @@ var PropertyPanel = memo22(function PropertyPanel2({
18855
18870
  onSetHtmlAttribute
18856
18871
  }
18857
18872
  ),
18858
- STUDIO_COLOR_GRADING_ENABLED && isColorGradingCapableElement(element) && /* @__PURE__ */ jsx54(
18873
+ STUDIO_COLOR_GRADING_ENABLED && sections.colorGrading && /* @__PURE__ */ jsx54(
18859
18874
  ColorGradingSection,
18860
18875
  {
18861
18876
  element,
@@ -22337,6 +22352,9 @@ function normalizeText(v) {
22337
22352
  const t = v.trim();
22338
22353
  return t === "" ? null : t;
22339
22354
  }
22355
+ function countHfIdInSource(source, id) {
22356
+ return source.split(`data-hf-id="${id}"`).length - 1 + (source.split(`data-hf-id='${id}'`).length - 1);
22357
+ }
22340
22358
  function resolveSnapshot(session, id) {
22341
22359
  const els = session.getElements();
22342
22360
  const exact = els.find((el) => el.scopedId === id);
@@ -22379,8 +22397,9 @@ function checkOpValue(op, el, hfId) {
22379
22397
  if (actual === expected) return null;
22380
22398
  return { kind: "value_mismatch", hfId, property, expected, actual };
22381
22399
  }
22382
- function sdkResolverShadowCheck(session, hfId, ops) {
22400
+ function sdkResolverShadowCheck(session, hfId, ops, sourceContent) {
22383
22401
  if (!resolveSnapshot(session, hfId)) {
22402
+ if (sourceContent !== void 0 && !sourceContent.includes(hfId)) return [];
22384
22403
  return [{ kind: "element_not_found", hfId }];
22385
22404
  }
22386
22405
  const shadowable = ops.filter(isShadowableOp);
@@ -22416,28 +22435,58 @@ function redactMismatches(mismatches) {
22416
22435
  actual: redactValue(m.actual)
22417
22436
  }));
22418
22437
  }
22419
- function runResolverShadow(session, hfId, ops) {
22438
+ function runResolverShadow(session, hfId, ops, sourceContent) {
22420
22439
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22421
22440
  if (!hfId) return;
22422
22441
  try {
22423
- const mismatches = sdkResolverShadowCheck(session, hfId, ops);
22442
+ const mismatches = sdkResolverShadowCheck(session, hfId, ops, sourceContent);
22424
22443
  if (mismatches.length === 0) return;
22444
+ const isElementNotFound = mismatches.some((m) => m.kind === "element_not_found");
22425
22445
  trackStudioEvent("sdk_resolver_shadow", {
22426
22446
  hfId,
22447
+ // sessionElementCount > 0 + element_not_found = runtime-only element;
22448
+ // sessionElementCount === 0 = session is empty/broken (actionable).
22449
+ sessionElementCount: session.getElements().length,
22450
+ // Count of data-hf-id="<id>" occurrences in source for an emitted
22451
+ // element_not_found. >1 = duplicate ids → resolver picked the wrong
22452
+ // instance; =1 = single static node the SDK parse dropped (foreign-content
22453
+ // exclusion / sub-comp inlining gap); =0 = the runtime-node filter above
22454
+ // uses a loose substring match (biased toward keeping signal) while this
22455
+ // count uses a strict attribute match — an emitted event with count 0
22456
+ // means hfId appeared in source as plain text (e.g. a class name, comment,
22457
+ // or script string) but never as a data-hf-id attribute. Treat 0 as "not
22458
+ // a genuine attribute occurrence," not as a contradiction.
22459
+ ...isElementNotFound && sourceContent !== void 0 ? { sourceHfIdCount: countHfIdInSource(sourceContent, hfId) } : {},
22427
22460
  mismatchCount: mismatches.length,
22428
22461
  mismatches: JSON.stringify(redactMismatches(mismatches))
22429
22462
  });
22430
22463
  } catch {
22431
22464
  }
22432
22465
  }
22433
- function recordResolverParity(session, hfId, opLabel) {
22466
+ async function recordResolverParity(session, hfId, opLabel, readSource) {
22434
22467
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22435
22468
  if (!session || !hfId) return;
22436
22469
  try {
22437
22470
  if (resolveSnapshot(session, hfId)) return;
22471
+ const sessionElementCount = session.getElements().length;
22472
+ let source;
22473
+ if (readSource) {
22474
+ try {
22475
+ source = await readSource();
22476
+ } catch {
22477
+ source = void 0;
22478
+ }
22479
+ }
22480
+ if (source !== void 0 && !source.includes(hfId)) return;
22438
22481
  trackStudioEvent("sdk_resolver_shadow", {
22439
22482
  hfId,
22440
22483
  opLabel,
22484
+ sessionElementCount,
22485
+ // sourceHfIdCount: strict data-hf-id="..." attribute count. Can be 0 even
22486
+ // on an emitted (non-suppressed) event — the suppression check above is a
22487
+ // loose substring match (biased toward keeping signal); see the longer
22488
+ // comment on this field in runResolverShadow for the full explanation.
22489
+ ...source !== void 0 ? { sourceHfIdCount: countHfIdInSource(source, hfId) } : {},
22441
22490
  mismatchCount: 1,
22442
22491
  mismatches: JSON.stringify([
22443
22492
  { kind: "element_not_found", hfId }
@@ -22450,11 +22499,13 @@ function recordAnimationResolverParity(session, animationId, opLabel) {
22450
22499
  if (!STUDIO_SDK_RESOLVER_SHADOW_ENABLED) return;
22451
22500
  if (!session || !animationId) return;
22452
22501
  try {
22453
- const resolves = session.getElements().some((el) => el.animationIds.includes(animationId));
22502
+ const elements = session.getElements();
22503
+ const resolves = elements.some((el) => el.animationIds.includes(animationId));
22454
22504
  if (resolves) return;
22455
22505
  trackStudioEvent("sdk_resolver_shadow", {
22456
22506
  animationId,
22457
22507
  opLabel,
22508
+ sessionElementCount: elements.length,
22458
22509
  mismatchCount: 1,
22459
22510
  mismatches: JSON.stringify([
22460
22511
  { kind: "animation_not_found", animationId }
@@ -22551,7 +22602,7 @@ function isSafeAttributeValue(name, value) {
22551
22602
  return true;
22552
22603
  }
22553
22604
 
22554
- // src/utils/sdkCutover.ts
22605
+ // src/utils/sdkCutoverEligibility.ts
22555
22606
  var CUTOVER_OP_TYPES = /* @__PURE__ */ new Set([
22556
22607
  "inline-style",
22557
22608
  "text-content",
@@ -22611,6 +22662,8 @@ function shouldDeclineTextCutoverForTarget(target, ops) {
22611
22662
  function shouldUseSdkCutover(flagEnabled, hasSession, hfId, ops) {
22612
22663
  return flagEnabled && hasSession && !!hfId && ops.length > 0 && ops.every((o) => CUTOVER_OP_TYPES.has(o.type)) && !ops.some(mapsToReservedAttr) && !hasUnsafeHtmlAttributeOp(ops);
22613
22664
  }
22665
+
22666
+ // src/utils/sdkCutover.ts
22614
22667
  async function captureOnDiskBefore(deps, targetPath, serializedFallback) {
22615
22668
  if (!deps.readProjectFile) return serializedFallback;
22616
22669
  try {
@@ -22666,7 +22719,13 @@ async function sdkCutoverPersist(selection, ops, originalContent, targetPath, sd
22666
22719
  }
22667
22720
  }
22668
22721
  async function sdkTimingPersist(hfId, targetPath, timingUpdate, sdkSession, deps, options) {
22669
- recordResolverParity(sdkSession, hfId, "setTiming");
22722
+ const timingSrc = deps.readProjectFile;
22723
+ void recordResolverParity(
22724
+ sdkSession,
22725
+ hfId,
22726
+ "setTiming",
22727
+ timingSrc ? () => timingSrc(targetPath) : void 0
22728
+ );
22670
22729
  if (!STUDIO_SDK_CUTOVER_ENABLED) return false;
22671
22730
  if (!sdkSession || !sdkSession.getElement(hfId)) return false;
22672
22731
  if (wrongCompositionFile(deps, targetPath)) return false;
@@ -22685,8 +22744,15 @@ async function sdkTimingPersist(hfId, targetPath, timingUpdate, sdkSession, deps
22685
22744
  }
22686
22745
  }
22687
22746
  function sdkGsapTweenPersist(targetPath, op, sdkSession, deps, options) {
22688
- if (op.kind === "add") recordResolverParity(sdkSession, op.target, "addGsapTween");
22689
- else
22747
+ if (op.kind === "add") {
22748
+ const gsapSrc = deps.readProjectFile;
22749
+ void recordResolverParity(
22750
+ sdkSession,
22751
+ op.target,
22752
+ "addGsapTween",
22753
+ gsapSrc ? () => gsapSrc(targetPath) : void 0
22754
+ );
22755
+ } else
22690
22756
  recordAnimationResolverParity(
22691
22757
  sdkSession,
22692
22758
  op.animationId,
@@ -22832,7 +22898,12 @@ function sdkReplaceWithKeyframesPersist(targetPath, animationId, targetSelector,
22832
22898
  );
22833
22899
  }
22834
22900
  async function sdkDeletePersist(hfId, originalContent, targetPath, sdkSession, deps) {
22835
- recordResolverParity(sdkSession, hfId, "removeElement");
22901
+ void recordResolverParity(
22902
+ sdkSession,
22903
+ hfId,
22904
+ "removeElement",
22905
+ () => Promise.resolve(originalContent)
22906
+ );
22836
22907
  if (!STUDIO_SDK_CUTOVER_ENABLED) return false;
22837
22908
  if (!sdkSession || !sdkSession.getElement(hfId)) return false;
22838
22909
  if (wrongCompositionFile(deps, targetPath)) return false;
@@ -29761,7 +29832,7 @@ function useDomEditSession({
29761
29832
  showToast,
29762
29833
  refreshPreviewDocumentVersion,
29763
29834
  queueDomEditSave,
29764
- readProjectFile: _readProjectFile,
29835
+ readProjectFile,
29765
29836
  writeProjectFile,
29766
29837
  updateEditingFileContent,
29767
29838
  domEditSaveTimestampRef,
@@ -29785,7 +29856,6 @@ function useDomEditSession({
29785
29856
  forceReloadSdkSession
29786
29857
  }) {
29787
29858
  void _setRefreshKey;
29788
- void _readProjectFile;
29789
29859
  const {
29790
29860
  domEditSelection,
29791
29861
  domEditGroupSelections,
@@ -29916,7 +29986,7 @@ function useDomEditSession({
29916
29986
  buildDomSelectionFromTarget,
29917
29987
  forceReloadSdkSession,
29918
29988
  onTrySdkPersist: sdkSession ? (selection, operations, originalContent, targetPath, options) => {
29919
- runResolverShadow(sdkSession, selection.hfId, operations);
29989
+ runResolverShadow(sdkSession, selection.hfId, operations, originalContent);
29920
29990
  return sdkCutoverPersist(
29921
29991
  selection,
29922
29992
  operations,
@@ -29944,7 +30014,9 @@ function useDomEditSession({
29944
30014
  // SDK persist), but the tripwire is decoupled from cutover — record whether
29945
30015
  // the SDK resolves each reordered element (the reorderElements op's targets).
29946
30016
  onReorderShadow: sdkSession ? (targets) => {
29947
- for (const target of targets) recordResolverParity(sdkSession, target, "reorderElements");
30017
+ const reorderSrc = activeCompPath ? () => readProjectFile(activeCompPath) : void 0;
30018
+ for (const target of targets)
30019
+ void recordResolverParity(sdkSession, target, "reorderElements", reorderSrc);
29948
30020
  } : void 0
29949
30021
  });
29950
30022
  const { groupSelection, ungroupSelection } = useGroupCommits({
@@ -32559,26 +32631,18 @@ function writeViewModeToUrl(mode) {
32559
32631
  }
32560
32632
  window.history.replaceState(window.history.state, "", url);
32561
32633
  }
32562
- function useViewModeState(enabled) {
32563
- const [viewMode, setMode] = useState58(
32564
- () => enabled ? readViewModeFromUrl() : "timeline"
32565
- );
32634
+ function useViewModeState() {
32635
+ const [viewMode, setMode] = useState58(() => readViewModeFromUrl());
32566
32636
  useEffect44(() => {
32567
- if (!enabled) return;
32568
32637
  const onPopState = () => setMode(readViewModeFromUrl());
32569
32638
  window.addEventListener("popstate", onPopState);
32570
32639
  return () => window.removeEventListener("popstate", onPopState);
32571
- }, [enabled]);
32572
- const setViewMode = useCallback77(
32573
- (mode) => {
32574
- if (!enabled) return;
32575
- setMode(mode);
32576
- writeViewModeToUrl(mode);
32577
- },
32578
- [enabled]
32579
- );
32580
- const effectiveMode = enabled ? viewMode : "timeline";
32581
- return useMemo27(() => ({ viewMode: effectiveMode, setViewMode }), [effectiveMode, setViewMode]);
32640
+ }, []);
32641
+ const setViewMode = useCallback77((mode) => {
32642
+ setMode(mode);
32643
+ writeViewModeToUrl(mode);
32644
+ }, []);
32645
+ return useMemo27(() => ({ viewMode, setViewMode }), [viewMode, setViewMode]);
32582
32646
  }
32583
32647
  var ViewModeContext = createContext5(null);
32584
32648
  function useViewMode() {
@@ -32828,7 +32892,7 @@ function StudioHeader({
32828
32892
  /* @__PURE__ */ jsx67("span", { className: "text-neutral-700 select-none", "aria-hidden": "true", children: "|" }),
32829
32893
  /* @__PURE__ */ jsx67("span", { className: "text-[11px] font-medium text-neutral-300", children: projectId })
32830
32894
  ] }),
32831
- STUDIO_STORYBOARD_ENABLED && /* @__PURE__ */ jsx67(ViewModeToggle, {}),
32895
+ /* @__PURE__ */ jsx67(ViewModeToggle, {}),
32832
32896
  /* @__PURE__ */ jsxs56("div", { className: "flex items-center gap-1.5", children: [
32833
32897
  /* @__PURE__ */ jsx67(
32834
32898
  "button",
@@ -36957,7 +37021,7 @@ function OffCanvasIndicators({
36957
37021
  const selectOffCanvas = async () => {
36958
37022
  const el = elements.current.get(r.key);
36959
37023
  if (!el) return;
36960
- const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-VZMLL4AP.js");
37024
+ const { resolveDomEditSelection: resolveDomEditSelection2 } = await import("./domEditingLayers-EK7R7R4G.js");
36961
37025
  const acp = activeCompositionPathRef.current ?? "index.html";
36962
37026
  const sel = await resolveDomEditSelection2(el, {
36963
37027
  activeCompositionPath: acp,
@@ -42664,9 +42728,9 @@ function makeSlideshowNotesController() {
42664
42728
  });
42665
42729
  };
42666
42730
  return {
42667
- schedule(manifest, persist, delayMs) {
42731
+ schedule(manifest, persist2, delayMs) {
42668
42732
  if (timer !== null) clearTimeout(timer);
42669
- pending = { manifest, persist };
42733
+ pending = { manifest, persist: persist2 };
42670
42734
  timer = setTimeout(() => {
42671
42735
  timer = null;
42672
42736
  drainPending();
@@ -44757,7 +44821,7 @@ import { jsx as jsx113, jsxs as jsxs96 } from "react/jsx-runtime";
44757
44821
  function StudioApp() {
44758
44822
  const { projectId, resolving, waitingForServer } = useServerConnection();
44759
44823
  const initialUrlStateRef = useRef90(readStudioUrlStateFromWindow());
44760
- const viewModeValue = useViewModeState(STUDIO_STORYBOARD_ENABLED);
44824
+ const viewModeValue = useViewModeState();
44761
44825
  useEffect67(() => {
44762
44826
  if (resolving || waitingForServer) return;
44763
44827
  if (hasFiredSessionStart()) return;