@hyperframes/studio 0.7.57 → 0.7.59

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 (247) hide show
  1. package/dist/assets/{hyperframes-player--Z69cEkE.js → hyperframes-player-Bm07FLkl.js} +1 -1
  2. package/dist/assets/{index-KsfE1bUu.js → index-B995FG46.js} +1 -1
  3. package/dist/assets/index-CrkAdJkb.js +426 -0
  4. package/dist/assets/index-Dj5p8U_A.css +1 -0
  5. package/dist/assets/{index-Bf1x1y8H.js → index-FvzmPhfG.js} +1 -1
  6. package/dist/{chunk-5QSIMBEJ.js → chunk-OBAG3GWK.js} +33 -21
  7. package/dist/chunk-OBAG3GWK.js.map +1 -0
  8. package/dist/{domEditingLayers-6LQGKPOI.js → domEditingLayers-2CKWGEHS.js} +2 -2
  9. package/dist/index.d.ts +27 -7
  10. package/dist/index.html +2 -2
  11. package/dist/index.js +18029 -11563
  12. package/dist/index.js.map +1 -1
  13. package/dist/styles/tailwind-preset.d.ts +5 -0
  14. package/dist/styles/tailwind-preset.js +8 -1
  15. package/dist/styles/tailwind-preset.js.map +1 -1
  16. package/package.json +7 -7
  17. package/src/App.tsx +6 -6
  18. package/src/components/DesignPanelPromoteProvider.tsx +15 -2
  19. package/src/components/StudioFeedbackBar.tsx +2 -2
  20. package/src/components/StudioRightPanel.tsx +29 -51
  21. package/src/components/editor/AnimationCard.test.tsx +134 -0
  22. package/src/components/editor/AnimationCard.tsx +19 -4
  23. package/src/components/editor/ArcPathControls.tsx +1 -0
  24. package/src/components/editor/CanvasContextMenu.test.tsx +27 -14
  25. package/src/components/editor/CanvasContextMenu.tsx +71 -6
  26. package/src/components/editor/DomEditOverlay.tsx +5 -2
  27. package/src/components/editor/GestureRecordControl.tsx +7 -1
  28. package/src/components/editor/GsapAnimationSection.tsx +109 -14
  29. package/src/components/editor/InspectorHeaderActions.tsx +42 -4
  30. package/src/components/editor/LayersPanel.tsx +110 -99
  31. package/src/components/editor/PropertyPanel.test.tsx +948 -0
  32. package/src/components/editor/PropertyPanel.tsx +144 -148
  33. package/src/components/editor/PropertyPanelEmptyState.test.tsx +74 -0
  34. package/src/components/editor/PropertyPanelEmptyState.tsx +179 -1
  35. package/src/components/editor/PropertyPanelFlat.tsx +580 -0
  36. package/src/components/editor/PropertyPanelFlatFooter.test.tsx +83 -0
  37. package/src/components/editor/PropertyPanelFlatFooter.tsx +73 -0
  38. package/src/components/editor/PropertyPanelFlatHeader.test.tsx +79 -0
  39. package/src/components/editor/PropertyPanelFlatHeader.tsx +112 -0
  40. package/src/components/editor/canvasContextMenuZOrder.test.ts +57 -0
  41. package/src/components/editor/canvasContextMenuZOrder.ts +40 -1
  42. package/src/components/editor/domEditingDom.ts +5 -6
  43. package/src/components/editor/domEditingElement.ts +15 -32
  44. package/src/components/editor/gsapAnimationCallbacks.ts +27 -0
  45. package/src/components/editor/gsapLivePreview.ts +34 -0
  46. package/src/components/editor/layersPanelSort.ts +80 -0
  47. package/src/components/editor/manualEditingAvailability.test.ts +10 -0
  48. package/src/components/editor/manualEditingAvailability.ts +9 -0
  49. package/src/components/editor/propertyPanel3dTransform.tsx +5 -0
  50. package/src/components/editor/propertyPanelColor.test.tsx +28 -0
  51. package/src/components/editor/propertyPanelColor.tsx +31 -1
  52. package/src/components/editor/propertyPanelColorGradingControls.tsx +11 -2
  53. package/src/components/editor/propertyPanelColorGradingSection.tsx +45 -365
  54. package/src/components/editor/propertyPanelColorGradingSlider.tsx +19 -5
  55. package/src/components/editor/propertyPanelFill.tsx +22 -5
  56. package/src/components/editor/propertyPanelFlatColorGradingSection.test.tsx +622 -0
  57. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +570 -0
  58. package/src/components/editor/propertyPanelFlatLayoutSection.test.tsx +293 -0
  59. package/src/components/editor/propertyPanelFlatLayoutSection.tsx +383 -0
  60. package/src/components/editor/propertyPanelFlatMaskInsetRows.tsx +102 -0
  61. package/src/components/editor/propertyPanelFlatMediaSection.test.tsx +436 -0
  62. package/src/components/editor/propertyPanelFlatMediaSection.tsx +286 -0
  63. package/src/components/editor/propertyPanelFlatMotionSection.test.tsx +272 -0
  64. package/src/components/editor/propertyPanelFlatMotionSection.tsx +315 -0
  65. package/src/components/editor/propertyPanelFlatPrimitives.test.tsx +1174 -0
  66. package/src/components/editor/propertyPanelFlatPrimitives.tsx +563 -0
  67. package/src/components/editor/propertyPanelFlatSelectRow.tsx +101 -0
  68. package/src/components/editor/propertyPanelFlatStyleHelpers.test.ts +23 -0
  69. package/src/components/editor/propertyPanelFlatStyleHelpers.ts +27 -0
  70. package/src/components/editor/propertyPanelFlatStyleSections.test.tsx +598 -0
  71. package/src/components/editor/propertyPanelFlatStyleSections.tsx +516 -0
  72. package/src/components/editor/propertyPanelFlatTextSection.test.tsx +465 -0
  73. package/src/components/editor/propertyPanelFlatTextSection.tsx +407 -0
  74. package/src/components/editor/propertyPanelFlatTimingDerivation.test.ts +71 -0
  75. package/src/components/editor/propertyPanelFlatTimingDerivation.ts +59 -0
  76. package/src/components/editor/propertyPanelFlatToggle.test.tsx +63 -0
  77. package/src/components/editor/propertyPanelFlatToggle.tsx +53 -0
  78. package/src/components/editor/propertyPanelFont.test.tsx +30 -0
  79. package/src/components/editor/propertyPanelFont.tsx +136 -95
  80. package/src/components/editor/propertyPanelHelpers.ts +73 -0
  81. package/src/components/editor/propertyPanelInputCoverage.test.tsx +587 -0
  82. package/src/components/editor/propertyPanelMediaSection.tsx +10 -0
  83. package/src/components/editor/propertyPanelPrimitives.tsx +60 -29
  84. package/src/components/editor/propertyPanelSections.test.tsx +161 -0
  85. package/src/components/editor/propertyPanelSections.tsx +154 -90
  86. package/src/components/editor/propertyPanelStyleSections.tsx +12 -1
  87. package/src/components/editor/propertyPanelTimingSection.tsx +1 -1
  88. package/src/components/editor/propertyPanelTypes.ts +14 -1
  89. package/src/components/editor/propertyPanelValueTier.test.ts +32 -0
  90. package/src/components/editor/propertyPanelValueTier.ts +28 -0
  91. package/src/components/editor/useColorGradingController.test.ts +418 -0
  92. package/src/components/editor/useColorGradingController.ts +596 -0
  93. package/src/components/editor/useLayerRevealOverride.test.ts +169 -0
  94. package/src/components/editor/useLayerRevealOverride.ts +389 -0
  95. package/src/components/nle/PreviewOverlays.tsx +27 -2
  96. package/src/components/nle/TimelinePane.test.ts +3 -0
  97. package/src/components/nle/TimelinePane.tsx +11 -1
  98. package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +440 -0
  99. package/src/components/nle/useCanvasZOrderTimelineMirror.ts +191 -0
  100. package/src/components/nle/useTimelineEditCallbacks.ts +1 -0
  101. package/src/components/nle/zLaneGesture.test.ts +176 -0
  102. package/src/components/nle/zLaneGesture.ts +77 -0
  103. package/src/components/panels/VariablesPanel.tsx +4 -0
  104. package/src/components/sidebar/CompositionsTab.test.ts +28 -2
  105. package/src/components/sidebar/CompositionsTab.tsx +3 -1
  106. package/src/components/storyboard/FramePoster.tsx +24 -4
  107. package/src/components/storyboard/StoryboardFrameFocus.tsx +4 -0
  108. package/src/components/storyboard/StoryboardFrameTile.tsx +30 -1
  109. package/src/components/storyboard/StoryboardGrid.tsx +23 -1
  110. package/src/components/storyboard/StoryboardLoaded.tsx +58 -2
  111. package/src/components/storyboard/StoryboardView.tsx +9 -2
  112. package/src/components/storyboard/frameComments.test.ts +95 -0
  113. package/src/components/storyboard/frameComments.ts +115 -0
  114. package/src/components/storyboard/useFrameComments.ts +82 -0
  115. package/src/contexts/DesignPanelInputContext.test.tsx +81 -0
  116. package/src/contexts/DesignPanelInputContext.tsx +48 -0
  117. package/src/contexts/DomEditContext.tsx +4 -0
  118. package/src/contexts/VariablePromoteContext.test.tsx +67 -0
  119. package/src/contexts/VariablePromoteContext.tsx +10 -5
  120. package/src/hooks/domEditCommitRunner.ts +11 -0
  121. package/src/hooks/domEditCommitTypes.ts +17 -1
  122. package/src/hooks/elementLifecycleOpsTestUtils.ts +25 -0
  123. package/src/hooks/fetchStubTestUtils.ts +16 -0
  124. package/src/hooks/gsapScriptCommitTypes.ts +6 -4
  125. package/src/hooks/timelineEditingHelpers.test.ts +92 -1
  126. package/src/hooks/timelineEditingHelpers.ts +32 -15
  127. package/src/hooks/timelineMoveAdapter.ts +2 -1
  128. package/src/hooks/timelineTimingSync.test.ts +703 -33
  129. package/src/hooks/timelineTimingSync.ts +282 -97
  130. package/src/hooks/timelineTrackVisibility.test.ts +55 -1
  131. package/src/hooks/timelineTrackVisibility.ts +31 -13
  132. package/src/hooks/useAppHotkeys.ts +9 -0
  133. package/src/hooks/useClipboard.ts +19 -17
  134. package/src/hooks/useDomEditAttributeCommits.ts +123 -1
  135. package/src/hooks/useDomEditCommits.test.tsx +316 -38
  136. package/src/hooks/useDomEditCommits.ts +81 -162
  137. package/src/hooks/useDomEditCommitsHelpers.ts +158 -0
  138. package/src/hooks/useDomEditSession.test.tsx +3 -1
  139. package/src/hooks/useDomEditSession.ts +18 -6
  140. package/src/hooks/useDomEditTextCommits.ts +14 -9
  141. package/src/hooks/useDomEditWiring.ts +10 -9
  142. package/src/hooks/useElementLifecycleOps.test.tsx +426 -60
  143. package/src/hooks/useElementLifecycleOps.ts +182 -84
  144. package/src/hooks/useFileManager.projectOwnership.test.tsx +98 -0
  145. package/src/hooks/useFileManager.ts +82 -60
  146. package/src/hooks/useGsapAnimationOps.ts +7 -6
  147. package/src/hooks/useGsapKeyframeOps.ts +6 -5
  148. package/src/hooks/useGsapPropertyDebounce.ts +43 -28
  149. package/src/hooks/useGsapPropertyDebounceFlush.test.ts +35 -0
  150. package/src/hooks/useGsapScriptCommits.test.tsx +48 -2
  151. package/src/hooks/useGsapScriptCommits.ts +150 -50
  152. package/src/hooks/useGsapSelectionHandlers.test.tsx +116 -0
  153. package/src/hooks/useGsapSelectionHandlers.ts +94 -31
  154. package/src/hooks/useInspectorSplitResize.ts +51 -0
  155. package/src/hooks/usePersistentEditHistory.projectOwnership.test.tsx +60 -0
  156. package/src/hooks/usePersistentEditHistory.test.ts +49 -1
  157. package/src/hooks/usePersistentEditHistory.ts +71 -31
  158. package/src/hooks/usePreviewPersistence.ts +2 -2
  159. package/src/hooks/useProjectCompositionVariables.ts +10 -10
  160. package/src/hooks/useProjectSignaturePoll.test.tsx +135 -0
  161. package/src/hooks/useProjectSignaturePoll.ts +68 -0
  162. package/src/hooks/useSdkSession.lifecycle.test.tsx +137 -0
  163. package/src/hooks/useSdkSession.ts +155 -14
  164. package/src/hooks/useSlideshowPersist.ts +7 -1
  165. package/src/hooks/useStoryboard.ts +23 -4
  166. package/src/hooks/useTimelineEditing.test.tsx +306 -155
  167. package/src/hooks/useTimelineEditing.ts +111 -101
  168. package/src/hooks/useTimelineEditingTypes.ts +4 -1
  169. package/src/hooks/useTimelineGroupEditing.ts +51 -5
  170. package/src/hooks/useVariablesPersist.ts +9 -7
  171. package/src/icons/SystemIcons.tsx +2 -0
  172. package/src/player/components/Timeline.test.ts +31 -21
  173. package/src/player/components/Timeline.tsx +62 -91
  174. package/src/player/components/TimelineCanvas.tsx +36 -5
  175. package/src/player/components/TimelineLanes.tsx +32 -10
  176. package/src/player/components/TimelineOverlays.tsx +36 -0
  177. package/src/player/components/TimelineRuler.tsx +18 -5
  178. package/src/player/components/TrackGapContextMenu.tsx +116 -0
  179. package/src/player/components/timelineCallbacks.ts +4 -1
  180. package/src/player/components/timelineClipDragCommit.test.ts +279 -3
  181. package/src/player/components/timelineClipDragCommit.ts +173 -71
  182. package/src/player/components/timelineGapCommit.test.ts +163 -0
  183. package/src/player/components/timelineGapCommit.ts +103 -0
  184. package/src/player/components/timelineGaps.test.ts +221 -0
  185. package/src/player/components/timelineGaps.ts +182 -0
  186. package/src/player/components/timelineLayout.test.ts +2 -1
  187. package/src/player/components/timelineLayout.ts +22 -6
  188. package/src/player/components/timelineMarquee.test.ts +20 -10
  189. package/src/player/components/timelineMarquee.ts +9 -2
  190. package/src/player/components/timelineStackingSync.test.ts +42 -1
  191. package/src/player/components/timelineStackingSync.ts +31 -16
  192. package/src/player/components/timelineZMirror.test.ts +667 -0
  193. package/src/player/components/timelineZMirror.ts +393 -0
  194. package/src/player/components/timelineZoom.test.ts +2 -2
  195. package/src/player/components/timelineZoom.ts +7 -3
  196. package/src/player/components/useTimelineClipDrag.ts +1 -1
  197. package/src/player/components/useTimelineGapHighlights.test.ts +119 -0
  198. package/src/player/components/useTimelineGapHighlights.ts +110 -0
  199. package/src/player/components/useTimelinePlayhead.ts +4 -3
  200. package/src/player/components/useTimelineRangeSelection.ts +6 -4
  201. package/src/player/components/useTimelineScrollViewport.ts +73 -0
  202. package/src/player/components/useTimelineStackingSync.test.tsx +0 -1
  203. package/src/player/components/useTimelineStackingSync.ts +5 -3
  204. package/src/player/components/useTimelineTrackDerivations.ts +38 -0
  205. package/src/player/components/useTrackGapMenu.ts +155 -0
  206. package/src/player/hooks/useExpandedTimelineElements.test.ts +67 -4
  207. package/src/player/hooks/useExpandedTimelineElements.ts +12 -1
  208. package/src/player/hooks/useTimelineSyncCallbacks.ts +2 -0
  209. package/src/player/lib/layerOrdering.test.ts +1 -15
  210. package/src/player/lib/layerOrdering.ts +5 -28
  211. package/src/player/lib/timelineDOM.test.ts +72 -0
  212. package/src/player/lib/timelineDOM.ts +3 -0
  213. package/src/player/lib/timelineElementHelpers.ts +35 -11
  214. package/src/player/store/playerStore.ts +12 -1
  215. package/src/styles/studio.css +22 -0
  216. package/src/styles/tailwind-preset.shared.js +7 -0
  217. package/src/telemetry/events.test.ts +10 -0
  218. package/src/telemetry/events.ts +1 -0
  219. package/src/utils/designInputTracking.test.ts +97 -0
  220. package/src/utils/designInputTracking.ts +80 -0
  221. package/src/utils/domEditSaveQueue.ts +4 -3
  222. package/src/utils/editHistory.test.ts +82 -0
  223. package/src/utils/editHistory.ts +14 -2
  224. package/src/utils/gsapSoftReload.test.ts +54 -115
  225. package/src/utils/gsapSoftReload.ts +42 -152
  226. package/src/utils/gsapUndoRestore.test.ts +269 -0
  227. package/src/utils/gsapUndoRestore.ts +269 -0
  228. package/src/utils/sdkCutover.gate.test.ts +7 -7
  229. package/src/utils/sdkCutover.test.ts +490 -57
  230. package/src/utils/sdkCutover.ts +181 -219
  231. package/src/utils/sdkEditTransaction.ts +292 -0
  232. package/src/utils/sdkResolverAttempts.ts +94 -0
  233. package/src/utils/sdkResolverShadow.test.ts +148 -0
  234. package/src/utils/sdkResolverShadow.ts +139 -106
  235. package/src/utils/setSlideshowManifest.test.ts +0 -9
  236. package/src/utils/setSlideshowManifest.ts +20 -27
  237. package/src/utils/sourceScopedSelectorIndex.ts +29 -0
  238. package/src/utils/studioFileHistory.test.ts +41 -0
  239. package/src/utils/studioFileHistory.ts +30 -27
  240. package/src/utils/studioFileMutationCoordinator.ts +45 -0
  241. package/src/utils/studioHelpers.test.ts +45 -0
  242. package/src/utils/studioHelpers.ts +45 -14
  243. package/src/utils/studioSaveDiagnostics.ts +17 -0
  244. package/dist/assets/index-DfmYkU44.js +0 -423
  245. package/dist/assets/index-_pqzyxB1.css +0 -1
  246. package/dist/chunk-5QSIMBEJ.js.map +0 -1
  247. /package/dist/{domEditingLayers-6LQGKPOI.js.map → domEditingLayers-2CKWGEHS.js.map} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { Minus, Plus, RotateCcw, Settings } from "../../icons/SystemIcons";
3
3
  import { LABEL } from "./propertyPanelHelpers";
4
+ import { useTrackDesignInput } from "../../contexts/DesignPanelInputContext";
4
5
 
5
6
  const SLIDER_THUMB_SIZE = 10;
6
7
  const SLIDER_THUMB_RADIUS = SLIDER_THUMB_SIZE / 2;
@@ -59,9 +60,11 @@ export function ColorGradingSliderControl({
59
60
  onClick: () => void;
60
61
  };
61
62
  }) {
63
+ const track = useTrackDesignInput();
62
64
  const [draftState, setDraftState] = useState<{ value: number; source: number } | null>(null);
63
65
  const [inputDraft, setInputDraft] = useState<{ value: string; source: number } | null>(null);
64
66
  const commitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
67
+ const interactionChangedRef = useRef(false);
65
68
  const valueRef = useRef(value);
66
69
  valueRef.current = value;
67
70
 
@@ -92,9 +95,13 @@ export function ColorGradingSliderControl({
92
95
  (nextValue: number) => {
93
96
  const clamped = setLocalDraft(nextValue);
94
97
  if (commitTimerRef.current) clearTimeout(commitTimerRef.current);
98
+ if (interactionChangedRef.current) {
99
+ interactionChangedRef.current = false;
100
+ track("slider", label);
101
+ }
95
102
  if (clamped !== valueRef.current) onCommit(clamped);
96
103
  },
97
- [onCommit, setLocalDraft],
104
+ [label, onCommit, setLocalDraft, track],
98
105
  );
99
106
 
100
107
  const scheduleCommit = useCallback(
@@ -115,6 +122,7 @@ export function ColorGradingSliderControl({
115
122
  const commitInputDraft = useCallback(() => {
116
123
  const parsed = parseNumericInput(inputValue, scale);
117
124
  if (parsed === null) {
125
+ interactionChangedRef.current = false;
118
126
  setInputDraft(null);
119
127
  return;
120
128
  }
@@ -123,6 +131,7 @@ export function ColorGradingSliderControl({
123
131
 
124
132
  const nudge = useCallback(
125
133
  (direction: -1 | 1) => {
134
+ interactionChangedRef.current = true;
126
135
  commitDraft(draft + step * direction);
127
136
  },
128
137
  [commitDraft, draft, step],
@@ -166,6 +175,7 @@ export function ColorGradingSliderControl({
166
175
  aria-label={`Reset ${label}`}
167
176
  onClick={(event) => {
168
177
  event.stopPropagation();
178
+ track("button", `Reset ${label}`);
169
179
  onReset();
170
180
  }}
171
181
  className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
@@ -205,7 +215,10 @@ export function ColorGradingSliderControl({
205
215
  value={draft}
206
216
  disabled={disabled}
207
217
  aria-label={label}
208
- onChange={(event) => scheduleCommit(Number(event.currentTarget.value))}
218
+ onChange={(event) => {
219
+ interactionChangedRef.current = true;
220
+ scheduleCommit(Number(event.currentTarget.value));
221
+ }}
209
222
  onMouseUp={() => commitDraft(draft)}
210
223
  onTouchEnd={() => commitDraft(draft)}
211
224
  onBlur={() => commitDraft(draft)}
@@ -223,9 +236,10 @@ export function ColorGradingSliderControl({
223
236
  max={max / scale}
224
237
  step={step / scale}
225
238
  disabled={disabled}
226
- onChange={(event) =>
227
- setInputDraft({ value: event.currentTarget.value, source: valueRef.current })
228
- }
239
+ onChange={(event) => {
240
+ interactionChangedRef.current = true;
241
+ setInputDraft({ value: event.currentTarget.value, source: valueRef.current });
242
+ }}
229
243
  onBlur={commitInputDraft}
230
244
  onKeyDown={(event) => {
231
245
  if (event.key === "Enter") {
@@ -16,6 +16,7 @@ import {
16
16
  SliderControl,
17
17
  } from "./propertyPanelPrimitives";
18
18
  import { ColorField } from "./propertyPanelColor";
19
+ import { useTrackDesignInput } from "../../contexts/DesignPanelInputContext";
19
20
 
20
21
  /* ------------------------------------------------------------------ */
21
22
  /* Asset path helpers */
@@ -87,6 +88,7 @@ export function ImageFillField({
87
88
  onCommit: (nextValue: string) => void;
88
89
  onImportAssets?: (files: FileList) => Promise<string[]>;
89
90
  }) {
91
+ const track = useTrackDesignInput();
90
92
  const fileInputRef = useRef<HTMLInputElement | null>(null);
91
93
  const [uploading, setUploading] = useState(false);
92
94
  const imageAssets = useMemo(() => assets.filter((a) => IMAGE_EXT.test(a)), [assets]);
@@ -102,7 +104,10 @@ export function ImageFillField({
102
104
  try {
103
105
  const uploaded = await onImportAssets(files);
104
106
  const nextImage = uploaded.find((a) => IMAGE_EXT.test(a));
105
- if (nextImage) onCommit(`url("${toProjectRootAssetPath(nextImage)}")`);
107
+ if (nextImage) {
108
+ track("button", "Upload image");
109
+ onCommit(`url("${toProjectRootAssetPath(nextImage)}")`);
110
+ }
106
111
  } finally {
107
112
  setUploading(false);
108
113
  }
@@ -156,6 +161,7 @@ export function ImageFillField({
156
161
  disabled={disabled}
157
162
  onChange={(e) => {
158
163
  const next = e.target.value;
164
+ track("select", "Project asset");
159
165
  if (!next) {
160
166
  onCommit("none");
161
167
  return;
@@ -205,6 +211,7 @@ export function GradientField({
205
211
  disabled?: boolean;
206
212
  onCommit: (nextValue: string) => void;
207
213
  }) {
214
+ const track = useTrackDesignInput();
208
215
  const previewRef = useRef<HTMLDivElement | null>(null);
209
216
  const parsed = parseGradient(value) ?? buildDefaultGradientModel(fallbackColor);
210
217
 
@@ -226,11 +233,13 @@ export function GradientField({
226
233
  ? Math.min(100, (parsed.stops.at(-1)?.position ?? 90) + 10)
227
234
  : 100,
228
235
  );
236
+ track("button", "Add gradient stop");
229
237
  commit(nextGradient);
230
238
  };
231
239
 
232
240
  const removeStop = (index: number) => {
233
241
  if (parsed.stops.length <= 2) return;
242
+ track("button", `Remove gradient stop ${index + 1}`);
234
243
  commit({ ...parsed, stops: parsed.stops.filter((_, i) => i !== index) });
235
244
  };
236
245
 
@@ -263,6 +272,7 @@ export function GradientField({
263
272
  </div>
264
273
  <div className="flex min-w-0 flex-wrap items-center gap-2">
265
274
  <SegmentedControl
275
+ trackName="Gradient type"
266
276
  disabled={disabled}
267
277
  value={parsed.kind}
268
278
  onChange={(next) => patch({ kind: next as GradientModel["kind"] })}
@@ -277,7 +287,10 @@ export function GradientField({
277
287
  type="checkbox"
278
288
  checked={parsed.repeating}
279
289
  disabled={disabled}
280
- onChange={(e) => patch({ repeating: e.target.checked })}
290
+ onChange={(e) => {
291
+ track("toggle", "Repeat gradient");
292
+ patch({ repeating: e.target.checked });
293
+ }}
281
294
  className="h-4 w-4 rounded border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent"
282
295
  />
283
296
  Repeat
@@ -285,15 +298,16 @@ export function GradientField({
285
298
  <button
286
299
  type="button"
287
300
  disabled={disabled}
288
- onClick={() =>
301
+ onClick={() => {
302
+ track("button", "Reverse gradient");
289
303
  commit({
290
304
  ...parsed,
291
305
  stops: [...parsed.stops].reverse().map((stop) => ({
292
306
  ...stop,
293
307
  position: 100 - stop.position,
294
308
  })),
295
- })
296
- }
309
+ });
310
+ }}
297
311
  className="inline-flex h-7 items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white disabled:cursor-not-allowed disabled:text-neutral-600"
298
312
  >
299
313
  <RotateCcw size={12} />
@@ -306,6 +320,7 @@ export function GradientField({
306
320
  <div className="grid gap-1.5">
307
321
  <span className={LABEL}>{parsed.kind === "linear" ? "Angle" : "Start angle"}</span>
308
322
  <SliderControl
323
+ trackName={parsed.kind === "linear" ? "Angle" : "Start angle"}
309
324
  value={parsed.angle}
310
325
  min={0}
311
326
  max={360}
@@ -342,6 +357,7 @@ export function GradientField({
342
357
  <div className="grid min-w-0 gap-1.5">
343
358
  <span className={LABEL}>Center X</span>
344
359
  <SliderControl
360
+ trackName="Center X"
345
361
  value={parsed.centerX}
346
362
  min={0}
347
363
  max={100}
@@ -355,6 +371,7 @@ export function GradientField({
355
371
  <div className="grid min-w-0 gap-1.5">
356
372
  <span className={LABEL}>Center Y</span>
357
373
  <SliderControl
374
+ trackName="Center Y"
358
375
  value={parsed.centerY}
359
376
  min={0}
360
377
  max={100}