@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
@@ -2,12 +2,13 @@
2
2
  // kind-specific section here follows.
3
3
  //
4
4
  // ROWS COME FROM THE CONTRACT, NEVER FROM THE STORED VALUES. The widget for a
5
- // prop is decided by its ControlSpec through the shared `PropControl` cluster,
6
- // the same dispatch the motion editor's InstanceSection and the canvas editor's
7
- // EmbedSection use — so a colour prop gets a swatch and an enum gets its
8
- // options here for exactly the reason it does there. Deriving rows from
9
- // `instance.props` instead would show only props somebody had already changed,
10
- // with no kinds, no ranges and no labels.
5
+ // prop is decided by its ControlSpec through the shared `PropRow`, the one row
6
+ // every ControlSpec surface renders (a definition's published props, a code
7
+ // component's defaults, one use of a component on the board or in Components),
8
+ // so a colour prop gets the colour field and an enum its words here for exactly
9
+ // the reason it does there. Deriving rows from `instance.props` instead would
10
+ // show only props somebody had already changed, with no kinds, no ranges and
11
+ // no labels.
11
12
  //
12
13
  // WHICH contract: the LIVE one from the registry when the project still has the
13
14
  // component, falling back to the `{source:"snapshot"}` contract the document
@@ -18,7 +19,7 @@
18
19
  // ── QUARANTINE, WHICH IS THE POINT OF THE WHOLE SECTION ─────────────────
19
20
  //
20
21
  // A stored prop whose contract key has gone is NOT dropped and NOT hidden. The
21
- // rename/orphan planner (`instancePropPlan`, which is
22
+ // Accept plan (`planInstanceAccept`, whose rename/orphan planner is
22
23
  // `resolveTracksAgainstContract` underneath) classifies it, and it gets its own
23
24
  // row above the contract rows: the value it is holding, what it was keyed on,
24
25
  // and the two things an author can do about it. Both documented real-world
@@ -31,59 +32,28 @@
31
32
  // default and stores nothing; `buildProps` supplies it at render time. Baking
32
33
  // defaults in at insert time would freeze them against later edits to the
33
34
  // component itself.
34
- import { useEffect, useState } from "react";
35
+ import { useEffect, useMemo, useState } from "react";
35
36
  import {
36
- instanceContractDrift,
37
- instancePropPlan,
38
- orphanedPropKeys,
37
+ INSTANCE_PROP_PREFIX,
38
+ acceptStepWords,
39
+ findLayer,
40
+ frozenDefaultWords,
41
+ frozenDefaultsOf,
42
+ planInstanceAccept,
39
43
  } from "@dailies-studio/shot/doc";
44
+ import type { FrozenDefault, InstanceAcceptPlan } from "@dailies-studio/shot/doc";
45
+ import { controlLabel, orderSpecs } from "@dailies-studio/contract";
40
46
  import type { Contract, ControlSpec } from "@dailies-studio/contract";
41
47
  import type { Value } from "@dailies-studio/engine";
42
- import { InspectorRow, InspectorSection, NumberField, PropControl } from "@dailies-studio/ui";
48
+ import { InspectorRow, InspectorSection, NumberField, PropRow, useContextMenu, useLocked } from "@dailies-studio/ui";
49
+ import type { PropControlAsset, PropRowReset } from "@dailies-studio/ui";
43
50
  import { useShotEditor, useStore } from "../../../state/context.js";
44
- import { NO_SCRUB, useSelectedLayers } from "../useDocWrite.js";
45
-
46
- /** Contract kinds whose values are engine `Value`s and can therefore live in
47
- * `instance.props`. Everything else is composite (curve/gradient — those ride
48
- * an encoded string, which IS a Value) or not a value at all (slot/event). */
49
- const VALUE_KINDS = new Set([
50
- "number",
51
- "string",
52
- "color",
53
- "boolean",
54
- "enum",
55
- "asset",
56
- "vec2",
57
- "vec3",
58
- "quaternion",
59
- "curve",
60
- "gradient",
61
- ]);
62
-
63
- /** Contract order, then key — the motion inspector's `orderedSpecs` rule. */
64
- function orderedSpecs(contract: Contract): ControlSpec[] {
65
- return Object.values(contract.keys).sort((a, b) => {
66
- const ao = a.order ?? Number.MAX_SAFE_INTEGER;
67
- const bo = b.order ?? Number.MAX_SAFE_INTEGER;
68
- return ao !== bo ? ao - bo : a.key.localeCompare(b.key);
69
- });
70
- }
71
-
72
- /** PropControl hands back whatever its widget produced; only engine Values may
73
- * be stored. Anything else is refused rather than written, because the next
74
- * save would refuse the document. */
75
- function asValue(raw: unknown): Value | null {
76
- if (typeof raw === "number" && Number.isFinite(raw)) return raw;
77
- if (typeof raw === "string" || typeof raw === "boolean") return raw;
78
- if (
79
- Array.isArray(raw) &&
80
- (raw.length === 2 || raw.length === 3 || raw.length === 4) &&
81
- raw.every((n) => typeof n === "number" && Number.isFinite(n))
82
- ) {
83
- return raw as Value;
84
- }
85
- return null;
86
- }
51
+ import { instanceDriftOf, instanceRegistryContract, withInstanceClockOffset } from "../../../state/store.js";
52
+ import { sameUseValue } from "../../../state/workspace.js";
53
+ import { useExposeOffered } from "../ExposeDot.js";
54
+ import { KeyframeDot } from "../KeyframeDot.js";
55
+ import { coercePropValue, isVectorSpec, outsideRangeOf, projectAssetsOf, propEditOf, shownPropValue } from "../propValue.js";
56
+ import { useSelectedLayers, useSelectionScrub } from "../useDocWrite.js";
87
57
 
88
58
  export function InstanceSection() {
89
59
  const store = useStore();
@@ -93,14 +63,35 @@ export function InstanceSection() {
93
63
  const libraryLoaded = useShotEditor((s) => s.libraryLoaded);
94
64
  const libraryError = useShotEditor((s) => s.libraryError);
95
65
  const layers = useSelectedLayers();
66
+ const failures = useShotEditor((s) => s.stagePieceFailures);
96
67
  const [rebinding, setRebinding] = useState(false);
68
+ // Also the refusal of a Replace, which binds a contract the same way.
97
69
  const [rebindError, setRebindError] = useState<string | null>(null);
70
+ const replaceMenu = useContextMenu();
71
+ // A viewer's panel: the verbs below write, so they are disabled with it, as
72
+ // every widget under the lock is. Open code only opens, and stays live.
73
+ const locked = useLocked();
74
+ // The Project's assets, which an asset prop picks from. Memoised on the
75
+ // asset table: the section repaints on every document identity change, and
76
+ // the table itself changes only on an upload.
77
+ const assetTable = doc?.assets;
78
+ const assets = useMemo(() => projectAssetsOf(assetTable), [assetTable]);
98
79
 
99
80
  // ONE layer at a time, deliberately. An instance's props belong to one
100
81
  // component's contract, so a fan-out across a mixed selection would write
101
82
  // keys the other layers' components never had.
102
83
  const layer = layers.length === 1 && layers[0]?.kind === "instance" ? layers[0] : null;
103
84
  const componentId = layer?.instance.component ?? null;
85
+ const layerId = layer?.id ?? null;
86
+
87
+ // The Clock field's DRAG half: a drag moves the module's clock while the
88
+ // pointer is down, one undo entry, rather than waiting for the release.
89
+ // Guarded on the id, because the seam fans out over the live selection.
90
+ const clockScrub = useSelectionScrub((l, raw) =>
91
+ l.kind === "instance" && l.id === layerId && typeof raw === "number" && Number.isFinite(raw)
92
+ ? { ...l, instance: withInstanceClockOffset(l.instance, raw) }
93
+ : l,
94
+ );
104
95
 
105
96
  // Hooks stay unconditional (the section mounts for every selection and
106
97
  // self-gates on kind).
@@ -113,57 +104,156 @@ export function InstanceSection() {
113
104
  if (doc === null || layer === null || componentId === null) return null;
114
105
 
115
106
  const row = library.find((r) => r.id === componentId);
107
+ const loadError = failures.find((f) => f.kind === "instance" && f.layerId === layer.id);
116
108
  const ref = layer.instance.contract;
117
109
  const snapshot: Contract | null = ref?.source === "snapshot" ? ref.contract : null;
118
110
  const live = contracts[componentId];
119
111
  const contract = live ?? snapshot;
120
- // Judged only once the registry has ANSWERED — and "answered" means a
121
- // successful fetch, not merely "loading finished". A FAILED fetch also ends
122
- // with an empty list, and reporting every instance as an unknown component
123
- // over a transient network failure would tell the author to delete work.
124
- const drift = libraryLoaded
125
- ? instanceContractDrift(doc, (id) => {
126
- const r = library.find((c) => c.id === id);
127
- const c = contracts[id];
128
- return r === undefined || c === undefined
129
- ? null
130
- : { availability: r.contractAvailability ?? "inferred", contract: c };
131
- }).filter((d) => d.layerId === layer.id)
132
- : [];
112
+ const drift = instanceDriftOf({ doc, library, libraryLoaded, contracts }, layer.id);
133
113
  const missing = libraryLoaded && row === undefined;
134
114
 
135
- const specs = contract === null || contract === undefined ? [] : orderedSpecs(contract);
136
- // The quarantine list. Against the LIVE contract when there is one: a key is
137
- // only orphaned relative to what the component accepts today.
138
- const orphans =
139
- live === undefined ? [] : orphanedPropKeys(instancePropPlan(layer.instance.props, snapshot ?? live, live));
115
+ const specs = contract === null || contract === undefined ? [] : orderSpecs(contract);
116
+ // THE ACCEPT PLAN, ONE for the drift notice and the quarantine list, so the
117
+ // notice says what the press does and the list is exactly what it sets
118
+ // aside. Planned on the STORED layer: `layer` is painted at the playhead,
119
+ // so an animated prop's value there is a sample, and a plan from it would
120
+ // describe a write the store does not make. Against the LIVE contract only:
121
+ // a key is orphaned relative to what the component accepts today. The
122
+ // registry row only says how Accept would bind, which no step depends on,
123
+ // so a list that has not loaded (or failed) still shows the quarantine,
124
+ // with its Discard. A malformed contract throws, and then there is no plan.
125
+ const registry =
126
+ instanceRegistryContract({ library, contracts }, componentId) ??
127
+ (live === undefined ? null : { availability: "inferred" as const, contract: live });
128
+ const stored = findLayer(doc, layer.id)?.layer;
129
+ let plan: InstanceAcceptPlan | null = null;
130
+ if (registry !== null && stored?.kind === "instance" && stored.id === layer.id) {
131
+ try {
132
+ plan = planInstanceAccept(stored, registry, null);
133
+ } catch {
134
+ plan = null;
135
+ }
136
+ }
137
+ // WHAT THE STAGE RENDERS for each unset prop of a snapshot-bound layer: the
138
+ // FROZEN default, not the live one the rows would otherwise show. Shown on
139
+ // the row and named in the drift notice, so a picture that differs from
140
+ // the component's current look says why.
141
+ const frozen: FrozenDefault[] =
142
+ registry !== null && stored?.kind === "instance" && stored.id === layer.id ? frozenDefaultsOf(stored, registry) : [];
143
+ const orphans = (plan?.steps ?? []).flatMap((step) =>
144
+ step.op === "keep-aside" && step.value ? [{ key: step.key, reason: step.reason }] : [],
145
+ );
140
146
 
141
147
  return (
142
- <InspectorSection title="Component" persistKey="shot:instance">
143
- <InspectorRow label="Module">
144
- <span className="insp-fixed" title={componentId}>
145
- {row?.name ?? componentId}
146
- </span>
148
+ <InspectorSection className="insp-object" title="Component" persistKey="shot:instance">
149
+ {/* THE MODULE, and for a PROJECT component it is a door. A builtin ships
150
+ inside a package and has no file to open, and a component the registry
151
+ has lost has nothing to open either, so both stay plain text — the
152
+ panel's rule for a verb this subject cannot do at all.
153
+
154
+ The plain-text arm is a READ-ONLY VALUE, so the row is the house
155
+ readout (`insp-readout`), its value the `.insp-dim` span, with
156
+ `.is-text` when it has a NAME to show: a display name set in the
157
+ numeral font reads as a measurement. It said `insp-fixed` for a long
158
+ time, a class with no rule anywhere in the repo, so the row had no
159
+ face at all. */}
160
+ <InspectorRow label="Module" {...(row === undefined || row.builtin === true ? { className: "insp-readout" } : {})}>
161
+ {row === undefined || row.builtin === true ? (
162
+ <span className={row?.name === undefined ? "insp-dim" : "insp-dim is-text"} title={componentId}>
163
+ {row?.name ?? componentId}
164
+ </span>
165
+ ) : (
166
+ <button
167
+ type="button"
168
+ className="link-button"
169
+ data-testid="instance-open-code"
170
+ title={`Open ${row.rel}`}
171
+ onClick={() => void store.openCodeComponent(componentId)}
172
+ >
173
+ {row.name}
174
+ </button>
175
+ )}
147
176
  </InspectorRow>
148
177
 
178
+ {/* THE LOAD OR DRAW ERROR (F50). The stage draws a dashed placeholder where this
179
+ layer would be and reports it as a piece failure; this row names the
180
+ error and offers the three ways out. Replace picks another component
181
+ and swaps it in place, one undo step (`replaceInstanceComponent`), so
182
+ the layer keeps its place, timing and props. Remove deletes THIS layer
183
+ by id, never whatever block or key has focus. */}
184
+ {loadError !== undefined && (
185
+ <div className="insp-inline-error" data-testid="instance-load-error">
186
+ <span>{`This component failed to draw: ${loadError.message}`}</span>
187
+ <div className="insp-buttons">
188
+ {row === undefined || row.builtin === true ? null : (
189
+ <button
190
+ type="button"
191
+ className="btn"
192
+ data-testid="instance-error-open-code"
193
+ onClick={() => void store.openCodeComponent(componentId)}
194
+ >
195
+ Open code
196
+ </button>
197
+ )}
198
+ <button
199
+ type="button"
200
+ className="btn"
201
+ data-testid="instance-replace"
202
+ title="Swap this layer's component for another"
203
+ disabled={locked}
204
+ onClick={(e) => {
205
+ const at = e.currentTarget.getBoundingClientRect();
206
+ const others = library.filter((r) => r.host === "three" && r.id !== componentId);
207
+ replaceMenu.open(
208
+ { x: at.left, y: at.bottom },
209
+ others.length === 0
210
+ ? [{ kind: "action", id: "none", label: "No other components to use", disabled: true, onSelect() {} }]
211
+ : others.map((r) => ({
212
+ kind: "action",
213
+ id: r.id,
214
+ label: r.name,
215
+ detail: r.rel,
216
+ onSelect() {
217
+ void store.replaceInstanceComponent(layer.id, r.id).then(setRebindError);
218
+ },
219
+ })),
220
+ { search: "Find a component" },
221
+ );
222
+ }}
223
+ >
224
+ Replace
225
+ </button>
226
+ <button type="button" className="btn" title="Delete this layer" disabled={locked} onClick={() => store.removeLayer(layer.id)}>
227
+ Remove
228
+ </button>
229
+ </div>
230
+ {replaceMenu.menu}
231
+ </div>
232
+ )}
233
+
149
234
  {/* mo1's "Play" on an instance, and the reason it is a NUMBER rather than
150
235
  a button: retriggering a module is an offset, not state, so it stays a
151
236
  pure function of the frame and an out-of-order export samples the same
152
237
  pose a scrub does. Negative holds the module at its own frame 0 until
153
- the scene catches up. */}
154
- <InspectorRow label="Clock">
238
+ the scene catches up. Its name starts with the word in its box, so
239
+ the Named label over the box and the Compact prefix in it agree. */}
240
+ <InspectorRow>
155
241
  <NumberField
156
242
  value={layer.instance.clockOffsetFrames ?? 0}
157
- ariaLabel="Instance clock offset"
243
+ label="Clock"
244
+ ariaLabel="Clock offset"
158
245
  unit="f"
159
246
  step={1}
160
247
  onCommit={(next) => store.setInstanceClockOffset(layer.id, next)}
248
+ onScrubStart={clockScrub.start}
249
+ onScrub={clockScrub.move}
250
+ onScrubEnd={clockScrub.end}
161
251
  />
162
252
  </InspectorRow>
163
253
 
164
254
  {libraryError !== null && (
165
255
  <div className="insp-inline-error">
166
- {`The component registry could not be read (${libraryError}) — this instance cannot be checked.`}
256
+ {`The component registry could not be read (${libraryError}), so this instance cannot be checked.`}
167
257
  </div>
168
258
  )}
169
259
 
@@ -177,49 +267,78 @@ export function InstanceSection() {
177
267
  {drift.length > 0 && (
178
268
  <div className="insp-notice is-warn">
179
269
  {drift.map((d) => (
180
- <span key={d.code}>{d.message}</span>
270
+ <span key={d.code} className="insp-notice-line">
271
+ {d.message}
272
+ </span>
181
273
  ))}
274
+ {/* What the press will do, BEFORE it: the same plan the store
275
+ writes and `dailies validate` prints, in the same words. */}
276
+ {frozen.length > 0 && (
277
+ <span className="insp-notice-line" data-instance-frozen={layer.id}>
278
+ {`On screen now this layer ${frozen.map(frozenDefaultWords).join("; ")}.`}
279
+ </span>
280
+ )}
281
+ {plan !== null && (
282
+ <span className="insp-notice-line" data-instance-accept-steps={layer.id}>
283
+ {plan.steps.length > 0
284
+ ? `Accepting will: ${plan.steps.map(acceptStepWords).join("; ")}.`
285
+ : frozen.length > 0
286
+ ? "Accepting renders the live defaults."
287
+ : "Accepting only records the current contract."}
288
+ </span>
289
+ )}
182
290
  {/* The ONE action drift offers, and it is never automatic. Re-binding
183
291
  is a statement that the component's CURRENT contract is the one
184
292
  this instance means; the planner moves every renamed key's value
185
- and leaves every orphan exactly where it is. */}
186
- <button
187
- className="btn"
188
- disabled={rebinding}
189
- data-instance-rebind={layer.id}
190
- title="Re-record this instance's contract hash and source from the project's current component, following any renames"
191
- onClick={() => {
192
- setRebinding(true);
193
- void store.rebindInstance(layer.id).then((err) => {
194
- setRebinding(false);
195
- setRebindError(err);
196
- });
197
- }}
198
- >
199
- Accept current contract
200
- </button>
293
+ and keyframes, every value outside a range comes back inside it,
294
+ and every orphan's value stays exactly where it is. */}
295
+ <div className="insp-buttons">
296
+ <button
297
+ className="btn"
298
+ disabled={rebinding || locked}
299
+ data-instance-rebind={layer.id}
300
+ title="Re-record this instance's contract hash and source from the project's current component, following any renames and bringing values back inside its ranges"
301
+ onClick={() => {
302
+ setRebinding(true);
303
+ void store.rebindInstance(layer.id).then((err) => {
304
+ setRebinding(false);
305
+ setRebindError(err);
306
+ });
307
+ }}
308
+ >
309
+ Accept current contract
310
+ </button>
311
+ </div>
201
312
  </div>
202
313
  )}
203
314
  {rebindError !== null && <div className="insp-inline-error">{rebindError}</div>}
204
315
 
205
- {orphans.map((key) => (
316
+ {orphans.map(({ key, reason }) => (
206
317
  <div key={`orphan:${key}`} className="insp-notice is-warn" data-instance-orphan={key}>
207
- {`"${key}" is not a prop of this component any more. Its value is kept here and is not ` +
208
- `passed to the component — nothing was deleted.`}
209
- <button
210
- className="btn"
211
- data-instance-drop={key}
212
- title={`Delete the stored value for "${key}". This is the only thing that ever removes it.`}
213
- onClick={() => store.clearInstanceProp(layer.id, key)}
214
- >
215
- Discard
216
- </button>
318
+ {/* Never "nothing was deleted": Accept deletes such a key's
319
+ keyframes. The value is what this row keeps. */}
320
+ {reason === "gone"
321
+ ? `"${key}" is not a prop of this component any more. Its value is kept here and is not ` +
322
+ `passed to the component.`
323
+ : `"${key}" now takes a different kind of value. The value stored here is of the old kind, ` +
324
+ `and the component is still handed it. Discard it to use the default.`}
325
+ <div className="insp-buttons">
326
+ <button
327
+ className="btn"
328
+ data-instance-drop={key}
329
+ disabled={locked}
330
+ title={`Delete the stored value for "${key}". This is the only thing that ever removes it.`}
331
+ onClick={() => store.clearInstanceProp(layer.id, key)}
332
+ >
333
+ Discard
334
+ </button>
335
+ </div>
217
336
  </div>
218
337
  ))}
219
338
 
220
339
  {contract === null || contract === undefined ? (
221
340
  <div className="insp-notice">
222
- No contract available for this component — its props cannot be edited here.
341
+ No contract available for this component, so its props cannot be edited here.
223
342
  </div>
224
343
  ) : (
225
344
  <>
@@ -236,6 +355,9 @@ export function InstanceSection() {
236
355
  layerId={layer.id}
237
356
  spec={spec}
238
357
  value={layer.instance.props[spec.key]}
358
+ frozen={frozen.find((f) => f.key === spec.key)}
359
+ animated={layer.tracks?.[`${INSTANCE_PROP_PREFIX}${spec.key}`] !== undefined}
360
+ assets={assets}
239
361
  />
240
362
  ))}
241
363
  </>
@@ -244,59 +366,156 @@ export function InstanceSection() {
244
366
  );
245
367
  }
246
368
 
247
- function InstancePropRow(props: { layerId: string; spec: ControlSpec; value: Value | undefined }) {
248
- const { layerId, spec } = props;
369
+ /**
370
+ * One prop of one placed instance: the row, its diamond, its Reset.
371
+ *
372
+ * `value` is the layer AS PAINTED at the playhead (`useSelectedLayers`), so an
373
+ * animated prop shows its sampled value, and a typed one keys at the playhead
374
+ * (`patchInstance` routes the edit).
375
+ */
376
+ function InstancePropRow(props: {
377
+ layerId: string;
378
+ spec: ControlSpec;
379
+ value: Value | undefined;
380
+ /** The frozen default the stage renders for this unset prop, when it is
381
+ * not the live one (`frozenDefaultsOf`). */
382
+ frozen?: FrozenDefault | undefined;
383
+ /** The prop carries keyframes (`instance.props.<key>` has a track). */
384
+ animated: boolean;
385
+ assets: readonly PropControlAsset[];
386
+ }) {
387
+ const { layerId, spec, animated } = props;
249
388
  const store = useStore();
389
+ const locked = useLocked();
250
390
  const [error, setError] = useState<string | null>(null);
251
- // An unset prop shows (and edits from) the spec's default — the value the
252
- // runtime's `buildProps` will supply — without ever storing it. `slot` and
253
- // `event` carry no default at all, hence the presence check.
254
- const value = props.value ?? ("default" in spec ? spec.default : undefined);
255
- const storable = VALUE_KINDS.has(spec.kind);
256
- const inherited = props.value === undefined;
391
+ const label = controlLabel(spec);
392
+ const path = `${INSTANCE_PROP_PREFIX}${spec.key}`;
393
+ const vector = isVectorSpec(spec);
394
+ const pinOffered = useExposeOffered(path);
395
+ // An unset prop shows (and edits from) the default the runtime's
396
+ // `buildProps` will supply, without ever storing it: the FROZEN one while
397
+ // the layer still renders its snapshot, else the spec's. `slot` and `event`
398
+ // carry no default at all, hence the presence check.
399
+ const fallback = props.frozen !== undefined ? props.frozen.frozen : "default" in spec ? spec.default : undefined;
400
+ const stored = props.value !== undefined;
401
+ // THE BLUE DOT (C4): a value this use stores that differs from the master,
402
+ // compared as the engine value the default is stored as (a curve's default
403
+ // is its decoded object, its stored value the canonical JSON).
404
+ const master = fallback === undefined ? null : coercePropValue(spec, fallback);
405
+ const changed = props.value !== undefined && !(master !== null && sameUseValue(props.value, master, spec.kind));
406
+
407
+ // A STORED value the range no longer holds (the component narrowed it, or
408
+ // a hand edit put it there), said under the row with the verb that brings
409
+ // it inside. Not for an animated prop: its keyframes are its value, and a
410
+ // typed edit of it is a key at the playhead.
411
+ const outside = animated ? null : outsideRangeOf(spec, props.value);
257
412
 
258
413
  const commit = (raw: unknown): void => {
259
- const next = asValue(raw);
260
- if (next === null) {
261
- setError("instance props hold engine values only (number, string, boolean, vec2/3/4)");
414
+ const edit = propEditOf(spec, raw, fallback);
415
+ if (edit.kind === "refused") {
416
+ setError(edit.reason);
417
+ return;
418
+ }
419
+ // An asset's None CLEARS the prop, which under a track is the reset's data
420
+ // loss again (`routeTrackedEdit` takes the animation with a removed
421
+ // value), and a keyframe cannot hold "no asset" either. Refused, as the
422
+ // reset is, rather than dropping every key from a picker.
423
+ if (edit.kind === "clear" && animated) {
424
+ setError(`${label} is animated. Choosing None would delete its keyframes: remove them on the timeline first.`);
262
425
  return;
263
426
  }
264
427
  setError(null);
265
- store.setInstanceProp(layerId, spec.key, next);
428
+ if (edit.kind === "clear") store.clearInstanceProp(layerId, spec.key);
429
+ else store.setInstanceProp(layerId, spec.key, edit.value);
266
430
  };
267
431
 
432
+ // THE DRAG HALF. A colour picker's three bars, a range field's slider and a
433
+ // gradient stop's colour route a drag entirely through `scrub.move` and
434
+ // never call `commit` while the pointer is down, so a row holding only the
435
+ // commit half is a control whose thumb moves and whose document does not.
436
+ //
437
+ // A value the contract refuses is REFUSED by writing nothing rather than by
438
+ // setting `error`: a capture runs per pointermove and inside the store's
439
+ // own transaction, and a React state write from there would re-render the
440
+ // panel mid-gesture sixty times a second to say what the typed path says
441
+ // once. Guarded on the ID as well as the kind: the seam fans out over the
442
+ // LIVE selection, and a prop key belongs to one component's contract.
443
+ const scrub = useSelectionScrub((layer, raw) => {
444
+ if (layer.kind !== "instance" || layer.id !== layerId) return layer;
445
+ const next = coercePropValue(spec, raw);
446
+ return next === null
447
+ ? layer
448
+ : { ...layer, instance: { ...layer.instance, props: { ...layer.instance.props, [spec.key]: next } } };
449
+ });
450
+
451
+ // THE RESET, shown while a value is stored. REFUSED while the prop is
452
+ // animated: clearing a tracked prop reads to the router as "take its
453
+ // animation with it" (`routeTrackedEdit`), so one press deleted every key
454
+ // the author had set while the tooltip promised only the default back.
455
+ const reset: PropRowReset | undefined = stored
456
+ ? {
457
+ onReset: () => store.clearInstanceProp(layerId, spec.key),
458
+ title: "Clear the stored value and use the component's default",
459
+ props: { "data-instance-reset": spec.key },
460
+ ...(animated
461
+ ? { refused: `${label} is animated. A reset would delete its keyframes: remove them on the timeline first.` }
462
+ : {}),
463
+ }
464
+ : undefined;
465
+
268
466
  return (
269
- <div className={`insp-prop${inherited ? " is-inherited" : ""}`}>
270
- <div className="insp-row">
271
- {spec.kind !== "number" && (
272
- <span className="insp-label" title={spec.description ?? spec.key}>
273
- {spec.label ?? spec.key}
274
- </span>
275
- )}
276
- <div className="insp-control">
277
- {storable ? (
278
- <PropControl spec={spec} value={value} commit={commit} scrub={NO_SCRUB} />
279
- ) : (
280
- <span
281
- className="badge"
282
- title={`"${spec.kind}" values are not engine Values, so they cannot be stored on an instance — set this prop inside the component itself.`}
283
- >
284
- {spec.kind} — not editable on an instance
467
+ <PropRow
468
+ spec={spec}
469
+ value={shownPropValue(spec, props.value ?? fallback)}
470
+ commit={commit}
471
+ scrub={scrub}
472
+ // The path is the contract key's, and `layerTrackablePath` types it from
473
+ // this same spec. The diamond is ABSENT rather than dead for a `slot` or
474
+ // `event` prop and for a stale key, since `layerTrackRefusal` refuses
475
+ // both, the rule it follows in every other section.
476
+ //
477
+ // PER BOX: every box of a vector carries the vector's diamond, which
478
+ // names the axes its boxes show as keying together. A one-box control
479
+ // keeps its diamond and its expose pin together in its box.
480
+ trailing={({ axes }) => <KeyframeDot path={path} label={label} pin={!vector} parts={axes} />}
481
+ // A VECTOR'S ONE PIN, in the label column once per row (KeyframeDot's
482
+ // header: one pin per track). In its first box's lane beside the
483
+ // diamond it overflowed that third of the row and was clipped at the
484
+ // panel's floor. Only while it is offered: the column draws a slot for
485
+ // whatever it is handed, even a pin that renders nothing.
486
+ {...(vector && pinOffered ? { actions: <KeyframeDot path={path} label={label} diamond={false} /> } : {})}
487
+ changed={changed}
488
+ inherited={!stored}
489
+ {...(reset !== undefined ? { reset } : {})}
490
+ error={error}
491
+ notice={
492
+ outside === null ? (
493
+ props.frozen === undefined || stored ? undefined : (
494
+ <span data-instance-frozen-key={spec.key}>
495
+ Frozen default. The component now defaults to {String(shownPropValue(spec, props.frozen.live))}; Accept current contract
496
+ to render it.
285
497
  </span>
286
- )}
287
- {!inherited && (
288
- <button
289
- className="btn"
290
- data-instance-reset={spec.key}
291
- title={`Clear the stored value for "${spec.label ?? spec.key}" and fall back to the component's default.`}
292
- onClick={() => store.clearInstanceProp(layerId, spec.key)}
293
- >
294
- Reset
295
- </button>
296
- )}
297
- </div>
298
- </div>
299
- {error !== null && <div className="insp-inline-error">{error}</div>}
300
- </div>
498
+ )
499
+ ) : (
500
+ <>
501
+ <span>{outside.words}</span>
502
+ <div className="insp-buttons">
503
+ <button
504
+ type="button"
505
+ className="btn"
506
+ disabled={locked}
507
+ data-instance-inside={spec.key}
508
+ title={outside.title}
509
+ onClick={() => commit(outside.inside)}
510
+ >
511
+ Bring inside
512
+ </button>
513
+ </div>
514
+ </>
515
+ )
516
+ }
517
+ assets={props.assets}
518
+ dotProps={{ "data-use-changed": spec.key }}
519
+ />
301
520
  );
302
521
  }