@hyperframes/studio 0.7.38 → 0.7.40

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 (102) hide show
  1. package/dist/assets/{index-ClUipc8i.js → index-89kPtC4s.js} +1 -1
  2. package/dist/assets/{index-BLRTwY5l.js → index-B2Utv-2b.js} +200 -200
  3. package/dist/assets/index-BpM6cnfP.css +1 -0
  4. package/dist/assets/{index-Ykq7ihge.js → index-CjX8Ljc8.js} +1 -1
  5. package/dist/{chunk-JND3XUJL.js → chunk-BA66NM4L.js} +1 -1
  6. package/dist/chunk-BA66NM4L.js.map +1 -0
  7. package/dist/{domEditingLayers-UIQZJCOA.js → domEditingLayers-H7LDFIJ7.js} +2 -2
  8. package/dist/index.d.ts +47 -29
  9. package/dist/index.html +2 -2
  10. package/dist/index.js +4432 -3045
  11. package/dist/index.js.map +1 -1
  12. package/package.json +7 -7
  13. package/src/App.tsx +9 -7
  14. package/src/components/StudioPreviewArea.tsx +14 -0
  15. package/src/components/StudioRightPanel.tsx +9 -0
  16. package/src/components/editor/DomEditCropHandles.tsx +238 -0
  17. package/src/components/editor/DomEditOverlay.tsx +140 -151
  18. package/src/components/editor/DomEditRotateHandle.tsx +41 -0
  19. package/src/components/editor/PropertyPanel.test.ts +73 -0
  20. package/src/components/editor/PropertyPanel.tsx +42 -37
  21. package/src/components/editor/PropertyPanelEmptyState.tsx +33 -0
  22. package/src/components/editor/SnapToolbar.tsx +20 -1
  23. package/src/components/editor/clipPathHelpers.ts +113 -0
  24. package/src/components/editor/domEditOverlayCrop.test.ts +106 -0
  25. package/src/components/editor/domEditOverlayCrop.ts +115 -0
  26. package/src/components/editor/domEditOverlayGeometry.ts +14 -0
  27. package/src/components/editor/domEditOverlayShape.ts +39 -0
  28. package/src/components/editor/domEditing.test.ts +2 -0
  29. package/src/components/editor/domEditingElement.ts +8 -1
  30. package/src/components/editor/domEditingTypes.ts +3 -0
  31. package/src/components/editor/manualEditsDom.ts +1 -18
  32. package/src/components/editor/manualEditsStyleHelpers.ts +18 -0
  33. package/src/components/editor/marqueeCommit.ts +3 -2
  34. package/src/components/editor/offCanvasIndicatorGeometry.ts +74 -0
  35. package/src/components/editor/offCanvasIndicatorRefresh.test.tsx +154 -0
  36. package/src/components/editor/offCanvasIndicatorRefresh.ts +99 -0
  37. package/src/components/editor/propertyPanelHelpers.ts +21 -34
  38. package/src/components/editor/propertyPanelStyleSections.tsx +75 -1
  39. package/src/components/editor/propertyPanelTypes.ts +3 -0
  40. package/src/components/editor/snapTargetCollection.ts +2 -3
  41. package/src/components/editor/useDomEditCompositionRect.ts +68 -0
  42. package/src/components/editor/useDomEditOverlayGestures.ts +13 -6
  43. package/src/components/editor/useDomEditOverlayRects.ts +5 -3
  44. package/src/components/sidebar/AssetsTab.test.ts +87 -0
  45. package/src/components/sidebar/AssetsTab.tsx +113 -15
  46. package/src/components/sidebar/BlocksTab.tsx +2 -1
  47. package/src/components/sidebar/GlobalAssetsView.tsx +91 -0
  48. package/src/contexts/TimelineEditContext.tsx +1 -0
  49. package/src/hooks/domSelectionTestHarness.ts +1 -0
  50. package/src/hooks/gsapDragCommit.test.ts +56 -0
  51. package/src/hooks/gsapDragCommit.ts +41 -109
  52. package/src/hooks/gsapDragPositionCommit.ts +3 -0
  53. package/src/hooks/gsapDragStaticSetHelpers.ts +109 -0
  54. package/src/hooks/gsapRuntimeBridge.ts +23 -2
  55. package/src/hooks/gsapRuntimeKeyframes.test.ts +34 -0
  56. package/src/hooks/gsapRuntimeKeyframes.ts +13 -1
  57. package/src/hooks/gsapWholePropertyOffsetCommit.ts +1 -0
  58. package/src/hooks/timelineEditingHelpers.ts +17 -5
  59. package/src/hooks/timelineTrackVisibility.test.ts +202 -0
  60. package/src/hooks/timelineTrackVisibility.ts +371 -0
  61. package/src/hooks/useCropMode.ts +91 -0
  62. package/src/hooks/useDomEditCommits.test.tsx +2 -0
  63. package/src/hooks/useRenderClipContent.test.ts +68 -2
  64. package/src/hooks/useRenderClipContent.ts +5 -6
  65. package/src/hooks/useTimelineEditing.ts +35 -30
  66. package/src/hooks/useTimelineEditingTypes.ts +30 -0
  67. package/src/icons/SystemIcons.tsx +12 -4
  68. package/src/player/components/AudioWaveform.tsx +11 -8
  69. package/src/player/components/CompositionThumbnail.tsx +14 -11
  70. package/src/player/components/PlayheadIndicator.tsx +21 -11
  71. package/src/player/components/ShortcutsPanel.tsx +9 -0
  72. package/src/player/components/Timeline.test.ts +105 -5
  73. package/src/player/components/Timeline.tsx +37 -47
  74. package/src/player/components/TimelineCanvas.tsx +275 -256
  75. package/src/player/components/TimelineClip.test.tsx +105 -0
  76. package/src/player/components/TimelineClip.tsx +44 -57
  77. package/src/player/components/TimelineRuler.tsx +17 -23
  78. package/src/player/components/TimelineShortcutHint.tsx +26 -0
  79. package/src/player/components/TimelineTypes.ts +18 -0
  80. package/src/player/components/VideoThumbnail.tsx +16 -13
  81. package/src/player/components/timelineCallbacks.ts +1 -0
  82. package/src/player/components/timelineIcons.tsx +2 -44
  83. package/src/player/components/timelineLayout.ts +18 -22
  84. package/src/player/components/timelineMotionStyles.test.ts +101 -0
  85. package/src/player/components/timelineTheme.test.ts +21 -4
  86. package/src/player/components/timelineTheme.ts +15 -15
  87. package/src/player/components/useTimelineActiveClips.test.ts +93 -0
  88. package/src/player/components/useTimelineActiveClips.ts +125 -0
  89. package/src/player/components/useTimelinePlayhead.ts +6 -0
  90. package/src/player/hooks/useExpandedTimelineElements.test.ts +110 -3
  91. package/src/player/hooks/useExpandedTimelineElements.ts +106 -19
  92. package/src/player/lib/time.test.ts +1 -0
  93. package/src/player/lib/timelineDOM.test.ts +48 -1
  94. package/src/player/lib/timelineDOM.ts +7 -0
  95. package/src/player/lib/timelineIframeHelpers.ts +1 -0
  96. package/src/player/store/playerStore.ts +19 -1
  97. package/src/styles/studio.css +108 -0
  98. package/src/utils/sourcePatcher.test.ts +11 -0
  99. package/src/utils/sourcePatcher.ts +6 -4
  100. package/dist/assets/index-DJaiR8T2.css +0 -1
  101. package/dist/chunk-JND3XUJL.js.map +0 -1
  102. /package/dist/{domEditingLayers-UIQZJCOA.js.map → domEditingLayers-H7LDFIJ7.js.map} +0 -0
@@ -1,4 +1,4 @@
1
- import { useRef, useMemo, useCallback, useState, useEffect, memo, type ReactNode } from "react";
1
+ import { useRef, useMemo, useCallback, useState, useEffect, memo } from "react";
2
2
  import { useMusicBeatAnalysis } from "../../hooks/useMusicBeatAnalysis";
3
3
  import { isMusicTrack } from "../../utils/timelineInspector";
4
4
  import { remapBeatAnalysisToComposition } from "../../utils/beatEditActions";
@@ -6,9 +6,10 @@ import { usePlayerStore, type TimelineElement } from "../store/playerStore";
6
6
  import { useExpandedTimelineElements } from "../hooks/useExpandedTimelineElements";
7
7
  import { useMountEffect } from "../../hooks/useMountEffect";
8
8
  import { EditPopover } from "./EditModal";
9
- import { defaultTimelineTheme, type TimelineTheme } from "./timelineTheme";
9
+ import { defaultTimelineTheme } from "./timelineTheme";
10
10
  import { useTimelineRangeSelection } from "./useTimelineRangeSelection";
11
11
  import { useTimelinePlayhead } from "./useTimelinePlayhead";
12
+ import { useTimelineActiveClips } from "./useTimelineActiveClips";
12
13
  import { type TrackVisualStyle, getTrackStyle } from "./timelineIcons";
13
14
  import { getTimelinePixelsPerSecond } from "./timelineZoom";
14
15
  import { useTimelineZoom } from "./useTimelineZoom";
@@ -21,17 +22,15 @@ import {
21
22
  } from "./KeyframeDiamondContextMenu";
22
23
  import { useTimelineClipDrag } from "./useTimelineClipDrag";
23
24
  import { ClipContextMenu } from "./ClipContextMenu";
25
+ import { TimelineShortcutHint } from "./TimelineShortcutHint";
24
26
  import {
25
27
  GUTTER,
26
28
  generateTicks,
27
29
  getTimelineCanvasHeight,
28
30
  shouldShowTimelineShortcutHint,
29
31
  } from "./timelineLayout";
30
- import type { TimelineDropCallbacks } from "./timelineCallbacks";
31
- import {
32
- useResolvedTimelineEditCallbacks,
33
- type TimelineEditOverrides,
34
- } from "./useResolvedTimelineEditCallbacks";
32
+ import { useResolvedTimelineEditCallbacks } from "./useResolvedTimelineEditCallbacks";
33
+ import type { TimelineProps } from "./TimelineTypes";
35
34
 
36
35
  // Re-export pure utilities so existing imports from "./Timeline" still resolve.
37
36
  export {
@@ -48,19 +47,6 @@ export {
48
47
  getDefaultDroppedTrack,
49
48
  } from "./timelineLayout";
50
49
 
51
- interface TimelineProps extends TimelineDropCallbacks, TimelineEditOverrides {
52
- onSeek?: (time: number) => void;
53
- onDrillDown?: (element: TimelineElement) => void;
54
- renderClipContent?: (
55
- element: TimelineElement,
56
- style: { clip: string; label: string },
57
- ) => ReactNode;
58
- renderClipOverlay?: (element: TimelineElement) => ReactNode;
59
- onDeleteElement?: (element: TimelineElement) => Promise<void> | void;
60
- onSelectElement?: (element: TimelineElement | null) => void;
61
- theme?: Partial<TimelineTheme>;
62
- }
63
-
64
50
  export const Timeline = memo(function Timeline({
65
51
  onSeek,
66
52
  onDrillDown,
@@ -162,20 +148,26 @@ export const Timeline = memo(function Timeline({
162
148
  });
163
149
  }, [syncShortcutHintVisibility]);
164
150
 
165
- const setContainerRef = useCallback(
151
+ const setContainerRef = useCallback((el: HTMLDivElement | null) => {
152
+ containerRef.current = el;
153
+ }, []);
154
+
155
+ const setScrollRef = useCallback(
166
156
  (el: HTMLDivElement | null) => {
167
157
  if (roRef.current) {
168
158
  roRef.current.disconnect();
169
159
  roRef.current = null;
170
160
  }
171
- containerRef.current = el;
161
+ scrollRef.current = el;
172
162
  if (!el) return;
173
- setViewportWidth(el.clientWidth);
174
- scheduleShortcutHintVisibilitySync();
175
- roRef.current = new ResizeObserver(([entry]) => {
176
- setViewportWidth(entry.contentRect.width);
163
+
164
+ const syncScrollViewport = () => {
165
+ setViewportWidth(el.clientWidth);
177
166
  scheduleShortcutHintVisibilitySync();
178
- });
167
+ };
168
+
169
+ syncScrollViewport();
170
+ roRef.current = new ResizeObserver(syncScrollViewport);
179
171
  roRef.current.observe(el);
180
172
  },
181
173
  [scheduleShortcutHintVisibilitySync],
@@ -273,6 +265,13 @@ export const Timeline = memo(function Timeline({
273
265
  const pps = getTimelinePixelsPerSecond(fitPps, zoomMode, manualZoomPercent);
274
266
  ppsRef.current = pps;
275
267
  const trackContentWidth = Math.max(0, effectiveDuration * pps);
268
+ const clipStateVersion = useMemo(
269
+ () =>
270
+ expandedElements
271
+ .map((el) => `${el.key ?? el.id}:${el.start}:${el.duration}:${el.track}`)
272
+ .join("|"),
273
+ [expandedElements],
274
+ );
276
275
  const zoomModeRef = useRef(zoomMode);
277
276
  zoomModeRef.current = zoomMode;
278
277
  const manualZoomPercentRef = useRef(manualZoomPercent);
@@ -301,6 +300,11 @@ export const Timeline = memo(function Timeline({
301
300
  setManualZoomPercent,
302
301
  onSeek,
303
302
  });
303
+ useTimelineActiveClips({
304
+ scrollRef,
305
+ currentTime,
306
+ clipStateVersion,
307
+ });
304
308
 
305
309
  const {
306
310
  rangeSelection,
@@ -340,8 +344,7 @@ export const Timeline = memo(function Timeline({
340
344
  () => generateTicks(effectiveDuration, pps),
341
345
  [effectiveDuration, pps],
342
346
  );
343
- const majorTickInterval =
344
- major.length >= 2 ? Math.max(0.25, major[1] - major[0]) : effectiveDuration;
347
+ const majorTickInterval = major.length >= 2 ? major[1] - major[0] : effectiveDuration;
345
348
 
346
349
  useEffect(() => {
347
350
  syncShortcutHintVisibility();
@@ -406,7 +409,7 @@ export const Timeline = memo(function Timeline({
406
409
  }}
407
410
  >
408
411
  <div
409
- ref={scrollRef}
412
+ ref={setScrollRef}
410
413
  tabIndex={-1}
411
414
  className={`${zoomMode === "fit" ? "overflow-x-hidden" : "overflow-x-auto"} overflow-y-auto h-full outline-none`}
412
415
  onDragOver={handleAssetDragOver}
@@ -434,7 +437,6 @@ export const Timeline = memo(function Timeline({
434
437
  totalH={totalH}
435
438
  effectiveDuration={effectiveDuration}
436
439
  majorTickInterval={majorTickInterval}
437
- shiftHeld={shiftHeld}
438
440
  rangeSelection={rangeSelection}
439
441
  theme={theme}
440
442
  displayTrackOrder={displayTrackOrder}
@@ -472,6 +474,9 @@ export const Timeline = memo(function Timeline({
472
474
  const elKey = el.key ?? el.id;
473
475
  setSelectedElementId(elKey);
474
476
  onSelectElement?.(el);
477
+ // Visually select the clicked diamond (matches shift-click / motion-path
478
+ // selection); cleared above so this single-selects it.
479
+ toggleSelectedKeyframe(`${elKey}:${pct}`);
475
480
  const absTime = el.start + (pct / 100) * el.duration;
476
481
  onSeek?.(absTime);
477
482
  const kfData = keyframeCache?.get(elKey);
@@ -519,22 +524,7 @@ export const Timeline = memo(function Timeline({
519
524
  </div>
520
525
 
521
526
  {showShortcutHint && !showPopover && !rangeSelection && (
522
- <div className="absolute bottom-2 right-3 pointer-events-none z-20">
523
- <div
524
- className="flex items-center gap-1.5 px-2 py-1 rounded-md border"
525
- style={{ background: "rgba(17,23,35,0.84)", borderColor: theme.gutterBorder }}
526
- >
527
- <kbd
528
- className="text-[9px] font-mono px-1 py-0.5 rounded"
529
- style={{ color: theme.textSecondary, background: "rgba(255,255,255,0.06)" }}
530
- >
531
- Shift
532
- </kbd>
533
- <span className="text-[9px]" style={{ color: theme.textSecondary }}>
534
- + drag/click to edit range
535
- </span>
536
- </div>
537
- </div>
527
+ <TimelineShortcutHint theme={theme} />
538
528
  )}
539
529
 
540
530
  {showPopover && rangeSelection && (