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,101 @@
1
+ /**
2
+ * A project's chat record, kept by the app in the project's own folder (`.film/chat`, src/chat-store.mjs): its
3
+ * conversations, the one shown last, and each finished turn with what the chat shows of it. Read once when the
4
+ * project opens (openProjectChat), held here so the chat reads it as it always has, and written back as it changes.
5
+ */
6
+ import React from 'react';
7
+ import type { Turn } from '@openfilm/shared';
8
+ import type { TranscriptItem } from '@/lib/agent-transcript';
9
+ import type { ProjectSessionItem } from '@/lib/project-sessions';
10
+ import { app } from '../app-bridge';
11
+
12
+ export interface SavedTurn { turn: Turn; items: TranscriptItem[] }
13
+ interface ProjectChat { sessions: ProjectSessionItem[]; last: string | null; turns: SavedTurn[] }
14
+
15
+ const projects = new Map<string, ProjectChat>();
16
+ const loading = new Map<string, Promise<void>>();
17
+ /** which project each folder's record was read for: a project deleted and another made in its folder (a new
18
+ * "Untitled" where the last one was) is not the same project, and does not get its chat */
19
+ const readFor = new Map<string, string>();
20
+ const EMPTY: ProjectChat = { sessions: [], last: null, turns: [] };
21
+
22
+ /**
23
+ * Read the project's record from its folder (once a launch, and again when another project takes the folder); the
24
+ * chat waits for it before it shows the project. `project`: its folder; `id`: Studio's id for it.
25
+ */
26
+ export function openProjectChat(project: string, id: string): Promise<void> {
27
+ if (readFor.get(project) !== id) {
28
+ readFor.set(project, id);
29
+ projects.delete(project);
30
+ }
31
+ if (projects.has(project)) return Promise.resolve();
32
+ const key = `${id}\0${project}`;
33
+ const inFlight = loading.get(key);
34
+ if (inFlight) return inFlight;
35
+ const mine = () => readFor.get(project) === id;
36
+ const pending: Promise<void> = app.chat.load(project)
37
+ .then((chat) => {
38
+ if (!mine()) return;
39
+ projects.set(project, {
40
+ sessions: Array.isArray(chat?.sessions) ? chat.sessions as ProjectSessionItem[] : [],
41
+ last: typeof chat?.last === 'string' ? chat.last : null,
42
+ turns: Array.isArray(chat?.turns) ? chat.turns as SavedTurn[] : [],
43
+ });
44
+ })
45
+ /* not readable (not one of Studio's projects yet, a folder gone): an empty record, written once it changes */
46
+ .catch(() => { if (mine()) projects.set(project, { ...EMPTY, sessions: [], turns: [] }); })
47
+ .finally(() => { loading.delete(key); });
48
+ loading.set(key, pending);
49
+ return pending;
50
+ }
51
+
52
+ const record = (project: string): ProjectChat => {
53
+ let chat = projects.get(project);
54
+ if (!chat) { chat = { ...EMPTY, sessions: [], turns: [] }; projects.set(project, chat); }
55
+ return chat;
56
+ };
57
+
58
+ /* the conversations are written a moment after the last change: renaming one, or a turn touching it, is a burst */
59
+ const sessionWrites = new Map<string, ReturnType<typeof setTimeout>>();
60
+ function writeSessions(project: string) {
61
+ clearTimeout(sessionWrites.get(project));
62
+ sessionWrites.set(project, setTimeout(() => {
63
+ sessionWrites.delete(project);
64
+ const chat = record(project);
65
+ void app.chat.saveSessions(project, chat.sessions, chat.last).catch(() => { /* written next time */ });
66
+ }, 150));
67
+ }
68
+
69
+ export const chatSessions = (project: string): ProjectSessionItem[] => record(project).sessions;
70
+ export function setChatSessions(project: string, sessions: ProjectSessionItem[]) {
71
+ record(project).sessions = sessions;
72
+ writeSessions(project);
73
+ }
74
+
75
+ export const lastSession = (project: string): string | null => record(project).last;
76
+ export function setLastSession(project: string, id: string) {
77
+ const chat = record(project);
78
+ if (chat.last === id) return;
79
+ chat.last = id;
80
+ writeSessions(project);
81
+ }
82
+
83
+ export const chatTurns = (project: string): SavedTurn[] => record(project).turns;
84
+ export function putChatTurn(project: string, entry: SavedTurn) {
85
+ const chat = record(project);
86
+ chat.turns = [...chat.turns.filter((t) => t.turn.id !== entry.turn.id), entry];
87
+ void app.chat.saveTurn(project, entry.turn, entry.items).catch(() => { /* kept for this launch */ });
88
+ }
89
+
90
+ /** Whether `project`'s record has been read (reading it if not): the chat shows the project once it has. */
91
+ export function useProjectChat(project: { id: string; path: string } | null): boolean {
92
+ const key = project ? `${project.id}\0${project.path}` : null;
93
+ const [loaded, setLoaded] = React.useState<string | null>(() => (project && readFor.get(project.path) === project.id && projects.has(project.path) ? key : null));
94
+ React.useEffect(() => {
95
+ if (!project || !key) return undefined;
96
+ let alive = true;
97
+ void openProjectChat(project.path, project.id).then(() => { if (alive) setLoaded(key); });
98
+ return () => { alive = false; };
99
+ }, [key]);
100
+ return key != null && loaded === key;
101
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Putting things into the composer: dropped, pasted or picked from "+", all take this path.
3
+ *
4
+ * Two kinds: images go into the first message as references; everything else goes the files way, as a path only.
5
+ * Sorting turns nothing away, for format, count or size.
6
+ *
7
+ * Sorting is kept apart from reading bytes: the first is pure and tested with plain objects; the second only
8
+ * makes sense in a browser (FileReader) and has no branches worth testing.
9
+ */
10
+ import { TURN_ATTACHMENT_THUMB_MAX_CHARS } from '@openfilm/shared';
11
+ import {
12
+ DOCUMENT_MEDIA_TYPES,
13
+ IMAGE_EXTENSIONS,
14
+ VIDEO_UPLOAD_EXTENSIONS,
15
+ } from './upload-limits';
16
+
17
+ /** A batch of files sorted in two. Nothing is refused: not for format, count or size. */
18
+ export interface AttachmentPlan<T> {
19
+ images: T[];
20
+ documents: T[];
21
+ }
22
+
23
+ /** The file name's extension (lower case, no dot); empty when there is no dot. */
24
+ function extensionOf(name: string): string {
25
+ const dot = name.lastIndexOf('.');
26
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : '';
27
+ }
28
+
29
+ /**
30
+ * Whether this is an image.
31
+ *
32
+ * MIME first: a pasted screenshot often has no useful name (Chrome says "image.png", Safari sometimes nothing),
33
+ * so the extension tells nothing. Only without a MIME type does the extension decide, as for some files dragged
34
+ * from Finder.
35
+ */
36
+ export function isImageAttachment(file: { name: string; type: string }): boolean {
37
+ if (file.type.startsWith('image/')) return true;
38
+ return (IMAGE_EXTENSIONS as readonly string[]).includes(extensionOf(file.name));
39
+ }
40
+
41
+ /**
42
+ * Whether this file goes as a path: everything but images.
43
+ *
44
+ * The model never reads a file's bytes; pdf or mp4, it gets a path, and a path works for any format. So this asks
45
+ * only "is it an image", not "what format is it".
46
+ */
47
+ export function isDocumentAttachment(file: { name: string; type: string }): boolean {
48
+ return !isImageAttachment(file);
49
+ }
50
+
51
+ /** Whether this is a video: MIME first, the extension only without one (as in isImageAttachment). */
52
+ export function isVideoAttachment(file: { name: string; type?: string; mediaType?: string }): boolean {
53
+ const mime = file.type || file.mediaType || '';
54
+ if (mime.startsWith('video/')) return true;
55
+ return VIDEO_UPLOAD_EXTENSIONS.has(extensionOf(file.name));
56
+ }
57
+
58
+ /** The mediaType sent on: from the table, else the one the browser reports. */
59
+ export function documentMediaType(file: { name: string; type: string }): string {
60
+ return DOCUMENT_MEDIA_TYPES[extensionOf(file.name)] ?? file.type ?? 'application/octet-stream';
61
+ }
62
+
63
+ /**
64
+ * Splits the batch into images and paths, in the order the person attached them. None is turned away.
65
+ *
66
+ * Generic only so tests can pass plain { name, type } objects; callers pass File[].
67
+ */
68
+ export function planComposerAttachments<T extends { name: string; type: string }>(
69
+ files: readonly T[],
70
+ ): AttachmentPlan<T> {
71
+ const images: T[] = [];
72
+ const documents: T[] = [];
73
+ for (const file of files) {
74
+ if (isImageAttachment(file)) images.push(file);
75
+ else documents.push(file);
76
+ }
77
+ return { images, documents };
78
+ }
79
+
80
+ /**
81
+ * The files in a paste or a drop.
82
+ *
83
+ * `files` is the standard place, but Safari sometimes pastes into `items` only and leaves `files` empty, so both
84
+ * are read.
85
+ */
86
+ export function filesFromTransfer(data: DataTransfer | null | undefined): File[] {
87
+ if (!data) return [];
88
+ if (data.files && data.files.length > 0) return Array.from(data.files);
89
+ return Array.from(data.items ?? [])
90
+ .filter((item) => item.kind === 'file')
91
+ .map((item) => item.getAsFile())
92
+ .filter((file): file is File => file !== null);
93
+ }
94
+
95
+ /** Whether this drop or paste carries files from the system (not text, nor a drag inside the app). */
96
+ export function transferHasFiles(data: DataTransfer | null | undefined): boolean {
97
+ return !!data && Array.from(data.types).includes('Files');
98
+ }
99
+
100
+ /**
101
+ * Thumbnail edge sizes, tried largest first.
102
+ *
103
+ * 320 so it still reads when opened: this copy is not only the 40px tile in the message but the only thing the
104
+ * person can open later; the original goes into the agent's workspace and never comes back to the UI.
105
+ *
106
+ * Over the limit, the next size is tried rather than giving up: a busy photo may not fit at 320 but is still
107
+ * recognizable at 224, which is all this copy is for.
108
+ */
109
+ const THUMB_STEPS = [320, 224, 160] as const;
110
+
111
+ /**
112
+ * A thumbnail that stays in the conversation.
113
+ *
114
+ * The original image goes to the agent's workspace and never comes back to the UI; without a copy, the person
115
+ * cannot tell later which image they sent. So a copy of some tens of KB is kept with the message.
116
+ *
117
+ * webp, not jpeg: screenshots are often UI with transparent backgrounds, which jpeg turns black. Without webp
118
+ * support toDataURL falls back to png, which is larger, hence the sizes.
119
+ * Any failure returns undefined: a missing thumbnail must not block a message.
120
+ */
121
+ export function makeAttachmentThumb(dataUrl: string): Promise<string | undefined> {
122
+ return new Promise((resolve) => {
123
+ const image = new Image();
124
+ image.onload = () => {
125
+ try {
126
+ const canvas = document.createElement('canvas');
127
+ const ctx = canvas.getContext('2d');
128
+ if (!ctx) return resolve(undefined);
129
+ for (const edge of THUMB_STEPS) {
130
+ const longest = Math.max(image.naturalWidth, image.naturalHeight);
131
+ // An SVG may have no intrinsic size (naturalWidth 0): fill the edge, don't divide by zero.
132
+ const scale = longest > 0 ? Math.min(1, edge / longest) : 1;
133
+ canvas.width = Math.max(1, Math.round((image.naturalWidth || edge) * scale));
134
+ canvas.height = Math.max(1, Math.round((image.naturalHeight || edge) * scale));
135
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
136
+ ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
137
+ const thumb = canvas.toDataURL('image/webp', 0.75);
138
+ if (thumb.length <= TURN_ATTACHMENT_THUMB_MAX_CHARS) return resolve(thumb);
139
+ }
140
+ resolve(undefined);
141
+ } catch {
142
+ // A cross-origin image taints the canvas and toDataURL throws; inputs here are data URLs, but still.
143
+ resolve(undefined);
144
+ }
145
+ };
146
+ image.onerror = () => resolve(undefined);
147
+ image.src = dataUrl;
148
+ });
149
+ }
150
+
151
+ /**
152
+ * One frame of a video as a data URL, the thumbnail for its tile.
153
+ *
154
+ * At 1 s (a tenth in for short clips), not frame 0: many clips fade in from black.
155
+ * Formats the browser can't decode (often mkv, avi) end in onerror, and the caller keeps the file-name tile.
156
+ */
157
+ export function videoFrameDataUrl(file: Blob): Promise<string> {
158
+ return new Promise((resolve, reject) => {
159
+ const url = URL.createObjectURL(file);
160
+ const video = document.createElement('video');
161
+ const end = (frame?: string, error?: unknown) => {
162
+ video.removeAttribute('src'); video.load(); URL.revokeObjectURL(url);
163
+ if (frame) resolve(frame); else reject(error ?? new Error('no frame'));
164
+ };
165
+ video.muted = true;
166
+ video.preload = 'auto';
167
+ video.onloadedmetadata = () => { video.currentTime = Math.min(1, (video.duration || 0) * 0.1); };
168
+ video.onseeked = () => {
169
+ try {
170
+ const canvas = document.createElement('canvas');
171
+ canvas.width = video.videoWidth;
172
+ canvas.height = video.videoHeight;
173
+ canvas.getContext('2d')?.drawImage(video, 0, 0);
174
+ end(canvas.toDataURL('image/jpeg', 0.85));
175
+ } catch (error) { end(undefined, error); }
176
+ };
177
+ video.onerror = () => end(undefined, video.error);
178
+ video.src = url;
179
+ });
180
+ }
181
+
182
+ export function readFileDataUrl(file: Blob): Promise<string> {
183
+ return new Promise((resolve, reject) => {
184
+ const reader = new FileReader();
185
+ reader.onload = () => resolve(String(reader.result));
186
+ reader.onerror = () => reject(reader.error ?? new Error('read failed'));
187
+ reader.readAsDataURL(file);
188
+ });
189
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The files brought into a message (dragged in, pasted, picked with +): a row of thumbnails (a picture's own, a
3
+ * video's frame) and names above the sentence. Any number, and folders too. A file or folder from the disk stays where
4
+ * it is: the message names its path, as an agent's own chat does, and the agent copies into the project what the film
5
+ * uses. Only a pasted one, which has no path, is kept in the project (.film/attachments/). The message says where they
6
+ * are (attachedText), the bubble shows their names and thumbnails. Pictures are also sent to the agent beside the text,
7
+ * as one pasted in its own chat is, to an agent that takes pictures (attachedPictures).
8
+ */
9
+ import React from 'react';
10
+ import type { TurnAttachment } from '@openfilm/shared';
11
+ import { isImageAttachment, isVideoAttachment, makeAttachmentThumb, readFileDataUrl, videoFrameDataUrl } from '@/lib/composer-attachments';
12
+ import { attachmentKind } from '@/lib/project-resources';
13
+ import { app } from '../app-bridge';
14
+ import type { PromptImage } from '@/lib/desktop-bridge';
15
+ import { attachedPicture } from '@/lib/ref-pictures';
16
+
17
+ export interface DesktopAttachment {
18
+ id: string;
19
+ name: string;
20
+ kind: 'image' | 'document';
21
+ mediaType: string;
22
+ size: number;
23
+ /** a folder from the disk: its path names it, nothing in it is read */
24
+ folder?: boolean;
25
+ /** the file itself, while it is in the composer: an image shows it, and any one opens it to look at (AttachmentPreview) */
26
+ previewUrl?: string;
27
+ /** the small picture kept with the message: an image's, or a frame of a video */
28
+ thumb?: string;
29
+ state: 'pending' | 'ready' | 'failed';
30
+ /** where it is: its own path on the disk, or where a pasted one was kept in the project */
31
+ path?: string;
32
+ /** the file, until the message goes: a picture is read for its agent only then, and only as many as are sent */
33
+ file?: File;
34
+ error?: string;
35
+ }
36
+
37
+ /** at most this many pictures go with a message (the app sends no more: src/local-agents-ipc.mjs PROMPT_IMAGES_MAX) */
38
+ export const PROMPT_PICTURES_MAX = 20;
39
+
40
+ /** thumbnails made at once: a folder's worth of photos read all together would hold every one of them in memory */
41
+ const THUMBS_AT_ONCE = 4;
42
+
43
+ const SIZE_UNITS = ['B', 'KB', 'MB', 'GB'];
44
+ export function formatFileSize(bytes: number): string {
45
+ let value = bytes;
46
+ let unit = 0;
47
+ while (value >= 1024 && unit < SIZE_UNITS.length - 1) { value /= 1024; unit += 1; }
48
+ return `${unit ? value.toFixed(value < 10 ? 1 : 0) : value} ${SIZE_UNITS[unit]}`;
49
+ }
50
+
51
+ export function useDesktopAttachments(project: string) {
52
+ const [files, setFiles] = React.useState<DesktopAttachment[]>([]);
53
+ const patch = (id: string, next: Partial<DesktopAttachment>) => setFiles((list) => list.map((f) => (f.id === id ? { ...f, ...next } : f)));
54
+
55
+ const thumbs = React.useRef<{ running: number; waiting: Array<() => Promise<void>> }>({ running: 0, waiting: [] });
56
+ const thumbnail = React.useCallback((make: () => Promise<void>) => {
57
+ const queue = thumbs.current;
58
+ const next = () => {
59
+ if (queue.running >= THUMBS_AT_ONCE) return;
60
+ const job = queue.waiting.shift();
61
+ if (!job) return;
62
+ queue.running += 1;
63
+ void job().catch(() => {}).finally(() => { queue.running -= 1; next(); });
64
+ };
65
+ queue.waiting.push(make);
66
+ next();
67
+ }, []);
68
+
69
+ const add = React.useCallback((incoming: File[]) => {
70
+ const bridge = (window as unknown as { openfilmDesktop?: { pathForFile?: (f: File) => string } }).openfilmDesktop;
71
+ const items = incoming.map((file) => {
72
+ const image = isImageAttachment(file);
73
+ const item: DesktopAttachment = {
74
+ id: `a${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`,
75
+ name: file.name || (image ? 'image.png' : 'file'),
76
+ kind: image ? 'image' : 'document',
77
+ mediaType: file.type,
78
+ size: file.size,
79
+ previewUrl: URL.createObjectURL(file),
80
+ state: 'pending',
81
+ file,
82
+ };
83
+ return { item, file, path: bridge?.pathForFile?.(file) ?? '' };
84
+ });
85
+ setFiles((list) => [...list, ...items.map(({ item }) => item)]);
86
+ for (const { item, file } of items) {
87
+ if (item.kind === 'image') {
88
+ thumbnail(() => readFileDataUrl(file).then(makeAttachmentThumb).then((thumb) => { if (thumb) patch(item.id, { thumb }); }));
89
+ } else if (isVideoAttachment(file)) {
90
+ /* a video shows a frame of itself, as a picture does: its name alone says little of what was sent */
91
+ thumbnail(() => videoFrameDataUrl(file).then(makeAttachmentThumb).then((thumb) => { if (thumb) patch(item.id, { thumb }); }));
92
+ }
93
+ }
94
+ /* from the disk: its path, once the app has said whether it is a file or a folder (nothing is copied) */
95
+ const onDisk = items.filter(({ path }) => path);
96
+ if (onDisk.length) {
97
+ void app.chat.describePaths(onDisk.map(({ path }) => path))
98
+ .then((found) => onDisk.forEach(({ item }, i) => {
99
+ const one = found[i];
100
+ if (!one || 'error' in one) { patch(item.id, { state: 'failed', error: one?.error ?? 'That is not a file.' }); return; }
101
+ if (one.folder) {
102
+ URL.revokeObjectURL(item.previewUrl!);
103
+ patch(item.id, { state: 'ready', path: one.path, folder: true, kind: 'document', previewUrl: undefined, file: undefined, thumb: undefined });
104
+ } else patch(item.id, { state: 'ready', path: one.path, size: one.size ?? item.size });
105
+ }))
106
+ .catch((error: unknown) => onDisk.forEach(({ item }) => patch(item.id, { state: 'failed', error: error instanceof Error ? error.message : String(error) })));
107
+ }
108
+ /* pasted: no path of its own, so its bytes are kept in the project */
109
+ for (const { item, file, path } of items) {
110
+ if (path) continue;
111
+ void file.arrayBuffer()
112
+ .then((bytes) => app.chat.saveAttachment(project, item.name, bytes))
113
+ .then((r) => patch(item.id, { state: 'ready', path: r.path }))
114
+ .catch((error: unknown) => patch(item.id, { state: 'failed', error: error instanceof Error ? error.message : String(error) }));
115
+ }
116
+ }, [project, thumbnail]);
117
+
118
+ const drop = React.useCallback((id: string) => {
119
+ setFiles((list) => {
120
+ const gone = list.find((f) => f.id === id);
121
+ if (gone?.previewUrl) URL.revokeObjectURL(gone.previewUrl);
122
+ return list.filter((f) => f.id !== id);
123
+ });
124
+ }, []);
125
+
126
+ /** Hand over the files for sending; the composer is empty of them after. */
127
+ const current = React.useRef(files);
128
+ current.current = files;
129
+ const take = React.useCallback((): DesktopAttachment[] => {
130
+ const taken = current.current;
131
+ /* sent: the message keeps their thumbnails, the agent their paths */
132
+ for (const f of taken) if (f.previewUrl) URL.revokeObjectURL(f.previewUrl);
133
+ current.current = [];
134
+ setFiles([]);
135
+ return taken;
136
+ }, []);
137
+
138
+ return { files, add, drop, take };
139
+ }
140
+
141
+ /** What still keeps the message from going: a file still on its way, or one that could not be attached. */
142
+ export function attachmentBlocker(files: readonly DesktopAttachment[], t: (path: string) => string): string | null {
143
+ if (files.some((f) => f.state === 'pending')) return t('composer.filesPending');
144
+ const failed = files.filter((f) => f.state === 'failed');
145
+ return failed.length
146
+ ? t('composer.filesFailed').replace('{names}', failed.map((f) => f.name).join(', ')).replace('{error}', failed[0]!.error ?? '')
147
+ : null;
148
+ }
149
+
150
+ /** After the message, for the agent: where the files are. A folder's path ends in "/". */
151
+ export function attachedText(files: readonly DesktopAttachment[]): string {
152
+ const placed = files.filter((f) => f.path);
153
+ if (!placed.length) return '';
154
+ const line = (f: DesktopAttachment) => (f.folder
155
+ ? `- ${f.path!.replace(/[\\/]+$/, '')}/ (folder)`
156
+ : `- ${f.path} (${attachmentKind({ name: f.name, mediaType: f.mediaType })}, ${formatFileSize(f.size)})`);
157
+ return `\n\nAttached files:\n${placed.map(line).join('\n')}`;
158
+ }
159
+
160
+ /**
161
+ * The pictures among them, as their agent is sent them: the first `room`, in the order they were attached (the rest go
162
+ * by their path only). Read now, as the message goes: a folder's worth of photos would not all be sent anyway.
163
+ */
164
+ export async function attachedPictures(files: readonly DesktopAttachment[], room: number): Promise<PromptImage[]> {
165
+ const wanted = files.filter((f) => f.kind === 'image' && f.file).slice(0, Math.max(0, room));
166
+ const made = await Promise.all(wanted.map((f) => attachedPicture(f.file!)));
167
+ return made.filter((picture): picture is PromptImage => picture !== null);
168
+ }
169
+
170
+ /** What the message keeps of them, to show in its bubble (a folder's name ends in "/", and its path opens it). */
171
+ export function turnAttachments(files: readonly DesktopAttachment[]): TurnAttachment[] {
172
+ return files.map((f) => ({
173
+ kind: f.kind,
174
+ name: f.folder ? `${f.name}/` : f.name,
175
+ ...(f.thumb ? { thumb: f.thumb } : {}),
176
+ ...(f.folder && f.path ? { path: f.path } : {}),
177
+ }));
178
+ }