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,415 @@
1
+ /**
2
+ * The window is one page: Studio's editor, and the chat in the column Studio leaves for it. This is what each gets
3
+ * from the other and from the app, in the page:
4
+ *
5
+ * · window.openfilmHost Studio's host (its lib/host.ts StudioHost): the window's controls, the chat's column,
6
+ * "Reference in chat", Settings → Agents.
7
+ * · window.openfilmDesktop the chat's bridge (chat/src/app-bridge.ts and lib/desktop-bridge.ts).
8
+ *
9
+ * What only the app can do (the agents, a file on the disk, the window, updates) is the preload's `openfilmNative`
10
+ * (src/preload.cjs). Imported before Studio's modules and the chat's: Studio reads its host as it loads.
11
+ */
12
+ import { flushSync } from 'react-dom';
13
+ import type { CommunityItem, HostAgent, HostAsk, HostProject as Project, HostUpdate, HostUpdateState, StudioCommand, StudioHost, StudioRef, StudioSelection } from 'openfilm/studio-ui/lib/host.ts';
14
+
15
+ type Listener<T> = (value: T) => void;
16
+ type Stop = () => void;
17
+ type Theme = 'light' | 'dark';
18
+
19
+ interface Native {
20
+ platform: string;
21
+ hello(): Promise<{ flavor: string; version: string; ffmpeg: unknown; update: unknown; chatOpen: boolean; chatWidth: number; chatMin: number; studioMin: number; fullScreen: boolean }>;
22
+ pathForFile(file: File): string;
23
+ revealPath(path: string): Promise<void>;
24
+ pickFolder(options?: { title?: string; button?: string; at?: string }): Promise<string | null>;
25
+ capture(rect: { x: number; y: number; width: number; height: number }): Promise<string | null>;
26
+ agents: NonNullable<StudioHost['agents']>;
27
+ /* the window's tabs (src/tabs.mjs) */
28
+ tabs: NonNullable<StudioHost['tabs']>;
29
+ localAgents: Record<string, unknown>;
30
+ chat: Record<string, (...args: never[]) => Promise<unknown>>;
31
+ /* the composer's Skills button (src/skills-ipc.mjs) */
32
+ skills: Record<string, (...args: never[]) => Promise<unknown>>;
33
+ prefs: { get(): Promise<AppPrefs>; set(next: Partial<AppPrefs>): Promise<unknown>; onChange(listener: Listener<AppPrefs>): Stop };
34
+ saveLayout(layout: { chatOpen: boolean; chatWidth: number }): void;
35
+ setTheme(theme: Theme): void;
36
+ setLanguage(language: string): void;
37
+ onFullScreen(listener: Listener<boolean>): Stop;
38
+ onToggleChat(listener: Listener<void>): Stop;
39
+ onOpenSettings(listener: Listener<void>): Stop;
40
+ onStudioBrowser(listener: Listener<unknown>): Stop;
41
+ onFfmpeg(listener: Listener<unknown>): Stop;
42
+ onUpdate(listener: Listener<unknown>): Stop;
43
+ restartToUpdate(): Promise<{ ok: boolean; error?: string }>;
44
+ updateState(): Promise<unknown>;
45
+ checkForUpdate(): Promise<unknown>;
46
+ }
47
+
48
+ const native = (window as unknown as { openfilmNative: Native }).openfilmNative;
49
+ const hello = native.hello();
50
+
51
+ /** A value that tells its listeners when it changes. */
52
+ function signal<T>(value: T) {
53
+ const listeners = new Set<Listener<T>>();
54
+ return {
55
+ get: () => value,
56
+ set(next: T) { value = next; for (const listener of listeners) listener(next); },
57
+ on(listener: Listener<T>): Stop { listeners.add(listener); return () => { listeners.delete(listener); }; },
58
+ };
59
+ }
60
+ const events = <T,>() => signal<T>(undefined as T);
61
+
62
+ /* ── the chat's column: open or not, how wide, and the assets drawer over it ── */
63
+ const TOP_BAR = 32;
64
+ const column = document.getElementById('of-chat')!;
65
+ const layout = { open: true, width: 420, min: 340, studioMin: 640 };
66
+ /** the assets open in the column (Studio draws them, above the composer); `bottom`: the composer's height, all the
67
+ chat shows then */
68
+ const assets = { open: false, bottom: 120 };
69
+ const assetsSignal = signal(false);
70
+ const chatOpenSignal = signal(true);
71
+
72
+ /** The column's width as the window allows it: Studio keeps its least width, the chat its own while it fits. */
73
+ const shownWidth = () => (layout.open ? Math.max(Math.min(layout.width, innerWidth - layout.studioMin), Math.min(layout.min, innerWidth)) : 0);
74
+ const panelState = () => ({ open: layout.open, width: shownWidth(), assets: { open: assets.open && layout.open, bottom: assets.bottom } });
75
+ const panelListeners = new Set<Listener<ReturnType<typeof panelState>>>();
76
+
77
+ /** The page shows a project (not a new tab's launcher): only then is there a chat to show. */
78
+ const onProject = () => /^\/projects\/[\w-]+/.test(location.pathname);
79
+ function place() {
80
+ column.hidden = !layout.open || !onProject();
81
+ column.style.width = `${shownWidth()}px`;
82
+ column.style.top = `${TOP_BAR}px`;
83
+ /* the column never moves or resizes for the assets: the chat shows only its composer then, and the rest of the
84
+ column lets the drawer under it be seen and used (shell.css) */
85
+ column.toggleAttribute('data-assets-open', assets.open && layout.open);
86
+ const state = panelState();
87
+ for (const listener of panelListeners) listener(state);
88
+ }
89
+ addEventListener('resize', place);
90
+
91
+ function setChatOpen(open: boolean) {
92
+ if (open === layout.open) return;
93
+ layout.open = open;
94
+ flushSync(() => {
95
+ if (!open && assets.open) { assets.open = false; assetsSignal.set(false); }
96
+ place();
97
+ chatOpenSignal.set(open);
98
+ });
99
+ native.saveLayout({ chatOpen: layout.open, chatWidth: layout.width });
100
+ }
101
+ function setChatWidth(width: number, done = false) {
102
+ if (!Number.isFinite(width)) return;
103
+ layout.width = Math.round(Math.max(layout.min, Math.min(width, innerWidth - layout.studioMin)));
104
+ place();
105
+ if (done) native.saveLayout({ chatOpen: layout.open, chatWidth: layout.width });
106
+ }
107
+ function setAssetsOpen(open: boolean) {
108
+ if (open && !layout.open) setChatOpen(true);
109
+ assets.open = open && layout.open;
110
+ /* the chat folding to its composer and Studio's drawer appearing are one frame: neither is seen without the other */
111
+ flushSync(() => {
112
+ assetsSignal.set(assets.open);
113
+ place();
114
+ });
115
+ }
116
+ native.onToggleChat(() => setChatOpen(!layout.open));
117
+
118
+ /* ── the theme and the language are the page's (Studio sets them on <html>) ── */
119
+ const root = document.documentElement;
120
+ const themeOf = (): Theme => (root.dataset.theme === 'dark' ? 'dark' : 'light');
121
+ const theme = signal<Theme>(themeOf());
122
+ const language = signal(root.lang || 'en');
123
+ new MutationObserver(() => {
124
+ if (themeOf() !== theme.get()) theme.set(themeOf());
125
+ if ((root.lang || 'en') !== language.get()) language.set(root.lang || 'en');
126
+ }).observe(root, { attributes: true, attributeFilter: ['data-theme', 'lang'] });
127
+ /* the window behind the page, and the system's menus and dialogs, take the same theme */
128
+ native.setTheme(theme.get());
129
+ theme.on((next) => native.setTheme(next));
130
+ /* and the app keeps the language, for the window it shows before the page (an update installed at launch) */
131
+ native.setLanguage(language.get());
132
+ language.on((next) => native.setLanguage(next));
133
+
134
+ /* ── the project Studio shows (its address, /projects/<id>) ── */
135
+ const project = signal<Project | null>(null);
136
+ let projectReady: Promise<void> = Promise.resolve();
137
+ let projectRequest = 0;
138
+ const projectId = () => /^\/projects\/([\w-]+)/.exec(location.pathname)?.[1] ?? null;
139
+ function projectChanged(next: Project, request?: number) {
140
+ if (next.id !== projectId()) return;
141
+ if (request === undefined) projectRequest += 1;
142
+ else if (request !== projectRequest) return;
143
+ const current = project.get();
144
+ if (current?.id === next.id && current.name === next.name && current.path === next.path) return;
145
+ project.set({ id: next.id, name: next.name, path: next.path });
146
+ }
147
+ async function follow() {
148
+ /* Reads from before a rename or a previous visit cannot restore an old folder. */
149
+ const request = ++projectRequest;
150
+ const id = projectId();
151
+ if (!id) return; // Keep the chat mounted while the project list covers it.
152
+ try {
153
+ const res = await fetch('/api/projects');
154
+ const body = await res.json() as { projects?: Project[] };
155
+ const found = body.projects?.find((p) => p.id === id);
156
+ if (found) projectChanged(found, request);
157
+ } catch { /* told next time the address changes */ }
158
+ }
159
+ for (const method of ['pushState', 'replaceState'] as const) {
160
+ const original = history[method].bind(history);
161
+ history[method] = (...args: Parameters<History['pushState']>) => { original(...args); projectReady = follow(); place(); };
162
+ }
163
+ addEventListener('popstate', () => { projectReady = follow(); place(); });
164
+ projectReady = follow();
165
+
166
+ /* ── between Studio and the chat: what is selected, what is referenced, what Studio is asked to show ── */
167
+ const selection = signal<StudioSelection | null>(null);
168
+ const refer = events<StudioRef>();
169
+ /* a skill picked in the examples (Studio's ExamplesDialog), to be named in the message; and "Add a skill" there */
170
+ const skillPicked = events<{ name: string; description: string; newConversation?: boolean }>();
171
+ const skillAdd = events<void>();
172
+ /** what the pointer is over in Studio (null: nothing), for the chat to light its pills that point at it */
173
+ const hover = signal<StudioRef | null>(null);
174
+ let command: ((command: StudioCommand) => void) | null = null;
175
+ const fullScreen = signal(false);
176
+ native.onFullScreen((full) => fullScreen.set(full));
177
+
178
+ /*
179
+ * ── a reference's picture of the viewer (StudioRefImage `viewer`): what the person sees there now, taken by the app as
180
+ * they point at it. The picture's overlays (selection frames, handles, Studio's toolbars) step aside for that frame;
181
+ * the subtitles stay, they are part of the picture. Without a viewer on screen, or when the app cannot take it, the
182
+ * reference goes without a picture.
183
+ */
184
+ let pictured: Promise<void> = Promise.resolve();
185
+ const captureStyle = document.createElement('style');
186
+ captureStyle.textContent = '[data-capturing] [data-film-select], [data-capturing] [data-capture-hide] { visibility: hidden !important; }';
187
+ document.head.append(captureStyle);
188
+ const nextFrame = () => new Promise<void>((done) => { requestAnimationFrame(() => requestAnimationFrame(() => done())); });
189
+ async function withPicture(ref: StudioRef): Promise<StudioRef> {
190
+ const image = ref.image;
191
+ if (!image?.viewer) return ref;
192
+ const { image: _viewer, ...bare } = ref;
193
+ document.documentElement.toggleAttribute('data-capturing', true);
194
+ try {
195
+ await nextFrame();
196
+ const film = [...document.querySelectorAll<HTMLIFrameElement>('iframe[title="film"]')]
197
+ .find((f) => f.getBoundingClientRect().width > 0 && getComputedStyle(f).visibility !== 'hidden');
198
+ if (!film) return bare as StudioRef;
199
+ const r = film.getBoundingClientRect();
200
+ let box = { x: r.left, y: r.top, width: r.width, height: r.height };
201
+ if (image.crop && image.stage?.w) {
202
+ const k = r.width / image.stage.w;
203
+ /* an element is shown with what is around it (a line of text alone is a strip the agent cannot place); a region
204
+ is what the person drew, as drawn */
205
+ const pad = ref.kind === 'layer' ? Math.max(120, Math.max(image.crop.w, image.crop.h) / 2) : 0;
206
+ const crop = { x: image.crop.x - pad, y: image.crop.y - pad, w: image.crop.w + 2 * pad, h: image.crop.h + 2 * pad };
207
+ const left = Math.max(r.left, r.left + crop.x * k);
208
+ const top = Math.max(r.top, r.top + crop.y * k);
209
+ const right = Math.min(r.right, r.left + (crop.x + crop.w) * k);
210
+ const bottom = Math.min(r.bottom, r.top + (crop.y + crop.h) * k);
211
+ box = { x: left, y: top, width: right - left, height: bottom - top };
212
+ }
213
+ const jpeg = await native.capture(box).catch(() => null);
214
+ return (jpeg ? { ...bare, image: { src: `data:image/jpeg;base64,${jpeg}` } } : bare) as StudioRef;
215
+ } finally {
216
+ document.documentElement.toggleAttribute('data-capturing', false);
217
+ }
218
+ }
219
+
220
+ /* ── what an agent's command asks before it spends (Studio words it): the chat shows it above its composer ── */
221
+ const asks = signal<HostAsk[]>([]);
222
+ let answerAsk: (id: string, allow: boolean) => void = () => {};
223
+
224
+ /* ── Settings → Developer (kept by the app): Studio switches it, the chat shows each turn's log and changes by it ── */
225
+ interface AppPrefs { developer: boolean; changes: boolean }
226
+ const developerOf = (prefs: AppPrefs) => ({ logs: prefs.developer === true, changes: prefs.changes !== false });
227
+ const developer = {
228
+ get: async () => developerOf(await native.prefs.get()),
229
+ set: (next: { logs?: boolean; changes?: boolean }) => native.prefs.set({
230
+ ...(typeof next.logs === 'boolean' ? { developer: next.logs } : {}),
231
+ ...(typeof next.changes === 'boolean' ? { changes: next.changes } : {}),
232
+ }),
233
+ onChange: (listener: Listener<{ logs: boolean; changes: boolean }>) => native.prefs.onChange((prefs) => listener(developerOf(prefs))),
234
+ };
235
+
236
+ /* ── the app's update, for Studio's top bar: a version downloaded and ready (src/updates.mjs), Restart to install it ── */
237
+ const WAITING = ['agent-busy', 'exporting', 'install-failed'] as const;
238
+ function shownUpdate(state: unknown): HostUpdate | null {
239
+ const s = state as { status?: string; availableVersion?: string | null; error?: string | null } | null;
240
+ if (!s || (s.status !== 'ready' && s.status !== 'installing')) return null;
241
+ const waiting = WAITING.find((w) => w === s.error) ?? null;
242
+ return { status: s.status, version: s.availableVersion ?? '', waiting };
243
+ }
244
+ const update: NonNullable<StudioHost['update']> = {
245
+ onChange(listener) {
246
+ /* the state the app started with, unless a newer one came first */
247
+ let told = false;
248
+ void hello.then((h) => { if (!told) listener(shownUpdate(h.update)); });
249
+ return native.onUpdate((state) => { told = true; listener(shownUpdate(state)); });
250
+ },
251
+ restart: native.restartToUpdate,
252
+ /* the whole state, for Settings → About: now, then every change */
253
+ onState(listener) {
254
+ let told = false;
255
+ void native.updateState().then((s) => { if (!told && s) listener(s as HostUpdateState); });
256
+ return native.onUpdate((s) => { told = true; if (s) listener(s as HostUpdateState); });
257
+ },
258
+ check: async () => (await native.checkForUpdate()) as HostUpdateState | null,
259
+ };
260
+
261
+ /* ── Studio's host ── */
262
+ const mac = native.platform === 'darwin';
263
+ const windows = native.platform === 'win32';
264
+ /** the window's own controls: macOS's traffic lights end at 66 px (Studio's logo a little after them); Windows draws
265
+ its buttons over the top bar's right */
266
+ const INSET = 74;
267
+ const INSET_RIGHT = 138;
268
+
269
+ const host: StudioHost = {
270
+ version: 1,
271
+ name: 'OpenFilm',
272
+ projectChanged,
273
+ ...(mac ? { titlebar: { inset: INSET, onChange: (listener: Listener<number>) => { fullScreen.on((full) => listener(full ? 6 : INSET)); } } } : {}),
274
+ ...(windows ? { titlebar: { inset: 6, insetRight: INSET_RIGHT } } : {}),
275
+ /* in order, each with its picture: two pointed at quickly must not swap or share a screenshot */
276
+ refer: (ref) => {
277
+ if (!layout.open) setChatOpen(true);
278
+ pictured = pictured.then(() => withPicture(ref)).then((done) => refer.set(done));
279
+ },
280
+ onSelection: (next) => selection.set(next),
281
+ onHover: (ref) => hover.set(ref ?? null),
282
+ onCommand: (listener) => { command = listener; },
283
+ settings: { onOpen: (listener) => native.onOpenSettings(listener) },
284
+ /* read live: Studio may read the panel before or after the kept layout arrives */
285
+ panel: {
286
+ label: 'Chat',
287
+ get open() { return layout.open; },
288
+ get width() { return shownWidth(); },
289
+ get assets() { return panelState().assets; },
290
+ setOpen: setChatOpen,
291
+ setAssetsOpen,
292
+ setWidth: (width, done) => setChatWidth(width, done === true),
293
+ onChange: (listener) => { panelListeners.add(listener); listener(panelState()); },
294
+ },
295
+ agents: native.agents,
296
+ asks: { show: (list, answer) => { answerAsk = answer; asks.set(list); } },
297
+ developer,
298
+ update,
299
+ folders: { pick: native.pickFolder, pathOf: native.pathForFile },
300
+ tabs: native.tabs,
301
+ useSkill: (skill) => skillPicked.set(skill),
302
+ skills: { add: () => skillAdd.set(undefined) },
303
+ };
304
+ (window as unknown as { openfilmHost: StudioHost }).openfilmHost = host;
305
+
306
+ /* the layout the app kept: the panel starts as it was, before Studio first reads it */
307
+ const ready = hello.then((h) => {
308
+ layout.open = h.chatOpen;
309
+ layout.width = h.chatWidth;
310
+ layout.min = h.chatMin;
311
+ layout.studioMin = h.studioMin;
312
+ fullScreen.set(h.fullScreen);
313
+ chatOpenSignal.set(layout.open);
314
+ place();
315
+ return h;
316
+ });
317
+
318
+ /* ── the chat's bridge ── */
319
+ /** Where a reference takes the person: the clip selected, the moment, the range marked, the box shown, the track, the
320
+ file. A layer whose place on the picture Studio told is shown as that box (it is what was pointed at); one without,
321
+ by its clip. */
322
+ function commandFor(ref: StudioRef): StudioCommand {
323
+ switch (ref.kind) {
324
+ case 'clip': return { type: 'select-clip', id: ref.id, loc: ref.loc, seek: true };
325
+ case 'layer': return ref.box && typeof ref.time === 'number'
326
+ ? { type: 'show-box', time: ref.time, box: ref.box }
327
+ : { type: 'select-clip', id: ref.clipId, loc: ref.loc, seek: true };
328
+ case 'region': return { type: 'show-box', time: ref.time, box: ref.box };
329
+ case 'time': return { type: 'seek', time: ref.time };
330
+ case 'range': return { type: 'show-range', start: ref.start, end: ref.end };
331
+ case 'track': return { type: 'show-track', track: ref.track };
332
+ case 'file': return { type: 'show-file', path: ref.path };
333
+ case 'subtitle': return { type: 'show-subtitle', start: ref.start, end: ref.end };
334
+ /* another film: the examples, where it came from */
335
+ case 'example': return { type: 'show-examples', open: { kind: 'example', id: ref.id } };
336
+ }
337
+ }
338
+
339
+ (window as unknown as { openfilmDesktop: unknown }).openfilmDesktop = {
340
+ bridgeVersion: '1',
341
+ platform: native.platform,
342
+ pathForFile: native.pathForFile,
343
+ revealPath: native.revealPath,
344
+ agents: {
345
+ list: native.agents.list,
346
+ connect: native.agents.connect,
347
+ onChange: native.agents.onChange,
348
+ },
349
+ localAgents: native.localAgents,
350
+ openSettings: (section: string) => command?.({ type: 'settings', section: section === 'providers' ? 'providers' : 'agents' }),
351
+ async hello() {
352
+ const h = await ready;
353
+ await projectReady;
354
+ return {
355
+ platform: native.platform, flavor: h.flavor, theme: theme.get(), language: language.get(), chatOpen: layout.open,
356
+ project: project.get(), selection: selection.get(), ffmpeg: h.ffmpeg, version: h.version, chatMin: layout.min,
357
+ };
358
+ },
359
+ onTheme: theme.on,
360
+ onLanguage: language.on,
361
+ onProject: (listener: Listener<Project>) => project.on((p) => { if (p) listener(p); }),
362
+ onChatOpen: chatOpenSignal.on,
363
+ onStudioBrowser: native.onStudioBrowser,
364
+ /* Studio failing to start leaves no page to tell: the app shows that itself (src/main.mjs) */
365
+ onStudioFailed: () => () => {},
366
+ onRefer: refer.on,
367
+ onSelection: (listener: Listener<StudioSelection>) => selection.on((s) => { if (s) listener(s); }),
368
+ onFfmpeg: native.onFfmpeg,
369
+ showInStudio: (ref: StudioRef) => command?.(commandFor(ref)),
370
+ /* an example's folder on this computer (Studio fetches it when needed), for a reference to one in a message sent */
371
+ async exampleFolder(id: string) {
372
+ const res = await fetch(`/api/examples/${encodeURIComponent(id)}/folder`, { method: 'POST' });
373
+ const body = await res.json().catch(() => ({})) as { folder?: string; error?: string };
374
+ if (!res.ok || !body.folder) throw new Error(body.error ?? `Studio answered ${res.status}`);
375
+ return body.folder;
376
+ },
377
+ /* the composer's examples button: the examples over the film, to point at one or name a skill */
378
+ showExamples: (open?: CommunityItem) => command?.({ type: 'show-examples', ...(open ? { open } : {}) }),
379
+ onUseSkill: skillPicked.on,
380
+ onAddSkill: skillAdd.on,
381
+ useSkill: (skill: { name: string; description: string; newConversation?: boolean }) => skillPicked.set(skill),
382
+ /* a reference made in the chat (⌘L there, the @ menu) gets its picture as Studio's own do */
383
+ withPicture: (ref: StudioRef) => { const done = pictured.then(() => withPicture(ref)); pictured = done.then(() => {}); return done; },
384
+ /* what a pill hovered in the chat points at, lit in Studio while it is (null: none); and the references of the
385
+ message being written, numbered there */
386
+ highlightInStudio: (ref: StudioRef | null) => command?.({ type: 'highlight', ref }),
387
+ draftRefsInStudio: (refs: StudioRef[]) => command?.({ type: 'draft-refs', refs }),
388
+ onStudioHover: hover.on,
389
+ /* the project's files, for the @ menu: Studio's own listing (this page is Studio's, so its API is too) */
390
+ async projectFiles() {
391
+ const id = project.get()?.id;
392
+ if (!id) return { files: [] };
393
+ try { return await (await fetch(`/api/projects/${encodeURIComponent(id)}/files`)).json(); } catch { return { files: [] }; }
394
+ },
395
+ setChatWidth: (width: number, done?: boolean) => setChatWidth(width, done === true),
396
+ setChatOpen,
397
+ assetsOpen: () => assets.open,
398
+ setAssetsOpen,
399
+ onAssets: assetsSignal.on,
400
+ setComposerHeight(px: number) {
401
+ const bottom = Math.round(px);
402
+ if (!Number.isFinite(bottom) || bottom < 40 || bottom === assets.bottom) return;
403
+ assets.bottom = bottom;
404
+ place();
405
+ },
406
+ /* a menu of the chat's floats in its own layer over everything: nothing to make room for */
407
+ setMenuOpen() {},
408
+ /* the chat's record of each project, in its .film/chat and .film/logs */
409
+ chat: native.chat,
410
+ skills: native.skills,
411
+ asks: { get: asks.get, onChange: asks.on, answer: (id: string, allow: boolean) => answerAsk(id, allow) },
412
+ developer,
413
+ };
414
+
415
+ export type { HostAgent };
@@ -0,0 +1,27 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>OpenFilm</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%228%206%2084%2092%22%20role%3D%22img%22%20aria-label%3D%22OpenFilm%22%3E%3Ctitle%3EOpenFilm%3C%2Ftitle%3E%3Cstyle%3E.i%7Bfill%3A%23111%7D%40media%20(prefers-color-scheme%3Adark)%7B.i%7Bfill%3A%23fff%7D%7D%3C%2Fstyle%3E%3Cmask%20id%3D%22gap%22%20maskUnits%3D%22userSpaceOnUse%22%20x%3D%220%22%20y%3D%220%22%20width%3D%22100%22%20height%3D%22100%22%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22%23fff%22%2F%3E%3Cpath%20d%3D%22M17%2012H37Q40%2012%2040%2015V27Q40%2029%2038.5%2030.5L31%2037V88Q31%2092%2027%2092Q23%2092%2023%2088V37L15.5%2030.5Q14%2029%2014%2027V15Q14%2012%2017%2012Z%22%20stroke%3D%22%23000%22%20stroke-width%3D%227%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fmask%3E%3Cg%20class%3D%22i%22%20mask%3D%22url(%23gap)%22%3E%3Crect%20x%3D%2222%22%20y%3D%2212%22%20width%3D%2264%22%20height%3D%2220%22%20rx%3D%225%22%2F%3E%3Crect%20x%3D%2225%22%20y%3D%2244%22%20width%3D%2247%22%20height%3D%2219%22%20rx%3D%225%22%2F%3E%3C%2Fg%3E%3Cpath%20class%3D%22i%22%20d%3D%22M17%2012H37Q40%2012%2040%2015V27Q40%2029%2038.5%2030.5L31%2037V88Q31%2092%2027%2092Q23%2092%2023%2088V37L15.5%2030.5Q14%2029%2014%2027V15Q14%2012%2017%2012Z%22%2F%3E%3C%2Fsvg%3E">
8
+ <script>
9
+ /* the theme before the first paint, so the wrong one never flashes: dark unless the person chose another */
10
+ (function () {
11
+ var t = 'dark';
12
+ try { t = localStorage.getItem('openfilm.theme') || 'dark'; } catch (e) {}
13
+ if (t === 'system') t = matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
14
+ document.documentElement.dataset.theme = t === 'dark' ? 'dark' : 'light';
15
+ })();
16
+ </script>
17
+ </head>
18
+ <body>
19
+ <div id="root"></div>
20
+ <!-- the chat's column beside Studio (the panel Studio leaves room for), and its layer for what floats. Both are
21
+ Studio's host panel (its keys leave them alone) and take focus: a click anywhere in the chat, not only in its
22
+ fields, keeps the keyboard in the chat (Space, Delete, ⌘A there never reach Studio). -->
23
+ <div id="of-chat" class="of-chat" data-studio-host-panel tabindex="-1"></div>
24
+ <div id="of-chat-layer" class="of-chat" data-studio-host-panel tabindex="-1"></div>
25
+ <script type="module" src="./main.tsx"></script>
26
+ </body>
27
+ </html>
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The app's window: Studio's editor, as the open-source Studio is (its App, unchanged), and the chat in the column
3
+ * Studio leaves for it (lib/host.ts `panel`). One page, one theme, one layer of dialogs over both.
4
+ */
5
+ import './bridge';
6
+ import { StrictMode } from 'react';
7
+ import { createRoot } from 'react-dom/client';
8
+ import { App } from 'openfilm/studio-ui/App.tsx';
9
+ import { FocusNav } from 'openfilm/studio-ui/components/FocusNav.tsx';
10
+ import { applyStudioHost } from 'openfilm/studio-ui/lib/host.ts';
11
+ import { mountChat } from '../chat/src/mount';
12
+ import './shell.css';
13
+ import '../chat/src/chat.css';
14
+
15
+ applyStudioHost();
16
+ createRoot(document.getElementById('root')!).render(<StrictMode><FocusNav /><App /></StrictMode>);
17
+ void mountChat(document.getElementById('of-chat')!);
@@ -0,0 +1,17 @@
1
+ /* Studio's styles, with Tailwind over both trees (the chat's own styles come scoped, chat/src/chat.css) */
2
+ @import 'openfilm/studio-ui/globals.css';
3
+ @source '../node_modules/openfilm/studio/ui/src';
4
+ @source '../chat/src';
5
+
6
+ /* the chat's column: beside Studio, under its top bar (bridge.ts places and sizes it); over Studio's panes and its
7
+ assets drawer, under its menus and dialogs */
8
+ #of-chat { position: fixed; right: 0; bottom: 0; height: auto; z-index: 40; background: transparent; }
9
+ #of-chat[hidden] { display: none; }
10
+ /* the assets open: Studio's drawer is in the column, above the composer; only the chat's pane (its composer then)
11
+ takes the pointer, the rest of the column is the drawer's */
12
+ #of-chat[data-assets-open] { pointer-events: none; }
13
+ #of-chat[data-assets-open] aside { pointer-events: auto; }
14
+ /* the chat's layer takes no room: what it holds is fixed in place over the window */
15
+ #of-chat-layer { height: 0; background: transparent; }
16
+ /* focused by a click to keep the keyboard in the chat (index.html), never ringed */
17
+ #of-chat:focus, #of-chat-layer:focus { outline: none; }
@@ -0,0 +1,65 @@
1
+ import { defineConfig, normalizePath, type Plugin } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+ import tailwind from '@tailwindcss/vite';
4
+ import type { Plugin as PostcssPlugin } from 'postcss';
5
+ import { createRequire } from 'node:module';
6
+ import { dirname, join } from 'node:path';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ /*
10
+ * The app's window: one page, Studio's editor and the chat beside it, built into shell/dist and served by the app's
11
+ * Studio as its editor (studio-process.mjs `editorDir`). Studio's sources are the open-source ones as they are.
12
+ */
13
+ const STUDIO = join(dirname(createRequire(import.meta.url).resolve('openfilm/package.json')), 'studio/ui/src/');
14
+ /* with forward slashes, as Vite names files on Windows too */
15
+ const CHAT = normalizePath(fileURLToPath(new URL('../chat/src/', import.meta.url)));
16
+
17
+ /** `@/…` is the importing file's own tree: Studio's files and the chat's each name their `src` so. */
18
+ function twoTrees(): Plugin {
19
+ return {
20
+ name: 'openfilm-two-trees',
21
+ enforce: 'pre',
22
+ resolveId(source, importer, options) {
23
+ if (!source.startsWith('@/') || !importer) return null;
24
+ const base = normalizePath(importer).startsWith(CHAT) ? CHAT : STUDIO;
25
+ return this.resolve(join(base, source.slice(2)), importer, { ...options, skipSelf: true });
26
+ },
27
+ };
28
+ }
29
+
30
+ /**
31
+ * The chat's styles, kept to the chat: every selector applies inside `.of-chat` (its column and its layer); `:root`,
32
+ * `html` and `body` mean the chat itself; a theme on the page reaches the chat under it. Rules only a page of the
33
+ * chat's own needed (its title bar, the see-through column, a theme from the system, the focus ring and the timeline
34
+ * cursor Studio already has) are left out.
35
+ */
36
+ const scopeChat: PostcssPlugin = {
37
+ postcssPlugin: 'openfilm-scope-chat',
38
+ Once(root, { result }) {
39
+ if (!normalizePath(result.opts.from ?? '').startsWith(CHAT)) return;
40
+ root.walkRules((rule) => {
41
+ if (rule.parent?.type === 'atrule' && /keyframes$/i.test((rule.parent as { name: string }).name)) return;
42
+ const selectors = new Set<string>();
43
+ for (const raw of rule.selectors) {
44
+ const s = raw.trim();
45
+ if (/:not\(\[data-theme\]\)|data-assets-open|openfilm-inset-titlebar|openfilm-fullscreen|tl-scrubbing|data-focus-nav/.test(s)) continue;
46
+ const page = /^(?::root|html|body)(?![\w-])/.exec(s);
47
+ const theme = /^\[data-theme='(?:light|dark)'\]/.exec(s);
48
+ if (page) selectors.add(`.of-chat${s.slice(page[0].length)}`);
49
+ else if (theme) selectors.add(`${theme[0]} .of-chat${s.slice(theme[0].length)}`);
50
+ else selectors.add(`.of-chat ${s}`);
51
+ }
52
+ if (selectors.size) rule.selectors = [...selectors];
53
+ else rule.remove();
54
+ });
55
+ },
56
+ };
57
+
58
+ export default defineConfig({
59
+ root: fileURLToPath(new URL('.', import.meta.url)),
60
+ plugins: [twoTrees(), react(), tailwind()],
61
+ css: { postcss: { plugins: [scopeChat] } },
62
+ /* one React for both trees (each would find its own package's otherwise) */
63
+ resolve: { dedupe: ['react', 'react-dom', 'lucide-react'] },
64
+ build: { outDir: 'dist', emptyOutDir: true, target: 'es2022', sourcemap: false },
65
+ });
@@ -0,0 +1,31 @@
1
+ // @ts-check
2
+ /** Desktop metadata only; discovery, launch and sign-in behavior stay in their existing modules. */
3
+ /** @satisfies {Record<string, { name: string, install: `https://${string}` | null }>} */
4
+ const definitions = {
5
+ codex: { name: 'Codex', install: 'https://chatgpt.com/download/' },
6
+ claude: { name: 'Claude Code', install: 'https://claude.com/download' },
7
+ copilot: { name: 'GitHub Copilot', install: 'https://github.com/features/copilot/cli' },
8
+ cursor: { name: 'Cursor', install: 'https://cursor.com/cli' },
9
+ opencode: { name: 'OpenCode', install: 'https://opencode.ai/download' },
10
+ codebuddy: { name: 'CodeBuddy', install: 'https://www.codebuddy.cn/docs/cli/installation' },
11
+ qwen: { name: 'Qwen Code', install: 'https://github.com/QwenLM/qwen-code' },
12
+ kimi: { name: 'Kimi', install: 'https://www.kimi.com/code/docs/en/kimi-code-cli/guides/getting-started.html' },
13
+ minimax: { name: 'MiniMax Code', install: 'https://www.npmjs.com/package/@minimax-ai/code' },
14
+ deepseek: { name: 'DeepSeek Harness', install: 'https://github.com/deepseek-ai/deepseek-harness' },
15
+ byok: { name: 'Own key', install: null },
16
+ };
17
+
18
+ /** @type {Readonly<{ [Id in keyof typeof definitions]: Readonly<typeof definitions[Id]> }>} */
19
+ export const AGENT_DEFINITIONS = Object.freeze(definitions);
20
+ /** @typedef {keyof typeof AGENT_DEFINITIONS} AgentId */
21
+
22
+ export const AGENT_IDS = Object.freeze(/** @type {AgentId[]} */ (Object.keys(AGENT_DEFINITIONS)));
23
+ export const OWN_AGENT_IDS = Object.freeze(AGENT_IDS.filter((id) => id !== 'byok'));
24
+
25
+ /** All desktop choices, including own key; safe for persisted data and unknown historical ids.
26
+ * @param {unknown} value
27
+ * @returns {value is AgentId}
28
+ */
29
+ export function isAgentId(value) {
30
+ return typeof value === 'string' && Object.hasOwn(AGENT_DEFINITIONS, value);
31
+ }
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenFilm contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.