@hyperframes/studio 0.8.55 → 0.8.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/assets/hyperframes-player-c23wnIfc.js +464 -0
  2. package/dist/assets/{index-DrHQAuGO.css → index-DKJpHYYd.css} +1 -1
  3. package/dist/assets/{index-BEIRytvV.js → index-DgkAzx3n.js} +1 -1
  4. package/dist/assets/{index-DaNpNFJC.js → index-IDNr3X1H.js} +1 -1
  5. package/dist/assets/{index-TcBZyzNA.js → index-gbOCDCuZ.js} +202 -202
  6. package/dist/index.d.ts +111 -17
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +21534 -20683
  9. package/dist/index.js.map +1 -1
  10. package/dist/styles.css +1 -1
  11. package/package.json +7 -7
  12. package/src/App.tsx +18 -1
  13. package/src/components/EditorShell.readOnlyHostMount.test.tsx +116 -0
  14. package/src/components/EditorShell.selectionSync.test.tsx +57 -67
  15. package/src/components/EditorShell.tsx +62 -51
  16. package/src/components/EditorShellTestMocks.ts +46 -0
  17. package/src/components/ProjectUnreachableBanner.test.tsx +113 -0
  18. package/src/components/ProjectUnreachableBanner.tsx +89 -0
  19. package/src/components/editor/DomEditOverlay.tsx +19 -55
  20. package/src/components/editor/DomEditOverlayHover.test.tsx +1 -15
  21. package/src/components/editor/DomEditSelectionChrome.tsx +11 -9
  22. package/src/components/editor/GridOverlay.tsx +12 -23
  23. package/src/components/editor/MotionPathOverlay.test.tsx +66 -0
  24. package/src/components/editor/MotionPathOverlay.tsx +3 -1
  25. package/src/components/editor/PreviewGuides.test.tsx +6 -1
  26. package/src/components/editor/PreviewGuides.tsx +8 -10
  27. package/src/components/editor/PreviewOverlayProvider.test.tsx +90 -0
  28. package/src/components/editor/PreviewOverlayProvider.tsx +86 -0
  29. package/src/components/editor/PropertyPanel.deselect.test.tsx +71 -0
  30. package/src/components/editor/PropertyPanel.tsx +4 -3
  31. package/src/components/editor/SnapToolbar.test.tsx +26 -12
  32. package/src/components/editor/SnapToolbar.tsx +30 -59
  33. package/src/components/editor/domEditOverlayTestMocks.ts +16 -0
  34. package/src/components/editor/previewReadOnly.overlay.test.tsx +260 -0
  35. package/src/components/editor/previewReadOnlyContext.tsx +59 -0
  36. package/src/components/editor/propertyPanelFlatLayoutSection.tsx +9 -0
  37. package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +2 -2
  38. package/src/components/editor/propertyPanelFlatMediaSection.tsx +46 -12
  39. package/src/components/editor/propertyPanelFlatPrimitives.tsx +4 -1
  40. package/src/components/editor/propertyPanelInputCoverage.test.tsx +3 -13
  41. package/src/components/editor/useDomEditNudge.ts +9 -0
  42. package/src/components/editor/useDomEditOverlayGestures.ts +4 -0
  43. package/src/components/editor/useOffCanvasIndicators.ts +50 -0
  44. package/src/components/editor/usePreviewCompositionRect.test.tsx +19 -13
  45. package/src/components/editor/usePreviewCompositionRect.ts +12 -12
  46. package/src/components/editor/useVolumeAutomation.test.tsx +68 -0
  47. package/src/components/editor/useVolumeAutomation.ts +57 -16
  48. package/src/components/nle/NLEContext.tsx +40 -4
  49. package/src/components/nle/NLEPreview.test.ts +66 -1
  50. package/src/components/nle/NLEPreview.tsx +88 -44
  51. package/src/components/nle/PreviewOverlays.topologyLens.test.tsx +4 -35
  52. package/src/components/nle/PreviewOverlays.tsx +19 -24
  53. package/src/components/nle/PreviewOverlaysTestMocks.ts +40 -0
  54. package/src/components/nle/PreviewPane.tsx +17 -2
  55. package/src/components/nle/TimelinePane.tsx +5 -1
  56. package/src/components/nle/previewReadOnly.test.tsx +129 -0
  57. package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +1 -1
  58. package/src/components/nle/useCanvasZOrderTimelineMirror.ts +3 -6
  59. package/src/components/nle/usePreviewBlockDrop.ts +3 -1
  60. package/src/hooks/appHotkeysDispatch.ts +16 -1
  61. package/src/hooks/newTweenTargetHooks.test.tsx +1 -0
  62. package/src/hooks/timelineAudioGroupCreate.ts +3 -4
  63. package/src/hooks/timelineTrackVisibility.ts +7 -8
  64. package/src/hooks/useAppHotkeys.test.ts +71 -3
  65. package/src/hooks/useAppHotkeys.textEditing.test.tsx +1 -0
  66. package/src/hooks/useAppHotkeys.ts +4 -0
  67. package/src/hooks/useCompositionDimensions.test.tsx +38 -0
  68. package/src/hooks/useCompositionDimensions.ts +17 -1
  69. package/src/hooks/useDomEditAttributeCommits.test.ts +23 -0
  70. package/src/hooks/useDomEditAttributeCommits.ts +14 -3
  71. package/src/hooks/useDomEditCommits.test.tsx +1 -0
  72. package/src/hooks/useDomEditCommits.ts +3 -0
  73. package/src/hooks/useDomEditSession.test.tsx +1 -0
  74. package/src/hooks/useDomEditSession.ts +7 -4
  75. package/src/hooks/useDomEditWiring.ts +40 -40
  76. package/src/hooks/useDomGeometryCommits.test.tsx +85 -2
  77. package/src/hooks/useDomGeometryCommits.ts +26 -20
  78. package/src/hooks/useFileTree.test.tsx +56 -0
  79. package/src/hooks/useFileTree.ts +8 -1
  80. package/src/hooks/useGestureCommit.test.tsx +29 -0
  81. package/src/hooks/useGestureCommit.ts +23 -0
  82. package/src/hooks/useInlineTextEdit.test.tsx +23 -2
  83. package/src/hooks/useInlineTextEdit.ts +11 -2
  84. package/src/hooks/useSdkSession.lifecycle.test.tsx +80 -0
  85. package/src/hooks/useSdkSession.ts +46 -10
  86. package/src/hooks/useServerConnection.hashProject.test.tsx +303 -0
  87. package/src/hooks/useServerConnection.ts +81 -8
  88. package/src/hooks/useTimelineAssetDropOps.test.tsx +3 -4
  89. package/src/hooks/useTimelineAssetDropOps.ts +1 -6
  90. package/src/hooks/useTimelineEditing.test.tsx +10 -7
  91. package/src/hooks/useTimelineEditing.ts +2 -7
  92. package/src/index.ts +12 -0
  93. package/src/player/components/Player.test.ts +141 -13
  94. package/src/player/components/Player.tsx +58 -2
  95. package/src/player/components/ShortcutsPanel.test.tsx +4 -10
  96. package/src/player/components/Timeline.tsx +23 -573
  97. package/src/player/components/TimelineCanvas.tsx +9 -30
  98. package/src/player/components/TimelineClip.test.tsx +52 -0
  99. package/src/player/components/TimelineClip.tsx +13 -2
  100. package/src/player/components/TimelineOverlays.test.ts +67 -32
  101. package/src/player/components/TimelineOverlays.tsx +46 -107
  102. package/src/player/components/TimelineProvider.tsx +191 -0
  103. package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
  104. package/src/player/components/TimelineTrackHeader.tsx +1 -1
  105. package/src/player/components/testRootHarness.ts +18 -0
  106. package/src/player/components/timelineMotionStyles.test.ts +3 -3
  107. package/src/player/components/timelineProviderStateBuilders.test.ts +109 -0
  108. package/src/player/components/timelineProviderStateBuilders.ts +83 -0
  109. package/src/player/components/timelineTheme.test.ts +4 -4
  110. package/src/player/components/timelineTheme.ts +34 -30
  111. package/src/player/components/timelineTrackDisplay.ts +1 -4
  112. package/src/player/components/useTimelineClipContextMenu.ts +1 -1
  113. package/src/player/components/useTimelineOverlaysState.ts +100 -0
  114. package/src/player/components/useTimelineProviderState.tsx +583 -0
  115. package/src/player/components/useTimelineTrackDerivations.ts +2 -2
  116. package/src/player/hooks/playbackAdapterResolution.ts +98 -0
  117. package/src/player/hooks/timelinePlayerTestHarness.ts +120 -0
  118. package/src/player/hooks/timelineSyncHydration.test.ts +0 -18
  119. package/src/player/hooks/timelineSyncHydration.ts +0 -23
  120. package/src/player/hooks/usePreviewFirstFrameTelemetry.ts +51 -0
  121. package/src/player/hooks/useShadowPreviewReload.ts +183 -0
  122. package/src/player/hooks/useTimelinePlayer.seek.test.ts +8 -88
  123. package/src/player/hooks/useTimelinePlayer.shadowReload.test.ts +457 -0
  124. package/src/player/hooks/useTimelinePlayer.ts +65 -99
  125. package/src/player/hooks/useTimelineSyncCallbacks.ts +159 -91
  126. package/src/player/store/playerStore.ts +0 -4
  127. package/src/player/store/timelineElement.ts +2 -4
  128. package/src/player/store/timelineResetState.ts +0 -1
  129. package/src/styles/hex-baseline.json +3 -4
  130. package/src/styles/studio.css +7 -7
  131. package/src/styles/theme.css +29 -0
  132. package/src/telemetry/events.test.ts +18 -0
  133. package/src/telemetry/events.ts +10 -0
  134. package/src/telemetry/system.test.ts +49 -0
  135. package/src/telemetry/system.ts +6 -0
  136. package/src/telemetry/tabId.test.ts +43 -0
  137. package/src/telemetry/tabId.ts +44 -0
  138. package/src/utils/previewCompositionSize.ts +26 -0
  139. package/src/utils/reloadDebug.ts +2 -4
  140. package/src/utils/sourcePatcher.ts +1 -1
  141. package/src/utils/studioTelemetry.test.ts +14 -0
  142. package/src/utils/studioTelemetry.ts +4 -0
  143. package/dist/assets/hyperframes-player-BjAGc2ID.js +0 -464
  144. package/src/player/hooks/useTimelineRowElements.test.ts +0 -28
  145. package/src/player/hooks/useTimelineRowElements.ts +0 -17
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A per-page-load identifier, attached to every Studio event from BOTH
3
+ * transports (`studio:*` via utils/studioTelemetry.ts, `studio_*` via
4
+ * telemetry/system.ts).
5
+ *
6
+ * Why it exists: `distinct_id` identifies a browser, not a page. Several
7
+ * investigations have stalled on "two tabs or one tab switching?" — a burst of
8
+ * events alternating between two project hashes is indistinguishable from a
9
+ * user moving between projects, and `session_start` fires once per page LOAD
10
+ * (main.tsx), so counting it cannot tell them apart either. Nothing in the
11
+ * payload named the page. This does.
12
+ *
13
+ * Semantics, stated plainly because the name invites the wrong reading: one id
14
+ * per PAGE LOAD. A hard reload mints a new one. It is not "the same tab across
15
+ * reloads" — that is a different, harder question, and nobody has needed it yet.
16
+ *
17
+ * Deliberately not persisted. Storage would survive a reload and turn this into
18
+ * a tab-session id, silently changing what every query means; and a blocked or
19
+ * cleared storage would make the property intermittently absent, which reads as
20
+ * "old client" rather than "storage unavailable".
21
+ *
22
+ * Memoized like agentRuntime.ts and distinctId.ts: fixed for the life of the
23
+ * page, so a per-event caller does not pay for generation on every emit.
24
+ */
25
+
26
+ import { generateId } from "../utils/generateId";
27
+
28
+ let cached: string | null = null;
29
+
30
+ /** The page's id. Generated once, on first read. */
31
+ export function resolveTabId(): string {
32
+ if (cached === null) cached = generateId();
33
+ return cached;
34
+ }
35
+
36
+ /** The same value as a telemetry property, for the `studio_*` transport's meta. */
37
+ export function tabIdProperty(): string {
38
+ return resolveTabId();
39
+ }
40
+
41
+ /** Test seam: the memo would otherwise leak between cases in one module load. */
42
+ export function resetTabIdForTests(): void {
43
+ cached = null;
44
+ }
@@ -0,0 +1,26 @@
1
+ export interface PreviewCompositionSize {
2
+ width: number;
3
+ height: number;
4
+ }
5
+
6
+ /** The root composition's authored size, read from the preview document itself. */
7
+ // fallow-ignore-next-line complexity
8
+ export function readPreviewCompositionSize(
9
+ iframe: HTMLIFrameElement | null,
10
+ ): PreviewCompositionSize | null {
11
+ try {
12
+ const doc = iframe?.contentDocument;
13
+ const root =
14
+ doc?.querySelector("[data-composition-id][data-width][data-height]") ??
15
+ doc?.querySelector("[data-width][data-height]");
16
+ if (!root) return null;
17
+ const width = Number.parseInt(root.getAttribute("data-width") ?? "", 10);
18
+ const height = Number.parseInt(root.getAttribute("data-height") ?? "", 10);
19
+ if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
20
+ return null;
21
+ }
22
+ return { width, height };
23
+ } catch {
24
+ return null;
25
+ }
26
+ }
@@ -1,10 +1,8 @@
1
1
  // Preview full-reload diagnostics — grep [hf-reload]. Off by default; opt in per
2
2
  // session with `localStorage.setItem("hf-reload-debug", "1")` (then reload).
3
3
  //
4
- // A full reload blanks the stage for ~100-300ms, so any reload the user did not
5
- // ask for reads as a flash. These lines answer the only question that matters
6
- // when one appears: who asked for it, and why the write that triggered it was
7
- // not recognised as Studio's own.
4
+ // A full reload no longer blanks the stage (see useShadowPreviewReload.ts). These lines answer
5
+ // who asked for a reload and why the write was not recognised as Studio's own.
8
6
  import { makeStudioDebugLogger } from "./studioDebug";
9
7
 
10
8
  export const logReload = makeStudioDebugLogger("reload");
@@ -420,7 +420,7 @@ function findMatchingClosingTagIndex(html: string, tagName: string, contentStart
420
420
  return -1;
421
421
  }
422
422
 
423
- const HTML_BOOLEAN_ATTRIBUTES = new Set([
423
+ export const HTML_BOOLEAN_ATTRIBUTES = new Set([
424
424
  "loop",
425
425
  "muted",
426
426
  "autoplay",
@@ -66,6 +66,20 @@ describe("studioTelemetry — shared opt-out and canary properties", () => {
66
66
  });
67
67
  });
68
68
 
69
+ // The property is useless if the transport does not attach it: the module
70
+ // has its own test, but nothing there fails if this line is deleted.
71
+ it("stamps every event with a tab id that is stable within the page", async () => {
72
+ trackStudioEvent("first");
73
+ trackStudioEvent("second");
74
+ const events = await sentEvents();
75
+ expect(events).toHaveLength(2);
76
+ const tabIds = events.map((e) => (e["properties"] as Record<string, unknown>)["tab_id"]);
77
+ expect(typeof tabIds[0]).toBe("string");
78
+ expect((tabIds[0] as string).length).toBeGreaterThan(0);
79
+ // Same page, same id — otherwise a burst still reads as several sources.
80
+ expect(tabIds[1]).toBe(tabIds[0]);
81
+ });
82
+
69
83
  it("lets an explicit property win over the canary mixin", async () => {
70
84
  trackStudioEvent("thing_happened", { "$feature/canary-test-one": "false" });
71
85
  const events = await sentEvents();
@@ -2,6 +2,7 @@ import { resolveStudioDistinctId } from "../telemetry/distinctId";
2
2
  import { browserTelemetryAllowed } from "../telemetry/policy";
3
3
  import { canaryEventProperties } from "../telemetry/canary";
4
4
  import { agentRuntimeProperty } from "../telemetry/agentRuntime";
5
+ import { tabIdProperty } from "../telemetry/tabId";
5
6
 
6
7
  // PostHog public ingest key — write-only, safe to ship in the client bundle
7
8
  const POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
@@ -48,6 +49,9 @@ function getSessionProperties(): EventProperties {
48
49
  // and a property that only some events carry cannot answer it — the
49
50
  // breakdown silently reads as though the agent never used the rest.
50
51
  agent_runtime: agentRuntimeProperty(),
52
+ // Which page load this came from — distinct_id identifies the browser, not
53
+ // the page, so two tabs are otherwise one indistinguishable stream.
54
+ tab_id: tabIdProperty(),
51
55
  screen_width: window.screen?.width,
52
56
  screen_height: window.screen?.height,
53
57
  viewport_width: window.innerWidth,