auricui-skill 0.0.0-stage → 0.1.2

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 (362) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -2
  3. package/bin/install.mjs +66 -0
  4. package/package.json +29 -4
  5. package/skill/SKILL.md +64 -0
  6. package/skill/agents/openai.yaml +4 -0
  7. package/skill/assets/examples/ApprovalCard.tsx +8 -0
  8. package/skill/assets/examples/BasicApp.tsx +38 -0
  9. package/skill/assets/examples/BriefingCard.tsx +8 -0
  10. package/skill/assets/examples/ChatControls.tsx +49 -0
  11. package/skill/assets/examples/ChatPanel.tsx +73 -0
  12. package/skill/assets/examples/Checkbox.tsx +13 -0
  13. package/skill/assets/examples/Combobox.tsx +14 -0
  14. package/skill/assets/examples/Drawer.tsx +10 -0
  15. package/skill/assets/examples/MemoryCard.tsx +9 -0
  16. package/skill/assets/examples/MessageList.tsx +8 -0
  17. package/skill/assets/examples/Theme.tsx +14 -0
  18. package/skill/assets/examples/ToolCall.tsx +8 -0
  19. package/skill/assets/examples/Tree.tsx +15 -0
  20. package/skill/assets/examples/examples.css +11 -0
  21. package/skill/assets/library/auricui-0.1.0-preview.1.tgz +0 -0
  22. package/skill/assets/library/auricui-0.1.0-preview.3.tgz +0 -0
  23. package/skill/assets/library/auricui-0.1.0-preview.4.tgz +0 -0
  24. package/skill/assets/upstream/LICENSE +21 -0
  25. package/skill/assets/upstream/apps/desktop/chat/src/App.tsx +1099 -0
  26. package/skill/assets/upstream/apps/desktop/chat/src/app-bridge.ts +154 -0
  27. package/skill/assets/upstream/apps/desktop/chat/src/chat.css +1812 -0
  28. package/skill/assets/upstream/apps/desktop/chat/src/components/AddSkill.tsx +223 -0
  29. package/skill/assets/upstream/apps/desktop/chat/src/components/AgentActivityRow.tsx +716 -0
  30. package/skill/assets/upstream/apps/desktop/chat/src/components/AgentIcons.tsx +46 -0
  31. package/skill/assets/upstream/apps/desktop/chat/src/components/AttachmentCard.tsx +34 -0
  32. package/skill/assets/upstream/apps/desktop/chat/src/components/AttachmentPreview.tsx +331 -0
  33. package/skill/assets/upstream/apps/desktop/chat/src/components/BalanceNotice.tsx +22 -0
  34. package/skill/assets/upstream/apps/desktop/chat/src/components/BrandLogo.tsx +35 -0
  35. package/skill/assets/upstream/apps/desktop/chat/src/components/ComposerCard.tsx +182 -0
  36. package/skill/assets/upstream/apps/desktop/chat/src/components/ConversationSessionBar.tsx +292 -0
  37. package/skill/assets/upstream/apps/desktop/chat/src/components/CopyDiagnostics.tsx +24 -0
  38. package/skill/assets/upstream/apps/desktop/chat/src/components/Divider.tsx +47 -0
  39. package/skill/assets/upstream/apps/desktop/chat/src/components/FileChangesList.tsx +119 -0
  40. package/skill/assets/upstream/apps/desktop/chat/src/components/Markdown.tsx +198 -0
  41. package/skill/assets/upstream/apps/desktop/chat/src/components/ModelPopover.tsx +280 -0
  42. package/skill/assets/upstream/apps/desktop/chat/src/components/NoticeCard.tsx +87 -0
  43. package/skill/assets/upstream/apps/desktop/chat/src/components/Popover.tsx +195 -0
  44. package/skill/assets/upstream/apps/desktop/chat/src/components/PreviewFrame.tsx +129 -0
  45. package/skill/assets/upstream/apps/desktop/chat/src/components/ProjectConversation.tsx +852 -0
  46. package/skill/assets/upstream/apps/desktop/chat/src/components/ProjectStarters.tsx +118 -0
  47. package/skill/assets/upstream/apps/desktop/chat/src/components/Tooltip.tsx +191 -0
  48. package/skill/assets/upstream/apps/desktop/chat/src/components/TurnLog.tsx +583 -0
  49. package/skill/assets/upstream/apps/desktop/chat/src/components/TurnQuestionCard.tsx +133 -0
  50. package/skill/assets/upstream/apps/desktop/chat/src/components/dock-pane-bar.ts +14 -0
  51. package/skill/assets/upstream/apps/desktop/chat/src/components/project-version.ts +37 -0
  52. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/MentionMenuPlugin.tsx +372 -0
  53. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptEditor.tsx +476 -0
  54. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptPill.tsx +399 -0
  55. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptReferenceNode.tsx +164 -0
  56. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/index.ts +31 -0
  57. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/mention-menu.ts +206 -0
  58. package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/prompt-text.ts +171 -0
  59. package/skill/assets/upstream/apps/desktop/chat/src/components/useStickToBottom.ts +320 -0
  60. package/skill/assets/upstream/apps/desktop/chat/src/components/useTicker.ts +55 -0
  61. package/skill/assets/upstream/apps/desktop/chat/src/i18n/index.ts +42 -0
  62. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/en-US.ts +408 -0
  63. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/es.ts +407 -0
  64. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/ja.ts +407 -0
  65. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/ko.ts +403 -0
  66. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/words.ts +6 -0
  67. package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/zh-CN.ts +407 -0
  68. package/skill/assets/upstream/apps/desktop/chat/src/i18n/lookup.ts +12 -0
  69. package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-activity-runs.ts +154 -0
  70. package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-menu-rows.ts +41 -0
  71. package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-refs.ts +105 -0
  72. package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-transcript.ts +440 -0
  73. package/skill/assets/upstream/apps/desktop/chat/src/lib/asset-lists.ts +75 -0
  74. package/skill/assets/upstream/apps/desktop/chat/src/lib/attachment-place.ts +17 -0
  75. package/skill/assets/upstream/apps/desktop/chat/src/lib/balance.ts +16 -0
  76. package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-layer.ts +10 -0
  77. package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-menu.ts +18 -0
  78. package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-store.ts +101 -0
  79. package/skill/assets/upstream/apps/desktop/chat/src/lib/composer-attachments.ts +189 -0
  80. package/skill/assets/upstream/apps/desktop/chat/src/lib/desktop-attachments.ts +178 -0
  81. package/skill/assets/upstream/apps/desktop/chat/src/lib/desktop-bridge.ts +291 -0
  82. package/skill/assets/upstream/apps/desktop/chat/src/lib/developer.ts +39 -0
  83. package/skill/assets/upstream/apps/desktop/chat/src/lib/esc-stop.ts +23 -0
  84. package/skill/assets/upstream/apps/desktop/chat/src/lib/file-changes.ts +32 -0
  85. package/skill/assets/upstream/apps/desktop/chat/src/lib/live-activity.ts +20 -0
  86. package/skill/assets/upstream/apps/desktop/chat/src/lib/local-agent-run.ts +1174 -0
  87. package/skill/assets/upstream/apps/desktop/chat/src/lib/local-sessions.ts +25 -0
  88. package/skill/assets/upstream/apps/desktop/chat/src/lib/model-calls.ts +87 -0
  89. package/skill/assets/upstream/apps/desktop/chat/src/lib/pretty-json.ts +12 -0
  90. package/skill/assets/upstream/apps/desktop/chat/src/lib/preview-top.ts +15 -0
  91. package/skill/assets/upstream/apps/desktop/chat/src/lib/project-resources.ts +66 -0
  92. package/skill/assets/upstream/apps/desktop/chat/src/lib/project-sessions.ts +66 -0
  93. package/skill/assets/upstream/apps/desktop/chat/src/lib/project-versions.ts +116 -0
  94. package/skill/assets/upstream/apps/desktop/chat/src/lib/ref-pictures.ts +119 -0
  95. package/skill/assets/upstream/apps/desktop/chat/src/lib/relative-time.ts +46 -0
  96. package/skill/assets/upstream/apps/desktop/chat/src/lib/skill-pages.ts +29 -0
  97. package/skill/assets/upstream/apps/desktop/chat/src/lib/step-file.ts +23 -0
  98. package/skill/assets/upstream/apps/desktop/chat/src/lib/structural-share.ts +52 -0
  99. package/skill/assets/upstream/apps/desktop/chat/src/lib/studio-film.ts +44 -0
  100. package/skill/assets/upstream/apps/desktop/chat/src/lib/studio-transfer.ts +40 -0
  101. package/skill/assets/upstream/apps/desktop/chat/src/lib/unprompted.ts +27 -0
  102. package/skill/assets/upstream/apps/desktop/chat/src/lib/upload-limits.ts +53 -0
  103. package/skill/assets/upstream/apps/desktop/chat/src/mount.tsx +19 -0
  104. package/skill/assets/upstream/apps/desktop/chat/src/state/local-agent-store.ts +296 -0
  105. package/skill/assets/upstream/apps/desktop/chat/src/studio-refs.ts +307 -0
  106. package/skill/assets/upstream/apps/desktop/package.json +51 -0
  107. package/skill/assets/upstream/apps/desktop/shell/bridge.ts +415 -0
  108. package/skill/assets/upstream/apps/desktop/shell/index.html +27 -0
  109. package/skill/assets/upstream/apps/desktop/shell/main.tsx +17 -0
  110. package/skill/assets/upstream/apps/desktop/shell/shell.css +17 -0
  111. package/skill/assets/upstream/apps/desktop/shell/vite.config.ts +65 -0
  112. package/skill/assets/upstream/apps/desktop/src/agent-definitions.mjs +31 -0
  113. package/skill/assets/upstream/packages/openfilm/LICENSE +21 -0
  114. package/skill/assets/upstream/packages/openfilm/package.json +88 -0
  115. package/skill/assets/upstream/packages/openfilm/src/film-doc.d.mts +125 -0
  116. package/skill/assets/upstream/packages/openfilm/src/film-doc.mjs +986 -0
  117. package/skill/assets/upstream/packages/openfilm/src/overrides.mjs +73 -0
  118. package/skill/assets/upstream/packages/openfilm/src/timeline.mjs +1280 -0
  119. package/skill/assets/upstream/packages/openfilm/studio/server/bridge.js +209 -0
  120. package/skill/assets/upstream/packages/openfilm/studio/server/film-subtitle.mjs +785 -0
  121. package/skill/assets/upstream/packages/openfilm/studio/server/ops.mjs +403 -0
  122. package/skill/assets/upstream/packages/openfilm/studio/server/stage.js +1649 -0
  123. package/skill/assets/upstream/packages/openfilm/studio/server/track-layout.mjs +43 -0
  124. package/skill/assets/upstream/packages/openfilm/studio/ui/src/App.tsx +156 -0
  125. package/skill/assets/upstream/packages/openfilm/studio/ui/src/api.ts +328 -0
  126. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/AudioMeters.tsx +188 -0
  127. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BlockThumbs.tsx +84 -0
  128. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BlockWaveform.tsx +201 -0
  129. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BrandLoadingScreen.tsx +56 -0
  130. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BrandLogo.tsx +25 -0
  131. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Card.tsx +89 -0
  132. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ColorPicker.tsx +204 -0
  133. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ConfirmDialog.tsx +96 -0
  134. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ContextMenu.tsx +422 -0
  135. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/DesktopDialog.tsx +271 -0
  136. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ExamplesGallery.tsx +372 -0
  137. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmFullscreenBar.tsx +147 -0
  138. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmInspector.tsx +2310 -0
  139. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmStageSelect.tsx +3296 -0
  140. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FocusNav.tsx +28 -0
  141. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FontFamilyPicker.tsx +293 -0
  142. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FramePicker.tsx +69 -0
  143. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/InspectorPanel.tsx +25 -0
  144. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Launcher.tsx +38 -0
  145. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/LayersPanel.tsx +277 -0
  146. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/LiveTime.tsx +101 -0
  147. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MediaPool.tsx +1413 -0
  148. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MediaPreview.tsx +188 -0
  149. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MenuSelect.tsx +161 -0
  150. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MgOutline.tsx +24 -0
  151. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/OperationHistory.tsx +184 -0
  152. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PaneDivider.tsx +292 -0
  153. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PasteAttrsDialog.tsx +69 -0
  154. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PlaybackMenu.tsx +110 -0
  155. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Popover.tsx +159 -0
  156. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectCover.tsx +60 -0
  157. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectHistoryControls.tsx +748 -0
  158. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectLayoutMenu.tsx +82 -0
  159. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectMenu.tsx +111 -0
  160. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectSources.tsx +587 -0
  161. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectView.tsx +3222 -0
  162. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectsLibrary.tsx +567 -0
  163. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectsView.tsx +1593 -0
  164. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PropertiesSection.tsx +418 -0
  165. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ResourceInspector.tsx +100 -0
  166. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SelectionToolbar.tsx +157 -0
  167. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SkillsLibrary.tsx +504 -0
  168. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SourcePreview.tsx +464 -0
  169. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SpendAsks.tsx +88 -0
  170. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/StageMaskOverlay.tsx +299 -0
  171. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/StageTransformFrame.tsx +246 -0
  172. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleInspector.module.css +46 -0
  173. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleInspector.tsx +884 -0
  174. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleOverlay.tsx +148 -0
  175. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleRow.tsx +419 -0
  176. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/TabStrip.tsx +188 -0
  177. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Timeline.tsx +4911 -0
  178. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/TimelineMarkers.tsx +329 -0
  179. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Toast.tsx +55 -0
  180. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Tooltip.tsx +193 -0
  181. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/UpdateButton.tsx +104 -0
  182. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ViewerBar.tsx +98 -0
  183. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/WhatsNew.tsx +147 -0
  184. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/dock-pane-bar.ts +16 -0
  185. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/export/ExportCenter.tsx +1502 -0
  186. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/export/ExportTray.tsx +284 -0
  187. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/film-stage-hit.ts +18 -0
  188. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/inspector-fields.tsx +863 -0
  189. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/inspector-look.tsx +1357 -0
  190. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/player-controls/controls-bar.tsx +397 -0
  191. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/player-controls/transport.tsx +294 -0
  192. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/replace-drop.ts +20 -0
  193. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/AgentsSection.tsx +613 -0
  194. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/CustomServices.tsx +104 -0
  195. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/DiagnosticsGroup.tsx +202 -0
  196. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ModelsSection.tsx +179 -0
  197. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ProviderMark.tsx +24 -0
  198. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ProvidersSection.tsx +177 -0
  199. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/SettingsModal.tsx +511 -0
  200. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/SettingsSelect.tsx +84 -0
  201. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ShortcutsSection.tsx +291 -0
  202. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/StorageGroup.tsx +119 -0
  203. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/theme.ts +47 -0
  204. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ui.tsx +139 -0
  205. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/use-settings.ts +10 -0
  206. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/stage-menu.tsx +283 -0
  207. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/timeline-head-extras.tsx +91 -0
  208. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/typing-target.ts +45 -0
  209. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-editor-tools.tsx +408 -0
  210. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-page-changes.ts +48 -0
  211. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-project-history.ts +113 -0
  212. package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/useBlockDrag.ts +639 -0
  213. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/Preview.tsx +426 -0
  214. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/player.ts +558 -0
  215. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/preview-frames.ts +36 -0
  216. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/steps.ts +36 -0
  217. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/undo.ts +156 -0
  218. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/use-film.ts +212 -0
  219. package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/use-preview-renders.ts +89 -0
  220. package/skill/assets/upstream/packages/openfilm/studio/ui/src/globals.css +559 -0
  221. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/en.ts +1901 -0
  222. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/es.ts +1900 -0
  223. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/index.ts +130 -0
  224. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/ja.ts +1900 -0
  225. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/ko.ts +1896 -0
  226. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/words.ts +6 -0
  227. package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/zh-CN.ts +1901 -0
  228. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/agent-row.ts +21 -0
  229. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/asset-timeline.ts +469 -0
  230. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/audio-levels.ts +60 -0
  231. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/batch-map.ts +28 -0
  232. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/browser.ts +10 -0
  233. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/canvas-fit.ts +116 -0
  234. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/chat-marks.ts +114 -0
  235. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-attrs.ts +102 -0
  236. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-fade.ts +70 -0
  237. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-look.ts +520 -0
  238. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-mask.ts +536 -0
  239. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-replace.ts +60 -0
  240. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/component-props.ts +128 -0
  241. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/context-length.ts +12 -0
  242. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/desktop-build.ts +44 -0
  243. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/diagnostics.ts +68 -0
  244. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/double-press.ts +22 -0
  245. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/dropped-overrides.ts +139 -0
  246. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/export-jobs.ts +191 -0
  247. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/export-spec.ts +261 -0
  248. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/field-input.ts +155 -0
  249. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-clip-edits.ts +82 -0
  250. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-frame.ts +134 -0
  251. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-overrides.ts +107 -0
  252. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-shape.ts +172 -0
  253. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film.ts +80 -0
  254. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/font-menu.ts +236 -0
  255. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/font-styles.ts +129 -0
  256. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/freeze-frame.ts +49 -0
  257. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/host.ts +438 -0
  258. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/inspect-scrub.ts +26 -0
  259. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/keymap-store.ts +74 -0
  260. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/keymap.ts +529 -0
  261. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/layers-tree.ts +133 -0
  262. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/look.ts +83 -0
  263. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/loudness.ts +58 -0
  264. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/markers.ts +152 -0
  265. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-bar.ts +58 -0
  266. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-folders.ts +45 -0
  267. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-place.ts +102 -0
  268. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-queue.ts +107 -0
  269. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-selection.ts +64 -0
  270. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/menu-position.ts +77 -0
  271. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/mg-transform.ts +111 -0
  272. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/mixed.ts +3 -0
  273. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/page-changes.ts +45 -0
  274. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/picture-files.ts +19 -0
  275. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/preview-prefs.ts +46 -0
  276. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/preview-renders.ts +71 -0
  277. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-delete.ts +20 -0
  278. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-folders.ts +144 -0
  279. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-history.ts +188 -0
  280. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-sources.ts +283 -0
  281. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/projects-page.ts +40 -0
  282. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/provider-marks.ts +35 -0
  283. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/providers.ts +163 -0
  284. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/resource-drag.ts +131 -0
  285. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/retry-image.ts +17 -0
  286. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/saved-insert.ts +40 -0
  287. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/saved.ts +125 -0
  288. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/selection-toolbar.ts +158 -0
  289. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/shortcut-hint.ts +21 -0
  290. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/similar-layers.ts +46 -0
  291. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/solo.ts +19 -0
  292. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-clip.ts +60 -0
  293. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-crop.ts +232 -0
  294. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-gesture.ts +277 -0
  295. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-layers.ts +71 -0
  296. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-mask.ts +176 -0
  297. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-measure.ts +53 -0
  298. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-types.ts +155 -0
  299. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/studio-refs.ts +248 -0
  300. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/studio-version.ts +41 -0
  301. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/subtitle-cues.ts +238 -0
  302. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/subtitles.ts +263 -0
  303. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/tab-menu.ts +6 -0
  304. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/template-controls.ts +131 -0
  305. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/theme-changes.ts +31 -0
  306. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-cache.ts +63 -0
  307. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-cell.ts +8 -0
  308. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-frame.ts +76 -0
  309. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-pick.ts +42 -0
  310. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timecode.ts +161 -0
  311. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-clipboard.ts +279 -0
  312. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-drag.ts +589 -0
  313. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-edit.ts +1138 -0
  314. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-keys.ts +63 -0
  315. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-layout.ts +1038 -0
  316. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-nav.ts +160 -0
  317. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-prefs.ts +73 -0
  318. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-split.ts +40 -0
  319. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-view.ts +105 -0
  320. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/title.ts +39 -0
  321. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/track-heights.ts +55 -0
  322. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/track-lock.ts +27 -0
  323. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/transitions.ts +459 -0
  324. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/type-metrics.ts +83 -0
  325. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-film-subtitles.ts +111 -0
  326. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-fonts.ts +181 -0
  327. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-loaded-thumbs.ts +91 -0
  328. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-edits.ts +134 -0
  329. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-font.ts +51 -0
  330. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-style.ts +68 -0
  331. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/waveform-columns.ts +48 -0
  332. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/web-update.ts +41 -0
  333. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/whats-new.ts +401 -0
  334. package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/workspace-resources.ts +373 -0
  335. package/skill/assets/upstream/packages/openfilm/studio/ui/src/main.tsx +10 -0
  336. package/skill/assets/upstream/packages/openfilm/studio/ui/vite.config.ts +12 -0
  337. package/skill/assets/upstream/packages/shared/package.json +45 -0
  338. package/skill/assets/upstream/packages/shared/src/agent-activity.ts +263 -0
  339. package/skill/assets/upstream/packages/shared/src/contract.ts +285 -0
  340. package/skill/assets/upstream/packages/shared/src/index.ts +17 -0
  341. package/skill/assets/upstream/packages/shared/src/markdown-blocks.ts +252 -0
  342. package/skill/assets/upstream/packages/shared/src/modality.ts +4 -0
  343. package/skill/assets/upstream/packages/shared/src/prompt-reference.ts +113 -0
  344. package/skill/assets/upstream/packages/shared/src/title.ts +76 -0
  345. package/skill/assets/upstream/packages/shared/src/turn.ts +68 -0
  346. package/skill/assets/upstream/packages/shared/src/video.ts +26 -0
  347. package/skill/references/agent-components.md +33 -0
  348. package/skill/references/api-index.json +2693 -0
  349. package/skill/references/chat-components.md +65 -0
  350. package/skill/references/components.md +23 -0
  351. package/skill/references/core-components.md +44 -0
  352. package/skill/references/dependencies.md +22 -0
  353. package/skill/references/esm-package.md +48 -0
  354. package/skill/references/integration.md +43 -0
  355. package/skill/references/library-manifest.json +12 -0
  356. package/skill/references/source-manifest.json +325 -0
  357. package/skill/references/studio-components.md +24 -0
  358. package/skill/references/theme-motion.md +49 -0
  359. package/skill/references/troubleshooting.md +27 -0
  360. package/skill/references/verification.md +13 -0
  361. package/skill/scripts/check_dependencies.py +109 -0
  362. package/skill/scripts/inspect_api.py +60 -0
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Undo by clip id: the edit that takes the film after an edit back to the film before it, for what that edit touched.
3
+ * Worked out from the two films, not by remembering the files, so undoing puts back one clip without putting back
4
+ * what the agent changed meanwhile.
5
+ */
6
+ import type { Clip, Film, Op } from '../api';
7
+ import { DEFAULT_STAGE } from '../../../../src/film-doc.mjs';
8
+
9
+ /** Deep copy of plain JSON. */
10
+ const copy = <T,>(v: T): T => structuredClone(v);
11
+
12
+ /** Where each clip is: its track and its row. */
13
+ function index(film: Film) {
14
+ const at = new Map<string, { track: number; row: Clip }>();
15
+ film.tracks.forEach((track, t) => track.clips.forEach((row) => at.set(row.id, { track: t, row })));
16
+ return at;
17
+ }
18
+
19
+ /**
20
+ * Where a track that was at `track` in `before` is in `after`. Tracks have no names: a track is known by the clips it
21
+ * kept. `moved` are the clips the edit touched (they do not say where their track went). Returns its index, or where a
22
+ * new one should go when the edit left it empty and it went.
23
+ */
24
+ function trackNow(before: Film, after: Film, track: number, moved: Set<string>): { track: number } | { newTrack: number } {
25
+ /** a track known by a clip it kept (one the edit did not touch) */
26
+ const anchored = (t: number) => {
27
+ const anchors = (before.tracks[t]?.clips.map((c) => c.id) ?? []).filter((id) => !moved.has(id));
28
+ if (!anchors.length) return null;
29
+ const i = after.tracks.findIndex((tr) => tr.clips.some((c) => anchors.includes(c.id)));
30
+ return i < 0 ? null : i;
31
+ };
32
+ const findBy = (t: number) => {
33
+ if ((before.tracks[t]?.clips ?? []).some((c) => !moved.has(c.id))) return anchored(t);
34
+ /* every clip of it was touched: it is a track holding only its clips (no other track's joined them there), still
35
+ between the same tracks; of several, the one holding most of them (else it went, and any of those is new) */
36
+ const own = before.tracks[t]?.clips.map((c) => c.id) ?? [];
37
+ const others = new Set(before.tracks.flatMap((tr, k) => (k === t ? [] : tr.clips.map((c) => c.id))));
38
+ const fits = (i: number) => {
39
+ if (after.tracks[i].clips.some((c) => others.has(c.id))) return false;
40
+ for (let k = 0; k < before.tracks.length; k++) {
41
+ const j = k === t ? null : anchored(k);
42
+ if (j != null && (k < t ? j >= i : j <= i)) return false;
43
+ }
44
+ return true;
45
+ };
46
+ const homes = after.tracks
47
+ .map((tr, i) => ({ i, mine: tr.clips.filter((c) => own.includes(c.id)).length }))
48
+ .filter((h) => h.mine > 0 && fits(h.i))
49
+ .sort((a, b) => b.mine - a.mine || a.i - b.i);
50
+ return homes.length ? homes[0].i : null;
51
+ };
52
+ const same = findBy(track);
53
+ if (same != null) return { track: same };
54
+ /* it went: a new one just below the nearest track above it that is still there */
55
+ for (let t = track - 1; t >= 0; t--) { const i = findBy(t); if (i != null) return { newTrack: i + 1 }; }
56
+ return { newTrack: 0 };
57
+ }
58
+
59
+ /** A clip's fields other than its identity and its place (those go back by a move). */
60
+ const fieldsOf = (row: Clip) => Object.keys(row).filter((k) => k !== 'id' && k !== 'at');
61
+
62
+ /**
63
+ * The edit that takes `after` back to `before`, for the clips and tracks `ops` touched: fields go back one by one, a
64
+ * moved clip goes back to its track and second, a removed one comes back, an added one goes.
65
+ */
66
+ export function inverse(before: Film, after: Film, ops: Op[]): Op[] {
67
+ const was = index(before);
68
+ const now = index(after);
69
+ const ids = new Set<string>();
70
+ const undo: Op[] = [];
71
+ for (const op of ops) {
72
+ if (op.op === 'track') { undo.push({ op: 'track', track: op.track, field: op.field, value: Boolean(before.tracks[op.track]?.[op.field]) }); continue; }
73
+ if (op.op === 'reorder') { undo.push({ op: 'reorder', from: op.to, to: op.from }); continue; }
74
+ if (op.op === 'stage') { const { w, h } = before.stage ?? DEFAULT_STAGE; undo.push({ op: 'stage', w, h }); continue; }
75
+ if (op.op === 'insert') { for (const [id] of now) if (!was.has(id)) ids.add(id); continue; }
76
+ if (op.op === 'props') {
77
+ for (const e of op.edits) {
78
+ if (e.prop === 'trackOrder') {
79
+ const { from, to } = e.value as { from: number; to: number };
80
+ undo.push({ op: 'reorder', from: Math.max(0, Math.min(after.tracks.length - 1, to)), to: from });
81
+ } else if (e.prop === 'locked' || e.prop === 'hidden' || e.prop === 'muted') {
82
+ const home = now.get(e.clip);
83
+ const old = was.get(e.clip);
84
+ if (home && old) undo.push({ op: 'track', track: home.track, field: e.prop, value: Boolean(before.tracks[old.track]?.[e.prop]) });
85
+ } else ids.add(e.clip);
86
+ }
87
+ continue;
88
+ }
89
+ ids.add(op.clip);
90
+ if (op.op === 'split') for (const [id] of now) if (!was.has(id)) ids.add(id);
91
+ }
92
+ /* a track the edit left empty went with its flags; made again, it gets them back (or a muted track would come back
93
+ sounding). Pushed before the op that makes it: the list is reversed below, so they apply right after it */
94
+ const flagsBack = (home: ReturnType<typeof trackNow>, track: number) => {
95
+ if (!('newTrack' in home)) return;
96
+ for (const field of ['locked', 'hidden', 'muted'] as const) {
97
+ if (before.tracks[track]?.[field]) undo.push(goneTrack({ op: 'track', track: home.newTrack, field, value: true }, track));
98
+ }
99
+ };
100
+ /* the ops that make a track the edit left empty again, by the track it was in `before` */
101
+ const gone = new Map<Op, number>();
102
+ const goneTrack = (op: Op, track: number): Op => { gone.set(op, track); return op; };
103
+ for (const id of ids) {
104
+ const old = was.get(id);
105
+ const cur = now.get(id);
106
+ if (!old && cur) { undo.push({ op: 'remove', clip: id }); continue; }
107
+ if (!old) continue;
108
+ const home = trackNow(before, after, old.track, ids);
109
+ if (!cur) {
110
+ flagsBack(home, old.track);
111
+ const op: Op = { op: 'insert', clip: copy(old.row), ...home };
112
+ undo.push('newTrack' in home ? goneTrack(op, old.track) : op);
113
+ continue;
114
+ }
115
+ if (!('track' in home && home.track === cur.track) || (old.row.at ?? 0) !== (cur.row.at ?? 0)) {
116
+ flagsBack(home, old.track);
117
+ const op: Op = { op: 'move', clip: id, at: old.row.at ?? 0, ...home };
118
+ undo.push('newTrack' in home ? goneTrack(op, old.track) : op);
119
+ }
120
+ const then = old.row as Record<string, unknown>;
121
+ const nowRow = cur.row as Record<string, unknown>;
122
+ const changed = [...new Set([...fieldsOf(old.row), ...fieldsOf(cur.row)])].filter((field) => JSON.stringify(then[field]) !== JSON.stringify(nowRow[field]));
123
+ if (changed.length) undo.push({ op: 'fields', clip: id, fields: Object.fromEntries(changed.map((field) => [field, field in then ? copy(then[field]) : null])) });
124
+ }
125
+ return reopenTracks(undo.reverse(), gone);
126
+ }
127
+
128
+ /**
129
+ * A track the edit left empty comes back once, with all of its clips: the first of them opens it (`newTrack`), the
130
+ * others go onto it (`track`), or each would open a track of its own (deleting a whole track's clips and undoing put
131
+ * every clip on a new track). These go last, after what works on tracks still there (their indexes are `after`'s, and
132
+ * a track opened before them would shift them), from the top track down: a `newTrack` is a place among `after`'s
133
+ * tracks, and each track reopened above it moves it down one.
134
+ */
135
+ function reopenTracks(ops: Op[], gone: ReadonlyMap<Op, number>): Op[] {
136
+ const out: Op[] = ops.filter((op) => !gone.has(op));
137
+ const byTrack = new Map<number, Op[]>();
138
+ for (const op of ops) {
139
+ const was = gone.get(op);
140
+ if (was != null) byTrack.set(was, [...(byTrack.get(was) ?? []), op]);
141
+ }
142
+ let above = 0;
143
+ for (const was of [...byTrack.keys()].sort((a, b) => a - b)) {
144
+ let at: number | null = null;
145
+ for (const op of byTrack.get(was)!) {
146
+ if (op.op === 'track') { if (at != null) out.push({ ...op, track: at }); continue; }
147
+ if (op.op !== 'insert' && op.op !== 'move') continue;
148
+ const { newTrack, ...rest } = op;
149
+ if (at != null) { out.push({ ...rest, track: at }); continue; }
150
+ at = (newTrack ?? 0) + above;
151
+ out.push({ ...rest, newTrack: at });
152
+ }
153
+ if (at != null) above += 1;
154
+ }
155
+ return out;
156
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * The film being edited: film.html as the server last wrote it, plus the edits made here since, already applied.
3
+ *
4
+ * An edit shows at once: it is applied here with the server's own code (server/ops.mjs), the picture is told, and the
5
+ * edit is sent to be written, one request at a time, each against the revision the last one produced. When the
6
+ * server answers that the film moved on without it (the agent removed the clip), the film here becomes the server's;
7
+ * so it does when the folder's film, once every edit here is written, is not the one here (the agent wrote it a moment
8
+ * before, and the server applied the edit on top). A film.html that cannot be read (the agent is halfway through
9
+ * writing it, or wrote it wrong) is `broken`: the film here stays as it last was, and edits wait for it to be whole.
10
+ *
11
+ * Undo and redo are edits too, worked out by clip id from the film before and after (`inverse`), so undoing a move
12
+ * puts that clip back without putting back what the agent changed meanwhile.
13
+ */
14
+ import { useCallback, useEffect, useRef, useState } from 'react';
15
+ import { applyOps, EditError } from '../../../server/ops.mjs';
16
+ import { filmHead } from '../../../../src/film-doc.mjs';
17
+ import { api, ApiError, projectEvents, type Film, type Op } from '../api';
18
+ import { inverse } from './undo';
19
+ import { dropStep, moveStep, nextStep, pushStep, type Steps } from './steps';
20
+
21
+ export type FilmState = { value: Film; rev: string };
22
+
23
+ /**
24
+ * One edit a person made here, undoable: what undoes it, what makes it again, and what to call it in the history.
25
+ * `outside`: an edit of another file than film.html (a subtitle's times, in its transcript), undone and made again by
26
+ * its own calls, which resolve to null or why they could not; its `undo` and `redo` are empty.
27
+ */
28
+ export type Step = { undo: Op[]; redo: Op[]; label: string; at: number; outside?: OutsideStep };
29
+ export type OutsideStep = { undo: () => Promise<string | null>; redo: () => Promise<string | null> };
30
+
31
+ /**
32
+ * The film of a project, editable. `onChange(value, kind, head)` tells the picture: `edit` for an edit made here (try
33
+ * it in place), `server` for the film as the folder has it now (the agent wrote it, or an edit was refused), with its
34
+ * file's head when known (a changed one is the film loaded again: its styles are not in the value).
35
+ */
36
+ export function useFilm(projectId: string, initial: FilmState | null, onChange: (value: Film, kind: 'edit' | 'server', head?: string) => void) {
37
+ const [state, setState] = useState<FilmState | null>(initial);
38
+ const [error, setError] = useState<string | null>(null);
39
+ /** why film.html cannot be read now (null: it can) */
40
+ const [broken, setBroken] = useState<string | null>(null);
41
+ const brokenRev = useRef<string | null>(null);
42
+ /** edits applied here and not yet written */
43
+ const pending = useRef(0);
44
+ const [steps, setStepsState] = useState<Steps<Step>>({ done: [], undone: [] });
45
+ /* the steps as they are now, ahead of the render: an undo pressed right after an edit (a nudge written the moment
46
+ before it) undoes that edit, not the one before */
47
+ const stepsRef = useRef(steps);
48
+ const setSteps = useCallback((next: (s: Steps<Step>) => Steps<Step>) => {
49
+ stepsRef.current = next(stepsRef.current);
50
+ setStepsState(stepsRef.current);
51
+ }, []);
52
+ const ref = useRef(state);
53
+ ref.current = state;
54
+ const queue = useRef(Promise.resolve());
55
+ /**
56
+ * revisions this editor wrote, oldest first: their echo from the folder watcher is not news. The watcher reports the
57
+ * file as it is once writes settle, so an echo also stands for every write before it, unreported; those are
58
+ * forgotten with it, or an outside write that happens to match one of them (the agent putting back an earlier text)
59
+ * would be taken for an echo and never shown.
60
+ */
61
+ const ours = useRef(new Set<string>());
62
+ const echoOf = (rev: string) => {
63
+ if (!ours.current.has(rev)) return false;
64
+ for (const r of ours.current) { ours.current.delete(r); if (r === rev) break; }
65
+ return true;
66
+ };
67
+ const changed = useRef(onChange);
68
+ changed.current = onChange;
69
+ useEffect(() => { if (initial && !ref.current) setState(initial); }, [initial]);
70
+
71
+ /** The film as the folder has it now; false when film.html cannot be read (`broken` says why). */
72
+ const fromServer = useCallback(async (): Promise<boolean> => {
73
+ const latest = await api.film(projectId);
74
+ if (!latest.doc || !latest.value) {
75
+ brokenRev.current = latest.rev;
76
+ setBroken(latest.problems.join('\n') || 'film.html cannot be read');
77
+ return false;
78
+ }
79
+ if (brokenRev.current) { brokenRev.current = null; setBroken(null); setError(null); }
80
+ const next = { value: latest.value, rev: latest.rev };
81
+ ref.current = next;
82
+ setState(next);
83
+ changed.current(next.value, 'server', filmHead(latest.text));
84
+ return true;
85
+ }, [projectId]);
86
+
87
+ /* the agent's writes: the film becomes the folder's (an edit of ours still on its way is applied on top by the server) */
88
+ useEffect(() => projectEvents(projectId, (event) => {
89
+ if (event.type !== 'film' || echoOf(event.rev)) return;
90
+ /* the same text as here is news only after the file could not be read (written whole again) */
91
+ if (event.rev === ref.current?.rev && !brokenRev.current) return;
92
+ /* not read: Studio went away (it was stopping) — it tells again when it is back */
93
+ void queue.current.then(fromServer).catch(() => {});
94
+ }), [projectId, fromServer]);
95
+
96
+ /**
97
+ * Apply `ops` here at once, then write them. Resolves once written: null, or why it was not (the film here is then
98
+ * the folder's again). Resolves at once with the reason when the film here refuses them.
99
+ */
100
+ /* `quiet`: the caller says why it was refused itself (the inspector, beside its field), so `error` is not set.
101
+ `one`: the step an undo or redo takes, moved to the other list once applied here */
102
+ const run = useCallback((ops: Op[], record: 'do' | 'undo' | 'redo' = 'do', label = 'history.stepEdit', quiet = false, one?: Step): Promise<string | null> => {
103
+ const current = ref.current;
104
+ if (!current || !ops.length) return Promise.resolve(null);
105
+ /* film.html cannot be read: the edit could not be written, and shown it would draw the picture again from the
106
+ broken file. Nothing changes; the note on the film already says why (`broken`) */
107
+ if (brokenRev.current) return Promise.resolve(null);
108
+ let after: Film;
109
+ try { after = applyOps(structuredClone(current.value), ops) as Film; }
110
+ catch (e) {
111
+ const why = e instanceof EditError ? e.message : String(e);
112
+ if (!quiet) setError(why);
113
+ return Promise.resolve(why);
114
+ }
115
+ const before = current.value;
116
+ const next = { value: after, rev: current.rev };
117
+ ref.current = next;
118
+ setState(next);
119
+ setError(null);
120
+ changed.current(after, 'edit');
121
+ pending.current += 1;
122
+ const step: Step = { undo: inverse(before, after, ops), redo: ops, label, at: Date.now() };
123
+ setSteps((s) => (record === 'do' ? pushStep(s, step) : one ? moveStep(s, one, record) : s));
124
+ const written = queue.current.then(async (): Promise<string | null> => {
125
+ try {
126
+ const done = await api.edit(projectId, ref.current?.rev ?? null, ops);
127
+ ours.current.delete(done.rev);
128
+ ours.current.add(done.rev);
129
+ pending.current -= 1;
130
+ const here = ref.current;
131
+ if (!here) return null;
132
+ /* every edit here written: the folder's film is the truth (another write came in under this one) */
133
+ const theirs = pending.current === 0 ? done.value : null;
134
+ if (theirs && (done.rebased || JSON.stringify(theirs) !== JSON.stringify(here.value))) {
135
+ ref.current = { value: theirs, rev: done.rev };
136
+ setState(ref.current);
137
+ changed.current(theirs, 'server', filmHead(done.text));
138
+ } else {
139
+ ref.current = { ...here, rev: done.rev };
140
+ setState((s) => (s ? { ...s, rev: done.rev } : s));
141
+ }
142
+ return null;
143
+ } catch (e) {
144
+ pending.current -= 1;
145
+ const why = e instanceof ApiError ? e.message : String(e);
146
+ /* what was not written is not a step to undo */
147
+ if (record === 'do') setSteps((s) => ({ ...s, done: s.done.filter((x) => x !== step) }));
148
+ if (!(await fromServer())) {
149
+ /* the folder's film cannot be read: back to the film as it was before this edit (the note on the film says
150
+ why, so it is not said again) */
151
+ ref.current = current;
152
+ setState(current);
153
+ changed.current(current.value, 'server');
154
+ return null;
155
+ }
156
+ if (!quiet) setError(why);
157
+ return why;
158
+ }
159
+ });
160
+ queue.current = written.then(() => {});
161
+ return written;
162
+ }, [projectId, fromServer, setSteps]);
163
+
164
+ /* a step of another file: moved between the lists at once, its call made in turn with the film's writes; one that
165
+ cannot be undone (the file changed since, elsewhere) leaves both lists, and says why */
166
+ const runOutside = useCallback((step: Step, kind: 'undo' | 'redo') => {
167
+ setSteps((s) => moveStep(s, step, kind));
168
+ const written = queue.current.then(async () => {
169
+ const why = await step.outside![kind]().catch((e: unknown) => String(e instanceof Error ? e.message : e));
170
+ if (why) {
171
+ setError(why);
172
+ setSteps((s) => dropStep(s, step));
173
+ }
174
+ });
175
+ queue.current = written;
176
+ }, [setSteps]);
177
+ /* a step of the film: one whose ops cannot be applied or written now (the agent removed its clip since) leaves both
178
+ lists, as one of another file does, so the next undo takes the step before it and the history ends at the oldest */
179
+ const step = useCallback((one: Step, kind: 'undo' | 'redo') => {
180
+ if (one.outside) { runOutside(one, kind); return; }
181
+ const ops = kind === 'undo' ? one.undo : one.redo;
182
+ /* nothing to apply (an edit that changed nothing): it only moves */
183
+ if (!ops.length) { setSteps((s) => moveStep(s, one, kind)); return; }
184
+ void run(ops, kind, undefined, false, one).then((why) => { if (why) setSteps((s) => dropStep(s, one)); });
185
+ }, [run, runOutside, setSteps]);
186
+ const undo = useCallback(() => {
187
+ const one = nextStep(stepsRef.current, 'undo');
188
+ if (one) step(one, 'undo');
189
+ }, [step]);
190
+ const redo = useCallback(() => {
191
+ const one = nextStep(stepsRef.current, 'redo');
192
+ if (one) step(one, 'redo');
193
+ }, [step]);
194
+ /** Undo or redo until `done` steps are done (the edit history's rows: click one to go back or forward to it). */
195
+ const rollbackTo = useCallback((done: number) => {
196
+ let { done: d, undone: u } = stepsRef.current;
197
+ while (d.length > done && d.length) { const one = d[d.length - 1]; step(one, 'undo'); u = [...u, one]; d = d.slice(0, -1); }
198
+ while (d.length < done && u.length) { const one = u[u.length - 1]; step(one, 'redo'); d = [...d, one]; u = u.slice(0, -1); }
199
+ }, [step]);
200
+ const edit = useCallback((ops: Op[], label?: string, quiet?: boolean) => run(ops, 'do', label, quiet), [run]);
201
+ /** An edit of another file, made already: kept as a step, so ⌘Z undoes it in turn with the film's. */
202
+ const record = useCallback((label: string, outside: OutsideStep) => {
203
+ setSteps((s) => pushStep(s, { undo: [], redo: [], label, at: Date.now(), outside }));
204
+ }, [setSteps]);
205
+ /** The film was replaced as a whole (an earlier version put back): the edits made before are not undone into it. */
206
+ const forget = useCallback(() => setSteps(() => ({ done: [], undone: [] })), [setSteps]);
207
+
208
+ return {
209
+ film: state?.value ?? null, rev: state?.rev ?? null, error, broken, edit, record, undo, redo, rollbackTo, forget, steps,
210
+ canUndo: steps.done.length > 0, canRedo: steps.undone.length > 0, settled: () => queue.current,
211
+ };
212
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Preview renders, the editor's side: the stretches that play late (measured as the film plays, lib/preview-renders)
3
+ * are asked to be rendered once the film has stopped for a moment (studio/server/preview-cache.mjs, in the
4
+ * background), followed while they are made, and told to the preview when they are, to play in the film's place.
5
+ * Anything changed in the project (film.html, a page, a file) and those renders are not played until the server names
6
+ * them for the project as it is now.
7
+ */
8
+ import { useCallback, useEffect, useMemo, useRef, useState, type RefObject } from 'react';
9
+ import { addHeat, joinStretches, lateStretches, sameStretches, type Heat } from '@/lib/preview-renders';
10
+ import type { PlayedReport, PreviewHandle, PreviewRender } from './Preview';
11
+
12
+ type Made = { a: number; b: number; key: string; state: 'waiting' | 'making' | 'ready' | 'failed'; ratio?: number };
13
+
14
+ /** How long the film stays stopped (and unchanged) before what plays late is rendered (ms). */
15
+ const IDLE_MS = 2000;
16
+ /** How often renders being made are asked after (ms). */
17
+ const FOLLOW_MS = 1500;
18
+
19
+ export function usePreviewRenders({ projectId, folder, fps, playing, on, film, files, preview }: {
20
+ projectId: string;
21
+ /** the project's folder on the film's origin (the preview's address of it) */
22
+ folder: string;
23
+ /** the film's frame rate: renders are made at it */
24
+ fps: number;
25
+ playing: boolean;
26
+ /** Settings → Playback: render what plays late */
27
+ on: boolean;
28
+ /** film.html as it is now, and the project's files' version: either changing makes the renders made before stale */
29
+ film: unknown;
30
+ files: unknown;
31
+ preview: RefObject<PreviewHandle | null>;
32
+ }) {
33
+ const heat = useRef<Heat>(new Map());
34
+ /* known late before this session (the server keeps them with the renders), and found late as it plays */
35
+ const known = useRef<[number, number][]>([]);
36
+ const [late, setLate] = useState<[number, number][]>([]);
37
+ const [made, setMade] = useState<Made[]>([]);
38
+ const madeRef = useRef(made);
39
+ madeRef.current = made;
40
+ const lateNow = () => joinStretches([...known.current, ...lateStretches(heat.current)]);
41
+ const onPlayed = useCallback((report: PlayedReport) => {
42
+ /* a half second played from its render says nothing of how the film itself plays there */
43
+ const rendered = madeRef.current.filter((r) => r.state === 'ready');
44
+ addHeat(heat.current, report.spans.filter(([t]) => !rendered.some((r) => t + 0.5 > r.a && t < r.b)));
45
+ const next = lateNow();
46
+ setLate((had) => (sameStretches(had, next) ? had : next));
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ }, []);
49
+ /* another project: what is known of how it plays, from the server (an earlier session's), and its renders */
50
+ useEffect(() => {
51
+ heat.current = new Map();
52
+ known.current = [];
53
+ setLate([]);
54
+ setMade([]);
55
+ if (!on || !(fps > 0)) return undefined;
56
+ let alive = true;
57
+ void fetch(`/api/projects/${encodeURIComponent(projectId)}/preview-cache?fps=${fps}`).then((r) => (r.ok ? r.json() : null), () => null)
58
+ .then((answer: { late?: [number, number][]; ranges?: Made[] } | null) => {
59
+ if (!alive || !answer?.late?.length) return;
60
+ known.current = answer.late;
61
+ setLate(lateNow());
62
+ setMade(answer.ranges ?? []);
63
+ });
64
+ return () => { alive = false; };
65
+ // eslint-disable-next-line react-hooks/exhaustive-deps
66
+ }, [projectId, on, fps]);
67
+ /* the project changed: what was rendered of it is not played until it is named for the project as it is now */
68
+ useEffect(() => { setMade([]); }, [film, files]);
69
+ useEffect(() => {
70
+ if (!on || playing || !late.length || !(fps > 0)) return undefined;
71
+ let alive = true;
72
+ let timer = 0;
73
+ const ask = async () => {
74
+ const answer = await fetch(`/api/projects/${encodeURIComponent(projectId)}/preview-cache`, {
75
+ method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ ranges: late, fps }),
76
+ }).then((r) => (r.ok ? r.json() : null), () => null) as { ranges?: Made[] } | null;
77
+ if (!alive || !answer?.ranges) return;
78
+ setMade(answer.ranges);
79
+ if (answer.ranges.some((r) => r.state === 'waiting' || r.state === 'making')) timer = window.setTimeout(() => { void ask(); }, FOLLOW_MS);
80
+ };
81
+ timer = window.setTimeout(() => { void ask(); }, IDLE_MS);
82
+ return () => { alive = false; window.clearTimeout(timer); };
83
+ }, [on, playing, late, fps, projectId, film, files]);
84
+ const list = useMemo<PreviewRender[]>(() => (on
85
+ ? made.filter((r) => r.state === 'ready').map((r) => ({ a: r.a, b: r.b, src: `${folder}__film/preview/${r.key}.mp4` }))
86
+ : []), [on, made, folder]);
87
+ useEffect(() => { preview.current?.renders?.(list); }, [list, preview]);
88
+ return { onPlayed };
89
+ }