@hyperframes/studio 0.7.90 → 0.7.93

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 (170) hide show
  1. package/dist/assets/{hyperframes-player-RS9ROX_S.js → hyperframes-player-NSdcoOcK.js} +1 -1
  2. package/dist/assets/{index-CquRJgm-.js → index-CCGnc_0M.js} +1 -1
  3. package/dist/assets/index-D78KEjgB.css +1 -0
  4. package/dist/assets/index-Dp1zVb9X.js +428 -0
  5. package/dist/assets/{index-Bi-CwQDX.js → index-wS6f7F5q.js} +1 -1
  6. package/dist/index.d.ts +40 -36
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +9598 -6798
  9. package/dist/index.js.map +1 -1
  10. package/package.json +8 -7
  11. package/src/App.tsx +14 -14
  12. package/src/components/DesignPanelPromoteProvider.tsx +1 -1
  13. package/src/components/ExternalFileConflictBanner.test.tsx +142 -0
  14. package/src/components/ExternalFileConflictBanner.tsx +255 -0
  15. package/src/components/StudioRightPanel.tsx +1 -1
  16. package/src/components/editor/GsapAnimationList.tsx +84 -0
  17. package/src/components/editor/GsapAnimationSection.test.tsx +100 -11
  18. package/src/components/editor/GsapAnimationSection.tsx +11 -41
  19. package/src/components/editor/PropertyPanel.tsx +1 -1
  20. package/src/components/editor/PropertyPanelFlat.tsx +29 -26
  21. package/src/components/editor/gsapAnimationCallbacks.ts +0 -10
  22. package/src/components/editor/propertyPanel3dTransform.tsx +1 -1
  23. package/src/components/editor/propertyPanelCommitField.tsx +37 -2
  24. package/src/components/editor/propertyPanelFlatDescriptors.ts +20 -0
  25. package/src/components/editor/propertyPanelFlatMotionSection.test.tsx +56 -55
  26. package/src/components/editor/propertyPanelFlatMotionSection.tsx +9 -37
  27. package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +69 -0
  28. package/src/components/editor/propertyPanelFlatPrimitives.tsx +2 -2
  29. package/src/components/editor/propertyPanelPrimitives.tsx +2 -2
  30. package/src/components/editor/propertyPanelTransformCommit.test.ts +57 -0
  31. package/src/components/editor/propertyPanelTransformCommit.ts +14 -14
  32. package/src/components/editor/propertyPanelTypes.ts +10 -4
  33. package/src/components/editor/useInspectorGestureTransaction.test.tsx +65 -0
  34. package/src/components/editor/useInspectorGestureTransaction.ts +66 -10
  35. package/src/components/nle/useTimelineEditCallbacks.test.tsx +78 -4
  36. package/src/components/nle/useTimelineEditCallbacks.ts +12 -7
  37. package/src/components/renders/useRenderQueue.ts +16 -3
  38. package/src/components/renders/useRenderQueueTelemetry.test.tsx +104 -0
  39. package/src/components/sidebar/AssetCard.tsx +4 -3
  40. package/src/components/sidebar/AudioRow.tsx +4 -3
  41. package/src/contexts/DomEditContext.tsx +1 -1
  42. package/src/contexts/FileManagerContext.tsx +12 -0
  43. package/src/hooks/externalFileChangeOwnership.test.ts +17 -0
  44. package/src/hooks/externalFileReloadBus.test.ts +34 -0
  45. package/src/hooks/externalFileReloadBus.ts +18 -0
  46. package/src/hooks/gsapDragCommit.test.ts +39 -1
  47. package/src/hooks/gsapDragCommit.ts +12 -40
  48. package/src/hooks/gsapEditOutcome.ts +72 -0
  49. package/src/hooks/gsapResizeIntercept.test.ts +90 -2
  50. package/src/hooks/gsapResizeIntercept.ts +44 -16
  51. package/src/hooks/gsapRuntimeBridge.test.ts +195 -27
  52. package/src/hooks/gsapRuntimeBridge.ts +93 -28
  53. package/src/hooks/useAnimatedPropertyCommit.test.tsx +194 -24
  54. package/src/hooks/useAnimatedPropertyCommit.ts +101 -49
  55. package/src/hooks/useAppHotkeys.textEditing.test.tsx +215 -0
  56. package/src/hooks/useDomEditSession.test.tsx +1 -1
  57. package/src/hooks/useDomEditSession.ts +1 -1
  58. package/src/hooks/useDomGeometryCommits.test.tsx +61 -0
  59. package/src/hooks/useDomGeometryCommits.ts +20 -0
  60. package/src/hooks/useEditorSave.test.tsx +131 -0
  61. package/src/hooks/useEditorSave.ts +113 -39
  62. package/src/hooks/useExternalFileChangeCoordinator.test.tsx +221 -0
  63. package/src/hooks/useExternalFileChangeCoordinator.ts +429 -0
  64. package/src/hooks/useFileManager.projectOwnership.test.tsx +139 -7
  65. package/src/hooks/useFileManager.ts +32 -3
  66. package/src/hooks/useGsapAnimationFetchFallback.ts +45 -24
  67. package/src/hooks/useGsapAwareEditing.test.tsx +125 -39
  68. package/src/hooks/useGsapAwareEditing.ts +88 -27
  69. package/src/hooks/useGsapInteractionFailureTelemetry.test.tsx +42 -0
  70. package/src/hooks/useGsapInteractionFailureTelemetry.ts +5 -1
  71. package/src/hooks/useGsapKeyframeOps.test.tsx +83 -5
  72. package/src/hooks/useGsapKeyframeOps.ts +20 -9
  73. package/src/hooks/usePreviewPersistence.test.ts +23 -0
  74. package/src/hooks/usePreviewPersistence.ts +22 -58
  75. package/src/hooks/useSdkSession.ts +8 -52
  76. package/src/hooks/useStudioExternalFileChanges.ts +81 -0
  77. package/src/hooks/useStudioSdkSessions.ts +2 -7
  78. package/src/hooks/useStudioSessionStart.ts +15 -0
  79. package/src/hooks/useStudioTestHooks.test.tsx +11 -7
  80. package/src/player/components/BeatStrip.test.tsx +374 -0
  81. package/src/player/components/BeatStrip.tsx +295 -37
  82. package/src/player/components/KeyframeDiamondContextMenu.test.tsx +16 -1
  83. package/src/player/components/KeyframeDiamondContextMenu.tsx +6 -5
  84. package/src/player/components/LayerDisclosureRow.tsx +2 -0
  85. package/src/player/components/Timeline.test.ts +26 -8
  86. package/src/player/components/Timeline.tsx +87 -88
  87. package/src/player/components/Timeline.virtualization.test.tsx +54 -11
  88. package/src/player/components/TimelineCanvas.tsx +14 -71
  89. package/src/player/components/TimelineClip.test.tsx +17 -2
  90. package/src/player/components/TimelineClip.tsx +20 -5
  91. package/src/player/components/TimelineClipDiamonds.test.tsx +536 -27
  92. package/src/player/components/TimelineClipDiamonds.tsx +118 -223
  93. package/src/player/components/TimelineCompactDiamonds.tsx +84 -0
  94. package/src/player/components/TimelineDiamondConnectors.tsx +31 -3
  95. package/src/player/components/TimelineGestureOverlay.test.tsx +90 -0
  96. package/src/player/components/TimelineGestureOverlay.tsx +96 -0
  97. package/src/player/components/TimelineLanes.test.tsx +64 -7
  98. package/src/player/components/TimelineLanes.tsx +84 -66
  99. package/src/player/components/TimelineOverlays.test.ts +161 -0
  100. package/src/player/components/TimelineOverlays.tsx +104 -11
  101. package/src/player/components/TimelinePropertyLanes.test.tsx +9 -5
  102. package/src/player/components/TimelinePropertyLanes.tsx +9 -2
  103. package/src/player/components/TimelineTrackHeader.test.tsx +1 -0
  104. package/src/player/components/TimelineTrackHeader.tsx +16 -0
  105. package/src/player/components/TimelineTrackRow.tsx +58 -7
  106. package/src/player/components/timelineCallbacks.ts +1 -1
  107. package/src/player/components/timelineClipDragGestureLifecycle.test.ts +18 -3
  108. package/src/player/components/timelineClipDragGestureLifecycle.ts +199 -51
  109. package/src/player/components/timelineClipDragPreview.test.ts +33 -0
  110. package/src/player/components/timelineClipDragPreview.ts +18 -12
  111. package/src/player/components/timelineClipDragTypes.ts +12 -0
  112. package/src/player/components/timelineDiamondTypes.ts +18 -21
  113. package/src/player/components/timelineDragDrop.test.tsx +206 -0
  114. package/src/player/components/timelineDragDrop.ts +152 -30
  115. package/src/player/components/timelineKeyboardNavigation.test.ts +303 -0
  116. package/src/player/components/timelineKeyboardNavigation.ts +337 -0
  117. package/src/player/components/timelineLaneProps.ts +3 -1
  118. package/src/player/components/timelineMarquee.test.ts +73 -25
  119. package/src/player/components/timelineMarquee.ts +44 -16
  120. package/src/player/components/timelineNavigationIdentity.ts +38 -0
  121. package/src/player/components/timelineTestViewport.ts +13 -0
  122. package/src/player/components/timelineViewModel.ts +4 -4
  123. package/src/player/components/useTimelineClipDrag.resize.test.tsx +219 -17
  124. package/src/player/components/useTimelineClipDrag.ts +112 -70
  125. package/src/player/components/useTimelineClipRenderWindow.ts +8 -39
  126. package/src/player/components/useTimelineFocusCoordinator.test.tsx +174 -0
  127. package/src/player/components/useTimelineFocusCoordinator.ts +221 -0
  128. package/src/player/components/useTimelineKeyboardActor.test.tsx +267 -0
  129. package/src/player/components/useTimelineKeyboardActor.ts +147 -0
  130. package/src/player/components/useTimelineKeyframeHandlers.test.tsx +43 -7
  131. package/src/player/components/useTimelineKeyframeHandlers.ts +465 -2
  132. package/src/player/components/useTimelineLaneMoveRefresh.ts +7 -0
  133. package/src/player/components/useTimelineLogicalFocus.ts +80 -0
  134. package/src/player/components/useTimelineLogicalRows.test.tsx +57 -0
  135. package/src/player/components/useTimelineLogicalRows.ts +47 -0
  136. package/src/player/components/useTimelineRangeSelection.test.tsx +204 -0
  137. package/src/player/components/useTimelineRangeSelection.ts +202 -69
  138. package/src/player/components/useTimelineRangeSelectionScrub.test.tsx +3 -2
  139. package/src/player/components/useTimelineRowVirtualization.ts +26 -29
  140. package/src/player/components/useTrackGapMenu.test.tsx +64 -0
  141. package/src/player/components/useTrackGapMenu.ts +32 -9
  142. package/src/player/store/keyframeSlice.ts +59 -18
  143. package/src/player/store/liveTime.ts +11 -0
  144. package/src/player/store/playerStore.test.ts +106 -23
  145. package/src/player/store/playerStore.ts +43 -40
  146. package/src/player/store/timelineFocusState.ts +15 -0
  147. package/src/telemetry/canary.test.ts +325 -0
  148. package/src/telemetry/canary.ts +268 -0
  149. package/src/telemetry/client.test.ts +8 -3
  150. package/src/telemetry/client.ts +13 -43
  151. package/src/telemetry/config.ts +16 -2
  152. package/src/telemetry/distinctId.test.ts +35 -0
  153. package/src/telemetry/distinctId.ts +11 -2
  154. package/src/telemetry/policy.test.ts +81 -0
  155. package/src/telemetry/policy.ts +104 -0
  156. package/src/utils/domEditSaveQueue.test.ts +37 -1
  157. package/src/utils/domEditSaveQueue.ts +22 -4
  158. package/src/utils/externalConflictStorage.test.ts +95 -0
  159. package/src/utils/externalConflictStorage.ts +172 -0
  160. package/src/utils/studioFileVersion.test.ts +28 -1
  161. package/src/utils/studioFileVersion.ts +30 -0
  162. package/src/utils/studioPendingEdits.test.ts +108 -1
  163. package/src/utils/studioPendingEdits.ts +39 -2
  164. package/src/utils/studioSaveDiagnostics.ts +5 -0
  165. package/src/utils/studioTelemetry.test.ts +76 -0
  166. package/src/utils/studioTelemetry.ts +14 -6
  167. package/dist/assets/index-BXkzp_OU.css +0 -1
  168. package/dist/assets/index-Cql15Yur.js +0 -428
  169. package/src/player/components/useTimelineRevealClip.test.tsx +0 -139
  170. package/src/player/components/useTimelineRevealClip.ts +0 -180
@@ -1,139 +0,0 @@
1
- // @vitest-environment happy-dom
2
-
3
- import React, { act, useRef } from "react";
4
- import { createRoot, type Root } from "react-dom/client";
5
- import { afterEach, describe, expect, it } from "vitest";
6
- import type { TimelineElement } from "../store/playerStore";
7
- import { usePlayerStore } from "../store/playerStore";
8
- import { createTimelineRowGeometry } from "./timelineLayout";
9
- import { useTimelineRevealClip } from "./useTimelineRevealClip";
10
-
11
- Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true });
12
-
13
- const element: TimelineElement = {
14
- id: "hero",
15
- tag: "div",
16
- start: 20,
17
- duration: 2,
18
- track: 1,
19
- };
20
- const geometry = createTimelineRowGeometry([1], [48]);
21
-
22
- function createHarnessRoot() {
23
- const host = document.createElement("div");
24
- document.body.append(host);
25
- return { host, root: createRoot(host) };
26
- }
27
-
28
- interface HarnessProps {
29
- mounted: boolean;
30
- version: number;
31
- width?: number;
32
- height?: number;
33
- deferFocusUntilViewportUpdate?: boolean;
34
- focusedElementId?: string;
35
- }
36
-
37
- function Harness({
38
- mounted,
39
- version,
40
- width = 300,
41
- height = 100,
42
- deferFocusUntilViewportUpdate = false,
43
- focusedElementId,
44
- }: HarnessProps) {
45
- const scrollRef = useRef<HTMLDivElement>(null);
46
- useTimelineRevealClip({
47
- scrollRef,
48
- elements: [element],
49
- rowGeometry: geometry,
50
- pixelsPerSecond: 100,
51
- contentOrigin: 32,
52
- allowHorizontal: true,
53
- deferFocusUntilViewportUpdate,
54
- focusedElementId,
55
- viewportVersion: version,
56
- sessionEpoch: 1,
57
- });
58
- return (
59
- <div
60
- ref={(node) => {
61
- scrollRef.current = node;
62
- if (node) {
63
- Object.defineProperty(node, "clientWidth", { configurable: true, value: width });
64
- Object.defineProperty(node, "clientHeight", { configurable: true, value: height });
65
- }
66
- }}
67
- >
68
- {mounted && <div data-el-id="hero" tabIndex={-1} />}
69
- </div>
70
- );
71
- }
72
-
73
- async function renderHarness(root: Root, props: HarnessProps): Promise<void> {
74
- await act(async () => root.render(<Harness {...props} />));
75
- }
76
-
77
- afterEach(() => {
78
- usePlayerStore.getState().reset();
79
- document.body.replaceChildren();
80
- });
81
-
82
- describe("useTimelineRevealClip", () => {
83
- it("scrolls from model coordinates, then consumes only after the clip mounts", async () => {
84
- const { host, root } = createHarnessRoot();
85
- usePlayerStore.getState().requestClipReveal("hero");
86
-
87
- await renderHarness(root, { mounted: false, version: 0 });
88
- const scroll = host.firstElementChild as HTMLDivElement;
89
- expect(scroll.scrollLeft).toBe(1_944);
90
- expect(usePlayerStore.getState().clipRevealRequest?.elementId).toBe("hero");
91
-
92
- await renderHarness(root, { mounted: true, version: 1 });
93
- expect(usePlayerStore.getState().clipRevealRequest).toBeNull();
94
- expect(document.activeElement?.getAttribute("data-el-id")).toBe("hero");
95
-
96
- scroll.scrollLeft = 0;
97
- await act(async () => usePlayerStore.getState().requestClipReveal("hero"));
98
- await renderHarness(root, { mounted: true, version: 2 });
99
- expect(scroll.scrollLeft).toBe(1_944);
100
- expect(usePlayerStore.getState().clipRevealRequest).toBeNull();
101
- await act(async () => root.unmount());
102
- });
103
-
104
- it("consumes an invalid target without scrolling", async () => {
105
- const { root } = createHarnessRoot();
106
- usePlayerStore.getState().requestClipReveal("missing");
107
- await renderHarness(root, { mounted: false, version: 0 });
108
- expect(usePlayerStore.getState().clipRevealRequest).toBeNull();
109
- await act(async () => root.unmount());
110
- });
111
-
112
- it("keeps a reveal pending through zero-size viewport and virtualized focus handoff", async () => {
113
- const { host, root } = createHarnessRoot();
114
- usePlayerStore.getState().requestClipReveal("hero");
115
-
116
- await renderHarness(root, { mounted: true, version: 0, width: 0, height: 0 });
117
- const scroll = host.firstElementChild as HTMLDivElement;
118
- expect(scroll.scrollLeft).toBe(0);
119
- expect(usePlayerStore.getState().clipRevealRequest?.elementId).toBe("hero");
120
- expect(document.activeElement?.getAttribute("data-el-id")).not.toBe("hero");
121
-
122
- await renderHarness(root, {
123
- mounted: true,
124
- version: 1,
125
- deferFocusUntilViewportUpdate: true,
126
- });
127
- expect(scroll.scrollLeft).toBe(1_944);
128
- expect(usePlayerStore.getState().clipRevealRequest?.elementId).toBe("hero");
129
- await renderHarness(root, {
130
- mounted: true,
131
- version: 2,
132
- deferFocusUntilViewportUpdate: true,
133
- focusedElementId: "hero",
134
- });
135
- expect(usePlayerStore.getState().clipRevealRequest).toBeNull();
136
- expect(document.activeElement?.getAttribute("data-el-id")).toBe("hero");
137
- await act(async () => root.unmount());
138
- });
139
- });
@@ -1,180 +0,0 @@
1
- import { useEffect, useRef } from "react";
2
- import type { TimelineElement } from "../store/playerStore";
3
- import { usePlayerStore } from "../store/playerStore";
4
- import { getTimelineElementIdentity } from "../lib/timelineElementHelpers";
5
- import { CLIP_Y, RULER_H, type TimelineRowGeometry } from "./timelineLayout";
6
- import { computeRevealScroll } from "./timelineRevealScroll";
7
-
8
- interface UseTimelineRevealClipInput {
9
- scrollRef: React.RefObject<HTMLDivElement | null>;
10
- elements: readonly TimelineElement[];
11
- rowGeometry: TimelineRowGeometry;
12
- pixelsPerSecond: number;
13
- contentOrigin: number;
14
- allowHorizontal: boolean;
15
- deferFocusUntilViewportUpdate: boolean;
16
- focusedElementId?: string;
17
- viewportVersion: unknown;
18
- sessionEpoch: number;
19
- }
20
-
21
- function escapeSelectorValue(value: string): string {
22
- return typeof CSS !== "undefined" && typeof CSS.escape === "function"
23
- ? CSS.escape(value)
24
- : value.replace(/["\\]/g, "\\$&");
25
- }
26
-
27
- function scrollToTimelineElement(
28
- container: HTMLDivElement,
29
- element: TimelineElement,
30
- row: number,
31
- rowGeometry: TimelineRowGeometry,
32
- pixelsPerSecond: number,
33
- contentOrigin: number,
34
- allowHorizontal: boolean,
35
- ): boolean {
36
- const clipLeft = contentOrigin + element.start * pixelsPerSecond;
37
- const target = computeRevealScroll({
38
- scrollLeft: container.scrollLeft,
39
- scrollTop: container.scrollTop,
40
- viewportWidth: container.clientWidth,
41
- viewportHeight: container.clientHeight,
42
- clipLeft,
43
- clipRight: clipLeft + Math.max(element.duration * pixelsPerSecond, 4),
44
- clipTop: rowGeometry.getRowTop(row) + CLIP_Y,
45
- clipBottom: rowGeometry.getRowTop(row) + rowGeometry.getRowHeight(row) - CLIP_Y,
46
- stickyLeft: contentOrigin,
47
- stickyTop: RULER_H,
48
- allowHorizontal,
49
- });
50
- if (target.left !== null) container.scrollLeft = target.left;
51
- if (target.top !== null) container.scrollTop = target.top;
52
- const didScroll = target.left !== null || target.top !== null;
53
- if (didScroll) container.dispatchEvent(new Event("scroll"));
54
- return didScroll;
55
- }
56
-
57
- function focusRevealedElement(container: HTMLDivElement, elementId: string): boolean {
58
- const clip = container.querySelector(`[data-el-id="${escapeSelectorValue(elementId)}"]`);
59
- if (!(clip instanceof HTMLElement)) return false;
60
- const alreadyHighlighted = clip.hasAttribute("data-reveal-highlight");
61
- clip.setAttribute("data-reveal-highlight", "true");
62
- clip.focus({ preventScroll: true });
63
- if (document.activeElement !== clip) {
64
- clip.removeAttribute("data-reveal-highlight");
65
- return false;
66
- }
67
- if (!alreadyHighlighted) {
68
- clip.addEventListener("blur", () => clip.removeAttribute("data-reveal-highlight"), {
69
- once: true,
70
- });
71
- }
72
- return true;
73
- }
74
-
75
- function focusAndConsumeReveal(
76
- container: HTMLDivElement,
77
- request: { elementId: string; nonce: number },
78
- deferUntilFocusPin: boolean,
79
- focusedElementId?: string,
80
- ): void {
81
- if (!focusRevealedElement(container, request.elementId)) return;
82
- if (deferUntilFocusPin && focusedElementId !== request.elementId) return;
83
- if (usePlayerStore.getState().clipRevealRequest === request) {
84
- usePlayerStore.getState().clearClipRevealRequest();
85
- }
86
- }
87
-
88
- function resolveRevealTarget(
89
- elements: readonly TimelineElement[],
90
- rowGeometry: TimelineRowGeometry,
91
- elementId: string,
92
- ): { element: TimelineElement; row: number } | null {
93
- const element = elements.find((candidate) => getTimelineElementIdentity(candidate) === elementId);
94
- if (!element) return null;
95
- const row = rowGeometry.getRowIndex(element.track);
96
- return row < 0 ? null : { element, row };
97
- }
98
-
99
- function shouldScrollReveal(
100
- previous: { request: { elementId: string; nonce: number }; sessionEpoch: number } | null,
101
- request: { elementId: string; nonce: number },
102
- sessionEpoch: number,
103
- ): boolean {
104
- return previous?.request !== request || previous.sessionEpoch !== sessionEpoch;
105
- }
106
-
107
- /** Coordinate-first reveal; the request remains pinned until its clip mounts. */
108
- export function useTimelineRevealClip({
109
- scrollRef,
110
- elements,
111
- rowGeometry,
112
- pixelsPerSecond,
113
- contentOrigin,
114
- allowHorizontal,
115
- deferFocusUntilViewportUpdate,
116
- focusedElementId,
117
- viewportVersion,
118
- sessionEpoch,
119
- }: UseTimelineRevealClipInput): void {
120
- const revealRequest = usePlayerStore((state) => state.clipRevealRequest);
121
- const scrolledRequestRef = useRef<{
122
- request: { elementId: string; nonce: number };
123
- sessionEpoch: number;
124
- } | null>(null);
125
-
126
- useEffect(() => {
127
- if (!revealRequest) {
128
- scrolledRequestRef.current = null;
129
- return;
130
- }
131
- const target = resolveRevealTarget(elements, rowGeometry, revealRequest.elementId);
132
- if (!target) {
133
- usePlayerStore.getState().clearClipRevealRequest();
134
- return;
135
- }
136
- const container = scrollRef.current;
137
- if (!container) return;
138
- if (container.clientWidth <= 0 || container.clientHeight <= 0) return;
139
-
140
- if (shouldScrollReveal(scrolledRequestRef.current, revealRequest, sessionEpoch)) {
141
- scrolledRequestRef.current = { request: revealRequest, sessionEpoch };
142
- const didScroll = scrollToTimelineElement(
143
- container,
144
- target.element,
145
- target.row,
146
- rowGeometry,
147
- pixelsPerSecond,
148
- contentOrigin,
149
- allowHorizontal,
150
- );
151
- // Keep the reveal pin alive until the scroll snapshot catches up. If the
152
- // request were consumed here, horizontal windowing could unmount and
153
- // recreate the focused clip between the programmatic scroll and the next
154
- // viewport publication.
155
- if (didScroll && deferFocusUntilViewportUpdate) return;
156
- }
157
-
158
- // Focus is the durable row/clip pin. Do not consume the reveal pin until
159
- // the focus listener has published that replacement, or windowing can
160
- // briefly unmount and recreate the element between the two owners.
161
- focusAndConsumeReveal(
162
- container,
163
- revealRequest,
164
- deferFocusUntilViewportUpdate,
165
- focusedElementId,
166
- );
167
- }, [
168
- allowHorizontal,
169
- contentOrigin,
170
- deferFocusUntilViewportUpdate,
171
- elements,
172
- focusedElementId,
173
- pixelsPerSecond,
174
- revealRequest,
175
- rowGeometry,
176
- scrollRef,
177
- sessionEpoch,
178
- viewportVersion,
179
- ]);
180
- }