@hyperframes/studio 0.8.49 → 0.8.51

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 (281) hide show
  1. package/dist/assets/{hyperframes-player-CqON2Rzo.js → hyperframes-player-Cn_PpHqP.js} +3 -3
  2. package/dist/assets/index-C05SNrvY.js +360 -0
  3. package/dist/assets/{index-mIgSsKqs.js → index-CPS3Cvj8.js} +1 -1
  4. package/dist/assets/{index-BGovDksE.js → index-Clrb11lj.js} +1 -1
  5. package/dist/assets/index-DUR9qzQM.css +1 -0
  6. package/dist/index.d.ts +29 -16
  7. package/dist/index.html +2 -2
  8. package/dist/index.js +7283 -8606
  9. package/dist/index.js.map +1 -1
  10. package/dist/styles/tailwind-preset.js.map +1 -1
  11. package/package.json +11 -12
  12. package/src/App.tsx +132 -143
  13. package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
  14. package/src/captions/components/CaptionOverlayUtils.ts +2 -3
  15. package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
  16. package/src/captions/components/CaptionTimeline.tsx +1 -1
  17. package/src/captions/components/shared.tsx +2 -2
  18. package/src/components/AskAgentModal.tsx +5 -5
  19. package/src/components/EditorShell.selectionSync.test.tsx +4 -0
  20. package/src/components/EditorShell.tsx +28 -7
  21. package/src/components/ExternalFileConflictBanner.tsx +16 -16
  22. package/src/components/LintModal.tsx +6 -16
  23. package/src/components/MediaPreview.tsx +2 -2
  24. package/src/components/SaveQueuePausedBanner.tsx +2 -2
  25. package/src/components/StudioHeader.tsx +2 -62
  26. package/src/components/StudioLeftSidebar.tsx +7 -4
  27. package/src/components/StudioOverlays.tsx +2 -6
  28. package/src/components/StudioRightPanel.tsx +4 -4
  29. package/src/components/StudioToast.tsx +2 -2
  30. package/src/components/TimelineToolbar.tsx +7 -7
  31. package/src/components/dock/Dock.test.tsx +192 -0
  32. package/src/components/dock/Dock.tsx +264 -0
  33. package/src/components/dock/dock.css +23 -0
  34. package/src/components/dock/dockLayout.ts +58 -0
  35. package/src/components/dock/dockLayoutSchema.ts +66 -0
  36. package/src/components/dock/dockLayoutStore.ts +91 -0
  37. package/src/components/dock/panelRegistry.ts +67 -0
  38. package/src/components/editor/AnimationCard.tsx +4 -4
  39. package/src/components/editor/AnimationCardParts.tsx +6 -6
  40. package/src/components/editor/BlockParamsPanel.tsx +4 -4
  41. package/src/components/editor/BorderRadiusEditor.tsx +1 -1
  42. package/src/components/editor/CanvasContextMenu.tsx +1 -1
  43. package/src/components/editor/ComputedTweenNotice.tsx +1 -1
  44. package/src/components/editor/DomEditOverlay.tsx +1 -1
  45. package/src/components/editor/EaseCurveSection.tsx +1 -1
  46. package/src/components/editor/EaseParamFields.tsx +5 -5
  47. package/src/components/editor/FileTree.tsx +4 -4
  48. package/src/components/editor/FileTreeIcons.tsx +1 -1
  49. package/src/components/editor/FileTreeNodes.tsx +13 -13
  50. package/src/components/editor/InlineTextToolbar.tsx +1 -1
  51. package/src/components/editor/InspectorHeaderActions.tsx +3 -3
  52. package/src/components/editor/KeyframeDiamond.tsx +1 -1
  53. package/src/components/editor/KeyframeEaseList.tsx +1 -1
  54. package/src/components/editor/LayersPanel.tsx +3 -3
  55. package/src/components/editor/MarqueeOverlay.tsx +1 -1
  56. package/src/components/editor/OffCanvasIndicators.tsx +1 -1
  57. package/src/components/editor/PreviewGuides.test.tsx +88 -0
  58. package/src/components/editor/PreviewGuides.tsx +125 -0
  59. package/src/components/editor/PromotableControl.tsx +2 -2
  60. package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
  61. package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
  62. package/src/components/editor/SnapToolbar.test.tsx +21 -0
  63. package/src/components/editor/SnapToolbar.tsx +27 -3
  64. package/src/components/editor/TimelineFxPopover.tsx +1 -1
  65. package/src/components/editor/TopologyLens.tsx +4 -4
  66. package/src/components/editor/Transform3DCube.tsx +2 -2
  67. package/src/components/editor/audioFxRevealTarget.ts +1 -0
  68. package/src/components/editor/domEditing.test.ts +1 -1
  69. package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
  70. package/src/components/editor/domEditingAgentPrompt.ts +9 -3
  71. package/src/components/editor/previewGuidesStore.ts +26 -0
  72. package/src/components/editor/propertyPanelColor.tsx +11 -11
  73. package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
  74. package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
  75. package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
  76. package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
  77. package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
  78. package/src/components/editor/propertyPanelCommitField.tsx +1 -1
  79. package/src/components/editor/propertyPanelFill.tsx +4 -4
  80. package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
  81. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
  82. package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
  83. package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
  84. package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
  85. package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
  86. package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
  87. package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
  88. package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
  89. package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
  90. package/src/components/editor/propertyPanelFont.tsx +5 -5
  91. package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
  92. package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
  93. package/src/components/editor/propertyPanelFxControls.tsx +6 -6
  94. package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
  95. package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
  96. package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
  97. package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
  98. package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
  99. package/src/components/editor/propertyPanelSections.tsx +8 -8
  100. package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
  101. package/src/components/feedback/feedbackTrigger.ts +0 -1
  102. package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
  103. package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
  104. package/src/components/nle/NLEPreview.test.ts +13 -0
  105. package/src/components/nle/NLEPreview.tsx +12 -7
  106. package/src/components/nle/PreviewOverlays.tsx +6 -6
  107. package/src/components/nle/PreviewPane.test.tsx +52 -0
  108. package/src/components/nle/PreviewPane.tsx +4 -2
  109. package/src/components/nle/TimelinePane.tsx +17 -3
  110. package/src/components/nle/TimelineResizeDivider.tsx +2 -2
  111. package/src/components/nle/useCanvasZOrderTimelineMirror.test.tsx +1 -1
  112. package/src/components/nle/useCanvasZOrderTimelineMirror.ts +3 -3
  113. package/src/components/panels/SlideshowPanel.tsx +2 -2
  114. package/src/components/panels/SlideshowSubPanels.tsx +20 -17
  115. package/src/components/panels/VariablesBindElement.tsx +2 -2
  116. package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
  117. package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
  118. package/src/components/panels/VariablesPanel.tsx +3 -3
  119. package/src/components/panels/VariablesValueControls.tsx +2 -2
  120. package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
  121. package/src/components/renders/RenderQueue.test.tsx +7 -0
  122. package/src/components/renders/RenderQueue.tsx +6 -6
  123. package/src/components/renders/RenderQueueItem.tsx +6 -6
  124. package/src/components/sidebar/AssetCard.tsx +8 -8
  125. package/src/components/sidebar/AssetContextMenu.tsx +8 -8
  126. package/src/components/sidebar/AssetsTab.tsx +2 -2
  127. package/src/components/sidebar/AudioRow.tsx +6 -6
  128. package/src/components/sidebar/BlocksTab.tsx +6 -6
  129. package/src/components/sidebar/CompositionsTab.tsx +5 -5
  130. package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
  131. package/src/components/sidebar/LeftSidebar.lintBadge.test.tsx +58 -0
  132. package/src/components/sidebar/LeftSidebar.tsx +19 -5
  133. package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
  134. package/src/components/ui/Button.test.tsx +60 -0
  135. package/src/components/ui/Button.tsx +4 -4
  136. package/src/components/ui/SearchInput.tsx +2 -2
  137. package/src/components/ui/Tooltip.tsx +1 -1
  138. package/src/hooks/appHotkeysDispatch.ts +11 -0
  139. package/src/hooks/timelineAudioGroupCreate.ts +2 -2
  140. package/src/hooks/timelineTrackVisibility.ts +8 -11
  141. package/src/hooks/useAppHotkeys.textEditing.test.tsx +1 -0
  142. package/src/hooks/useAppHotkeys.ts +4 -1
  143. package/src/hooks/useAskAgentModal.ts +2 -0
  144. package/src/hooks/useClipboard.test.ts +234 -0
  145. package/src/hooks/useClipboard.ts +264 -70
  146. package/src/hooks/useGroupCommits.test.ts +0 -1
  147. package/src/hooks/useLintModal.test.tsx +65 -0
  148. package/src/hooks/useLintModal.ts +3 -0
  149. package/src/hooks/useRazorSplit.history.test.tsx +0 -1
  150. package/src/hooks/useRazorSplit.test.ts +0 -2
  151. package/src/hooks/useRazorSplit.test.tsx +0 -1
  152. package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
  153. package/src/hooks/useSdkSession.ts +43 -10
  154. package/src/hooks/useStudioContextValue.ts +5 -19
  155. package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
  156. package/src/hooks/useTimelineAssetDropOps.ts +52 -11
  157. package/src/hooks/useTimelineEditing.test.tsx +0 -1
  158. package/src/main.tsx +9 -3
  159. package/src/player/components/AudioWaveform.tsx +1 -1
  160. package/src/player/components/AutomationSelectionMenu.tsx +2 -2
  161. package/src/player/components/ClipContextMenu.tsx +90 -10
  162. package/src/player/components/CompositionThumbnail.test.ts +0 -3
  163. package/src/player/components/CompositionThumbnail.tsx +3 -4
  164. package/src/player/components/EditModal.tsx +1 -1
  165. package/src/player/components/ImageThumbnail.tsx +1 -1
  166. package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
  167. package/src/player/components/LayerDisclosureRow.tsx +2 -2
  168. package/src/player/components/PlayerControls.tsx +1 -1
  169. package/src/player/components/ShortcutsPanel.tsx +9 -9
  170. package/src/player/components/SpeedMenu.tsx +3 -5
  171. package/src/player/components/Timeline.tsx +16 -15
  172. package/src/player/components/TimelineCanvas.tsx +45 -8
  173. package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
  174. package/src/player/components/TimelineEmptyState.tsx +5 -5
  175. package/src/player/components/TimelineFxButton.tsx +6 -6
  176. package/src/player/components/TimelineGestureOverlay.test.tsx +35 -1
  177. package/src/player/components/TimelineGestureOverlay.tsx +1 -1
  178. package/src/player/components/TimelineGroupHeader.tsx +5 -5
  179. package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
  180. package/src/player/components/TimelineGroupRow.tsx +1 -1
  181. package/src/player/components/TimelineLanes.test.tsx +54 -1
  182. package/src/player/components/TimelineLanes.tsx +2 -5
  183. package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
  184. package/src/player/components/TimelineOverlays.tsx +12 -0
  185. package/src/player/components/TimelineRuler.tsx +1 -1
  186. package/src/player/components/TimelineShortcutHint.tsx +1 -1
  187. package/src/player/components/TimelineTrackHeader.tsx +1 -1
  188. package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
  189. package/src/player/components/TimelineTypes.ts +6 -1
  190. package/src/player/components/TrackGapContextMenu.tsx +1 -1
  191. package/src/player/components/VideoThumbnail.tsx +4 -2
  192. package/src/player/components/VolumeControl.tsx +2 -2
  193. package/src/player/components/timelineAuthoredMoveTarget.ts +1 -2
  194. package/src/player/components/timelineCallbacks.ts +8 -8
  195. package/src/player/components/timelineClipChildren.test.ts +2 -2
  196. package/src/player/components/timelineClipChildren.tsx +2 -1
  197. package/src/player/components/timelineClipDragCommit.ts +2 -2
  198. package/src/player/components/timelineClipDragPreview.ts +51 -27
  199. package/src/player/components/timelineClipDragTypes.ts +3 -0
  200. package/src/player/components/timelineDragDrop.test.tsx +92 -1
  201. package/src/player/components/timelineDragDrop.ts +82 -33
  202. package/src/player/components/timelineEditCapabilities.ts +1 -2
  203. package/src/player/components/timelineEditing.test.ts +0 -16
  204. package/src/player/components/timelineGroupEditing.test.ts +1 -3
  205. package/src/player/components/timelineGroupEditing.ts +0 -1
  206. package/src/player/components/timelineLaneProps.ts +3 -0
  207. package/src/player/components/timelineSnapping.test.ts +27 -0
  208. package/src/player/components/timelineSnapping.ts +14 -1
  209. package/src/player/components/timelineTrackDisplay.ts +1 -1
  210. package/src/player/components/timelineTrackPersistPipeline.test.ts +0 -53
  211. package/src/player/components/trackHeaderLabelRows.tsx +4 -4
  212. package/src/player/components/useTimelineClipContextMenu.ts +32 -0
  213. package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
  214. package/src/player/components/useTimelineClipDrag.ts +30 -3
  215. package/src/player/components/useTimelineTrackDerivations.ts +1 -1
  216. package/src/player/hooks/timelineDomBinding.test.ts +0 -3
  217. package/src/player/hooks/timelineSyncHydration.test.ts +18 -0
  218. package/src/player/hooks/timelineSyncHydration.ts +37 -18
  219. package/src/player/hooks/useTimelinePlayer.test.ts +3 -20
  220. package/src/player/hooks/useTimelineRowElements.test.ts +28 -0
  221. package/src/player/hooks/useTimelineRowElements.ts +17 -0
  222. package/src/player/hooks/useTimelineSyncCallbacks.emptyManifest.test.tsx +41 -0
  223. package/src/player/hooks/useTimelineSyncCallbacks.ts +9 -10
  224. package/src/player/lib/describeClips.test.ts +95 -0
  225. package/src/player/lib/describeClips.ts +52 -0
  226. package/src/player/lib/timelineDOM.test.ts +0 -80
  227. package/src/player/lib/timelineDOM.ts +1 -61
  228. package/src/player/lib/timelineElementHelpers.ts +2 -53
  229. package/src/player/store/playerStore.ts +5 -0
  230. package/src/player/store/timelineElement.ts +1 -3
  231. package/src/shims/canvasBrowserStub.js +50 -0
  232. package/src/styles/iconTokens.ts +20 -0
  233. package/src/styles/palette-reset.css +6 -0
  234. package/src/styles/studio.css +24 -21
  235. package/src/styles/tailwind-preset.shared.js +3 -0
  236. package/src/styles/theme.css +213 -0
  237. package/src/styles/theme.test.ts +196 -0
  238. package/src/utils/clipboardPayload.test.ts +42 -2
  239. package/src/utils/clipboardPayload.ts +57 -32
  240. package/src/utils/previewSafeMargins.test.ts +34 -0
  241. package/src/utils/previewSafeMargins.ts +10 -0
  242. package/src/utils/razorSplitTransaction.test.ts +0 -1
  243. package/src/utils/sdkCanvasComposition.test.ts +77 -0
  244. package/src/utils/studioUiPreferences.test.ts +43 -0
  245. package/src/utils/studioUiPreferences.ts +34 -6
  246. package/src/utils/timelineDropTrackInsert.test.ts +134 -0
  247. package/src/utils/timelineDropTrackInsert.ts +83 -0
  248. package/src/utils/timelineElementSplit.test.ts +1 -2
  249. package/src/utils/timelineElementSplit.ts +0 -1
  250. package/src/webmcp/tools/lookTools.test.ts +40 -0
  251. package/src/webmcp/tools/lookTools.ts +32 -1
  252. package/dist/assets/index-B9LfWNyM.css +0 -1
  253. package/dist/assets/index-Bgmx2h7W.js +0 -441
  254. package/src/components/StudioGlobalDragOverlay.tsx +0 -24
  255. package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
  256. package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
  257. package/src/components/storyboard/FramePoster.test.tsx +0 -61
  258. package/src/components/storyboard/FramePoster.tsx +0 -86
  259. package/src/components/storyboard/StoryboardDirection.tsx +0 -45
  260. package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
  261. package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
  262. package/src/components/storyboard/StoryboardGrid.tsx +0 -55
  263. package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
  264. package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
  265. package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
  266. package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
  267. package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
  268. package/src/components/storyboard/StoryboardView.tsx +0 -169
  269. package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
  270. package/src/components/storyboard/frameComments.test.ts +0 -95
  271. package/src/components/storyboard/frameComments.ts +0 -115
  272. package/src/components/storyboard/frameStatus.ts +0 -36
  273. package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
  274. package/src/components/storyboard/storyboardReviewStage.ts +0 -45
  275. package/src/components/storyboard/useFrameComments.ts +0 -98
  276. package/src/contexts/ViewModeContext.tsx +0 -142
  277. package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
  278. package/src/hooks/useProjectSignaturePoll.ts +0 -68
  279. package/src/hooks/useStoryboard.ts +0 -99
  280. package/src/player/hooks/useExpandedTimelineElements.test.ts +0 -774
  281. package/src/player/hooks/useExpandedTimelineElements.ts +0 -460
@@ -79,7 +79,7 @@ function ColorSlider({
79
79
  aria-valuemax={max}
80
80
  aria-valuenow={value}
81
81
  aria-disabled={disabled}
82
- className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-none focus:border-panel-accent focus:ring-2 focus:ring-panel-accent/40 ${
82
+ className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-hidden focus:border-panel-accent focus:ring-2 focus:ring-panel-accent/40 ${
83
83
  disabled ? "cursor-not-allowed opacity-50" : "cursor-ew-resize"
84
84
  }`}
85
85
  style={{ background }}
@@ -328,7 +328,7 @@ export function ColorField({
328
328
  role="dialog"
329
329
  aria-label={`${label} color picker`}
330
330
  tabIndex={-1}
331
- className="fixed z-[9999] w-[292px] overflow-hidden rounded-2xl border border-neutral-700 bg-neutral-950 shadow-2xl shadow-black/50 outline-none"
331
+ className="fixed z-9999 w-[292px] overflow-hidden rounded-2xl border border-neutral-700 bg-neutral-950 shadow-2xl shadow-black/50 outline-hidden"
332
332
  style={{
333
333
  left: panelPosition?.left ?? -9999,
334
334
  top: panelPosition?.top ?? -9999,
@@ -367,8 +367,8 @@ export function ColorField({
367
367
  onPointerUp={settleColorGesture}
368
368
  onPointerCancel={cancelColorGesture}
369
369
  >
370
- <div className="absolute inset-0 bg-gradient-to-r from-white to-transparent" />
371
- <div className="absolute inset-0 bg-gradient-to-t from-black to-transparent" />
370
+ <div className="absolute inset-0 bg-linear-to-r from-white to-transparent" />
371
+ <div className="absolute inset-0 bg-linear-to-t from-black to-transparent" />
372
372
  <div
373
373
  className="pointer-events-none absolute top-0 h-full w-px -translate-x-1/2 bg-white/70 shadow-[0_0_0_1px_rgba(0,0,0,0.45)] mix-blend-difference"
374
374
  style={{ left: `${hsv.saturation * 100}%` }}
@@ -389,7 +389,7 @@ export function ColorField({
389
389
 
390
390
  <div className="flex min-w-0 items-center gap-3">
391
391
  <div
392
- className="h-9 w-9 flex-shrink-0 rounded-xl border border-neutral-600 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]"
392
+ className="h-9 w-9 shrink-0 rounded-xl border border-neutral-600 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]"
393
393
  style={{ backgroundColor: currentColor }}
394
394
  />
395
395
  <div className="min-w-0 flex-1">
@@ -442,7 +442,7 @@ export function ColorField({
442
442
  value={hexDraft}
443
443
  onChange={(event) => handleHexChange(event.target.value)}
444
444
  onBlur={settleColorGesture}
445
- className={`${FIELD} h-10 w-full text-[11px] font-medium outline-none`}
445
+ className={`${FIELD} h-10 w-full text-[11px] font-medium outline-hidden`}
446
446
  spellCheck={false}
447
447
  />
448
448
  </label>
@@ -475,7 +475,7 @@ export function ColorField({
475
475
  className="flex items-center gap-2 disabled:cursor-not-allowed"
476
476
  >
477
477
  <span
478
- className="h-4 w-4 flex-shrink-0 rounded-[4px]"
478
+ className="h-4 w-4 shrink-0 rounded-[4px]"
479
479
  style={{ backgroundColor: open ? currentColor : value || "transparent" }}
480
480
  />
481
481
  <span className="font-mono text-[11px] text-panel-text-0">
@@ -484,7 +484,7 @@ export function ColorField({
484
484
  {mixed && (
485
485
  <span
486
486
  data-color-mixed-indicator="true"
487
- className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
487
+ className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
488
488
  >
489
489
  Mixed
490
490
  </span>
@@ -504,7 +504,7 @@ export function ColorField({
504
504
  type="button"
505
505
  disabled={disabled}
506
506
  onClick={onReset}
507
- className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
507
+ className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
508
508
  >
509
509
  Reset
510
510
  </button>
@@ -519,7 +519,7 @@ export function ColorField({
519
519
  className={`${FIELD} flex items-center gap-3 text-left hover:border-neutral-700 disabled:cursor-not-allowed ${open ? "border-neutral-600" : ""}`}
520
520
  >
521
521
  <div
522
- className="relative h-7 w-7 flex-shrink-0 overflow-hidden rounded-lg border border-neutral-700 bg-neutral-950 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
522
+ className="relative h-7 w-7 shrink-0 overflow-hidden rounded-lg border border-neutral-700 bg-neutral-950 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
523
523
  style={{ backgroundColor: value || "transparent" }}
524
524
  />
525
525
  <span className="min-w-0 flex-1 truncate text-[11px] font-medium text-neutral-100">
@@ -528,7 +528,7 @@ export function ColorField({
528
528
  {mixed && (
529
529
  <span
530
530
  data-color-mixed-indicator="true"
531
- className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
531
+ className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
532
532
  >
533
533
  Mixed
534
534
  </span>
@@ -477,7 +477,7 @@ export function CurveGraph({
477
477
  onSettle();
478
478
  }
479
479
  }}
480
- className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded border border-panel-border-input bg-black/20 outline-none focus:ring-1 focus:ring-panel-accent"
480
+ className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded-sm border border-panel-border-input bg-black/20 outline-hidden focus:ring-1 focus:ring-panel-accent"
481
481
  >
482
482
  <defs>
483
483
  <linearGradient id={`hf-hue-axis-${tab.key}`}>
@@ -328,7 +328,7 @@ export function ColorGradingControls({
328
328
  <select
329
329
  value={String(grading.preset ?? "neutral")}
330
330
  onChange={(event) => applyPreset(event.target.value)}
331
- className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none"
331
+ className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden"
332
332
  >
333
333
  {HF_COLOR_GRADING_PRESETS.map((preset) => (
334
334
  <option key={preset.id} value={preset.id}>
@@ -358,14 +358,12 @@ export function ColorGradingControls({
358
358
  aria-expanded={lutOpen}
359
359
  >
360
360
  {lutOpen ? (
361
- <ChevronDown size={11} className="flex-shrink-0 text-panel-text-5" />
361
+ <ChevronDown size={11} className="shrink-0 text-panel-text-5" />
362
362
  ) : (
363
- <ChevronRight size={11} className="flex-shrink-0 text-panel-text-5" />
363
+ <ChevronRight size={11} className="shrink-0 text-panel-text-5" />
364
364
  )}
365
365
  <span className="min-w-0 flex-1 truncate">Custom LUT</span>
366
- {grading.lut && (
367
- <span className="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-studio-accent" />
368
- )}
366
+ {grading.lut && <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-studio-accent" />}
369
367
  </button>
370
368
  {lutOpen && (
371
369
  <div className="grid gap-1.5 border-t border-panel-border/60 p-1.5">
@@ -380,7 +378,7 @@ export function ColorGradingControls({
380
378
  nextSrc && grading.lut?.src === nextSrc ? grading.lut.intensity : 1,
381
379
  );
382
380
  }}
383
- className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none"
381
+ className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden"
384
382
  title="Uploaded .cube LUT"
385
383
  >
386
384
  <option value="">None</option>
@@ -441,7 +439,7 @@ export function ColorGradingControls({
441
439
  <div className="grid gap-2">
442
440
  {selectedProjectLut && (
443
441
  <div className="flex min-w-0 items-start gap-2 text-[10px] leading-4 text-panel-text-3">
444
- <span className="mt-[5px] h-1.5 w-1.5 flex-shrink-0 rounded-full bg-studio-accent" />
442
+ <span className="mt-[5px] h-1.5 w-1.5 shrink-0 rounded-full bg-studio-accent" />
445
443
  <span className="min-w-0 flex-1 truncate" title={selectedProjectLut}>
446
444
  <span className="font-medium text-panel-text-2">Uploaded LUT</span>
447
445
  {` · ${selectedProjectLut}`}
@@ -527,7 +525,7 @@ export function ColorGradingControls({
527
525
  aria-label="Close settings"
528
526
  title="Close settings"
529
527
  onClick={() => setDetailSettings(null)}
530
- 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"
528
+ className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
531
529
  >
532
530
  <X size={11} />
533
531
  </button>
@@ -22,10 +22,10 @@ function StatusPill({ status }: { status: RuntimeColorGradingStatus }) {
22
22
  : "bg-panel-text-5";
23
23
  return (
24
24
  <div
25
- className="flex min-w-0 items-center gap-1.5 rounded bg-panel-input px-2 py-1 text-[10px] font-medium text-panel-text-3"
25
+ className="flex min-w-0 items-center gap-1.5 rounded-sm bg-panel-input px-2 py-1 text-[10px] font-medium text-panel-text-3"
26
26
  title={status.message}
27
27
  >
28
- <span className={`h-1.5 w-1.5 flex-shrink-0 rounded-full ${dotClass}`} />
28
+ <span className={`h-1.5 w-1.5 shrink-0 rounded-full ${dotClass}`} />
29
29
  <span className="truncate">{status.message}</span>
30
30
  </div>
31
31
  );
@@ -47,7 +47,7 @@ function HdrMediaWarning({ metadata }: { metadata: MediaMetadata | null }) {
47
47
  <div className="mb-3 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] leading-4 text-amber-100">
48
48
  <div className="mb-1 flex min-w-0 items-center justify-between gap-2">
49
49
  <span className="font-semibold">{metadata.color.label} source</span>
50
- <span className="rounded bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
50
+ <span className="rounded-sm bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
51
51
  SDR preview
52
52
  </span>
53
53
  </div>
@@ -118,7 +118,7 @@ function HoldBeforeButton({
118
118
  event.preventDefault();
119
119
  onHoldChange(false);
120
120
  }}
121
- className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded transition-colors ${
121
+ className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors ${
122
122
  active
123
123
  ? "bg-studio-accent text-black"
124
124
  : "text-panel-text-4 hover:bg-panel-hover hover:text-panel-text-1"
@@ -194,7 +194,7 @@ export function ColorGradingSection({
194
194
  track("button", "Reset color grading");
195
195
  resetGrading();
196
196
  }}
197
- className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
197
+ className="flex h-6 w-6 shrink-0 items-center justify-center rounded-sm text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
198
198
  title="Reset color grading"
199
199
  >
200
200
  <RotateCcw size={12} />
@@ -218,7 +218,7 @@ export function ColorGradingSection({
218
218
  setApplyScope(event.currentTarget.value as typeof applyScope);
219
219
  }}
220
220
  disabled={applyBusy}
221
- className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none disabled:cursor-not-allowed disabled:opacity-50"
221
+ className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
222
222
  title="Choose where to copy these color grading settings"
223
223
  >
224
224
  <option value="source-file">Current file media</option>
@@ -157,7 +157,7 @@ export function ColorGradingSliderControl({
157
157
  event.stopPropagation();
158
158
  settings.onClick();
159
159
  }}
160
- className={`relative flex h-5 w-5 flex-shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${
160
+ className={`relative flex h-5 w-5 shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${
161
161
  settings.active ? "text-studio-accent" : "text-panel-text-5"
162
162
  }`}
163
163
  title={settings.label}
@@ -178,7 +178,7 @@ export function ColorGradingSliderControl({
178
178
  track("button", `Reset ${label}`);
179
179
  onReset();
180
180
  }}
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"
181
+ className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
182
182
  title={`Reset ${label}`}
183
183
  >
184
184
  <RotateCcw size={11} />
@@ -228,7 +228,7 @@ export function ColorGradingSliderControl({
228
228
  </div>
229
229
 
230
230
  <div className="flex min-w-0 items-center justify-end gap-1">
231
- <div className="flex flex-shrink-0 items-center rounded-md bg-panel-input px-1.5 py-px">
231
+ <div className="flex shrink-0 items-center rounded-md bg-panel-input px-1.5 py-px">
232
232
  <input
233
233
  type="number"
234
234
  value={inputValue}
@@ -256,12 +256,12 @@ export function ColorGradingSliderControl({
256
256
  nudge(-1);
257
257
  }
258
258
  }}
259
- className="hf-color-grading-number h-4 w-[36px] bg-transparent text-right text-[10px] font-medium tabular-nums text-panel-text-1 outline-none disabled:cursor-not-allowed"
259
+ className="hf-color-grading-number h-4 w-[36px] bg-transparent text-right text-[10px] font-medium tabular-nums text-panel-text-1 outline-hidden disabled:cursor-not-allowed"
260
260
  title={displayValue}
261
261
  />
262
262
  {suffix && <span className="ml-0.5 text-[10px] text-panel-text-5">{suffix}</span>}
263
263
  </div>
264
- <div className="flex flex-shrink-0 overflow-hidden rounded-md bg-panel-input">
264
+ <div className="flex shrink-0 overflow-hidden rounded-md bg-panel-input">
265
265
  <button
266
266
  type="button"
267
267
  disabled={disabled}
@@ -204,7 +204,7 @@ function TonalWheel({
204
204
  }
205
205
  }}
206
206
  onBlur={onSettle}
207
- className="relative mx-auto aspect-square w-full max-w-[112px] touch-none rounded-full border border-panel-border-input outline-none focus:ring-1 focus:ring-panel-accent disabled:opacity-40"
207
+ className="relative mx-auto aspect-square w-full max-w-[112px] touch-none rounded-full border border-panel-border-input outline-hidden focus:ring-1 focus:ring-panel-accent disabled:opacity-40"
208
208
  style={{
209
209
  background:
210
210
  "radial-gradient(circle, rgb(128 128 128) 0%, transparent 72%), conic-gradient(from 90deg, #f33, #f3f, #33f, #3ff, #3f3, #ff3, #f33)",
@@ -213,7 +213,7 @@ export function CommitField({
213
213
  }}
214
214
  onKeyDown={handleKeyDown}
215
215
  title={parseNumericToken(value) ? "Scroll or use Arrow keys to adjust" : undefined}
216
- className={`min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600 ${
216
+ className={`min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600 ${
217
217
  align === "right" ? "text-right" : "text-left"
218
218
  }`}
219
219
  />
@@ -133,7 +133,7 @@ export function ImageFillField({
133
133
  : "cursor-pointer hover:border-neutral-600 hover:text-white"
134
134
  }`}
135
135
  >
136
- <Plus size={12} className="flex-shrink-0" />
136
+ <Plus size={12} className="shrink-0" />
137
137
  <span className="truncate">{uploading ? "Uploading…" : "Upload image"}</span>
138
138
  </button>
139
139
  <input
@@ -178,7 +178,7 @@ export function ImageFillField({
178
178
  }
179
179
  onCommit(`url("${toProjectRootAssetPath(next)}")`);
180
180
  }}
181
- className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
181
+ className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600"
182
182
  >
183
183
  <option value="">None</option>
184
184
  {imageAssets.map((asset) => (
@@ -288,7 +288,7 @@ export function GradientField({
288
288
  position: Math.max(0, Math.min(100, Math.round(stop.position + delta))),
289
289
  });
290
290
  }}
291
- className="absolute top-1/2 h-4 w-4 -translate-y-1/2 cursor-ew-resize rounded-full border-2 border-white/90 shadow-[0_0_0_1px_rgba(0,0,0,0.35)] outline-none focus-visible:ring-2 focus-visible:ring-studio-accent"
291
+ className="absolute top-1/2 h-4 w-4 -translate-y-1/2 cursor-ew-resize rounded-full border-2 border-white/90 shadow-[0_0_0_1px_rgba(0,0,0,0.35)] outline-hidden focus-visible:ring-2 focus-visible:ring-studio-accent"
292
292
  style={{
293
293
  left: `calc(${stop.position}% - 8px)`,
294
294
  backgroundColor: stop.color,
@@ -339,7 +339,7 @@ export function GradientField({
339
339
  track("toggle", "Repeat gradient");
340
340
  patch({ repeating: e.target.checked });
341
341
  }}
342
- className="h-4 w-4 rounded border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent"
342
+ className="h-4 w-4 rounded-sm border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent"
343
343
  />
344
344
  Repeat
345
345
  </label>
@@ -74,7 +74,7 @@ export function FlatColorGradingAccessory({
74
74
  commitCompare(false);
75
75
  }}
76
76
  title="Hold to show original"
77
- className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
77
+ className="shrink-0 text-panel-text-3 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
78
78
  >
79
79
  <Compare size={12} />
80
80
  </button>
@@ -82,7 +82,7 @@ export function FlatColorGradingAccessory({
82
82
  <span
83
83
  data-flat-grade-status-dot="true"
84
84
  title={runtimeStatus.message}
85
- className={`h-[5px] w-[5px] flex-shrink-0 rounded-full ${STATUS_DOT_CLASS[runtimeStatus.state]}`}
85
+ className={`h-[5px] w-[5px] shrink-0 rounded-full ${STATUS_DOT_CLASS[runtimeStatus.state]}`}
86
86
  />
87
87
  <span
88
88
  data-flat-grade-status-message="true"
@@ -100,7 +100,7 @@ export function FlatColorGradingAccessory({
100
100
  track("button", "Reset color grading");
101
101
  resetGrading();
102
102
  }}
103
- className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1"
103
+ className="shrink-0 text-panel-text-3 hover:text-panel-text-1"
104
104
  >
105
105
  <RotateCcw size={12} />
106
106
  </button>
@@ -74,7 +74,7 @@ function HdrBanner({ metadata }: { metadata: MediaMetadata | null }) {
74
74
  >
75
75
  <div className="mb-0.5 flex items-center justify-between gap-2">
76
76
  <span className="font-semibold">{metadata.color.label} source</span>
77
- <span className="rounded bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
77
+ <span className="rounded-sm bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
78
78
  SDR preview
79
79
  </span>
80
80
  </div>
@@ -389,7 +389,7 @@ export function FlatColorGradingSection({
389
389
  height="10"
390
390
  viewBox="0 0 10 10"
391
391
  fill="currentColor"
392
- className={`flex-shrink-0 text-panel-text-5 transition-transform ${lutOpen ? "rotate-90" : ""}`}
392
+ className={`shrink-0 text-panel-text-5 transition-transform ${lutOpen ? "rotate-90" : ""}`}
393
393
  >
394
394
  <path d="M2 3l3 4 3-4z" />
395
395
  </svg>
@@ -409,7 +409,7 @@ export function FlatColorGradingSection({
409
409
  track("select", "Custom LUT");
410
410
  actions.applyLut(src || null, src && lut?.src === src ? lut.intensity : 1);
411
411
  }}
412
- className="border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-none hover:border-panel-border-input"
412
+ className="border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-hidden hover:border-panel-border-input"
413
413
  >
414
414
  <option value="">None</option>
415
415
  {lutAssets.map((asset) => (
@@ -423,7 +423,7 @@ export function FlatColorGradingSection({
423
423
  disabled={!onImportAssets}
424
424
  onClick={() => lutInputRef.current?.click()}
425
425
  title="Import .cube LUT"
426
- className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
426
+ className="shrink-0 text-panel-text-4 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
427
427
  >
428
428
  <Plus size={12} />
429
429
  </button>
@@ -467,7 +467,7 @@ export function FlatColorGradingSection({
467
467
  data-flat-grade-settings="vignette"
468
468
  title="Vignette settings"
469
469
  onClick={() => setDetailSettingsOpen((c) => (c === "vignette" ? null : "vignette"))}
470
- className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1"
470
+ className="shrink-0 text-panel-text-4 hover:text-panel-text-1"
471
471
  >
472
472
  <Settings size={12} />
473
473
  </button>
@@ -479,7 +479,7 @@ export function FlatColorGradingSection({
479
479
  data-flat-grade-settings="grain"
480
480
  title="Grain settings"
481
481
  onClick={() => setDetailSettingsOpen((c) => (c === "grain" ? null : "grain"))}
482
- className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1"
482
+ className="shrink-0 text-panel-text-4 hover:text-panel-text-1"
483
483
  >
484
484
  <Settings size={12} />
485
485
  </button>
@@ -505,7 +505,7 @@ export function FlatColorGradingSection({
505
505
  onSetApplyScope(e.target.value as "source-file" | "project");
506
506
  }}
507
507
  disabled={applyBusy}
508
- className="border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-none hover:border-panel-border-input disabled:opacity-50"
508
+ className="border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-hidden hover:border-panel-border-input disabled:opacity-50"
509
509
  >
510
510
  <option value="source-file">Current file media</option>
511
511
  <option value="project">All project media</option>
@@ -48,7 +48,7 @@ export function FlatEffectsAccessory({
48
48
  track("button", "Reset effects");
49
49
  onCommitColorGrading({ ...grading, effects: { ...DEFAULT_EFFECTS }, palette: null });
50
50
  }}
51
- className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1"
51
+ className="shrink-0 text-panel-text-3 hover:text-panel-text-1"
52
52
  >
53
53
  <RotateCcw size={12} />
54
54
  </button>
@@ -215,7 +215,7 @@ export function FlatEffectsSection({
215
215
  nextPalette[index] = event.target.value;
216
216
  onCommitColorGrading({ ...grading, palette: nextPalette });
217
217
  }}
218
- className="h-6 w-6 cursor-pointer rounded-sm border border-panel-border-input bg-transparent p-0"
218
+ className="h-6 w-6 cursor-pointer rounded-xs border border-panel-border-input bg-transparent p-0"
219
219
  />
220
220
  {palette.length > 2 && (
221
221
  <button
@@ -227,7 +227,7 @@ export function FlatEffectsSection({
227
227
  palette: palette.filter((_, colorIndex) => colorIndex !== index),
228
228
  })
229
229
  }
230
- className="absolute -right-1 -top-1 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-panel-bg text-panel-text-2 shadow group-hover/swatch:flex"
230
+ className="absolute -right-1 -top-1 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-panel-bg text-panel-text-2 shadow-sm group-hover/swatch:flex"
231
231
  >
232
232
  <X size={8} />
233
233
  </button>
@@ -244,7 +244,7 @@ export function FlatEffectsSection({
244
244
  palette: [...palette, palette.at(-1) ?? "#ffffff"],
245
245
  })
246
246
  }
247
- className="flex h-6 w-6 items-center justify-center rounded-sm border border-panel-border-input text-panel-text-4 hover:text-panel-text-1"
247
+ className="flex h-6 w-6 items-center justify-center rounded-xs border border-panel-border-input text-panel-text-4 hover:text-panel-text-1"
248
248
  >
249
249
  <Plus size={11} />
250
250
  </button>
@@ -146,7 +146,7 @@ export function FlatMediaSection({
146
146
  <div className="space-y-1.5">
147
147
  <div className="flex min-h-8 items-center justify-between gap-2">
148
148
  <span className="flex min-w-0 items-center gap-2">
149
- <span className="h-5 w-8 flex-shrink-0 rounded-[3px] bg-panel-surface" />
149
+ <span className="h-5 w-8 shrink-0 rounded-[3px] bg-panel-surface" />
150
150
  <span className="min-w-0 truncate font-mono text-[11px] text-panel-text-0">
151
151
  {srcAttr}
152
152
  </span>
@@ -161,14 +161,14 @@ export function FlatMediaSection({
161
161
  setTimeout(() => setCopied(false), 1500);
162
162
  });
163
163
  }}
164
- className="flex flex-shrink-0 items-center gap-1 text-[10px] text-panel-text-3 hover:text-panel-text-1"
164
+ className="flex shrink-0 items-center gap-1 text-[10px] text-panel-text-3 hover:text-panel-text-1"
165
165
  >
166
166
  {copied ? <Check size={11} /> : <ClipboardList size={11} />}
167
167
  {copied ? "Copied" : "Copy"}
168
168
  </button>
169
169
  </div>
170
170
  {isVisualMedia && (
171
- <div className="ml-[1px] border-l-2 border-panel-border-input py-1 pl-[10px]">
171
+ <div className="ml-px border-l-2 border-panel-border-input py-1 pl-[10px]">
172
172
  <div className="flex min-h-6 items-center justify-between">
173
173
  <span className="flex items-baseline gap-[7px]">
174
174
  <span className="text-[11px] font-semibold text-panel-text-1">Cutout</span>
@@ -95,7 +95,7 @@ export function FlatOverlaysSection({
95
95
  )}
96
96
  <Plus
97
97
  size={12}
98
- className="absolute right-1.5 top-1.5 text-white opacity-70 drop-shadow group-hover:text-panel-accent group-hover:opacity-100"
98
+ className="absolute right-1.5 top-1.5 text-white opacity-70 drop-shadow-sm group-hover:text-panel-accent group-hover:opacity-100"
99
99
  />
100
100
  </span>
101
101
  <span className="block truncate px-2 py-1.5 text-[10px] text-panel-text-2">
@@ -42,7 +42,7 @@ export function FlatRow({
42
42
  return (
43
43
  <div className="group flex min-h-[30px] items-center justify-between gap-3">
44
44
  <span className={`text-[11px] ${VALUE_TIER_LABEL_CLASS[tier]}`}>{label}</span>
45
- <span className="flex min-w-0 flex-shrink-0 items-center gap-1.5">
45
+ <span className="flex min-w-0 shrink-0 items-center gap-1.5">
46
46
  <span
47
47
  data-flat-row-value="true"
48
48
  className={`min-w-0 border-b pb-px font-mono text-[11px] ${VALUE_TIER_VALUE_CLASS[tier]} ${
@@ -73,7 +73,7 @@ export function FlatRow({
73
73
  track("button", `Reset ${label}`);
74
74
  onReset();
75
75
  }}
76
- className="flex-shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100"
76
+ className="shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100"
77
77
  >
78
78
  <RotateCcw size={11} />
79
79
  </button>
@@ -84,7 +84,7 @@ export function FlatRow({
84
84
  height="10"
85
85
  viewBox="0 0 10 10"
86
86
  fill="currentColor"
87
- className="flex-shrink-0 text-panel-text-5"
87
+ className="shrink-0 text-panel-text-5"
88
88
  >
89
89
  <path d="M2 3l3 4 3-4z" />
90
90
  </svg>
@@ -196,7 +196,7 @@ export function FlatGroupHeader({
196
196
  type="button"
197
197
  data-flat-group-collapsed="true"
198
198
  onClick={onToggleOpen}
199
- className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 w-full flex-shrink-0 items-center justify-between gap-2 border-b border-panel-hairline bg-panel-bg px-4 text-left`}
199
+ className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 w-full shrink-0 items-center justify-between gap-2 border-b border-panel-hairline bg-panel-bg px-4 text-left`}
200
200
  >
201
201
  <span className="flex min-w-0 items-center gap-2">
202
202
  <span className="text-[12px] font-medium text-panel-text-2">{title}</span>
@@ -211,7 +211,7 @@ export function FlatGroupHeader({
211
211
  height="12"
212
212
  viewBox="0 0 12 12"
213
213
  fill="currentColor"
214
- className="flex-shrink-0 text-panel-text-5"
214
+ className="shrink-0 text-panel-text-5"
215
215
  >
216
216
  <path d="M4 2l4 4-4 4z" />
217
217
  </svg>
@@ -221,7 +221,7 @@ export function FlatGroupHeader({
221
221
 
222
222
  return (
223
223
  <div
224
- className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 flex-shrink-0 items-center justify-between bg-panel-bg px-4`}
224
+ className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 shrink-0 items-center justify-between bg-panel-bg px-4`}
225
225
  >
226
226
  <span className="text-[12px] font-semibold text-panel-text-0">{title}</span>
227
227
  <span className="flex items-center gap-2.5 text-panel-text-5">
@@ -415,7 +415,7 @@ export function FlatSlider({
415
415
 
416
416
  return (
417
417
  <div className="flex min-h-[28px] items-center gap-2.5">
418
- <span className="w-[86px] flex-shrink-0 text-[11px] text-panel-text-3">{label}</span>
418
+ <span className="w-[86px] shrink-0 text-[11px] text-panel-text-3">{label}</span>
419
419
  <div
420
420
  data-flat-slider-track="true"
421
421
  role="slider"
@@ -516,7 +516,7 @@ export function FlatSlider({
516
516
  {centerTick && (
517
517
  <div
518
518
  data-flat-slider-center-tick="true"
519
- className="absolute left-1/2 top-[-1px] h-1 w-px -translate-x-1/2 bg-panel-text-5"
519
+ className="absolute left-1/2 -top-px h-1 w-px -translate-x-1/2 bg-panel-text-5"
520
520
  />
521
521
  )}
522
522
  {tier === "explicitCustom" && (
@@ -537,14 +537,14 @@ export function FlatSlider({
537
537
  </div>
538
538
  <span
539
539
  data-flat-slider-value="true"
540
- className={`w-11 flex-shrink-0 text-right font-mono text-[10px] ${
540
+ className={`w-11 shrink-0 text-right font-mono text-[10px] ${
541
541
  tier === "explicitCustom" ? "text-panel-text-0" : "text-panel-text-3"
542
542
  }`}
543
543
  >
544
544
  {displayValue}
545
545
  </span>
546
546
  {(centerTick || onReset) && (
547
- <span data-flat-slider-reset-slot="true" className="w-3.5 flex-shrink-0">
547
+ <span data-flat-slider-reset-slot="true" className="w-3.5 shrink-0">
548
548
  {tier === "explicitCustom" && onReset && (
549
549
  <button
550
550
  type="button"
@@ -68,7 +68,7 @@ export function FlatSelectRow({
68
68
  track("select", trackName);
69
69
  onChange(e.target.value);
70
70
  }}
71
- className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-none disabled:cursor-not-allowed ${VALUE_TIER_VALUE_CLASS[tier]}`}
71
+ className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-hidden disabled:cursor-not-allowed ${VALUE_TIER_VALUE_CLASS[tier]}`}
72
72
  >
73
73
  {renderedOptions.map((option) => (
74
74
  <option key={option.value} value={option.value}>
@@ -81,7 +81,7 @@ export function FlatSelectRow({
81
81
  height="10"
82
82
  viewBox="0 0 10 10"
83
83
  fill="currentColor"
84
- className="flex-shrink-0 text-panel-text-5"
84
+ className="shrink-0 text-panel-text-5"
85
85
  >
86
86
  <path d="M2 3l3 4 3-4z" />
87
87
  </svg>
@@ -96,7 +96,7 @@ export function FlatSelectRow({
96
96
  track("button", `Reset ${trackName}`);
97
97
  onReset();
98
98
  }}
99
- className="flex-shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-40"
99
+ className="shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-40"
100
100
  >
101
101
  <RotateCcw size={11} />
102
102
  </button>
@@ -2,8 +2,7 @@
2
2
 
3
3
  import React, { act, useState } from "react";
4
4
  import { createRoot } from "react-dom/client";
5
- import postcss from "postcss";
6
- import tailwindcss from "tailwindcss";
5
+ import { compile } from "tailwindcss";
7
6
  import { afterEach, describe, expect, it, vi } from "vitest";
8
7
  import { FlatTextLayerList, FlatTextSection } from "./propertyPanelFlatTextSection";
9
8
  import type { DomEditSelection, DomEditTextField } from "./domEditingTypes";
@@ -511,13 +510,13 @@ describe("FlatTextSection — multi-field", () => {
511
510
  expect(contentTextarea?.classList).toContain("resize-y");
512
511
  expect(contentTextarea?.classList).toContain("overflow-y-auto");
513
512
 
514
- const compiled = await postcss([
515
- tailwindcss({
516
- content: [{ raw: host.innerHTML, extension: "html" }],
517
- corePlugins: { preflight: false },
518
- }),
519
- ]).process("@tailwind utilities;", { from: undefined });
520
- expect(compiled.css).toContain("field-sizing: content");
513
+ // Tailwind v4 has no PostCSS-plugin JS API; `compile` takes the stylesheet
514
+ // and is handed the candidates directly instead of scanning raw content.
515
+ const candidates = Array.from(host.querySelectorAll<HTMLElement>("*")).flatMap((el) =>
516
+ Array.from(el.classList),
517
+ );
518
+ const compiled = await compile("@tailwind utilities;");
519
+ expect(compiled.build(candidates)).toContain("field-sizing: content");
521
520
 
522
521
  act(() => root.unmount());
523
522
  });