@dailies-studio/shot-editor 0.1.0 → 0.2.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 (432) hide show
  1. package/dist/.vite/manifest.json +382 -0
  2. package/dist/assets/AgentPanel-CQejLbiX.js +2 -0
  3. package/dist/assets/AgentPanel-DJEphah0.css +1 -0
  4. package/dist/assets/AgentQuestionDialog-BqkD0kXF.js +1 -0
  5. package/dist/assets/CodeStudio-Dv4ihHpz.js +23 -0
  6. package/dist/assets/CommentsPanel-B4yhhT1T.js +1 -0
  7. package/dist/assets/ComponentLibraryRail-Ch_dsAru.js +1 -0
  8. package/dist/assets/ComponentStage-BRhRotyH.js +1 -0
  9. package/dist/assets/CurvePane-BjFVabKX.css +1 -0
  10. package/dist/assets/CurvePane-R22WzllJ.js +1 -0
  11. package/dist/assets/ExportDialog-CWLQWTmG.js +2 -0
  12. package/dist/assets/FontPicker-DWKaYL7F.js +1 -0
  13. package/dist/assets/FontPicker-v0IOIArM.css +1 -0
  14. package/dist/assets/GenerateDrawer-Bh3SqDKt.js +2 -0
  15. package/dist/assets/InsertDrawer-Ce1fod1L.js +1 -0
  16. package/dist/assets/LibraryBrowser-D9sEGz5w.js +2 -0
  17. package/dist/assets/MakeItYoursSection-CMO4319_.js +2 -0
  18. package/dist/assets/MaterialGraphCanvas-BFzl4wWR.js +23 -0
  19. package/dist/assets/MaterialGraphCanvas-BIUD59ik.css +1 -0
  20. package/dist/assets/MaterialWorkspace-CqTWqaRA.js +14 -0
  21. package/dist/assets/PreferencesDialog-CBf9_OoD.js +1 -0
  22. package/dist/assets/PresetsSection-CUEaoCVU.js +1 -0
  23. package/dist/assets/SoundHistory-BeS8EW1h.js +1 -0
  24. package/dist/assets/SoundMode-BmcZru3i.js +3 -0
  25. package/dist/assets/StockSounds-DdAUGRYK.js +1 -0
  26. package/dist/assets/TemplateFieldSection-CaeXpLFo.js +1 -0
  27. package/dist/assets/VersionHistoryPanel-CxG6XhNP.js +1 -0
  28. package/dist/assets/board-B88EPuLO.css +1 -0
  29. package/dist/assets/board-CL-he7SL.js +3 -0
  30. package/dist/assets/components-0oOSPN0l.js +1 -0
  31. package/dist/assets/googleFontRanges-gp32wJMo.js +1 -0
  32. package/dist/assets/googleFonts-BNon6M7X.js +1 -0
  33. package/dist/assets/iconsAndAliases-DL7gXP5j.js +1863 -0
  34. package/dist/assets/index-4F5NQmI8.css +1 -0
  35. package/dist/assets/index-BGHQGV5z.js +117 -0
  36. package/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  37. package/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  38. package/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  39. package/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  40. package/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  41. package/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  42. package/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  43. package/dist/assets/libraryModel-CpIwfFha.js +1 -0
  44. package/dist/assets/lucide-6iK64tTB.js +9319 -0
  45. package/dist/assets/motionStamp-CH-Hew4E.js +1 -0
  46. package/dist/assets/takeMeasure-Tdg0TxEi.js +1 -0
  47. package/dist/assets/webmcp-BsB9gxWC.js +2 -0
  48. package/dist/index.html +34 -3
  49. package/package.json +12 -9
  50. package/src/addFamilies.ts +66 -0
  51. package/src/api.ts +1631 -55
  52. package/src/app/App.tsx +977 -198
  53. package/src/app/lazyPanel.tsx +137 -0
  54. package/src/app/lazySurfaces.tsx +203 -0
  55. package/src/audio/index.tsx +41 -10
  56. package/src/audio/meter.ts +485 -0
  57. package/src/audio/meterMath.ts +273 -0
  58. package/src/audio/meterStore.ts +155 -0
  59. package/src/audio/offlineMix.ts +289 -0
  60. package/src/audio/preview.ts +928 -118
  61. package/src/bridge.ts +15 -5
  62. package/src/bridgeTypes.ts +1611 -36
  63. package/src/channels.ts +17 -32
  64. package/src/fonts/googleFonts.ts +4 -0
  65. package/src/hostApi.ts +174 -0
  66. package/src/icons.tsx +770 -0
  67. package/src/layerWords.ts +61 -0
  68. package/src/main.tsx +56 -4
  69. package/src/panels/ResizeHandle.tsx +33 -43
  70. package/src/panels/agent/AgentDots.tsx +36 -0
  71. package/src/panels/agent/AgentPanel.tsx +119 -43
  72. package/src/panels/agent/AgentQuestionDialog.tsx +202 -0
  73. package/src/panels/agent/agent.css +113 -97
  74. package/src/panels/agent/agentGate.ts +35 -0
  75. package/src/panels/code/CodeDefaultsPanel.tsx +345 -0
  76. package/src/panels/code/CodeStudio.tsx +373 -0
  77. package/src/panels/code/FileTree.tsx +265 -0
  78. package/src/panels/code/codeBufferGuard.ts +52 -0
  79. package/src/panels/code/codeParts.tsx +321 -0
  80. package/src/panels/comments/CommentComposer.tsx +239 -0
  81. package/src/panels/comments/CommentThread.tsx +346 -0
  82. package/src/panels/comments/CommentsPanel.tsx +336 -0
  83. package/src/panels/comments/InboxButton.tsx +131 -0
  84. package/src/panels/comments/MentionMenu.tsx +98 -0
  85. package/src/panels/comments/parts.tsx +171 -0
  86. package/src/panels/export/ExportDialog.tsx +919 -96
  87. package/src/panels/export/ExportScrim.tsx +57 -0
  88. package/src/panels/files/AssetsTab.tsx +656 -0
  89. package/src/panels/files/FilePanel.tsx +1466 -216
  90. package/src/panels/files/LayerMarks.tsx +40 -0
  91. package/src/panels/files/LeftColumn.tsx +37 -0
  92. package/src/panels/files/McpSection.tsx +690 -47
  93. package/src/panels/files/MissingAssetsNotice.tsx +60 -0
  94. package/src/panels/files/assetDrag.ts +79 -0
  95. package/src/panels/files/resourceParts.tsx +255 -0
  96. package/src/panels/fonts/FontPicker.tsx +762 -0
  97. package/src/panels/fonts/faceCache.ts +207 -0
  98. package/src/panels/fonts/fontPicker.css +271 -0
  99. package/src/panels/fonts/pickerModel.ts +265 -0
  100. package/src/panels/fonts/pickerRequest.ts +46 -0
  101. package/src/panels/header/AccountAvatar.tsx +76 -0
  102. package/src/panels/header/AgentChip.tsx +77 -0
  103. package/src/panels/header/AppMenu.tsx +213 -0
  104. package/src/panels/header/Header.tsx +564 -84
  105. package/src/panels/header/HistoryControls.tsx +31 -5
  106. package/src/panels/header/PresenceStack.tsx +329 -0
  107. package/src/panels/header/ProjectTitle.tsx +232 -0
  108. package/src/panels/header/ShareButton.tsx +59 -0
  109. package/src/panels/header/StatusBadges.tsx +358 -27
  110. package/src/panels/header/SyncDeletedDialog.tsx +88 -0
  111. package/src/panels/header/ZoomControls.tsx +46 -15
  112. package/src/panels/header/agentHandoff.ts +52 -0
  113. package/src/panels/header/leave.ts +26 -0
  114. package/src/panels/help/HelpSection.tsx +189 -163
  115. package/src/panels/insert/InsertDrawer.tsx +723 -0
  116. package/src/panels/insert/iconSet.ts +109 -0
  117. package/src/panels/inspector/ExposeDot.tsx +117 -0
  118. package/src/panels/inspector/FramingRows.tsx +314 -44
  119. package/src/panels/inspector/InspectorFooter.tsx +91 -0
  120. package/src/panels/inspector/InspectorHead.tsx +425 -0
  121. package/src/panels/inspector/InspectorPanel.tsx +408 -124
  122. package/src/panels/inspector/KeyframeDot.tsx +373 -0
  123. package/src/panels/inspector/KeyframeSection.tsx +257 -0
  124. package/src/panels/inspector/MarkerSection.tsx +55 -11
  125. package/src/panels/inspector/ParamRow.tsx +84 -53
  126. package/src/panels/inspector/RightColumn.tsx +152 -0
  127. package/src/panels/inspector/TimeSection.tsx +866 -0
  128. package/src/panels/inspector/animate/EaseControl.tsx +244 -0
  129. package/src/panels/inspector/animate/PresetsSection.tsx +135 -0
  130. package/src/panels/inspector/animate/ShotSection.tsx +562 -48
  131. package/src/panels/inspector/design/AlignRow.tsx +95 -31
  132. package/src/panels/inspector/design/AppearanceSection.tsx +118 -9
  133. package/src/panels/inspector/design/ClonerSection.tsx +323 -62
  134. package/src/panels/inspector/design/ComponentSection.tsx +50 -43
  135. package/src/panels/inspector/design/CornerSection.tsx +45 -14
  136. package/src/panels/inspector/design/DefinitionProps.tsx +315 -0
  137. package/src/panels/inspector/design/DefinitionSection.tsx +136 -0
  138. package/src/panels/inspector/design/DeviceSection.tsx +210 -0
  139. package/src/panels/inspector/design/FillSection.tsx +83 -51
  140. package/src/panels/inspector/design/FontField.tsx +196 -0
  141. package/src/panels/inspector/design/HtmlSection.tsx +23 -12
  142. package/src/panels/inspector/design/InstanceSection.tsx +373 -154
  143. package/src/panels/inspector/design/MaskSection.tsx +84 -0
  144. package/src/panels/inspector/design/MaterialSection.tsx +177 -0
  145. package/src/panels/inspector/design/MediaSection.tsx +273 -120
  146. package/src/panels/inspector/design/MeshSection.tsx +910 -82
  147. package/src/panels/inspector/design/PaintRows.tsx +550 -0
  148. package/src/panels/inspector/design/PlateStyleSection.tsx +177 -0
  149. package/src/panels/inspector/design/ShadowSection.tsx +221 -48
  150. package/src/panels/inspector/design/SplineFollowSection.tsx +235 -0
  151. package/src/panels/inspector/design/StrokeSection.tsx +228 -43
  152. package/src/panels/inspector/design/TemplateFieldSection.tsx +57 -0
  153. package/src/panels/inspector/design/TransformSection.tsx +244 -50
  154. package/src/panels/inspector/design/TypographySection.tsx +679 -110
  155. package/src/panels/inspector/layout.ts +295 -0
  156. package/src/panels/inspector/propValue.ts +199 -0
  157. package/src/panels/inspector/scene/CameraSection.tsx +232 -22
  158. package/src/panels/inspector/scene/EffectsSection.tsx +722 -71
  159. package/src/panels/inspector/scene/MakeItYoursSection.tsx +180 -0
  160. package/src/panels/inspector/scene/SceneDocSection.tsx +191 -0
  161. package/src/panels/inspector/scene/SceneSection.tsx +219 -44
  162. package/src/panels/inspector/scene/TransitionSection.tsx +523 -72
  163. package/src/panels/inspector/storyboard/StoryboardReferenceSection.tsx +593 -0
  164. package/src/panels/inspector/storyboard/VideoStill.tsx +120 -0
  165. package/src/panels/inspector/timeModel.ts +190 -0
  166. package/src/panels/inspector/useDocWrite.ts +438 -146
  167. package/src/panels/library/LibraryBrowser.tsx +435 -0
  168. package/src/panels/library/LibraryGrid.tsx +190 -0
  169. package/src/panels/library/LibraryPreviewPanel.tsx +349 -0
  170. package/src/panels/library/LibraryRail.tsx +84 -0
  171. package/src/panels/library/LibrarySoundPanel.tsx +293 -0
  172. package/src/panels/library/SoundHistory.tsx +245 -0
  173. package/src/panels/library/StockSounds.tsx +390 -0
  174. package/src/panels/library/libraryModel.ts +147 -0
  175. package/src/panels/library/stockSearch.ts +77 -0
  176. package/src/panels/maskMarks.tsx +117 -0
  177. package/src/panels/material/MaterialCommentNode.tsx +54 -0
  178. package/src/panels/material/MaterialGraphCanvas.tsx +861 -0
  179. package/src/panels/material/MaterialGraphMenu.tsx +45 -0
  180. package/src/panels/material/MaterialGroupNode.tsx +56 -0
  181. package/src/panels/material/MaterialLibrary.tsx +253 -0
  182. package/src/panels/material/MaterialNode.tsx +453 -0
  183. package/src/panels/material/MaterialNodePalette.tsx +349 -0
  184. package/src/panels/material/MaterialParameterControls.tsx +525 -0
  185. package/src/panels/material/MaterialRerouteNode.tsx +42 -0
  186. package/src/panels/material/MaterialWorkspace.tsx +501 -0
  187. package/src/panels/material/NodePreview.tsx +70 -0
  188. package/src/panels/material/flowModel.ts +441 -0
  189. package/src/panels/material/parameterValues.ts +231 -0
  190. package/src/panels/material/previewScheduler.ts +235 -0
  191. package/src/panels/material/reactFlow.css +6 -0
  192. package/src/panels/material/socketGlyph.ts +85 -0
  193. package/src/panels/material/viewportMemory.ts +58 -0
  194. package/src/panels/preferences/PreferencesDialog.tsx +215 -0
  195. package/src/panels/share/ShareComponentDialog.tsx +247 -0
  196. package/src/panels/stage/StageMenu.tsx +35 -5
  197. package/src/panels/stage/StagePanel.tsx +338 -23
  198. package/src/panels/stage/cellSlots.tsx +70 -0
  199. package/src/panels/storyboard/BoardCanvas.tsx +739 -0
  200. package/src/panels/storyboard/BoardCursors.tsx +158 -0
  201. package/src/panels/storyboard/BoardDropDialog.tsx +161 -0
  202. package/src/panels/storyboard/BoardPlan.tsx +42 -0
  203. package/src/panels/storyboard/ComponentSheet.tsx +187 -0
  204. package/src/panels/storyboard/ComponentStage.tsx +235 -0
  205. package/src/panels/storyboard/MiniSketch.tsx +173 -0
  206. package/src/panels/storyboard/PartPicker.tsx +64 -0
  207. package/src/panels/storyboard/PlanPanel.tsx +617 -0
  208. package/src/panels/storyboard/ReferencePreview.tsx +217 -0
  209. package/src/panels/storyboard/ShotBrief.tsx +360 -0
  210. package/src/panels/storyboard/ShotCard.tsx +251 -0
  211. package/src/panels/storyboard/StagePreview.tsx +348 -0
  212. package/src/panels/storyboard/StillThumbs.tsx +162 -0
  213. package/src/panels/storyboard/StoryboardView.tsx +79 -0
  214. package/src/panels/storyboard/board.css +1065 -0
  215. package/src/panels/storyboard/board.ts +12 -0
  216. package/src/panels/storyboard/boardClipboard.ts +111 -0
  217. package/src/panels/storyboard/boardOrder.ts +68 -0
  218. package/src/panels/storyboard/boardRows.tsx +425 -0
  219. package/src/panels/storyboard/liveCap.ts +90 -0
  220. package/src/panels/storyboard/plan.css +295 -0
  221. package/src/panels/storyboard/posterCache.ts +133 -0
  222. package/src/panels/storyboard/shotboard/ComponentPicker.tsx +373 -0
  223. package/src/panels/storyboard/shotboard/ImagePicker.tsx +145 -0
  224. package/src/panels/storyboard/shotboard/ShotBoard.tsx +292 -0
  225. package/src/panels/storyboard/shotboard/SketchCanvas.tsx +443 -0
  226. package/src/panels/storyboard/shotboard/SketchInspector.tsx +471 -0
  227. package/src/panels/storyboard/shotboard/SketchItem.tsx +240 -0
  228. package/src/panels/storyboard/shotboard/SketchLayers.tsx +130 -0
  229. package/src/panels/storyboard/shotboard/SketchPlay.tsx +108 -0
  230. package/src/panels/storyboard/shotboard/SketchStrip.tsx +178 -0
  231. package/src/panels/storyboard/shotboard/SketchToolFooter.tsx +99 -0
  232. package/src/panels/storyboard/shotboard/sketch.css +1392 -0
  233. package/src/panels/storyboard/shotboard/sketchModel.ts +219 -0
  234. package/src/panels/storyboard/storyboardDrop.ts +164 -0
  235. package/src/panels/studio/ComponentLibraryRail.tsx +346 -0
  236. package/src/panels/studio/ComponentSidePanel.tsx +355 -0
  237. package/src/panels/studio/ComponentStudio.tsx +243 -0
  238. package/src/panels/studio/ComponentsHome.tsx +318 -0
  239. package/src/panels/studio/ComponentsPreview.tsx +179 -0
  240. package/src/panels/studio/EveryUse.tsx +244 -0
  241. package/src/panels/studio/ThisUsePanel.tsx +679 -0
  242. package/src/panels/studio/componentParts.tsx +341 -0
  243. package/src/panels/studio/components.ts +6 -0
  244. package/src/panels/studio/newCodeComponent.ts +66 -0
  245. package/src/panels/timeline/AudioFxButton.tsx +290 -0
  246. package/src/panels/timeline/AudioLane.tsx +674 -85
  247. package/src/panels/timeline/BlockRow.tsx +154 -85
  248. package/src/panels/timeline/ChannelRow.tsx +378 -129
  249. package/src/panels/timeline/CurvePane.tsx +508 -0
  250. package/src/panels/timeline/Filmstrip.tsx +361 -0
  251. package/src/panels/timeline/GenerateDrawer.tsx +407 -0
  252. package/src/panels/timeline/KeyStretch.tsx +115 -0
  253. package/src/panels/timeline/LifespanRow.tsx +730 -47
  254. package/src/panels/timeline/MarkerLane.tsx +46 -8
  255. package/src/panels/timeline/PlayButton.tsx +91 -0
  256. package/src/panels/timeline/PropertyPicker.tsx +254 -0
  257. package/src/panels/timeline/ShotRow.tsx +400 -0
  258. package/src/panels/timeline/SoundMode.tsx +1406 -0
  259. package/src/panels/timeline/TimelineDock.tsx +2338 -272
  260. package/src/panels/timeline/TransportVolume.tsx +163 -0
  261. package/src/panels/timeline/beatRuler.ts +74 -0
  262. package/src/panels/timeline/curveModel.ts +500 -0
  263. package/src/panels/timeline/duckUnderVoice.ts +68 -0
  264. package/src/panels/timeline/keyframeMenu.ts +138 -0
  265. package/src/panels/timeline/labelWidth.ts +49 -0
  266. package/src/panels/timeline/marquee.ts +155 -0
  267. package/src/panels/timeline/model.ts +1860 -199
  268. package/src/panels/timeline/snap.ts +125 -43
  269. package/src/panels/timeline/staggerPreview.tsx +92 -0
  270. package/src/panels/timeline/takeMeasure.ts +72 -0
  271. package/src/panels/timeline/useLive.ts +48 -0
  272. package/src/panels/timeline/viewport.ts +234 -0
  273. package/src/panels/toolbar/BackChip.tsx +116 -0
  274. package/src/panels/toolbar/CameraViewBar.tsx +118 -49
  275. package/src/panels/toolbar/CanvasBar.tsx +117 -0
  276. package/src/panels/toolbar/InsertButton.tsx +38 -0
  277. package/src/panels/toolbar/IsolationTrail.tsx +156 -0
  278. package/src/panels/toolbar/PanelToggles.tsx +86 -0
  279. package/src/panels/toolbar/ShadingBar.tsx +119 -26
  280. package/src/panels/toolbar/ToolDockBar.tsx +329 -80
  281. package/src/panels/toolbar/ToolFooter.tsx +102 -0
  282. package/src/panels/toolbar/ViewModeBar.tsx +247 -38
  283. package/src/panels/toolbar/WorkPlaneBar.tsx +94 -0
  284. package/src/panels/toolbar/useAddRows.tsx +326 -0
  285. package/src/panels/toolbar/useDismiss.ts +122 -0
  286. package/src/panels/versions/VersionHistoryPanel.tsx +274 -0
  287. package/src/panels/versions/versionModel.ts +103 -0
  288. package/src/panels/virtualWindow.ts +271 -0
  289. package/src/shotTokens.css +151 -60
  290. package/src/stage/FrustumGizmo.tsx +504 -84
  291. package/src/stage/GridPlane.tsx +225 -16
  292. package/src/stage/NodePreviews.tsx +628 -0
  293. package/src/stage/PerfHud.tsx +437 -47
  294. package/src/stage/SplitRenderer.tsx +816 -169
  295. package/src/stage/StageApp.tsx +4274 -486
  296. package/src/stage/StageRootBoundary.tsx +114 -0
  297. package/src/stage/StageTools.tsx +651 -151
  298. package/src/stage/TransformGizmo.tsx +696 -0
  299. package/src/stage/adaptivePresentation.ts +145 -0
  300. package/src/stage/bridgeClient.ts +2522 -41
  301. package/src/stage/exportWorker.ts +9 -0
  302. package/src/stage/feed.ts +305 -14
  303. package/src/stage/fontPreview.ts +176 -0
  304. package/src/stage/frustumGeometry.ts +319 -1
  305. package/src/stage/furnitureTokens.ts +33 -0
  306. package/src/stage/gizmoMath.ts +508 -16
  307. package/src/stage/gpuTimer.ts +276 -0
  308. package/src/stage/instanceModules.ts +194 -33
  309. package/src/stage/interactions/move.ts +24 -5
  310. package/src/stage/isolation.ts +291 -33
  311. package/src/stage/jsSelfProfiler.ts +325 -0
  312. package/src/stage/main.tsx +116 -16
  313. package/src/stage/modalState.ts +222 -23
  314. package/src/stage/moduleEpoch.ts +60 -0
  315. package/src/stage/motionPathStage.ts +279 -0
  316. package/src/stage/motionPreview.ts +74 -0
  317. package/src/stage/overlays/Overlays.tsx +1112 -159
  318. package/src/stage/overlays/Rulers.tsx +495 -0
  319. package/src/stage/panes.ts +159 -10
  320. package/src/stage/project.ts +68 -3
  321. package/src/stage/renderScaleController.ts +730 -0
  322. package/src/stage/rulers.ts +129 -0
  323. package/src/stage/sceneExtent.ts +75 -0
  324. package/src/stage/shading.ts +20 -0
  325. package/src/stage/splineStage.ts +632 -0
  326. package/src/stage/stagePerformanceTelemetry.ts +717 -0
  327. package/src/stage/stageTheme.ts +36 -0
  328. package/src/stage/stageWarmup.ts +11 -0
  329. package/src/stage/textAutoSize.ts +124 -0
  330. package/src/stage/tier1Export.ts +641 -0
  331. package/src/stage/tokenColor.ts +14 -9
  332. package/src/stage/view.ts +56 -9
  333. package/src/stage/workingView.ts +277 -33
  334. package/src/state/accountPrefs.ts +261 -0
  335. package/src/state/agentActions.ts +173 -15
  336. package/src/state/agentActivity.ts +198 -0
  337. package/src/state/agentQuestion.ts +239 -0
  338. package/src/state/bootOpen.ts +42 -0
  339. package/src/state/cameraVerbs.ts +139 -0
  340. package/src/state/clientId.ts +6 -1
  341. package/src/state/comments.ts +1099 -0
  342. package/src/state/context.tsx +19 -1
  343. package/src/state/editorPrefs.ts +220 -0
  344. package/src/state/errorReports.ts +121 -0
  345. package/src/state/fieldWrite.ts +161 -0
  346. package/src/state/fontRecents.ts +70 -0
  347. package/src/state/fonts.ts +656 -0
  348. package/src/state/graphClipboard.ts +414 -0
  349. package/src/state/imageThumbs.ts +216 -0
  350. package/src/state/insert.ts +444 -67
  351. package/src/state/isolationTrail.ts +92 -0
  352. package/src/state/keyMoves.ts +134 -0
  353. package/src/state/keyframeClipboard.ts +177 -0
  354. package/src/state/keymap.ts +1153 -117
  355. package/src/state/layoutPrefs.ts +272 -0
  356. package/src/state/layouts.ts +205 -0
  357. package/src/state/measureMedia.ts +167 -0
  358. package/src/state/menu.ts +2341 -28
  359. package/src/state/motionStamp.ts +7 -0
  360. package/src/state/panelSize.ts +375 -5
  361. package/src/state/pendingChanges.ts +119 -0
  362. package/src/state/playHold.ts +78 -0
  363. package/src/state/playbackPreparation.ts +562 -0
  364. package/src/state/posterCapture.ts +242 -0
  365. package/src/state/presence.ts +254 -0
  366. package/src/state/projectComponents.ts +160 -0
  367. package/src/state/renderEvents.ts +298 -18
  368. package/src/state/runtimeErrors.ts +46 -0
  369. package/src/state/selection.ts +12 -0
  370. package/src/state/shortcuts.ts +1606 -0
  371. package/src/state/sketchGuide.ts +51 -0
  372. package/src/state/splineRefs.ts +87 -0
  373. package/src/state/stageHealth.ts +259 -0
  374. package/src/state/stageWatchdog.ts +47 -0
  375. package/src/state/stillStage.ts +330 -0
  376. package/src/state/stillThumbs.ts +474 -0
  377. package/src/state/store.ts +23591 -2982
  378. package/src/state/storyboardModel.ts +528 -0
  379. package/src/state/sync.ts +1257 -0
  380. package/src/state/syncCache.ts +159 -0
  381. package/src/state/triangles.ts +13 -0
  382. package/src/state/types.ts +1698 -55
  383. package/src/state/unsavedGuard.ts +49 -0
  384. package/src/state/viewMode.ts +200 -0
  385. package/src/state/webmcp.ts +1128 -0
  386. package/src/state/workspace.ts +1147 -0
  387. package/src/styles/assets-tab.css +304 -0
  388. package/src/styles/canvas.css +545 -0
  389. package/src/styles/comments.css +1435 -0
  390. package/src/styles/controls.css +162 -0
  391. package/src/styles/dialogs.css +390 -0
  392. package/src/styles/insert-drawer.css +586 -0
  393. package/src/styles/inspector-board.css +207 -0
  394. package/src/styles/inspector-object.css +486 -0
  395. package/src/styles/inspector-shot.css +252 -0
  396. package/src/styles/inspector.css +360 -0
  397. package/src/styles/left-column.css +1628 -0
  398. package/src/styles/library-preview.css +575 -0
  399. package/src/styles/library.css +512 -0
  400. package/src/styles/modes/code.css +569 -0
  401. package/src/styles/modes/component.css +1561 -0
  402. package/src/styles/modes/material.css +1201 -0
  403. package/src/styles/modes/storyboard.css +518 -0
  404. package/src/styles/share-component.css +49 -0
  405. package/src/styles/shell.css +653 -0
  406. package/src/styles/shot-brief.css +360 -0
  407. package/src/styles/states.css +76 -0
  408. package/src/styles/sync.css +380 -0
  409. package/src/styles/timeline-graph.css +384 -0
  410. package/src/styles/timeline-meters.css +89 -0
  411. package/src/styles/timeline-rows.css +1959 -0
  412. package/src/styles/timeline-sound.css +1003 -0
  413. package/src/styles/timeline.css +754 -0
  414. package/src/styles/tool-footer.css +525 -0
  415. package/src/styles/topbar.css +886 -0
  416. package/src/styles/versions.css +262 -0
  417. package/src/styles.css +72 -2921
  418. package/src/tools.ts +108 -23
  419. package/dist/assets/index-B7notge7.css +0 -1
  420. package/dist/assets/index-BBE6Ta9L.js +0 -52
  421. package/src/panels/files/icons.tsx +0 -118
  422. package/src/panels/header/DocSwitcher.tsx +0 -85
  423. package/src/panels/header/TransportBar.tsx +0 -86
  424. package/src/panels/inspector/animate/BlockTiming.tsx +0 -341
  425. package/src/panels/inspector/animate/DuringBlocks.tsx +0 -85
  426. package/src/panels/inspector/animate/InOutPresets.tsx +0 -57
  427. package/src/panels/inspector/animate/ResolvedChannels.tsx +0 -150
  428. package/src/panels/inspector/animate/useBlocks.ts +0 -43
  429. package/src/panels/material/MaterialPicker.tsx +0 -172
  430. package/src/panels/timeline/SceneStrip.tsx +0 -145
  431. package/src/panels/toolbar/AddMenu.tsx +0 -230
  432. package/src/stage/MoveGizmo.tsx +0 -411
@@ -0,0 +1,443 @@
1
+ // THE SKETCH PAPER (Paper SB2, SB3, SB4): the open sketch drawn in the DOM,
2
+ // and every gesture on it.
3
+ //
4
+ // select a press on an item selects it (Shift adds or takes it away) and
5
+ // a drag moves the selection, snapped by `snapMove` to the frame
6
+ // and the other items, guides drawn in `--guide`. A press on the
7
+ // paper starts a marquee (`marqueeItems`), Shift adding to what
8
+ // was selected. One drag is ONE undo entry (`beginSketchDrag`,
9
+ // then absolute patches from the press, then `endSketchDrag`).
10
+ // resize eight handles round one selected box; a picture keeps its
11
+ // aspect (Shift frees it; on any other box Shift holds it). An
12
+ // arrow shows a handle on each end instead.
13
+ // draw with Text, Box or Arrow armed a press draws one, and the tool
14
+ // goes back to Select. A click makes a default size.
15
+ // text a double press, or Enter, edits a text item's words in place.
16
+ // drop an OS file, a Project asset tile (`ASSET_MIME`) or a board card
17
+ // (`STORYBOARD_MIME`) lands as an item at the drop point.
18
+ // cursors other people's pointers on this same sketch (`BoardCursors`
19
+ // with `where`), over the items.
20
+ //
21
+ // Positions are layer units, read from the paper's own client box at the
22
+ // press (`designPointAt`), so a zoom or a scroll mid-session cannot skew them.
23
+ import { useRef, useState } from "react";
24
+ import type {
25
+ CSSProperties,
26
+ DragEvent as ReactDragEvent,
27
+ FocusEvent as ReactFocusEvent,
28
+ MouseEvent as ReactMouseEvent,
29
+ PointerEvent as ReactPointerEvent,
30
+ ReactNode,
31
+ } from "react";
32
+ import { RESIZE_HANDLES, snapMove } from "@dailies-studio/shot/doc";
33
+ import type { Doc3D, NewSketchItem, SketchItem3D, SketchItemPatch, SnapGuide, StoryboardSketch3D } from "@dailies-studio/shot/doc";
34
+ import { startDrag } from "@dailies-studio/ui";
35
+ import { COMPONENT_ICON, kindIcon } from "../../../icons.js";
36
+ import { useShotEditor, useStore } from "../../../state/context.js";
37
+ import { rectBetween } from "../../timeline/marquee.js";
38
+ import type { MarqueeRect } from "../../timeline/marquee.js";
39
+ import { ASSET_MIME, readAssetDrag } from "../../files/assetDrag.js";
40
+ import { BoardCursors } from "../BoardCursors.js";
41
+ import { boxStyle } from "../MiniSketch.js";
42
+ import { STORYBOARD_MIME, readDragPayload } from "../storyboardDrop.js";
43
+ import { partNumberOf } from "../PlanPanel.js";
44
+ import { SketchItemView } from "./SketchItem.js";
45
+ import { designPointAt, itemRect, marqueeItems, movedPatch, resizedRect } from "./sketchModel.js";
46
+ import type { SketchRect } from "./sketchModel.js";
47
+
48
+ /** How far a press travels, in CSS px, before it is a drag and not a click. */
49
+ const DRAG_SLOP_PX = 3;
50
+ /** The snap's pull, in CSS px on screen at every zoom. */
51
+ const SNAP_PX = 6;
52
+ /** What a click with Box or Text armed makes, layer units. */
53
+ const DEFAULT_BOX: readonly [number, number] = [480, 270];
54
+ const DEFAULT_TEXT: readonly [number, number] = [760, 120];
55
+ /** What a click with Arrow armed makes: this long, pointing right. */
56
+ const DEFAULT_ARROW = 400;
57
+ /** A drag shorter than this, layer units, is a click. */
58
+ const MIN_DRAW = 16;
59
+
60
+ /** The eight handles, in `HANDLE_SITES`' order: corners, then edges. */
61
+ const HANDLES = ["nw", "ne", "se", "sw", "n", "e", "s", "w"] as const;
62
+
63
+ type Draft = { kind: "box" | "text"; rect: MarqueeRect } | { kind: "arrow"; from: [number, number]; to: [number, number] };
64
+
65
+ export interface SketchCanvasProps {
66
+ doc: Doc3D;
67
+ partId: string;
68
+ /** The open sketch, or undefined while the plan has none (the first add
69
+ * makes Sketch 1). */
70
+ sketch: StoryboardSketch3D | undefined;
71
+ /** The paper's width, CSS px. */
72
+ width: number;
73
+ /** The text item being edited, or null. */
74
+ editing: string | null;
75
+ onEdit(id: string | null): void;
76
+ }
77
+
78
+ export function SketchCanvas(props: SketchCanvasProps) {
79
+ const { doc, sketch, width } = props;
80
+ const store = useStore();
81
+ const selection = useShotEditor((s) => s.sketchSelection);
82
+ const tool = useShotEditor((s) => s.sketchTool);
83
+ const readOnly = useShotEditor((s) => s.readOnly);
84
+ // State, not a ref: the cursor layer binds to the element.
85
+ const [paper, setPaper] = useState<HTMLDivElement | null>(null);
86
+ const [guides, setGuides] = useState<readonly SnapGuide[]>([]);
87
+ const [marquee, setMarquee] = useState<MarqueeRect | null>(null);
88
+ const [draft, setDraft] = useState<Draft | null>(null);
89
+ const [drop, setDrop] = useState<[number, number] | null>(null);
90
+ // A press, not a Tab, focused the item: its focus must not select it again.
91
+ const pressing = useRef(false);
92
+ const frame = doc.size;
93
+ const items = sketch?.items ?? [];
94
+ const selected = new Set(selection);
95
+ const one = selection.length === 1 ? items.find((item) => item.id === selection[0]) : undefined;
96
+ const number = partNumberOf(doc, props.partId) ?? "this shot";
97
+
98
+ const pointAt = (clientX: number, clientY: number): [number, number] =>
99
+ designPointAt(paper!.getBoundingClientRect(), frame, clientX, clientY);
100
+ const scaleNow = (): number => paper!.getBoundingClientRect().width / frame.width;
101
+
102
+ // ── MOVE ──────────────────────────────────────────────────────────────
103
+ const moveGesture = (event: ReactPointerEvent<HTMLElement>, ids: readonly string[]): void => {
104
+ const moving = items.filter((item) => ids.includes(item.id));
105
+ const others = items.filter((item) => !ids.includes(item.id)).map(snapBox);
106
+ const scale = scaleNow();
107
+ let begun = false;
108
+ startDrag(event, {
109
+ onMove(ev) {
110
+ const cx = ev.clientX - event.clientX;
111
+ const cy = ev.clientY - event.clientY;
112
+ if (!begun && Math.hypot(cx, cy) < DRAG_SLOP_PX) return;
113
+ if (!begun) {
114
+ begun = true;
115
+ store.beginSketchDrag();
116
+ }
117
+ const raw = { dx: cx / scale, dy: cy / scale };
118
+ // ALT MOVES FREELY, the house's snap override.
119
+ const snap = ev.altKey ? { ...raw, guides: [] } : snapMove(moving.map(snapBox), others, frame, raw, SNAP_PX / scale);
120
+ const dx = Math.round(snap.dx);
121
+ const dy = Math.round(snap.dy);
122
+ setGuides(snap.guides);
123
+ store.dragSketchItems(Object.fromEntries(moving.map((item) => [item.id, movedPatch(item, dx, dy)])));
124
+ },
125
+ onEnd(commit) {
126
+ setGuides([]);
127
+ if (begun) store.endSketchDrag(commit);
128
+ },
129
+ });
130
+ };
131
+
132
+ // ── RESIZE ────────────────────────────────────────────────────────────
133
+ const resizeGesture = (event: ReactPointerEvent<HTMLElement>, item: SketchItem3D, handle: string): void => {
134
+ const scale = scaleNow();
135
+ const start = itemRect(item);
136
+ const picture = item.kind === "ref" && (refKind(doc, item) === "image" || refKind(doc, item) === "video");
137
+ let begun = false;
138
+ startDrag(event, {
139
+ onMove(ev) {
140
+ const dx = (ev.clientX - event.clientX) / scale;
141
+ const dy = (ev.clientY - event.clientY) / scale;
142
+ if (!begun) {
143
+ begun = true;
144
+ store.beginSketchDrag();
145
+ }
146
+ let patch: SketchItemPatch;
147
+ if (item.kind === "arrow") {
148
+ const end = handle === "from" ? item.from : item.to;
149
+ const at: [number, number] = [Math.round(end[0] + dx), Math.round(end[1] + dy)];
150
+ patch = handle === "from" ? { from: at } : { to: at };
151
+ } else {
152
+ const keep = picture !== ev.shiftKey;
153
+ const next = resizedRect(start, RESIZE_HANDLES[handle]!, dx, dy, keep);
154
+ patch = { position: [Math.round(next.x), Math.round(next.y)], size: [next.w, next.h] };
155
+ }
156
+ store.dragSketchItems({ [item.id]: patch });
157
+ },
158
+ onEnd(commit) {
159
+ if (begun) store.endSketchDrag(commit);
160
+ },
161
+ });
162
+ };
163
+
164
+ // ── MARQUEE ───────────────────────────────────────────────────────────
165
+ const marqueeGesture = (event: ReactPointerEvent<HTMLElement>, shift: boolean): void => {
166
+ const from = pointAt(event.clientX, event.clientY);
167
+ const add = shift ? selection : [];
168
+ let moved = false;
169
+ startDrag(event, {
170
+ onMove(ev) {
171
+ if (!moved && Math.hypot(ev.clientX - event.clientX, ev.clientY - event.clientY) < DRAG_SLOP_PX) return;
172
+ moved = true;
173
+ const to = pointAt(ev.clientX, ev.clientY);
174
+ const rect = rectBetween({ x: from[0], y: from[1] }, { x: to[0], y: to[1] });
175
+ setMarquee(rect);
176
+ store.setSketchSelection(marqueeItems(items, rect, add));
177
+ },
178
+ onEnd() {
179
+ setMarquee(null);
180
+ // A CLICK ON THE PAPER clears the selection; with Shift it keeps it.
181
+ if (!moved && !shift) store.setSketchSelection([]);
182
+ },
183
+ });
184
+ };
185
+
186
+ // ── DRAW ──────────────────────────────────────────────────────────────
187
+ const drawGesture = (event: ReactPointerEvent<HTMLElement>, kind: "box" | "text" | "arrow"): void => {
188
+ const from = pointAt(event.clientX, event.clientY);
189
+ let to = from;
190
+ startDrag(event, {
191
+ onMove(ev) {
192
+ to = pointAt(ev.clientX, ev.clientY);
193
+ setDraft(kind === "arrow" ? { kind, from, to } : { kind, rect: rectBetween({ x: from[0], y: from[1] }, { x: to[0], y: to[1] }) });
194
+ },
195
+ onEnd(commit) {
196
+ setDraft(null);
197
+ if (!commit) return;
198
+ const made = drawnItem(kind, from, to);
199
+ const ids = store.addSketchItems([made]);
200
+ store.setSketchTool("select");
201
+ // New words are typed straight away.
202
+ if (kind === "text" && ids[0] !== undefined) props.onEdit(ids[0]);
203
+ },
204
+ });
205
+ };
206
+
207
+ const onPointerDown = (event: ReactPointerEvent<HTMLDivElement>): void => {
208
+ if (event.button !== 0) return;
209
+ pressing.current = true;
210
+ const target = event.target as Element;
211
+ const handle = target.closest<HTMLElement>("[data-handle]")?.dataset["handle"];
212
+ const id = target.closest<HTMLElement | SVGElement>("[data-item]")?.getAttribute("data-item") ?? null;
213
+ if (!readOnly && (tool === "box" || tool === "text" || tool === "arrow")) {
214
+ event.preventDefault();
215
+ drawGesture(event, tool);
216
+ return;
217
+ }
218
+ if (handle !== undefined && one !== undefined && !readOnly) {
219
+ event.preventDefault();
220
+ resizeGesture(event, one, handle);
221
+ return;
222
+ }
223
+ if (id !== null) {
224
+ // SHIFT TOGGLES the item; a plain press on an unselected item selects
225
+ // it alone, on a selected one keeps the group so it moves together.
226
+ let ids: readonly string[];
227
+ if (event.shiftKey) ids = selected.has(id) ? selection.filter((s) => s !== id) : [...selection, id];
228
+ else ids = selected.has(id) ? selection : [id];
229
+ store.setSketchSelection(ids);
230
+ if (!readOnly && ids.includes(id)) moveGesture(event, ids);
231
+ return;
232
+ }
233
+ marqueeGesture(event, event.shiftKey);
234
+ };
235
+
236
+ // ── DROP ──────────────────────────────────────────────────────────────
237
+ const takes = (event: ReactDragEvent): boolean => {
238
+ const types = Array.from(event.dataTransfer.types);
239
+ return !readOnly && (types.includes("Files") || types.includes(ASSET_MIME) || types.includes(STORYBOARD_MIME));
240
+ };
241
+ const onDragOver = (event: ReactDragEvent<HTMLDivElement>): void => {
242
+ if (!takes(event)) return;
243
+ event.preventDefault();
244
+ event.dataTransfer.dropEffect = "copy";
245
+ setDrop(pointAt(event.clientX, event.clientY));
246
+ };
247
+ const onDrop = (event: ReactDragEvent<HTMLDivElement>): void => {
248
+ setDrop(null);
249
+ if (!takes(event)) return;
250
+ const at = pointAt(event.clientX, event.clientY);
251
+ const files = Array.from(event.dataTransfer.files ?? []);
252
+ const asset = readAssetDrag(event.dataTransfer);
253
+ const card = readDragPayload(event.dataTransfer);
254
+ // ONLY WHAT LANDS IS TAKEN: a board drag's payload reads only at the drop,
255
+ // and of those only a card lands here.
256
+ if (files.length === 0 && asset === null && card?.kind !== "reference") return;
257
+ event.preventDefault();
258
+ if (files.length > 0) void store.addSketchItemsFromFiles(files, at);
259
+ else if (asset !== null) void store.addSketchItemFromAsset(asset.asset, at);
260
+ else if (card?.kind === "reference") void store.addSketchItemFromCard(card.id, at);
261
+ };
262
+
263
+ const onDoubleClick = (event: ReactMouseEvent): void => {
264
+ const id = (event.target as Element).closest("[data-item]")?.getAttribute("data-item");
265
+ const item = items.find((i) => i.id === id);
266
+ if (item?.kind === "text" && !readOnly) props.onEdit(item.id);
267
+ };
268
+
269
+ const onFocus = (event: ReactFocusEvent): void => {
270
+ // A KEYBOARD FOCUS selects the item, as a press does; a press already did.
271
+ const id = (event.target as Element).getAttribute("data-item");
272
+ if (id !== null && !pressing.current && !selected.has(id)) store.setSketchSelection([id]);
273
+ };
274
+
275
+ return (
276
+ <div
277
+ ref={setPaper}
278
+ className="sketch-paper"
279
+ data-ground={sketch?.ground ?? "paper"}
280
+ data-tool={tool}
281
+ data-testid="sketch-paper"
282
+ style={{ width, aspectRatio: `${frame.width} / ${frame.height}` }}
283
+ onPointerDown={onPointerDown}
284
+ onPointerUp={() => {
285
+ pressing.current = false;
286
+ }}
287
+ onDoubleClick={onDoubleClick}
288
+ onFocus={onFocus}
289
+ onDragOver={onDragOver}
290
+ onDragLeave={(event) => {
291
+ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) setDrop(null);
292
+ }}
293
+ onDrop={onDrop}
294
+ >
295
+ {items.map((item) => (
296
+ <SketchItemView
297
+ key={item.id}
298
+ doc={doc}
299
+ item={item}
300
+ paperPx={width}
301
+ selected={selected.has(item.id)}
302
+ editing={props.editing === item.id}
303
+ onEditEnd={(text) => {
304
+ props.onEdit(null);
305
+ const trimmed = text.trim();
306
+ // AN EMPTIED TEXT GOES: a text item is never stored empty.
307
+ if (trimmed === "") store.removeSketchItems([item.id]);
308
+ else if (item.kind === "text" && trimmed !== item.text) store.updateSketchItem(item.id, { text: trimmed });
309
+ }}
310
+ />
311
+ ))}
312
+ {items.length === 0 && draft === null ? <EmptySketch /> : null}
313
+ {items.filter((item) => selected.has(item.id)).map((item) => (
314
+ <span key={item.id} className="sketch-selection" style={rectStyle(doc, itemRect(item))} aria-hidden="true" />
315
+ ))}
316
+ {one !== undefined && !readOnly && props.editing === null ? <Handles doc={doc} item={one} /> : null}
317
+ {guides.map((guide) => (
318
+ <span
319
+ key={`${guide.axis}${guide.at}`}
320
+ className={`sketch-guide is-${guide.axis}`}
321
+ aria-hidden="true"
322
+ style={guide.axis === "x" ? { left: `${(guide.at / frame.width + 0.5) * 100}%` } : { top: `${(guide.at / frame.height + 0.5) * 100}%` }}
323
+ />
324
+ ))}
325
+ {marquee === null ? null : <span className="sketch-marquee" data-testid="sketch-marquee" aria-hidden="true" style={marqueeStyle(doc, marquee)} />}
326
+ {draft === null ? null : <DraftView doc={doc} draft={draft} />}
327
+ {drop === null ? null : (
328
+ <span className="sketch-drop" aria-hidden="true" style={{ left: `${(drop[0] / frame.width + 0.5) * 100}%`, top: `${(drop[1] / frame.height + 0.5) * 100}%` }}>
329
+ {kindIcon("image")}
330
+ <span className="sketch-drop-chip">{`Drop to add · creates a card pinned to ${number}`}</span>
331
+ </span>
332
+ )}
333
+ {sketch === undefined ? null : <BoardCursors canvas={paper} where={sketch.id} />}
334
+ </div>
335
+ );
336
+ }
337
+
338
+ /** A box as `snapMove` reads one. */
339
+ function snapBox(item: SketchItem3D): { position: [number, number, number]; size: [number, number] } {
340
+ const r = itemRect(item);
341
+ return { position: [r.x, r.y, 0], size: [r.w, r.h] };
342
+ }
343
+
344
+ function refKind(doc: Doc3D, item: Extract<SketchItem3D, { kind: "ref" }>): string | undefined {
345
+ const card = doc.resources?.references?.find((r) => r.id === item.ref);
346
+ return card?.type === "asset" ? doc.assets[card.asset]?.kind : card?.type;
347
+ }
348
+
349
+ const rectStyle = (doc: Doc3D, r: SketchRect): CSSProperties => boxStyle(doc, [r.x, r.y], [r.w, r.h]);
350
+ const marqueeStyle = (doc: Doc3D, m: MarqueeRect): CSSProperties =>
351
+ boxStyle(doc, [(m.left + m.right) / 2, (m.top + m.bottom) / 2], [m.right - m.left, m.bottom - m.top]);
352
+
353
+ /** What a finished draw adds: the dragged box, else a default one centred on
354
+ * the click. */
355
+ function drawnItem(kind: "box" | "text" | "arrow", from: [number, number], to: [number, number]): NewSketchItem {
356
+ if (kind === "arrow") {
357
+ const long = Math.hypot(to[0] - from[0], to[1] - from[1]) >= MIN_DRAW;
358
+ return { kind: "arrow", from, to: long ? to : [from[0] + DEFAULT_ARROW, from[1]] };
359
+ }
360
+ const w = Math.abs(to[0] - from[0]);
361
+ const h = Math.abs(to[1] - from[1]);
362
+ const dragged = w >= MIN_DRAW && h >= MIN_DRAW;
363
+ const size: [number, number] = dragged ? [w, h] : [...(kind === "box" ? DEFAULT_BOX : DEFAULT_TEXT)];
364
+ // Whole design px: an odd width would put the centre on a half pixel.
365
+ const position: [number, number] = dragged ? [Math.round((from[0] + to[0]) / 2), Math.round((from[1] + to[1]) / 2)] : from;
366
+ return kind === "box" ? { kind: "box", position, size } : { kind: "text", text: "Text", role: "title", position, size };
367
+ }
368
+
369
+ /** The eight handles round one box, or a handle on each end of an arrow. */
370
+ function Handles({ doc, item }: { doc: Doc3D; item: SketchItem3D }) {
371
+ if (item.kind === "arrow") {
372
+ return (
373
+ <>
374
+ {(["from", "to"] as const).map((end) => (
375
+ <span
376
+ key={end}
377
+ className="sketch-handle is-end"
378
+ data-handle={end}
379
+ data-testid={`sketch-handle-${end}`}
380
+ aria-hidden="true"
381
+ style={{ left: `${(item[end][0] / doc.size.width + 0.5) * 100}%`, top: `${(item[end][1] / doc.size.height + 0.5) * 100}%` }}
382
+ />
383
+ ))}
384
+ </>
385
+ );
386
+ }
387
+ return (
388
+ <span className="sketch-handles" style={rectStyle(doc, itemRect(item))} aria-hidden="true">
389
+ {HANDLES.map((h) => (
390
+ <span key={h} className={`sketch-handle is-${h}`} data-handle={h} data-testid={`sketch-handle-${h}`} />
391
+ ))}
392
+ </span>
393
+ );
394
+ }
395
+
396
+ function DraftView({ doc, draft }: { doc: Doc3D; draft: Draft }) {
397
+ if (draft.kind === "arrow") {
398
+ const { width: w, height: h } = doc.size;
399
+ return (
400
+ <svg className="sketch-draft is-arrow" viewBox={`${-w / 2} ${-h / 2} ${w} ${h}`} preserveAspectRatio="none" aria-hidden="true">
401
+ <path d={`M ${draft.from[0]} ${draft.from[1]} L ${draft.to[0]} ${draft.to[1]}`} />
402
+ </svg>
403
+ );
404
+ }
405
+ return <span className={`sketch-draft is-${draft.kind}`} style={marqueeStyle(doc, draft.rect)} aria-hidden="true" />;
406
+ }
407
+
408
+ /** SB2's three starts, on an empty sketch. */
409
+ function EmptySketch() {
410
+ const store = useStore();
411
+ const readOnly = useShotEditor((s) => s.readOnly);
412
+ const start = (label: string, icon: ReactNode, tool: "image" | "component" | "box", key?: string): ReactNode => (
413
+ <button
414
+ type="button"
415
+ className="sketch-start"
416
+ data-testid={`sketch-start-${tool}`}
417
+ disabled={readOnly}
418
+ onPointerDown={(event) => event.stopPropagation()}
419
+ onClick={() => store.setSketchTool(tool)}
420
+ >
421
+ <span className="sketch-start-icon" aria-hidden="true">
422
+ {icon}
423
+ </span>
424
+ <span className="sketch-start-label">
425
+ {label}
426
+ {key === undefined ? null : <kbd className="sketch-start-key">{key}</kbd>}
427
+ </span>
428
+ </button>
429
+ );
430
+ return (
431
+ <div className="sketch-empty" data-testid="sketch-empty">
432
+ <div className="sketch-empty-copy">
433
+ <h3 className="sketch-empty-title">Sketch the shot</h3>
434
+ <p className="sketch-empty-lead">Place what we see, roughly. The agent and the Studio read it.</p>
435
+ </div>
436
+ <div className="sketch-empty-starts">
437
+ {start("Drop images", kindIcon("image"), "image")}
438
+ {start("Add a component", COMPONENT_ICON, "component")}
439
+ {start("Draw a box", kindIcon("rect"), "box", "R")}
440
+ </div>
441
+ </div>
442
+ );
443
+ }