@hyperframes/studio 0.8.50 → 0.8.53-desktop.0

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 (330) hide show
  1. package/dist/assets/{hyperframes-player-CxxDxHTW.js → hyperframes-player-PTEnAHhg.js} +3 -3
  2. package/dist/assets/{index-DUAIWxkI.js → index--myXp8ui.js} +1 -1
  3. package/dist/assets/{index-Dt3qBI-9.js → index-BjslOS9g.js} +1 -1
  4. package/dist/assets/index-BnXZuIJD.css +1 -0
  5. package/dist/assets/index-CNyviK_l.js +390 -0
  6. package/dist/index.css +29 -0
  7. package/dist/index.css.map +1 -0
  8. package/dist/index.d.ts +825 -24
  9. package/dist/index.html +2 -2
  10. package/dist/index.js +8911 -9853
  11. package/dist/index.js.map +1 -1
  12. package/dist/styles/tailwind-preset.js.map +1 -1
  13. package/package.json +13 -12
  14. package/src/App.tsx +145 -163
  15. package/src/captions/components/CaptionAnimationPanel.tsx +4 -4
  16. package/src/captions/components/CaptionOverlay.sender.test.tsx +52 -0
  17. package/src/captions/components/CaptionOverlay.tsx +8 -4
  18. package/src/captions/components/CaptionOverlayUtils.ts +2 -3
  19. package/src/captions/components/CaptionPropertyPanel.tsx +3 -3
  20. package/src/captions/components/CaptionTimeline.tsx +1 -1
  21. package/src/captions/components/shared.tsx +2 -2
  22. package/src/components/AskAgentModal.tsx +5 -5
  23. package/src/components/EditorShell.selectionSync.test.tsx +9 -4
  24. package/src/components/EditorShell.tsx +59 -62
  25. package/src/components/ExternalFileConflictBanner.tsx +16 -16
  26. package/src/components/LintModal.tsx +6 -16
  27. package/src/components/MediaPreview.tsx +2 -2
  28. package/src/components/RightPanelTabs.test.tsx +151 -0
  29. package/src/components/RightPanelTabs.tsx +59 -0
  30. package/src/components/SaveQueuePausedBanner.tsx +2 -2
  31. package/src/components/StudioHeader.dom.test.tsx +181 -0
  32. package/src/components/StudioHeader.test.ts +0 -12
  33. package/src/components/StudioHeader.tsx +118 -221
  34. package/src/components/StudioLeftPanels.tsx +202 -0
  35. package/src/components/StudioOverlays.tsx +1 -1
  36. package/src/components/{StudioRightPanel.tsx → StudioRightPanels.tsx} +57 -194
  37. package/src/components/{StudioRightPanel.types.ts → StudioRightPanels.types.ts} +4 -3
  38. package/src/components/StudioToast.tsx +2 -2
  39. package/src/components/TimelineHistoryButtons.dom.test.tsx +117 -0
  40. package/src/components/TimelineHistoryButtons.tsx +54 -0
  41. package/src/components/TimelineToolbar.test.tsx +8 -0
  42. package/src/components/TimelineToolbar.tsx +8 -12
  43. package/src/components/dock/Dock.test.tsx +274 -0
  44. package/src/components/dock/Dock.tsx +291 -0
  45. package/src/components/dock/dock.css +28 -0
  46. package/src/components/dock/dockAccessibility.test.ts +166 -0
  47. package/src/components/dock/dockAccessibility.ts +98 -0
  48. package/src/components/dock/dockLayout.test.ts +134 -0
  49. package/src/components/dock/dockLayout.ts +105 -0
  50. package/src/components/dock/dockLayoutSchema.ts +66 -0
  51. package/src/components/dock/dockLayoutStore.test.ts +28 -0
  52. package/src/components/dock/dockLayoutStore.ts +108 -0
  53. package/src/components/dock/dockTestHarness.ts +11 -0
  54. package/src/components/dock/panelRegistry.ts +67 -0
  55. package/src/components/editor/AnimationCard.tsx +4 -4
  56. package/src/components/editor/AnimationCardParts.tsx +6 -6
  57. package/src/components/editor/BlockParamsPanel.tsx +4 -4
  58. package/src/components/editor/BorderRadiusEditor.tsx +1 -1
  59. package/src/components/editor/CanvasContextMenu.tsx +1 -1
  60. package/src/components/editor/ComputedTweenNotice.tsx +1 -1
  61. package/src/components/editor/DomEditOverlay.tsx +1 -1
  62. package/src/components/editor/EaseCurveSection.tsx +1 -1
  63. package/src/components/editor/EaseParamFields.tsx +5 -5
  64. package/src/components/editor/FileTree.tsx +4 -4
  65. package/src/components/editor/FileTreeIcons.tsx +1 -1
  66. package/src/components/editor/FileTreeNodes.tsx +13 -13
  67. package/src/components/editor/InlineTextToolbar.tsx +1 -1
  68. package/src/components/editor/InspectorHeaderActions.tsx +3 -3
  69. package/src/components/editor/KeyframeDiamond.tsx +1 -1
  70. package/src/components/editor/KeyframeEaseList.tsx +1 -1
  71. package/src/components/editor/LayersPanel.test.ts +97 -1
  72. package/src/components/editor/LayersPanel.tsx +7 -5
  73. package/src/components/editor/MarqueeOverlay.tsx +1 -1
  74. package/src/components/editor/OffCanvasIndicators.tsx +1 -1
  75. package/src/components/editor/PreviewGuides.tsx +1 -1
  76. package/src/components/editor/PromotableControl.tsx +2 -2
  77. package/src/components/editor/PropertyPanelEmptyState.tsx +7 -7
  78. package/src/components/editor/PropertyPanelFlatHeader.tsx +2 -2
  79. package/src/components/editor/SnapToolbar.test.tsx +0 -3
  80. package/src/components/editor/SnapToolbar.tsx +2 -2
  81. package/src/components/editor/TimelineFxPopover.tsx +1 -1
  82. package/src/components/editor/TopologyLens.test.tsx +28 -0
  83. package/src/components/editor/TopologyLens.tsx +7 -5
  84. package/src/components/editor/Transform3DCube.tsx +2 -2
  85. package/src/components/editor/audioFxRevealTarget.ts +1 -0
  86. package/src/components/editor/domEditing.test.ts +1 -1
  87. package/src/components/editor/domEditingAgentPrompt.test.ts +8 -6
  88. package/src/components/editor/domEditingAgentPrompt.ts +4 -3
  89. package/src/components/editor/propertyPanelColor.tsx +11 -11
  90. package/src/components/editor/propertyPanelColorCurveGraph.tsx +1 -1
  91. package/src/components/editor/propertyPanelColorGradingControls.tsx +7 -9
  92. package/src/components/editor/propertyPanelColorGradingSection.tsx +6 -6
  93. package/src/components/editor/propertyPanelColorGradingSlider.tsx +5 -5
  94. package/src/components/editor/propertyPanelColorWheels.tsx +1 -1
  95. package/src/components/editor/propertyPanelCommitField.tsx +1 -1
  96. package/src/components/editor/propertyPanelFill.tsx +4 -4
  97. package/src/components/editor/propertyPanelFlatColorGradingAccessory.tsx +3 -3
  98. package/src/components/editor/propertyPanelFlatColorGradingSection.tsx +7 -7
  99. package/src/components/editor/propertyPanelFlatEffectsSection.tsx +4 -4
  100. package/src/components/editor/propertyPanelFlatMediaSection.tsx +3 -3
  101. package/src/components/editor/propertyPanelFlatOverlaysSection.tsx +1 -1
  102. package/src/components/editor/propertyPanelFlatPrimitives.tsx +10 -10
  103. package/src/components/editor/propertyPanelFlatSelectRow.tsx +3 -3
  104. package/src/components/editor/propertyPanelFlatTextSection.test.tsx +8 -9
  105. package/src/components/editor/propertyPanelFlatTextSection.tsx +5 -5
  106. package/src/components/editor/propertyPanelFlatToggle.tsx +1 -1
  107. package/src/components/editor/propertyPanelFont.tsx +5 -5
  108. package/src/components/editor/propertyPanelFxCarveModule.tsx +1 -1
  109. package/src/components/editor/propertyPanelFxControls.test.tsx +1 -1
  110. package/src/components/editor/propertyPanelFxControls.tsx +6 -6
  111. package/src/components/editor/propertyPanelFxEqModule.tsx +2 -2
  112. package/src/components/editor/propertyPanelFxPresetStyle.ts +2 -2
  113. package/src/components/editor/propertyPanelGradingNumberField.tsx +1 -1
  114. package/src/components/editor/propertyPanelMediaSection.tsx +1 -1
  115. package/src/components/editor/propertyPanelPrimitives.tsx +7 -7
  116. package/src/components/editor/propertyPanelSections.tsx +8 -8
  117. package/src/components/editor/useColorGradingController.test.ts +38 -0
  118. package/src/components/editor/useColorGradingController.ts +9 -1
  119. package/src/components/editor/useColorGradingPreviews.test.ts +76 -0
  120. package/src/components/editor/useColorGradingPreviews.ts +11 -1
  121. package/src/components/editor/usePreviewCompositionRect.test.tsx +95 -0
  122. package/src/components/editor/usePreviewCompositionRect.ts +21 -0
  123. package/src/components/feedback/StudioFeedbackCard.tsx +7 -7
  124. package/src/components/feedback/feedbackTrigger.ts +0 -1
  125. package/src/components/nle/AssetPreviewOverlay.tsx +6 -2
  126. package/src/components/nle/CompositionBreadcrumb.tsx +3 -3
  127. package/src/components/nle/NLEContext.tsx +0 -43
  128. package/src/components/nle/NLEPreview.tsx +3 -3
  129. package/src/components/nle/PreviewOverlays.tsx +6 -6
  130. package/src/components/nle/PreviewPane.tsx +3 -3
  131. package/src/components/nle/TimelinePane.tsx +53 -67
  132. package/src/components/panels/SlideshowPanel.tsx +3 -3
  133. package/src/components/panels/SlideshowSubPanels.tsx +20 -17
  134. package/src/components/panels/VariablesBindElement.tsx +2 -2
  135. package/src/components/panels/VariablesDeclarationForm.tsx +2 -2
  136. package/src/components/panels/VariablesOtherCompositions.tsx +1 -1
  137. package/src/components/panels/VariablesPanel.tsx +4 -4
  138. package/src/components/panels/VariablesValueControls.tsx +2 -2
  139. package/src/components/pointerTargetSize.test.tsx +8 -0
  140. package/src/components/renders/FfmpegRequiredNotice.tsx +5 -5
  141. package/src/components/renders/RenderQueue.test.tsx +7 -0
  142. package/src/components/renders/RenderQueue.tsx +7 -7
  143. package/src/components/renders/RenderQueueItem.tsx +6 -6
  144. package/src/components/renders/RenderQueuePanel.tsx +1 -1
  145. package/src/components/sidebar/AssetCard.tsx +8 -8
  146. package/src/components/sidebar/AssetContextMenu.tsx +8 -8
  147. package/src/components/sidebar/AssetsTab.tsx +2 -2
  148. package/src/components/sidebar/AudioRow.tsx +6 -6
  149. package/src/components/sidebar/BlocksTab.tsx +6 -6
  150. package/src/components/sidebar/{LeftSidebar.masterComposition.test.tsx → CompositionsPanel.masterComposition.test.tsx} +6 -9
  151. package/src/components/sidebar/CompositionsPanel.tsx +15 -0
  152. package/src/components/sidebar/CompositionsTab.tsx +5 -5
  153. package/src/components/sidebar/GlobalAssetsView.tsx +1 -1
  154. package/src/components/sidebar/PromptPreviewModal.tsx +3 -3
  155. package/src/components/sidebar/SidebarLintButton.test.tsx +53 -0
  156. package/src/components/sidebar/SidebarLintButton.tsx +50 -0
  157. package/src/components/timelineToolbarStyles.ts +5 -0
  158. package/src/components/ui/Button.test.tsx +252 -0
  159. package/src/components/ui/Button.tsx +89 -126
  160. package/src/components/ui/IconButton.tsx +38 -0
  161. package/src/components/ui/Input.tsx +115 -0
  162. package/src/components/ui/Menu.test.tsx +374 -0
  163. package/src/components/ui/Menu.tsx +213 -0
  164. package/src/components/ui/NumberField.tsx +122 -0
  165. package/src/components/ui/Popover.tsx +74 -0
  166. package/src/components/ui/SearchInput.tsx +2 -2
  167. package/src/components/ui/Select.tsx +100 -0
  168. package/src/components/ui/Slider.tsx +153 -0
  169. package/src/components/ui/Tabs.test.tsx +98 -0
  170. package/src/components/ui/Tabs.tsx +71 -0
  171. package/src/components/ui/Toggle.tsx +63 -0
  172. package/src/components/ui/Tooltip.test.tsx +110 -0
  173. package/src/components/ui/Tooltip.tsx +38 -112
  174. package/src/components/ui/cn.test.ts +30 -0
  175. package/src/components/ui/cn.ts +25 -0
  176. package/src/components/ui/index.ts +29 -2
  177. package/src/components/ui/mountHost.testHelpers.ts +24 -0
  178. package/src/components/ui/valueControls.test.tsx +524 -0
  179. package/src/contexts/PanelLayoutContext.tsx +3 -57
  180. package/src/hooks/appHotkeysDispatch.ts +3 -4
  181. package/src/hooks/timelineEditPermission.ts +31 -0
  182. package/src/hooks/useAppHotkeys.previewForwarding.test.tsx +0 -2
  183. package/src/hooks/useAppHotkeys.test.ts +0 -1
  184. package/src/hooks/useAppHotkeys.textEditing.test.tsx +0 -3
  185. package/src/hooks/useAppHotkeys.ts +21 -84
  186. package/src/hooks/useCaptionDetection.sender.test.tsx +46 -0
  187. package/src/hooks/useCaptionDetection.ts +3 -1
  188. package/src/hooks/useDomEditPreviewSync.ts +4 -6
  189. package/src/hooks/useDomEditSession.ts +0 -7
  190. package/src/hooks/useDomEditWiring.ts +4 -9
  191. package/src/hooks/useEditHistoryActions.test.tsx +90 -0
  192. package/src/hooks/useEditHistoryActions.ts +106 -0
  193. package/src/hooks/useFileManager.ts +21 -123
  194. package/src/hooks/useLintModal.test.tsx +65 -0
  195. package/src/hooks/useLintModal.ts +3 -0
  196. package/src/hooks/usePanelLayout.test.ts +108 -296
  197. package/src/hooks/usePanelLayout.ts +50 -255
  198. package/src/hooks/usePersistentEditHistory.ts +1 -1
  199. package/src/hooks/useProjectFileWriter.ts +150 -0
  200. package/src/hooks/useRightPanelIntents.test.ts +181 -0
  201. package/src/hooks/useRightPanelIntents.ts +63 -0
  202. package/src/hooks/useSdkSession.lifecycle.test.tsx +49 -4
  203. package/src/hooks/useSdkSession.ts +43 -10
  204. package/src/hooks/useServerConnection.hashProject.test.tsx +94 -0
  205. package/src/hooks/useServerConnection.ts +43 -10
  206. package/src/hooks/useSlideshowTabState.test.ts +9 -31
  207. package/src/hooks/useSlideshowTabState.ts +6 -19
  208. package/src/hooks/useStudioContextValue.test.ts +6 -18
  209. package/src/hooks/useStudioContextValue.ts +5 -26
  210. package/src/hooks/useTimelineAssetDropOps.test.tsx +41 -1
  211. package/src/hooks/useTimelineAssetDropOps.ts +52 -11
  212. package/src/hooks/useTimelineDeleteOps.test.tsx +111 -0
  213. package/src/hooks/useTimelineEditing.test.tsx +322 -3
  214. package/src/hooks/useTimelineEditing.ts +103 -16
  215. package/src/hooks/useTimelineEditingTypes.ts +3 -0
  216. package/src/hooks/useTrackPendingTimelineEdit.test.tsx +62 -0
  217. package/src/hooks/useTrackPendingTimelineEdit.ts +22 -0
  218. package/src/hostMount.test.tsx +222 -0
  219. package/src/index.ts +87 -0
  220. package/src/main.tsx +9 -3
  221. package/src/player/components/AudioWaveform.tsx +1 -1
  222. package/src/player/components/AutomationSelectionMenu.tsx +3 -3
  223. package/src/player/components/ClipContextMenu.tsx +3 -3
  224. package/src/player/components/CompositionThumbnail.test.ts +0 -3
  225. package/src/player/components/CompositionThumbnail.tsx +3 -4
  226. package/src/player/components/EditModal.tsx +1 -1
  227. package/src/player/components/ImageThumbnail.tsx +1 -1
  228. package/src/player/components/KeyframeDiamondContextMenu.tsx +4 -4
  229. package/src/player/components/LayerDisclosureRow.tsx +2 -2
  230. package/src/player/components/PlayerControls.tsx +1 -1
  231. package/src/player/components/ShortcutsPanel.tsx +9 -9
  232. package/src/player/components/SpeedMenu.tsx +3 -5
  233. package/src/player/components/Timeline.tsx +3 -2
  234. package/src/player/components/TimelineCanvas.tsx +21 -14
  235. package/src/player/components/TimelineClip.tsx +0 -1
  236. package/src/player/components/TimelineDiamondConnectors.tsx +1 -1
  237. package/src/player/components/TimelineEmptyState.tsx +5 -5
  238. package/src/player/components/TimelineFxButton.tsx +6 -6
  239. package/src/player/components/TimelineGroupHeader.tsx +5 -5
  240. package/src/player/components/TimelineGroupLaneLabels.tsx +1 -1
  241. package/src/player/components/TimelineGroupRow.tsx +1 -1
  242. package/src/player/components/TimelineLanes.test.tsx +32 -0
  243. package/src/player/components/TimelineLanes.tsx +2 -5
  244. package/src/player/components/TimelineLayerGroupHeader.tsx +2 -2
  245. package/src/player/components/TimelineRuler.tsx +1 -1
  246. package/src/player/components/TimelineShortcutHint.tsx +1 -1
  247. package/src/player/components/TimelineTrackHeader.test.tsx +26 -5
  248. package/src/player/components/TimelineTrackHeader.tsx +2 -2
  249. package/src/player/components/TimelineTrackPlainHeader.tsx +3 -3
  250. package/src/player/components/TimelineTypes.ts +2 -1
  251. package/src/player/components/TrackGapContextMenu.tsx +1 -1
  252. package/src/player/components/VideoThumbnail.tsx +4 -2
  253. package/src/player/components/VolumeControl.tsx +2 -2
  254. package/src/player/components/timelineCallbacks.ts +8 -8
  255. package/src/player/components/timelineClipDragPreview.ts +51 -27
  256. package/src/player/components/timelineClipDragTypes.ts +3 -0
  257. package/src/player/components/timelineDragDrop.test.tsx +61 -1
  258. package/src/player/components/timelineDragDrop.ts +57 -15
  259. package/src/player/components/timelineLaneProps.ts +3 -0
  260. package/src/player/components/timelineSnapping.test.ts +27 -0
  261. package/src/player/components/timelineSnapping.ts +14 -1
  262. package/src/player/components/timelineTheme.test.ts +4 -4
  263. package/src/player/components/timelineTheme.ts +30 -30
  264. package/src/player/components/trackHeaderLabelRows.tsx +4 -4
  265. package/src/player/components/useTimelineClipDrag.resize.test.tsx +136 -2
  266. package/src/player/components/useTimelineClipDrag.ts +30 -3
  267. package/src/player/lib/describeClips.ts +3 -54
  268. package/src/player/lib/playbackShortcuts.ts +2 -0
  269. package/src/player/lib/timelinePerformanceDiagnostics.test.ts +3 -2
  270. package/src/player/lib/timelinePerformanceDiagnostics.ts +1 -1
  271. package/src/player/store/previewIframeStore.notify.test.tsx +75 -0
  272. package/src/player/store/previewIframeStore.test.tsx +29 -0
  273. package/src/player/store/previewIframeStore.ts +25 -0
  274. package/src/renderQueueExports.test.tsx +58 -0
  275. package/src/styles/classCandidates.test.ts +105 -0
  276. package/src/styles/classCandidates.ts +281 -0
  277. package/src/styles/hex-baseline.json +84 -0
  278. package/src/styles/hexRatchet.test.ts +201 -0
  279. package/src/styles/iconTokens.ts +20 -0
  280. package/src/styles/palette-reset.css +6 -0
  281. package/src/styles/studio.css +24 -21
  282. package/src/styles/styleSources.test.ts +20 -0
  283. package/src/styles/styleSources.ts +47 -0
  284. package/src/styles/tailwind-preset.shared.js +3 -0
  285. package/src/styles/theme.css +270 -0
  286. package/src/styles/theme.test.ts +183 -0
  287. package/src/styles/tokenGate.test.ts +109 -0
  288. package/src/utils/sourcePatcher.ts +1 -1
  289. package/src/utils/studioHelpers.test.ts +18 -0
  290. package/src/utils/studioHelpers.ts +11 -8
  291. package/src/utils/studioUiPreferences.test.ts +10 -16
  292. package/src/utils/studioUiPreferences.ts +7 -16
  293. package/src/utils/timelineDropTrackInsert.test.ts +134 -0
  294. package/src/utils/timelineDropTrackInsert.ts +83 -0
  295. package/src/utils/typingTarget.ts +4 -0
  296. package/dist/assets/index-n2ml4tUn.js +0 -442
  297. package/dist/assets/index-pO6p0H27.css +0 -1
  298. package/src/components/PanelTabButton.tsx +0 -31
  299. package/src/components/StudioLeftSidebar.tsx +0 -204
  300. package/src/components/nle/TimelineResizeDivider.tsx +0 -101
  301. package/src/components/sidebar/LeftSidebar.storage.test.ts +0 -28
  302. package/src/components/sidebar/LeftSidebar.tsx +0 -334
  303. package/src/components/storyboard/AgentChatMessageButton.test.tsx +0 -47
  304. package/src/components/storyboard/AgentChatMessageButton.tsx +0 -45
  305. package/src/components/storyboard/FramePoster.test.tsx +0 -61
  306. package/src/components/storyboard/FramePoster.tsx +0 -86
  307. package/src/components/storyboard/StoryboardDirection.tsx +0 -45
  308. package/src/components/storyboard/StoryboardFrameFocus.tsx +0 -412
  309. package/src/components/storyboard/StoryboardFrameTile.tsx +0 -117
  310. package/src/components/storyboard/StoryboardGrid.tsx +0 -55
  311. package/src/components/storyboard/StoryboardLoaded.tsx +0 -307
  312. package/src/components/storyboard/StoryboardReviewGuide.tsx +0 -300
  313. package/src/components/storyboard/StoryboardScriptPanel.tsx +0 -26
  314. package/src/components/storyboard/StoryboardSourceEditor.tsx +0 -231
  315. package/src/components/storyboard/StoryboardStatusLegend.tsx +0 -21
  316. package/src/components/storyboard/StoryboardView.tsx +0 -169
  317. package/src/components/storyboard/StoryboardViewModeGuard.test.tsx +0 -187
  318. package/src/components/storyboard/frameComments.test.ts +0 -95
  319. package/src/components/storyboard/frameComments.ts +0 -115
  320. package/src/components/storyboard/frameStatus.ts +0 -36
  321. package/src/components/storyboard/storyboardReviewStage.test.ts +0 -40
  322. package/src/components/storyboard/storyboardReviewStage.ts +0 -45
  323. package/src/components/storyboard/useFrameComments.ts +0 -98
  324. package/src/contexts/ViewModeContext.tsx +0 -142
  325. package/src/hooks/useInspectorSplitResize.ts +0 -51
  326. package/src/hooks/useProjectSignaturePoll.test.tsx +0 -135
  327. package/src/hooks/useProjectSignaturePoll.ts +0 -68
  328. package/src/hooks/useStoryboard.ts +0 -99
  329. package/src/utils/fitPanels.test.ts +0 -164
  330. package/src/utils/fitPanels.ts +0 -151
@@ -1,6 +1,19 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
1
+ @import "tailwindcss" source(none);
2
+ @import "./palette-reset.css";
3
+ @import "./theme.css";
4
+
5
+ /* Explicit sources: Studio's own TypeScript and its shell HTML. Automatic
6
+ * detection would walk up to the repo root and scan `data/projects`, gitignored user content. */
7
+ @source "../**/*.{ts,tsx}";
8
+ @source "../../index.html";
9
+
10
+ /* Tailwind v4 preflight no longer gives enabled buttons a pointer cursor. */
11
+ @layer base {
12
+ button:not(:disabled),
13
+ [role="button"]:not(:disabled) {
14
+ cursor: pointer;
15
+ }
16
+ }
4
17
 
5
18
  /*
6
19
  * Studio is a dark-only UI — pin the user-agent color scheme to dark so that
@@ -14,9 +27,9 @@
14
27
  body {
15
28
  margin: 0;
16
29
  padding: 0;
17
- background: #0a0a0a;
18
- color: #e5e5e5;
19
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
30
+ background: var(--color-bg-0);
31
+ color: var(--color-text-body);
32
+ font-family: var(--font-sans);
20
33
  overflow: hidden;
21
34
  }
22
35
 
@@ -290,7 +303,7 @@ body {
290
303
  left: 12px;
291
304
  overflow: hidden;
292
305
  color: rgba(255, 255, 255, 0.34);
293
- font-family: "SF Mono", "Fira Code", monospace;
306
+ font-family: var(--font-mono);
294
307
  font-size: 9px;
295
308
  font-variant-numeric: tabular-nums;
296
309
  line-height: 1;
@@ -328,7 +341,7 @@ body {
328
341
  }
329
342
 
330
343
  .cm-editor .cm-scroller {
331
- font-family: "JetBrains Mono", "Fira Code", "SF Mono", monospace;
344
+ font-family: var(--font-mono);
332
345
  }
333
346
 
334
347
  .cm-editor.cm-focused {
@@ -385,12 +398,7 @@ body {
385
398
  }
386
399
 
387
400
  .hf-loader-title {
388
- font-family:
389
- Inter,
390
- -apple-system,
391
- BlinkMacSystemFont,
392
- "Segoe UI",
393
- sans-serif;
401
+ font-family: var(--font-sans);
394
402
  font-size: 1rem;
395
403
  font-weight: 600;
396
404
  letter-spacing: 0;
@@ -406,12 +414,7 @@ body {
406
414
  min-height: 2.5rem;
407
415
  overflow: hidden;
408
416
  color: var(--hf-text-secondary, rgba(244, 244, 245, 0.68));
409
- font-family:
410
- Inter,
411
- -apple-system,
412
- BlinkMacSystemFont,
413
- "Segoe UI",
414
- sans-serif;
417
+ font-family: var(--font-sans);
415
418
  font-size: 0.82rem;
416
419
  line-height: 1.6;
417
420
  }
@@ -423,7 +426,7 @@ body {
423
426
  text-overflow: ellipsis;
424
427
  white-space: nowrap;
425
428
  color: var(--hf-text-tertiary, rgba(244, 244, 245, 0.46));
426
- font-family: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
429
+ font-family: var(--font-mono);
427
430
  font-size: 0.75rem;
428
431
  letter-spacing: 0;
429
432
  font-variant-numeric: tabular-nums;
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { listSourceFiles, toPosixPath } from "./styleSources";
3
+
4
+ describe("style source paths", () => {
5
+ it("turns a Windows-style relative path into the forward-slash form the baseline uses", () => {
6
+ expect(toPosixPath("packages\\studio\\src\\App.tsx")).toBe("packages/studio/src/App.tsx");
7
+ });
8
+
9
+ it("leaves a forward-slash path alone", () => {
10
+ expect(toPosixPath("packages/studio/src/App.tsx")).toBe("packages/studio/src/App.tsx");
11
+ });
12
+
13
+ it("hands the keep filter and the map key the same forward-slash path", () => {
14
+ const seen: string[] = [];
15
+ const files = listSourceFiles((file) => (seen.push(file), file.endsWith("styleSources.ts")));
16
+
17
+ expect([...files.keys()]).toEqual(["styles/styleSources.ts"]);
18
+ expect(seen.every((file) => !file.includes("\\"))).toBe(true);
19
+ });
20
+ });
@@ -0,0 +1,47 @@
1
+ // Where the style tests read from: Tailwind's entry resolved from `node_modules`, and the file list
2
+ // that `@source` in `studio.css` describes, shared so the gate, ratchet and theme test agree.
3
+
4
+ import { createRequire } from "node:module";
5
+ import { readdirSync, readFileSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+
9
+ export const STYLES_DIR = path.dirname(fileURLToPath(import.meta.url));
10
+ const SRC_DIR = path.dirname(STYLES_DIR);
11
+ export const REPO_ROOT = path.resolve(SRC_DIR, "../../..");
12
+
13
+ const require = createRequire(import.meta.url);
14
+ export const TAILWIND_DIR = path.dirname(require.resolve("tailwindcss/package.json"));
15
+
16
+ /** Resolves `@import` for Tailwind's compiler: bare `tailwindcss`, or a path. */
17
+ export function loadStylesheet(id: string, base: string) {
18
+ const file = id === "tailwindcss" ? path.join(TAILWIND_DIR, "index.css") : path.resolve(base, id);
19
+ return { path: file, base: path.dirname(file), content: readFileSync(file, "utf8") };
20
+ }
21
+
22
+ /** Forward slashes on every platform, so keys match the baseline file and the gate's messages. */
23
+ export const toPosixPath = (relativePath: string): string => relativePath.replace(/\\/g, "/");
24
+
25
+ /**
26
+ * Studio's own sources, keyed by path relative to `from`, for every file the
27
+ * caller keeps. Build output and dependencies are never walked.
28
+ */
29
+ export function listSourceFiles(
30
+ keep: (relativePath: string) => boolean,
31
+ from: string = SRC_DIR,
32
+ ): Map<string, string> {
33
+ const files = new Map<string, string>();
34
+ const walk = (dir: string) => {
35
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
36
+ const full = path.join(dir, entry.name);
37
+ if (entry.isDirectory()) {
38
+ if (entry.name !== "node_modules" && entry.name !== "dist") walk(full);
39
+ } else {
40
+ const relative = toPosixPath(path.relative(from, full));
41
+ if (keep(relative)) files.set(relative, readFileSync(full, "utf8"));
42
+ }
43
+ }
44
+ };
45
+ walk(SRC_DIR);
46
+ return files;
47
+ }
@@ -1,3 +1,6 @@
1
+ /** @deprecated Superseded by `src/styles/theme.css` (published as
2
+ * `@hyperframes/studio/theme.css`); kept one more major for Tailwind v3
3
+ * consumers. Values are byte-identical; theme.test.ts fails if they drift. */
1
4
  const studioPreset = {
2
5
  theme: {
3
6
  extend: {
@@ -0,0 +1,270 @@
1
+ /* Studio's design tokens, the one place a design value is decided. Tailwind emits
2
+ * each as a utility and a CSS custom property; `@theme static` keeps unused
3
+ * tokens in `:root` so plain CSS and canvas code can still read them. */
4
+
5
+ @theme static {
6
+ /* ---------------------------------------------------------------- colour */
7
+
8
+ /* Background levels, darkest (app shell) to lightest (inset panel body). */
9
+ --color-bg-0: #0a0a0a;
10
+ --color-bg-1: #0c0c0e;
11
+ --color-bg-2: #121214;
12
+
13
+ /* Raised surfaces. */
14
+ --color-surface: #18181b;
15
+ --color-surface-alt: #141414;
16
+ --color-input: #161618;
17
+ --color-hover: #27272a;
18
+
19
+ /* Separators, weakest to strongest. */
20
+ --color-hairline: #1a1a1c;
21
+ --color-border: #1e1e1e;
22
+ --color-border-input: #27272a;
23
+ --color-border-strong: #262626;
24
+
25
+ /* Text, brightest (0) to dimmest (5). */
26
+ --color-text-0: #fafafa;
27
+ --color-text-1: #e4e4e7;
28
+ --color-text-2: #a1a1aa;
29
+ --color-text-3: #71717a;
30
+ --color-text-4: #52525b;
31
+ --color-text-5: #3f3f46;
32
+ --color-text-body: #e5e5e5;
33
+ --color-text-muted: #737373;
34
+
35
+ /* Roles. Selection and playhead are reserved and differ from the accent so a
36
+ * selected clip, the playhead and an accented control stay tellable apart. */
37
+ --color-accent: #3ce6ac;
38
+ --color-selection: #4ba3d2;
39
+ --color-playhead: #ffffff;
40
+ --color-danger: #ef4444;
41
+ --color-media: #00e3ff;
42
+ --color-container: #f5a623;
43
+
44
+ /* The two stops of the HeyGen brand gradient, worn by the header logo. Fixed brand values
45
+ * rather than roles, so they are not reachable as a background or text colour by mistake. */
46
+ --color-brand-gradient-from: #06e3fa;
47
+ --color-brand-gradient-to: #4fdb5e;
48
+
49
+ /* The timeline's own surfaces: darker and bluer than the shared scale, so they
50
+ * get their own names instead of snapping to the nearest --color-bg/-surface
51
+ * step (AD147: a host themes the timeline by overriding these). */
52
+ --timeline-shell-bg: #0a0a0b;
53
+ --timeline-shell-border: rgba(255, 255, 255, 0.05);
54
+ --timeline-ruler-border: rgba(255, 255, 255, 0.16);
55
+ --timeline-row-bg: #101014;
56
+ --timeline-row-border: rgba(255, 255, 255, 0.06);
57
+ --timeline-gutter-bg: #0e0f12;
58
+ --timeline-gutter-border: rgba(255, 255, 255, 0.1);
59
+ --timeline-text-primary: rgba(255, 255, 255, 0.92);
60
+ --timeline-text-secondary: rgba(255, 255, 255, 0.62);
61
+ --timeline-tick-text: rgba(255, 255, 255, 0.34);
62
+ --timeline-tick-major: rgba(255, 255, 255, 0.1);
63
+ --timeline-tick-minor: rgba(255, 255, 255, 0.06);
64
+ --timeline-clip-bg: #141922;
65
+ --timeline-clip-bg-active: #181e28;
66
+ --timeline-clip-border: rgba(255, 255, 255, 0.1);
67
+ --timeline-clip-border-hover: rgba(255, 255, 255, 0.18);
68
+ --timeline-clip-border-active: rgba(255, 255, 255, 0.24);
69
+ --timeline-handle: rgba(255, 255, 255, 0.2);
70
+ --timeline-resize-seam: rgba(255, 255, 255, 0.12);
71
+ --timeline-resize-active: rgba(255, 255, 255, 0.24);
72
+ --timeline-track-clip-fill: rgba(255, 255, 255, 0.055);
73
+ --timeline-track-clip-active: rgba(60, 230, 172, 0.16);
74
+ --timeline-track-label: rgba(255, 255, 255, 0.5);
75
+
76
+ /* -------------------------------------------------------------- geometry */
77
+
78
+ --radius-sm: 0.25rem;
79
+ --radius-md: 0.375rem;
80
+ --radius-lg: 0.5rem;
81
+
82
+ /* The one radius every push-button wears, named by its role so a button does
83
+ * not have to know which step of the scale it sits on. */
84
+ --radius-button: var(--radius-md);
85
+
86
+ /* Control heights: 24, 28 and 32 px. `--spacing-*` gives `h-ctl`, `min-h-ctl`
87
+ * and friends. */
88
+ --spacing-ctl-sm: 24px;
89
+ --spacing-ctl: 28px;
90
+ --spacing-ctl-lg: 32px;
91
+
92
+ /* ------------------------------------------------------------------ type */
93
+
94
+ /* Dense UI type sizes named by pixel value. `step` avoids a clash with
95
+ * Tailwind's own `--text-*` scale, whose `xs`, `sm` and `lg` are unchanged. */
96
+ --text-step-7: 7px;
97
+ --text-step-8: 8px;
98
+ --text-step-9: 9px;
99
+ --text-step-10: 10px;
100
+ --text-step-11: 11px;
101
+ --text-step-12: 12px;
102
+ --text-step-13: 13px;
103
+ --text-step-14: 14px;
104
+ --text-step-15: 15px;
105
+ --text-step-16: 16px;
106
+ --text-step-17: 17px;
107
+ --text-step-18: 18px;
108
+
109
+ --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
110
+ --font-mono: "JetBrains Mono", "IBM Plex Mono", "SF Mono", "Fira Code", monospace;
111
+
112
+ /* ---------------------------------------------------------------- shadow */
113
+
114
+ /* Floating chrome over a near-black canvas: the hairline ring does the edge
115
+ * definition that a light-mode drop shadow cannot. */
116
+ --shadow-menu: 0 8px 24px rgb(0 0 0 / 0.45), 0 0 0 1px rgb(0 0 0 / 0.4);
117
+ --shadow-popover: 0 16px 40px rgb(0 0 0 / 0.55), 0 0 0 1px rgb(0 0 0 / 0.45);
118
+
119
+ /* The primary button is the one light surface in a dark UI, so it reads as a
120
+ * raised key rather than a floating panel: a tight contact shadow, a soft
121
+ * ambient one, and an inner top highlight. */
122
+ --shadow-btn-primary:
123
+ 0 1px 2px rgb(0 0 0 / 0.6), 0 2px 6px rgb(0 0 0 / 0.35), inset 0 1px 0 rgb(255 255 255 / 0.65);
124
+
125
+ /* ---------------------------------------------------------------- motion */
126
+
127
+ --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
128
+ --ease-out-expo: cubic-bezier(0.23, 1, 0.32, 1);
129
+ --ease-in-out-circ: cubic-bezier(0.77, 0, 0.175, 1);
130
+
131
+ /* The curve for a small state change (hover, press, focus), named by role so
132
+ * a control does not have to pick one. The other two stay for motion that is
133
+ * meant to be noticed. */
134
+ --ease-standard: var(--ease-out-quint);
135
+
136
+ /* Durations have no Tailwind namespace, so these are plain variables paired
137
+ * with the `@utility` rules at the bottom of this file. All are at or under
138
+ * 200 ms. */
139
+ --duration-focus: 100ms;
140
+ --duration-press: 120ms;
141
+ --duration-hover: 150ms;
142
+ --duration-expand: 160ms;
143
+ --duration-open: 180ms;
144
+
145
+ /* ------------------------------------------------------------------ icon */
146
+
147
+ /* Read by `readIconTokens` and handed to Phosphor's `IconContext`. */
148
+ --icon-size: 16px;
149
+ --icon-weight: regular;
150
+
151
+ /* ------------------------------------------------- deprecated: old names */
152
+
153
+ /* The `studio.*` and `panel.*` scales, aliases of the tokens above so existing
154
+ * markup keeps working during the sweep. U12 removes them from Studio's
155
+ * source; the published JS preset keeps them one more major. */
156
+ --color-studio-bg: var(--color-bg-0);
157
+ --color-studio-surface: var(--color-surface-alt);
158
+ --color-studio-border: var(--color-border-strong);
159
+ --color-studio-text: var(--color-text-body);
160
+ --color-studio-muted: var(--color-text-muted);
161
+ --color-studio-accent: var(--color-accent);
162
+ --color-panel-bg: var(--color-bg-1);
163
+ --color-panel-bg-inset: var(--color-bg-2);
164
+ --color-panel-input: var(--color-input);
165
+ --color-panel-surface: var(--color-surface);
166
+ --color-panel-hover: var(--color-hover);
167
+ --color-panel-border: var(--color-border);
168
+ --color-panel-border-input: var(--color-border-input);
169
+ --color-panel-hairline: var(--color-hairline);
170
+ --color-panel-text-0: var(--color-text-0);
171
+ --color-panel-text-1: var(--color-text-1);
172
+ --color-panel-text-2: var(--color-text-2);
173
+ --color-panel-text-3: var(--color-text-3);
174
+ --color-panel-text-4: var(--color-text-4);
175
+ --color-panel-text-5: var(--color-text-5);
176
+ --color-panel-bg-soft: var(--color-bg-2);
177
+ --color-panel-bg-2: var(--color-bg-2);
178
+ --color-panel-accent: var(--color-accent);
179
+ --color-panel-danger: var(--color-danger);
180
+ --color-panel-media: var(--color-media);
181
+ --color-panel-container: var(--color-container);
182
+
183
+ /* Names Studio's markup invented that no config ever defined, so the classes
184
+ * compiled to nothing. Aliased to the token that was meant, rather than
185
+ * given values of their own, so there is still one source per decision.
186
+ * U12 rewrites the call sites and deletes these. */
187
+ --color-surface-hover: var(--color-hover);
188
+ --color-accent-red: var(--color-danger);
189
+ --text-2xs: var(--text-step-10);
190
+
191
+ /* ------------------------------------------- deprecated: Tailwind palette */
192
+
193
+ /* Verbatim Tailwind 4.3.3 defaults for stock colours markup still names, so
194
+ * `--color-*: initial` does not blank them. U12 deletes this block;
195
+ * `theme.test.ts` fails if a value drifts from upstream. */
196
+ --color-amber-50: oklch(98.7% 0.022 95.277);
197
+ --color-amber-100: oklch(96.2% 0.059 95.617);
198
+ --color-amber-200: oklch(92.4% 0.12 95.746);
199
+ --color-amber-300: oklch(87.9% 0.169 91.605);
200
+ --color-amber-400: oklch(82.8% 0.189 84.429);
201
+ --color-amber-500: oklch(76.9% 0.188 70.08);
202
+ --color-amber-900: oklch(41.4% 0.112 45.904);
203
+ --color-amber-950: oklch(27.9% 0.077 45.635);
204
+ --color-black: #000;
205
+ --color-blue-400: oklch(70.7% 0.165 254.624);
206
+ --color-blue-500: oklch(62.3% 0.214 259.815);
207
+ --color-cyan-400: oklch(78.9% 0.154 211.53);
208
+ --color-cyan-500: oklch(71.5% 0.143 215.221);
209
+ --color-emerald-300: oklch(84.5% 0.143 164.978);
210
+ --color-emerald-400: oklch(76.5% 0.177 163.223);
211
+ --color-emerald-500: oklch(69.6% 0.17 162.48);
212
+ --color-emerald-600: oklch(59.6% 0.145 163.225);
213
+ --color-emerald-700: oklch(50.8% 0.118 165.612);
214
+ --color-green-400: oklch(79.2% 0.209 151.711);
215
+ --color-green-500: oklch(72.3% 0.219 149.579);
216
+ --color-green-600: oklch(62.7% 0.194 149.214);
217
+ --color-neutral-50: oklch(98.5% 0 none);
218
+ --color-neutral-100: oklch(97% 0 none);
219
+ --color-neutral-200: oklch(92.2% 0 none);
220
+ --color-neutral-300: oklch(87% 0 none);
221
+ --color-neutral-400: oklch(70.8% 0 none);
222
+ --color-neutral-500: oklch(55.6% 0 none);
223
+ --color-neutral-600: oklch(43.9% 0 none);
224
+ --color-neutral-700: oklch(37.1% 0 none);
225
+ --color-neutral-800: oklch(26.9% 0 none);
226
+ --color-neutral-900: oklch(20.5% 0 none);
227
+ --color-neutral-950: oklch(14.5% 0 none);
228
+ --color-orange-300: oklch(83.7% 0.128 66.29);
229
+ --color-orange-400: oklch(75% 0.183 55.934);
230
+ --color-pink-400: oklch(71.8% 0.202 349.761);
231
+ --color-pink-500: oklch(65.6% 0.241 354.308);
232
+ --color-purple-200: oklch(90.2% 0.063 306.703);
233
+ --color-purple-300: oklch(82.7% 0.119 306.383);
234
+ --color-purple-400: oklch(71.4% 0.203 305.504);
235
+ --color-purple-500: oklch(62.7% 0.265 303.9);
236
+ --color-purple-900: oklch(38.1% 0.176 304.987);
237
+ --color-red-100: oklch(93.6% 0.032 17.717);
238
+ --color-red-200: oklch(88.5% 0.062 18.334);
239
+ --color-red-300: oklch(80.8% 0.114 19.571);
240
+ --color-red-400: oklch(70.4% 0.191 22.216);
241
+ --color-red-500: oklch(63.7% 0.237 25.331);
242
+ --color-red-600: oklch(57.7% 0.245 27.325);
243
+ --color-red-700: oklch(50.5% 0.213 27.518);
244
+ --color-red-800: oklch(44.4% 0.177 26.899);
245
+ --color-red-900: oklch(39.6% 0.141 25.723);
246
+ --color-red-950: oklch(25.8% 0.092 26.042);
247
+ --color-rose-400: oklch(71.2% 0.194 13.428);
248
+ --color-rose-500: oklch(64.5% 0.246 16.439);
249
+ --color-sky-200: oklch(90.1% 0.058 230.902);
250
+ --color-sky-300: oklch(82.8% 0.111 230.318);
251
+ --color-sky-400: oklch(74.6% 0.16 232.661);
252
+ --color-sky-500: oklch(68.5% 0.169 237.323);
253
+ --color-sky-700: oklch(50% 0.134 242.749);
254
+ --color-sky-900: oklch(39.1% 0.09 240.876);
255
+ --color-sky-950: oklch(29.3% 0.066 243.157);
256
+ --color-violet-400: oklch(70.2% 0.183 293.541);
257
+ --color-violet-500: oklch(60.6% 0.25 292.717);
258
+ --color-white: #fff;
259
+ }
260
+
261
+ /* Motion durations as utilities, since Tailwind has no `--duration-*`
262
+ * namespace. Each one zeroes itself under `prefers-reduced-motion`, so a
263
+ * caller cannot forget the reduced-motion case. */
264
+
265
+ @utility duration-* {
266
+ transition-duration: --value(--duration-*);
267
+ @media (prefers-reduced-motion: reduce) {
268
+ transition-duration: 0ms;
269
+ }
270
+ }
@@ -0,0 +1,183 @@
1
+ // @vitest-environment happy-dom
2
+
3
+ /** Guards the ways `theme.css` can silently break: a dropped CSS custom
4
+ * property, a utility that stops compiling, the stock Tailwind palette
5
+ * leaking back in, or the JS preset / legacy colours drifting from it. */
6
+
7
+ import { readFileSync } from "node:fs";
8
+ import path from "node:path";
9
+ import React, { act } from "react";
10
+ import { createRoot } from "react-dom/client";
11
+ import { Gear, IconContext } from "@phosphor-icons/react";
12
+ import { compile } from "tailwindcss";
13
+ import { afterEach, describe, expect, it } from "vitest";
14
+ import studioPreset from "./tailwind-preset.shared.js";
15
+ import { readIconTokens } from "./iconTokens";
16
+ import { loadStylesheet, STYLES_DIR, TAILWIND_DIR } from "./styleSources";
17
+
18
+ (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = true;
19
+
20
+ async function buildSource(source: string, candidates: string[]): Promise<string> {
21
+ const compiled = await compile(source, { base: STYLES_DIR, loadStylesheet });
22
+ return compiled.build(candidates);
23
+ }
24
+
25
+ const build = (entry: string, candidates: string[]) =>
26
+ buildSource(readFileSync(path.join(STYLES_DIR, entry), "utf8"), candidates);
27
+
28
+ function rootVariables(css: string): Map<string, string> {
29
+ const block = css.match(/:root, :host \{([\s\S]*?)\n {2}\}/);
30
+ const vars = new Map<string, string>();
31
+ for (const line of (block?.[1] ?? "").split(";")) {
32
+ const match = line.match(/(--[a-z0-9-]+):([\s\S]+)/i);
33
+ if (match) vars.set(match[1], match[2].trim().replace(/\s+/g, " "));
34
+ }
35
+ return vars;
36
+ }
37
+
38
+ const themeSource = readFileSync(path.join(STYLES_DIR, "theme.css"), "utf8");
39
+
40
+ function declaredValue(name: string): string {
41
+ const match = themeSource.match(new RegExp(`\\n\\s*${name}:\\s*([^;]+);`));
42
+ if (!match) throw new Error(`theme.css does not declare ${name}`);
43
+ return match[1].trim();
44
+ }
45
+
46
+ afterEach(() => {
47
+ document.body.innerHTML = "";
48
+ document.documentElement.removeAttribute("style");
49
+ });
50
+
51
+ describe("studio theme", () => {
52
+ it("emits the semantic palette as custom properties and as utilities", async () => {
53
+ const css = await build("studio.css", ["bg-accent", "text-text-2", "border-border"]);
54
+
55
+ expect(rootVariables(css).get("--color-accent")).toBe("#3ce6ac");
56
+ expect(css).toContain(".bg-accent {");
57
+ expect(css).toContain("background-color: var(--color-accent)");
58
+ });
59
+
60
+ it("resolves an alpha modifier on a plain hex token through color-mix", async () => {
61
+ const css = await build("studio.css", ["bg-accent/30"]);
62
+
63
+ expect(css).toMatch(/\.bg-accent\\\/30 \{[\s\S]*?color-mix\(/);
64
+ });
65
+
66
+ it("drops every Tailwind default color Studio does not use", async () => {
67
+ // `palette-reset.css` clears the stock palette. The entries Studio's
68
+ // markup still references are re-declared as deprecated aliases (U12
69
+ // removes them); anything outside that set must no longer resolve.
70
+ const css = await build("studio.css", ["bg-neutral-750", "text-teal-500", "bg-neutral-800"]);
71
+
72
+ expect(css).not.toContain("bg-neutral-750");
73
+ expect(css).not.toContain("text-teal-500");
74
+ expect(css).toContain(".bg-neutral-800 {");
75
+ });
76
+
77
+ it("leaves a host's stock Tailwind colors alone when only theme.css is imported", async () => {
78
+ const css = await buildSource('@import "tailwindcss";\n@import "./theme.css";', [
79
+ "bg-teal-500",
80
+ "text-accent",
81
+ ]);
82
+
83
+ expect(css).toContain(".bg-teal-500 {");
84
+ expect(css).toContain(".text-accent {");
85
+ });
86
+
87
+ it("keeps selection, playhead and accent as three distinct colors", () => {
88
+ const accent = declaredValue("--color-accent");
89
+ const selection = declaredValue("--color-selection");
90
+ const playhead = declaredValue("--color-playhead");
91
+
92
+ expect(new Set([accent, selection, playhead]).size).toBe(3);
93
+ });
94
+
95
+ it("names every micro type step in use and leaves the Tailwind sizes alone", async () => {
96
+ const steps = [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
97
+ const css = await build(
98
+ "studio.css",
99
+ steps.map((step) => `text-step-${step}`).concat(["text-xs", "text-sm", "text-lg"]),
100
+ );
101
+ const vars = rootVariables(css);
102
+
103
+ for (const step of steps) {
104
+ expect(vars.get(`--text-step-${step}`)).toBe(`${step}px`);
105
+ expect(css).toContain(`.text-step-${step} {`);
106
+ }
107
+ expect(vars.get("--text-xs")).toBe("0.75rem");
108
+ expect(vars.get("--text-sm")).toBe("0.875rem");
109
+ expect(vars.get("--text-lg")).toBe("1.125rem");
110
+ });
111
+
112
+ it("compiles a motion-duration utility with a reduced-motion variant", async () => {
113
+ const css = await build("studio.css", ["duration-press", "duration-open"]);
114
+
115
+ expect(css).toMatch(
116
+ /\.duration-press \{[\s\S]*?transition-duration: var\(--duration-press\)[\s\S]*?prefers-reduced-motion: reduce[\s\S]*?transition-duration: 0ms/,
117
+ );
118
+ expect(css).toContain(".duration-open {");
119
+ });
120
+
121
+ it("keeps every legacy Tailwind palette entry at its upstream default value", async () => {
122
+ // These are copies of Tailwind's own values, kept only so existing markup
123
+ // renders unchanged. A Tailwind upgrade that moves one of them would
124
+ // otherwise silently change Studio's colors.
125
+ const upstream = new Map(
126
+ [
127
+ ...readFileSync(path.join(TAILWIND_DIR, "theme.css"), "utf8").matchAll(
128
+ /\n\s*(--color-[a-z]+-(?:50|\d00|950)):\s*([^;]+);/g,
129
+ ),
130
+ ].map(([, name, value]) => [name, value.trim()]),
131
+ );
132
+ const legacy = [...themeSource.matchAll(/\n\s*(--color-[a-z]+-(?:50|\d00|950)):\s*([^;]+);/g)];
133
+
134
+ expect(legacy.length).toBeGreaterThan(0);
135
+ for (const [, name, value] of legacy) {
136
+ expect(upstream.has(name), `${name} is not a Tailwind default`).toBe(true);
137
+ expect(`${name}: ${value.trim()}`).toBe(`${name}: ${upstream.get(name)}`);
138
+ }
139
+ });
140
+
141
+ it("keeps the deprecated JS preset in step with the CSS it shadows", () => {
142
+ const colors = studioPreset.theme.extend.colors;
143
+
144
+ expect(colors.studio.accent.toLowerCase()).toBe(declaredValue("--color-accent"));
145
+ expect(colors.panel.surface.toLowerCase()).toBe(declaredValue("--color-surface"));
146
+ });
147
+
148
+ it("reads the icon defaults off the theme rather than hard-coding them", () => {
149
+ document.documentElement.style.setProperty("--icon-size", "14px");
150
+ document.documentElement.style.setProperty("--icon-weight", "bold");
151
+
152
+ expect(readIconTokens()).toEqual({ size: "14px", weight: "bold" });
153
+ });
154
+
155
+ it("gives a child icon the token size and weight when it sets neither", () => {
156
+ document.documentElement.style.setProperty("--icon-size", "14px");
157
+ document.documentElement.style.setProperty("--icon-weight", "bold");
158
+
159
+ const host = document.createElement("div");
160
+ document.body.append(host);
161
+ const root = createRoot(host);
162
+ act(() => {
163
+ root.render(
164
+ React.createElement(
165
+ IconContext.Provider,
166
+ { value: readIconTokens() },
167
+ React.createElement(Gear, null),
168
+ ),
169
+ );
170
+ });
171
+ const inherited = host.querySelector("svg");
172
+ const inheritedShape = inherited?.innerHTML;
173
+ act(() => {
174
+ root.render(React.createElement(Gear, { size: 14, weight: "bold" }));
175
+ });
176
+ const explicitShape = host.querySelector("svg")?.innerHTML;
177
+ act(() => root.unmount());
178
+
179
+ expect(inherited?.getAttribute("width")).toBe("14px");
180
+ expect(inherited?.getAttribute("height")).toBe("14px");
181
+ expect(inheritedShape).toBe(explicitShape);
182
+ });
183
+ });