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,107 @@
1
+ /**
2
+ * Studio's background requests for media (a file's length and size, a waveform, and the pictures: the timeline's
3
+ * thumbnails, a clip's poster, the media pane's covers): a few at a time.
4
+ *
5
+ * A browser keeps six connections to Studio. Opening a project used to ask for every file's facts and every picture at
6
+ * once, and those requests held all six for half a minute: an edit made meanwhile (a sound dropped at the film's end)
7
+ * waited for a free one, and the person saw it not land. So they go in two lines: a web page's facts and pictures are
8
+ * found by drawing it in the headless browser, one at a time on the server, so one at a time here (more would only
9
+ * wait there, each holding a connection); a media file's come from ffmpeg, MEDIA_IN_FLIGHT at a time. The editor's own
10
+ * requests always find a connection. A media file's facts go before its pictures (a video's length decides which
11
+ * frames are asked for); a page's pictures go before its facts (they are what the person waits to see, and a page
12
+ * clip's length is in film.html already).
13
+ */
14
+ import * as React from 'react';
15
+
16
+ export const MEDIA_IN_FLIGHT = 2;
17
+ export const PAGES_IN_FLIGHT = 1;
18
+
19
+ /** `first`: what waits there goes before `then` */
20
+ type Line = { running: number; most: number; first: Array<() => void>; then: Array<() => void>; picturesFirst: boolean };
21
+ const files: Line = { running: 0, most: MEDIA_IN_FLIGHT, first: [], then: [], picturesFirst: false };
22
+ const pages: Line = { running: 0, most: PAGES_IN_FLIGHT, first: [], then: [], picturesFirst: true };
23
+
24
+ /** A request about a web page (its `path`) is drawn in the headless browser. */
25
+ const lineOf = (url: string): Line => (/\.html?$/i.test(new URL(url, 'http://studio').searchParams.get('path') ?? '') ? pages : files);
26
+
27
+ /** Wait for a turn, run, give the turn on. `still`: asked when the turn comes; false skips it (not wanted any more). */
28
+ async function inTurn<T>(run: () => Promise<T>, line: Line, picture: boolean, still?: () => boolean): Promise<T | undefined> {
29
+ if (line.running >= line.most) await new Promise<void>((go) => (picture === line.picturesFirst ? line.first : line.then).push(go));
30
+ line.running += 1;
31
+ try {
32
+ if (still && !still()) return undefined;
33
+ return await run();
34
+ } finally {
35
+ line.running -= 1;
36
+ (line.first.shift() ?? line.then.shift())?.();
37
+ }
38
+ }
39
+
40
+ /** `fetch`, waiting its turn among the media requests. */
41
+ export async function mediaFetch(url: string, init?: RequestInit): Promise<Response> {
42
+ return (await inTurn(() => fetch(url, init), lineOf(url), false))!;
43
+ }
44
+
45
+ /** Pictures loaded once: the browser has them, so they are shown again without waiting a turn. */
46
+ const LOADED = new Set<string>();
47
+ const LOADED_MAX = 2000;
48
+ /** Pictures waiting their turn or loading, each asked for once. */
49
+ const WAITING = new Map<string, { done: Promise<boolean | null>; stills: Array<() => boolean> }>();
50
+
51
+ /**
52
+ * Load a picture into the browser's cache, waiting its turn; true when it loaded, false when it did not. `still` is
53
+ * asked when its turn comes: a picture not wanted any more by then (another zoom, a tile scrolled away) is not asked
54
+ * for at all (null).
55
+ */
56
+ export async function mediaImage(url: string, still?: () => boolean): Promise<boolean | null> {
57
+ if (LOADED.has(url)) return true;
58
+ /* asked again while waiting (the timeline asks again on every change): one request, wanted while anyone wants it */
59
+ const queued = WAITING.get(url);
60
+ if (queued) { queued.stills.push(still ?? (() => true)); return queued.done; }
61
+ const stills = [still ?? (() => true)];
62
+ const done = inTurn(() => new Promise<boolean>((loaded) => {
63
+ const img = new Image();
64
+ img.onload = () => loaded(true);
65
+ img.onerror = () => loaded(false);
66
+ img.src = url;
67
+ }), lineOf(url), true, () => stills.some((s) => s())).then((ok) => {
68
+ WAITING.delete(url);
69
+ if (ok) {
70
+ LOADED.add(url);
71
+ if (LOADED.size > LOADED_MAX) LOADED.delete(LOADED.values().next().value!);
72
+ }
73
+ return ok ?? null;
74
+ });
75
+ WAITING.set(url, { done, stills });
76
+ return done;
77
+ }
78
+
79
+ /**
80
+ * A picture's address once it has had its turn (loaded or not: a failure is the `<img>`'s to show and retry), else
81
+ * the last one that had it, so a picture made again never flickers. `wanted` false: not asked for yet (off screen).
82
+ */
83
+ export function useMediaImage(url: string | null | undefined, wanted = true): string | null {
84
+ const [ready, setReady] = React.useState<string | null>(() => (url && LOADED.has(url) ? url : null));
85
+ React.useEffect(() => {
86
+ if (!url || !wanted || ready === url) return undefined;
87
+ let live = true;
88
+ void mediaImage(url, () => live).then((had) => { if (live && had !== null) setReady(url); });
89
+ return () => { live = false; };
90
+ }, [url, wanted, ready]);
91
+ return url && LOADED.has(url) ? url : ready;
92
+ }
93
+
94
+ /** The pictures of `urls` (by any key) that have had their turn: what can be drawn now. */
95
+ export function useMediaImages(urls: Readonly<Record<string, string>>): Readonly<Record<string, string>> {
96
+ const [ready, setReady] = React.useState<ReadonlySet<string>>(() => new Set());
97
+ const wanted = React.useRef(urls);
98
+ wanted.current = urls;
99
+ React.useEffect(() => {
100
+ for (const url of new Set(Object.values(urls))) {
101
+ if (ready.has(url)) continue;
102
+ void mediaImage(url, () => Object.values(wanted.current).includes(url))
103
+ .then((had) => { if (had !== null) setReady((cur) => (cur.has(url) ? cur : new Set(cur).add(url))); });
104
+ }
105
+ }, [urls]); // eslint-disable-line react-hooks/exhaustive-deps
106
+ return React.useMemo(() => Object.fromEntries(Object.entries(urls).filter(([, url]) => ready.has(url) || LOADED.has(url))), [urls, ready]);
107
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * What is picked in the media pane, the way a file manager picks (Finder, File Explorer): a click picks one, ⌘ (Ctrl)
3
+ * adds one or takes it away, ⇧ picks the run from the one clicked last, and a box dragged over empty space picks what
4
+ * it touches (with ⌘ or ⇧ held, on top of what was picked). Items are named by their project-relative path, a
5
+ * folder's too, so a key is the path a move or a delete takes.
6
+ */
7
+
8
+ export interface PickMods {
9
+ /** ⌘ or Ctrl held: add or take away. */
10
+ toggle: boolean;
11
+ /** ⇧ held: the run from the anchor. */
12
+ range: boolean;
13
+ }
14
+
15
+ /** What is picked after a click on `key`; `order` is the items as shown, `anchor` the one clicked last. */
16
+ export function pickItem(
17
+ picked: readonly string[],
18
+ key: string,
19
+ order: readonly string[],
20
+ anchor: string | null,
21
+ mods: PickMods,
22
+ ): string[] {
23
+ const from = anchor == null ? -1 : order.indexOf(anchor);
24
+ const to = order.indexOf(key);
25
+ if (mods.range && from >= 0 && to >= 0) {
26
+ const run = order.slice(Math.min(from, to), Math.max(from, to) + 1);
27
+ return mods.toggle ? [...new Set([...picked, ...run])] : run;
28
+ }
29
+ if (mods.toggle) return picked.includes(key) ? picked.filter((k) => k !== key) : [...picked, key];
30
+ return [key];
31
+ }
32
+
33
+ export interface Box {
34
+ left: number;
35
+ top: number;
36
+ right: number;
37
+ bottom: number;
38
+ }
39
+
40
+ /** The box between two points, whichever way it was dragged. */
41
+ export function boxBetween(a: { x: number; y: number }, b: { x: number; y: number }): Box {
42
+ return { left: Math.min(a.x, b.x), top: Math.min(a.y, b.y), right: Math.max(a.x, b.x), bottom: Math.max(a.y, b.y) };
43
+ }
44
+
45
+ /** The items a dragged box touches, in the order given. */
46
+ export function boxHits(items: readonly { key: string; box: Box }[], box: Box): string[] {
47
+ return items
48
+ .filter(({ box: b }) => b.left < box.right && b.right > box.left && b.top < box.bottom && b.bottom > box.top)
49
+ .map(({ key }) => key);
50
+ }
51
+
52
+ /** What a box picks: what it touches, or that on top of what was picked before it (`add`). */
53
+ export function boxPick(before: readonly string[], hits: readonly string[], add: boolean): string[] {
54
+ return add ? [...new Set([...before, ...hits])] : [...hits];
55
+ }
56
+
57
+ /**
58
+ * Where things can be moved: the top ('') and every folder (paths under assets/), less the folders being moved and
59
+ * the folders inside them (a folder cannot go into itself).
60
+ */
61
+ export function moveTargets(folders: readonly string[], moving: readonly string[]): string[] {
62
+ const inside = (dir: string) => moving.some((m) => dir === m || dir.startsWith(`${m}/`));
63
+ return ['', ...folders.filter((dir) => !inside(dir)).sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))];
64
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Where a pointer-anchored menu should be placed.
3
+ *
4
+ * Kept as a pure function because it only goes wrong **near the edges**, which is rarely hit
5
+ * on a dev machine: the menu grows toward the bottom right by default and only overflows when
6
+ * you right-click near the bottom-right corner of the screen, and the blocks at the end of the
7
+ * timeline are exactly what gets right-clicked most often.
8
+ *
9
+ * Two steps: check whether there is room to grow in the preferred direction (flip to the
10
+ * opposite one if not), then clamp into the viewport regardless. Clamping is not just a
11
+ * fallback: when the menu is taller than the screen (small window + long menu), flipping fails
12
+ * on both sides and the only correct answer is to pin it to the top edge and let the user
13
+ * scroll the menu's own layer.
14
+ */
15
+
16
+ /** Minimum distance between a menu and the viewport edge. A menu flush with the edge looks half cut off. */
17
+ export const MENU_EDGE = 8;
18
+
19
+ export interface MenuPlacement {
20
+ left: number;
21
+ top: number;
22
+ /** Grew in the opposite direction; the UI uses this to decide which end the animation starts from. */
23
+ flippedX: boolean;
24
+ flippedY: boolean;
25
+ }
26
+
27
+ export function menuPosition(opts: {
28
+ /** Pointer position (viewport coordinates); for a menu opened from a button, the corner it hangs from. */
29
+ at: { x: number; y: number };
30
+ /** Measured menu size. Pass 0 until measured: that frame is placed at the pointer, then corrected once measured. */
31
+ size: { width: number; height: number };
32
+ viewport: { width: number; height: number };
33
+ /**
34
+ * 'end': the menu's right edge sits at `at.x` (a "⋯" button at the right of a card), flipping to grow rightward
35
+ * when that would leave the left edge. Default 'start': its left edge at `at.x`.
36
+ */
37
+ align?: 'start' | 'end';
38
+ /**
39
+ * Where a menu that does not fit below ends when it flips up. A menu hanging under a button flips to sit above
40
+ * the button (its top), not above the point under it, or it would cover the button. Default `at.y`.
41
+ */
42
+ flipTo?: number;
43
+ }): MenuPlacement {
44
+ const { at, size, viewport, align = 'start' } = opts;
45
+ const flipTo = opts.flipTo ?? at.y;
46
+ const flippedX = size.width > 0 && (align === 'end'
47
+ ? at.x - size.width < MENU_EDGE
48
+ : at.x + size.width > viewport.width - MENU_EDGE);
49
+ const growsLeft = (align === 'end') !== flippedX;
50
+ /* flip up only when there is more room above: a long menu near the middle stays below and is clamped */
51
+ const flippedY = size.height > 0 && at.y + size.height > viewport.height - MENU_EDGE && flipTo > viewport.height - at.y;
52
+ return {
53
+ left: Math.max(
54
+ MENU_EDGE,
55
+ Math.min(growsLeft ? at.x - size.width : at.x, viewport.width - size.width - MENU_EDGE),
56
+ ),
57
+ top: Math.max(
58
+ MENU_EDGE,
59
+ Math.min(flippedY ? flipTo - size.height : at.y, viewport.height - size.height - MENU_EDGE),
60
+ ),
61
+ flippedX,
62
+ flippedY,
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Which item the arrow keys land on within a menu.
68
+ *
69
+ * `from < 0` means the keyboard has not been used yet: Down then lands on the **first** item
70
+ * and Up on the last. Navigation wraps around at the ends: menus are short and hitting an end
71
+ * is common, and "the key does nothing" would make the keyboard seem broken.
72
+ */
73
+ export function menuStep(from: number, delta: number, count: number): number {
74
+ if (count <= 0) return -1;
75
+ if (from < 0) return delta > 0 ? 0 : count - 1;
76
+ return (from + delta + count) % count;
77
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * A clip's box on the stage, as five numbers over the picture's own size (see stage-clip for film.html's `box`).
3
+ *
4
+ * x / y place the unrotated box's top-left corner from its origin (px). scaleX / scaleY scale the picture's own size.
5
+ * rotate turns it clockwise, in degrees, about its center. Resizing is stage-gesture's resizedFrame, on the part of the
6
+ * clip that shows (stage-crop).
7
+ *
8
+ * The origin is the box's `x` / `y` (default 0, 0 = the stage's top left).
9
+ */
10
+ import type { MgBox, MgTransform } from '@/lib/stage-types';
11
+
12
+ export type { MgBox, MgTransform };
13
+
14
+ function originOf(box: MgBox): { x: number; y: number } {
15
+ return { x: box.x ?? 0, y: box.y ?? 0 };
16
+ }
17
+
18
+ /** Where the unrotated box's top-left corner is on the stage. */
19
+ function topLeft(t: MgTransform, box: MgBox): { x: number; y: number } {
20
+ const o = originOf(box);
21
+ return { x: o.x + t.x, y: o.y + t.y };
22
+ }
23
+
24
+ export function mgSize(t: MgTransform, box: MgBox): { w: number; h: number } {
25
+ return { w: box.w * t.scaleX, h: box.h * t.scaleY };
26
+ }
27
+
28
+ export function moveTransform(t: MgTransform, dx: number, dy: number): MgTransform {
29
+ return { ...t, x: t.x + dx, y: t.y + dy };
30
+ }
31
+
32
+ /* ── snapping while dragging ── */
33
+
34
+ /** A snap guide: `at` is its place on the stage; an `x` guide is vertical, a `y` guide horizontal. */
35
+ export interface MgGuide {
36
+ axis: 'x' | 'y';
37
+ at: number;
38
+ }
39
+
40
+ /** The three places on an axis worth lining up: both edges and the middle. */
41
+ function anchorsOf(start: number, size: number): [number, number, number] {
42
+ return [start, start + size / 2, start + size];
43
+ }
44
+
45
+ /**
46
+ * The nearest of the targets' anchors to any of `mine` (places on one axis), within `tolerance`: how far to go, and
47
+ * the anchor reached. Ties go to the target that comes first.
48
+ */
49
+ export function nearestAnchor(
50
+ mine: readonly number[],
51
+ axis: 'x' | 'y',
52
+ targets: readonly { x: number; y: number; w: number; h: number }[],
53
+ tolerance: number,
54
+ ): { delta: number; at: number } | null {
55
+ let best: { delta: number; at: number } | null = null;
56
+ for (const target of targets) {
57
+ const theirs = axis === 'x' ? anchorsOf(target.x, target.w) : anchorsOf(target.y, target.h);
58
+ for (const a of mine) {
59
+ for (const b of theirs) {
60
+ const delta = b - a;
61
+ if (Math.abs(delta) > tolerance) continue;
62
+ if (!best || Math.abs(delta) < Math.abs(best.delta)) best = { delta, at: b };
63
+ }
64
+ }
65
+ }
66
+ return best;
67
+ }
68
+
69
+ /**
70
+ * Snap a move to the nearest anchor, and say which guides it snapped to.
71
+ *
72
+ * Each axis is judged on its own (lined up across, not down, is a normal half state). `tolerance` is in the units of
73
+ * the boxes; the caller converts its screen px. Returns the correction to add to the move, not a position. Ties go to
74
+ * the target that comes first: the stage is given first, and its middle is what people most want to hit.
75
+ */
76
+ export function snapMove(
77
+ moved: { x: number; y: number; w: number; h: number },
78
+ targets: readonly { x: number; y: number; w: number; h: number }[],
79
+ tolerance: number,
80
+ ): { dx: number; dy: number; guides: MgGuide[] } {
81
+ if (!(tolerance > 0)) return { dx: 0, dy: 0, guides: [] };
82
+ const sx = nearestAnchor(anchorsOf(moved.x, moved.w), 'x', targets, tolerance);
83
+ const sy = nearestAnchor(anchorsOf(moved.y, moved.h), 'y', targets, tolerance);
84
+ const guides: MgGuide[] = [];
85
+ if (sx) guides.push({ axis: 'x', at: sx.at });
86
+ if (sy) guides.push({ axis: 'y', at: sy.at });
87
+ return { dx: sx?.delta ?? 0, dy: sy?.delta ?? 0, guides };
88
+ }
89
+
90
+ export function rotateTransform(
91
+ t: MgTransform,
92
+ box: MgBox,
93
+ from: { x: number; y: number },
94
+ to: { x: number; y: number },
95
+ snapDeg = 0,
96
+ ): MgTransform {
97
+ const { w, h } = mgSize(t, box);
98
+ const at = topLeft(t, box);
99
+ const cx = at.x + w / 2;
100
+ const cy = at.y + h / 2;
101
+ const a0 = Math.atan2(from.y - cy, from.x - cx);
102
+ const a1 = Math.atan2(to.y - cy, to.x - cx);
103
+ let rotate = t.rotate + ((a1 - a0) * 180) / Math.PI;
104
+ if (snapDeg > 0) rotate = Math.round(rotate / snapDeg) * snapDeg;
105
+ return { ...t, rotate };
106
+ }
107
+
108
+ export function sameMgTransform(a?: MgTransform | null, b?: MgTransform | null): boolean {
109
+ if (!a || !b) return a === b;
110
+ return a.x === b.x && a.y === b.y && a.scaleX === b.scaleX && a.scaleY === b.scaleY && a.rotate === b.rotate;
111
+ }
@@ -0,0 +1,3 @@
1
+ /** Several things selected that differ in a value. */
2
+ export const MIXED: unique symbol = Symbol('mixed');
3
+ export type Mixed = typeof MIXED;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A page clip's "Changes inside the page" (the inspector's list of its film.html `overrides`), with the ones whose
3
+ * element the page no longer has. Only the film's page can tell which those are (studio/server/stage.js
4
+ * `lostOverrides`); the editor keeps its last answer per clip.
5
+ */
6
+ import type { FilmOverride } from './film';
7
+
8
+ /** What of film.html this reads: each clip's id and changes. */
9
+ export type FilmParts = { tracks: readonly { clips: readonly { id?: string; overrides?: readonly unknown[] }[] }[] };
10
+
11
+ /**
12
+ * The stage's answer (per page clip with changes, `{ clip, lost }`: the selectors of the changes its page no longer
13
+ * has) as a map from clip id to its lost selectors; anything malformed is left out.
14
+ */
15
+ export function lostByClip(answer: unknown): Map<string, readonly string[]> {
16
+ const out = new Map<string, readonly string[]>();
17
+ if (!Array.isArray(answer)) return out;
18
+ for (const entry of answer) {
19
+ if (!entry || typeof entry !== 'object') continue;
20
+ const { clip, lost } = entry as { clip?: unknown; lost?: unknown };
21
+ if (typeof clip !== 'string' || !Array.isArray(lost)) continue;
22
+ out.set(clip, lost.filter((at): at is string => typeof at === 'string'));
23
+ }
24
+ return out;
25
+ }
26
+
27
+ /**
28
+ * The changes of the clip at `loc` (`film.html#<track>.<clip>`) and the lost ones among them; null when it has none.
29
+ * A selector reported lost that the clip no longer has a change for (removed since) is dropped.
30
+ */
31
+ export function pageChangesAt(
32
+ film: FilmParts | null | undefined,
33
+ loc: string,
34
+ lost: ReadonlyMap<string, readonly string[]>,
35
+ ): { list: readonly FilmOverride[]; lost: readonly string[]; own: readonly FilmOverride[] } | null {
36
+ const m = /^film\.html#(\d+)\.(\d+)$/.exec(loc);
37
+ const clip = m ? film?.tracks[Number(m[1])]?.clips[Number(m[2])] : undefined;
38
+ const all = (clip?.overrides ?? []) as readonly FilmOverride[];
39
+ /* the clip's own entry (its fades, without `at`) is not a change inside the page: kept apart, and kept */
40
+ const list = all.filter((o) => o.at !== undefined);
41
+ const own = all.filter((o) => o.at === undefined);
42
+ if (!list.length) return null;
43
+ const gone = clip?.id ? lost.get(clip.id) ?? [] : [];
44
+ return { list, lost: gone.filter((at) => list.some((o) => o.at === at)), own };
45
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Whether files that changed in the project's folder can change the picture, so the preview draws it again. A page
3
+ * draws from what it loads (styles, code, pictures, data, fonts), and a clip plays its media file; what no page loads
4
+ * (notes, scripts, logs, an agent's own working files) cannot change it, and redrawing the whole film for each of them
5
+ * would make the preview reload over and over while an agent writes them.
6
+ */
7
+
8
+ /** files no page loads: words to read, programs to run, settings of tools */
9
+ const NOT_PICTURE = /\.(md|markdown|txt|log|py|sh|bash|zsh|rb|go|rs|java|c|cc|cpp|h|hpp|swift|kt|toml|ya?ml|ini|cfg|env|lock|patch|diff|bak|tmp|swp)$/i;
10
+
11
+ /**
12
+ * True when any of `paths` (in the project, with `/`) can change the picture: a page or anything a page may load, or a
13
+ * media file a clip plays (`used`), made again in place. Other media under assets/ are loaded as they are used. No
14
+ * paths (the watcher could not tell): true.
15
+ */
16
+ export function changesPicture(paths: readonly string[] | undefined, used: ReadonlySet<string | undefined>): boolean {
17
+ if (!paths) return true;
18
+ return paths.some((path) => (path.startsWith('assets/') ? used.has(path) : !NOT_PICTURE.test(path)));
19
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The preview's switches, kept on this machine across projects:
3
+ * · `fitPages` (on by default): pages draw their canvas and 3D at the size the viewer shows them (their
4
+ * devicePixelRatio is the viewer's, see editor/Preview), not at the film's full size scaled down: the same picture
5
+ * in the viewer, for a fraction of the drawing. Exports always draw at full size.
6
+ * · `renders` (on by default): a stretch that plays late is rendered in the background once the film stops, and
7
+ * played from that render from then on (studio/server/preview-cache.mjs, editor/use-preview-renders).
8
+ * · `stats` (off by default, Settings → Developer): the viewer's corner says how playing goes.
9
+ */
10
+ import { useSyncExternalStore } from 'react';
11
+
12
+ export type PreviewPrefs = { fitPages: boolean; renders: boolean; stats: boolean };
13
+ type Pref = keyof PreviewPrefs;
14
+
15
+ const DEFAULTS: PreviewPrefs = { fitPages: true, renders: true, stats: false };
16
+ const KEY: Record<Pref, string> = { fitPages: 'openfilm.preview.fitPages', renders: 'openfilm.preview.renders', stats: 'openfilm.preview.stats' };
17
+
18
+ /** Without localStorage (private window), the choices last this session. */
19
+ const memory: Partial<PreviewPrefs> = {};
20
+ const listeners = new Set<() => void>();
21
+ let current: PreviewPrefs | null = null;
22
+
23
+ function read(pref: Pref): boolean {
24
+ try {
25
+ const raw = localStorage.getItem(KEY[pref]);
26
+ if (raw != null) return raw !== '0';
27
+ } catch { /* no localStorage: this session's choice */ }
28
+ return memory[pref] ?? DEFAULTS[pref];
29
+ }
30
+
31
+ export function readPreviewPrefs(): PreviewPrefs {
32
+ current ??= { fitPages: read('fitPages'), renders: read('renders'), stats: read('stats') };
33
+ return current;
34
+ }
35
+
36
+ export function writePreviewPref(pref: Pref, on: boolean): void {
37
+ memory[pref] = on;
38
+ try { localStorage.setItem(KEY[pref], on ? '1' : '0'); } catch { /* not stored: `memory` has it */ }
39
+ current = { ...readPreviewPrefs(), [pref]: on };
40
+ for (const fn of listeners) fn();
41
+ }
42
+
43
+ /** The switches, kept up to date wherever one is changed. */
44
+ export function usePreviewPrefs(): PreviewPrefs {
45
+ return useSyncExternalStore((fn) => { listeners.add(fn); return () => { listeners.delete(fn); }; }, readPreviewPrefs, () => DEFAULTS);
46
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Which stretches of a film play late in the preview, from how playing went (editor/Preview's `onPlayed`, about once a
3
+ * second while playing): the refreshes the screen had per half second of film, and how many of them got no new frame.
4
+ * A stretch where many did is rendered in the background (studio/server/preview-cache.mjs) and played from that
5
+ * render from then on. Kept free of React so it is tested as it is.
6
+ */
7
+
8
+ /** Per half second of film (its start, s): refreshes seen there while playing, and how many got no new frame. */
9
+ export type Heat = Map<number, { frames: number; late: number }>;
10
+
11
+ /** Refreshes a half second keeps counting before older ones count less: what it does now outweighs what it did. */
12
+ const HEAT_KEEP = 120;
13
+ /** Late on this share of refreshes or more, a half second plays late. */
14
+ export const LATE_SHARE = 0.12;
15
+ /** Refreshes a half second needs before it is judged (a stretch only glimpsed says little). */
16
+ const HEAT_SEEN = 20;
17
+ /** A stretch is rendered with this much more on each side (s), and at least this long. */
18
+ const PAD = 0.25;
19
+ const SHORTEST = 1;
20
+
21
+ /** Add how playing went (`spans`: [half second's start, refreshes, late]). */
22
+ export function addHeat(heat: Heat, spans: readonly (readonly [number, number, number])[]): void {
23
+ for (const [t, frames, late] of spans) {
24
+ if (!(frames > 0) || !Number.isFinite(t)) continue;
25
+ const had = heat.get(t) ?? { frames: 0, late: 0 };
26
+ let next = { frames: had.frames + frames, late: had.late + Math.min(late, frames) };
27
+ if (next.frames > HEAT_KEEP) {
28
+ const k = HEAT_KEEP / next.frames;
29
+ next = { frames: next.frames * k, late: next.late * k };
30
+ }
31
+ heat.set(t, next);
32
+ }
33
+ }
34
+
35
+ /**
36
+ * The stretches that play late, film seconds [a, b): half seconds late on LATE_SHARE of their refreshes or more,
37
+ * joined across a gap of half a second, PAD more on each side, SHORTEST at least, in order.
38
+ */
39
+ export function lateStretches(heat: Heat): [number, number][] {
40
+ const late = [...heat].filter(([, h]) => h.frames >= HEAT_SEEN && h.late / h.frames >= LATE_SHARE).map(([t]) => t).sort((a, b) => a - b);
41
+ const out: [number, number][] = [];
42
+ for (const t of late) {
43
+ const last = out.at(-1);
44
+ if (last && t <= last[1] + 0.5 + 1e-9) last[1] = t + 0.5;
45
+ else out.push([t, t + 0.5]);
46
+ }
47
+ return out.map(([a, b]) => {
48
+ let from = Math.max(0, a - PAD);
49
+ let to = b + PAD;
50
+ if (to - from < SHORTEST) { const mid = (from + to) / 2; from = Math.max(0, mid - SHORTEST / 2); to = from + SHORTEST; }
51
+ return [round(from), round(to)] as [number, number];
52
+ });
53
+ }
54
+
55
+ const round = (s: number) => Math.round(s * 1000) / 1000;
56
+
57
+ /** Two lists of stretches the same. */
58
+ export function sameStretches(a: readonly (readonly [number, number])[], b: readonly (readonly [number, number])[]): boolean {
59
+ return a.length === b.length && a.every(([x, y], i) => x === b[i][0] && y === b[i][1]);
60
+ }
61
+
62
+ /** Stretches as one list: in order, those that overlap or touch joined. */
63
+ export function joinStretches(list: readonly (readonly [number, number])[]): [number, number][] {
64
+ const out: [number, number][] = [];
65
+ for (const [a, b] of [...list].sort((x, y) => x[0] - y[0])) {
66
+ const last = out.at(-1);
67
+ if (last && a <= last[1] + 1e-9) last[1] = Math.max(last[1], b);
68
+ else out.push([a, b]);
69
+ }
70
+ return out;
71
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Which of the projects whose delete seemed to fail are still there, by the list read again (`list`): one gone already
3
+ * was deleted, and one whose answer was lost (`lost`: the request was cut off, as when the page showing it leaves
4
+ * it) may still be on its way to the Trash, so the list is read again a few times (`wait` between) before it counts.
5
+ * A list that cannot be read changes nothing.
6
+ */
7
+ export async function stillThere<F extends { id: string; lost: boolean }>(
8
+ failed: readonly F[],
9
+ list: () => Promise<readonly string[]>,
10
+ { tries = 6, wait = () => new Promise<void>((done) => setTimeout(done, 500)) }: { tries?: number; wait?: () => Promise<void> } = {},
11
+ ): Promise<F[]> {
12
+ let left = [...failed];
13
+ for (let i = 0; left.length && i < tries; i++) {
14
+ if (i) await wait();
15
+ const ids = await list().then((l) => new Set(l), () => null);
16
+ if (ids) left = left.filter((f) => ids.has(f.id));
17
+ if (!left.some((f) => f.lost)) break;
18
+ }
19
+ return left;
20
+ }