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,188 @@
1
+ /**
2
+ * The audio meters: the film's sound as it plays, left and right, peak (the light bar, with a mark that
3
+ * holds the loudest of late) and RMS (the solid bar inside it), on a dBFS scale. A channel that reached 0 dBFS lights
4
+ * its clip light at the top until it is clicked.
5
+ *
6
+ * Drawn on a canvas once a frame while the film plays, from the player's mix (editor/player.ts levels); paused, the
7
+ * bars fall back and then nothing runs at all.
8
+ */
9
+ import * as React from 'react';
10
+
11
+ import { useT } from '@/i18n';
12
+ import {
13
+ channelLevel, fallTo, formatDb, holdPeak, meterPosition, METER_FLOOR_DB, METER_MARKS, type HeldPeak,
14
+ } from '@/lib/audio-levels';
15
+ import type { MixSamples } from '@/editor/player';
16
+ import { useThemeChanges } from '@/lib/theme-changes';
17
+
18
+ /** How wide the meters are, px: two bars and their scale. */
19
+ export const METERS_W = 44;
20
+
21
+ const CLIP_H = 7;
22
+ const TOP = CLIP_H + 5;
23
+ const BOTTOM = 6;
24
+ const BAR_W = 6;
25
+ const BAR_GAP = 2;
26
+ const BARS_X = 4;
27
+
28
+ /** The bar's colors along the scale: green, yellow from −18 dB, red from −6 dB. */
29
+ function paint(ctx: CanvasRenderingContext2D, top: number, height: number, alpha: number) {
30
+ const g = ctx.createLinearGradient(0, top + height, 0, top);
31
+ const at = (db: number) => meterPosition(db);
32
+ g.addColorStop(0, `rgba(63,191,111,${alpha})`);
33
+ g.addColorStop(at(-18), `rgba(63,191,111,${alpha})`);
34
+ g.addColorStop(at(-17), `rgba(232,197,71,${alpha})`);
35
+ g.addColorStop(at(-7), `rgba(232,197,71,${alpha})`);
36
+ g.addColorStop(at(-6), `rgba(229,72,77,${alpha})`);
37
+ g.addColorStop(1, `rgba(229,72,77,${alpha})`);
38
+ return g;
39
+ }
40
+
41
+ type Shown = { peak: number; rms: number; held: HeldPeak & { shown: number } };
42
+ const quiet = (): Shown => ({ peak: METER_FLOOR_DB, rms: METER_FLOOR_DB, held: { db: METER_FLOOR_DB, at: 0, shown: METER_FLOOR_DB } });
43
+
44
+ export function AudioMeters({ levels, playing }: {
45
+ /** The mix's latest samples, or null when nothing plays (see Player.levels). */
46
+ levels: () => MixSamples | null;
47
+ playing: boolean;
48
+ }) {
49
+ const t = useT();
50
+ const canvasRef = React.useRef<HTMLCanvasElement | null>(null);
51
+ const boxRef = React.useRef<HTMLDivElement | null>(null);
52
+ const shown = React.useRef<[Shown, Shown]>([quiet(), quiet()]);
53
+ const [clipped, setClipped] = React.useState<[boolean, boolean]>([false, false]);
54
+ const clippedRef = React.useRef(clipped);
55
+ clippedRef.current = clipped;
56
+ const [size, setSize] = React.useState({ w: METERS_W, h: 0 });
57
+ const [peakText, setPeakText] = React.useState(METER_FLOOR_DB);
58
+
59
+ React.useLayoutEffect(() => {
60
+ const el = boxRef.current;
61
+ if (!el) return undefined;
62
+ const ro = new ResizeObserver(() => setSize({ w: el.clientWidth, h: el.clientHeight }));
63
+ ro.observe(el);
64
+ setSize({ w: el.clientWidth, h: el.clientHeight });
65
+ return () => ro.disconnect();
66
+ }, []);
67
+
68
+ /** One picture of the bars and the scale. */
69
+ const draw = React.useCallback(() => {
70
+ const canvas = canvasRef.current;
71
+ if (!canvas || size.h <= TOP + BOTTOM) return;
72
+ const dpr = window.devicePixelRatio || 1;
73
+ if (canvas.width !== Math.round(size.w * dpr) || canvas.height !== Math.round(size.h * dpr)) {
74
+ canvas.width = Math.round(size.w * dpr);
75
+ canvas.height = Math.round(size.h * dpr);
76
+ }
77
+ const ctx = canvas.getContext('2d');
78
+ if (!ctx) return;
79
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
80
+ ctx.clearRect(0, 0, size.w, size.h);
81
+ const h = size.h - TOP - BOTTOM;
82
+ const y = (db: number) => TOP + h * (1 - meterPosition(db));
83
+ const styles = getComputedStyle(canvas);
84
+ const faint = styles.getPropertyValue('--tl-faint').trim() || 'rgba(128,128,128,0.6)';
85
+ const lane = styles.getPropertyValue('--tl-lane').trim() || 'rgba(128,128,128,0.15)';
86
+ /* the scale: a line and a number every 6 dB, a number every 12 */
87
+ ctx.font = '8px ui-sans-serif, system-ui, sans-serif';
88
+ ctx.textBaseline = 'middle';
89
+ const scaleX = BARS_X + 2 * BAR_W + BAR_GAP + 3;
90
+ for (const mark of METER_MARKS) {
91
+ const my = Math.round(y(mark)) + 0.5;
92
+ ctx.fillStyle = faint;
93
+ ctx.fillRect(scaleX, my, 3, 1);
94
+ if (mark % 12 === 0) ctx.fillText(String(Math.abs(mark)), scaleX + 5, my);
95
+ }
96
+ shown.current.forEach((ch, i) => {
97
+ const x = BARS_X + i * (BAR_W + BAR_GAP);
98
+ ctx.fillStyle = lane;
99
+ ctx.fillRect(x, TOP, BAR_W, h);
100
+ /* peak: the light bar; RMS: the solid one */
101
+ ctx.fillStyle = paint(ctx, TOP, h, 0.45);
102
+ ctx.fillRect(x, y(ch.peak), BAR_W, TOP + h - y(ch.peak));
103
+ ctx.fillStyle = paint(ctx, TOP, h, 1);
104
+ ctx.fillRect(x, y(ch.rms), BAR_W, TOP + h - y(ch.rms));
105
+ if (ch.held.shown > METER_FLOOR_DB) {
106
+ ctx.fillStyle = ch.held.shown >= -6 ? '#e5484d' : ch.held.shown >= -18 ? '#e8c547' : '#d9f2e2';
107
+ ctx.fillRect(x, Math.round(y(ch.held.shown)), BAR_W, 2);
108
+ }
109
+ });
110
+ }, [size]);
111
+
112
+ /* its colors are the theme's, read as it draws: when the theme changes it draws again, or it would keep the old
113
+ theme's bars until the film plays */
114
+ const theme = useThemeChanges();
115
+ React.useEffect(() => { draw(); }, [draw, theme]);
116
+
117
+ /* while playing (and while the bars fall back after), once a frame; nothing otherwise */
118
+ React.useEffect(() => {
119
+ let frame = 0;
120
+ let last = performance.now();
121
+ const tick = () => {
122
+ const now = performance.now();
123
+ const dt = now - last;
124
+ last = now;
125
+ const samples = playing ? levels() : null;
126
+ let moving = false;
127
+ const clips: [boolean, boolean] = [...clippedRef.current];
128
+ shown.current = shown.current.map((ch, i) => {
129
+ const level = samples ? channelLevel(samples[i as 0 | 1]) : { peakDb: METER_FLOOR_DB, rmsDb: METER_FLOOR_DB, clipped: false };
130
+ if (level.clipped) clips[i] = true;
131
+ /* stopped, the bars drop four times as fast: what is left of them says nothing */
132
+ const fall = samples ? dt : dt * 4;
133
+ const next: Shown = {
134
+ peak: fallTo(ch.peak, level.peakDb, fall),
135
+ rms: fallTo(ch.rms, level.rmsDb, fall),
136
+ held: holdPeak(ch.held, level.peakDb, now),
137
+ };
138
+ if (next.peak > METER_FLOOR_DB || next.held.shown > METER_FLOOR_DB) moving = true;
139
+ return next;
140
+ }) as [Shown, Shown];
141
+ if (clips[0] !== clippedRef.current[0] || clips[1] !== clippedRef.current[1]) setClipped(clips);
142
+ draw();
143
+ if (playing || moving) frame = requestAnimationFrame(tick);
144
+ else setPeakText(METER_FLOOR_DB);
145
+ };
146
+ frame = requestAnimationFrame(tick);
147
+ /* the readout: the loudest held peak, a few times a second (a number that changes every frame cannot be read) */
148
+ const readout = playing ? window.setInterval(() => setPeakText(Math.max(shown.current[0].held.shown, shown.current[1].held.shown)), 250) : 0;
149
+ return () => { cancelAnimationFrame(frame); window.clearInterval(readout); };
150
+ }, [playing, levels, draw]);
151
+
152
+ const clipLabel = t('editorTools.metersClip');
153
+ return (
154
+ <div
155
+ className="flex h-full shrink-0 flex-col border-l"
156
+ style={{ width: METERS_W, borderColor: 'var(--tl-line)', background: 'var(--tl-head)' }}
157
+ data-audio-meters=""
158
+ aria-label={t('editorTools.meters')}
159
+ role="group"
160
+ >
161
+ <div className="flex h-[34px] shrink-0 items-center justify-center font-mono text-[9px] tabular-nums" style={{ color: peakText >= -0.05 ? '#e5484d' : 'var(--tl-faint)' }}
162
+ title={t('editorTools.metersPeak')}>
163
+ {formatDb(peakText)}
164
+ </div>
165
+ <div ref={boxRef} className="relative min-h-0 flex-1">
166
+ <canvas ref={canvasRef} className="absolute inset-0 h-full w-full" style={{ width: size.w, height: size.h }} />
167
+ {/* the clip lights: lit when a channel reached 0 dBFS; a click puts them out */}
168
+ {([0, 1] as const).map((i) => (
169
+ <button
170
+ key={i}
171
+ type="button"
172
+ aria-label={`${clipLabel} (${i ? 'R' : 'L'})`}
173
+ title={clipLabel}
174
+ data-meter-clip={clipped[i] ? '1' : '0'}
175
+ onClick={() => setClipped([false, false])}
176
+ className="absolute top-0 rounded-[1px]"
177
+ style={{
178
+ left: BARS_X + i * (BAR_W + BAR_GAP),
179
+ width: BAR_W,
180
+ height: CLIP_H,
181
+ background: clipped[i] ? '#e5484d' : 'var(--tl-lane)',
182
+ }}
183
+ />
184
+ ))}
185
+ </div>
186
+ </div>
187
+ );
188
+ }
@@ -0,0 +1,84 @@
1
+ import * as React from 'react';
2
+
3
+ import { THUMB_CELL_PX } from '@/lib/thumb-cache';
4
+ import { thumbCellStyle } from '@/lib/thumb-cell';
5
+ import { pickShot } from '@/lib/thumb-pick';
6
+ import { retryImage } from '@/lib/retry-image';
7
+
8
+ /**
9
+ * The row of thumbnails on a picture clip. The pictures come from outside (the `thumbs` prop); this only lays them
10
+ * out, on the source's own time grid (cells at whole steps from the source's start), cut by the clip's edges — as in
11
+ * any editor: trimming a clip crops its first and last pictures, it does not start the row again at its new edge.
12
+ * Laid out from the clip's left edge instead, a trim drew the first picture whole and the next one over it, and the
13
+ * row jumped to a new grid on every release.
14
+ *
15
+ * Every cell gets a picture, even one shot for another cell: zooming changes which time each cell stands for, and
16
+ * waiting for exact shots would blank the row on every zoom. So each cell takes the nearest shot (lib/thumb-pick:
17
+ * the cell straddling the clip's start stands for that start, and a shot from inside the clip comes first), with no
18
+ * distance limit — a far picture beats a half-empty clip that looks broken; exact shots replace it as they arrive.
19
+ */
20
+ export function BlockThumbs({
21
+ thumbs, startMs, endMs, pxPerMs, height, topOffset = 0, thumbWidthPx = THUMB_CELL_PX, visiblePx,
22
+ }: {
23
+ thumbs: ReadonlyMap<number, string>;
24
+ startMs: number;
25
+ endMs: number;
26
+ pxPerMs: number;
27
+ height: number;
28
+ /** How far below the clip's top to start: the info strip (name + length) is there and must stay visible. */
29
+ topOffset?: number;
30
+ thumbWidthPx?: number;
31
+ /**
32
+ * The stretch worth drawing (px from the clip's left edge); the whole clip without. A long source zoomed in is
33
+ * hundreds of cells while a screen shows dozens. The window is rounded to whole screens (see Timeline thumbWindow).
34
+ */
35
+ visiblePx?: { from: number; to: number } | undefined;
36
+ }): React.ReactElement | null {
37
+ /* the shot times, sorted: the one for each cell is found by binary search */
38
+ const times = React.useMemo(
39
+ () => [...thumbs.keys()].sort((a, b) => a - b),
40
+ [thumbs],
41
+ );
42
+
43
+ const cells = React.useMemo(() => {
44
+ if (!(pxPerMs > 0) || height <= 0 || !times.length) return [];
45
+ const stepMs = thumbWidthPx / pxPerMs;
46
+ const out: { left: number; url: string }[] = [];
47
+ /* the cell straddling the clip's start begins before it (a negative left) and is cut by the clip's edge */
48
+ const first = Math.floor(startMs / stepMs) * stepMs;
49
+ for (let t = first; t < endMs; t += stepMs) {
50
+ const left = (t - startMs) * pxPerMs;
51
+ if (visiblePx && (left + thumbWidthPx < visiblePx.from || left > visiblePx.to)) continue;
52
+ const shot = pickShot(times, Math.round(t), startMs, endMs);
53
+ const url = shot == null ? undefined : thumbs.get(shot);
54
+ if (!url) continue;
55
+ out.push({ left, url });
56
+ }
57
+ return out;
58
+ }, [thumbs, times, startMs, endMs, pxPerMs, height, thumbWidthPx, visiblePx]);
59
+
60
+ if (!cells.length) return null;
61
+
62
+ return (
63
+ <div
64
+ className="pointer-events-none absolute inset-x-0 bottom-0 overflow-hidden"
65
+ style={{ top: topOffset }}
66
+ >
67
+ {/* keyed by index: on zoom every cell's left changes, and keying by it would remount the whole row */}
68
+ {cells.map((cell, i) => (
69
+ <img
70
+ key={i}
71
+ src={cell.url}
72
+ alt=""
73
+ draggable={false}
74
+ className="absolute top-0 object-cover"
75
+ style={thumbCellStyle(cell.left, thumbWidthPx, height)}
76
+ /* a picture that fails is hidden (the clip color shows), better than a broken-image icon, and asked for
77
+ again a little later; it comes back when it loads */
78
+ onError={(e) => { e.currentTarget.style.visibility = 'hidden'; retryImage(e.currentTarget); }}
79
+ onLoad={(e) => { e.currentTarget.style.visibility = ''; }}
80
+ />
81
+ ))}
82
+ </div>
83
+ );
84
+ }
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The waveform on a clip. Without it a sound track is plain bars: no telling where someone speaks, where they breathe,
3
+ * where it is silent — and "did this cut land mid-word" can only be seen on the waveform.
4
+ *
5
+ * Drawn from the bottom, not mirrored: sound rows are short, and a mirrored waveform leaves half the height each way.
6
+ * A skyline still reads when low. On a canvas, not hundreds of divs: dozens of clips with
7
+ * hundreds of peaks each would be tens of thousands of nodes.
8
+ *
9
+ * Peaks come from the server: GET media?what=wave&path= → `{ duration (s), peaks }`, 40 peaks a second, 0..1;
10
+ * 503 while the file is still being written.
11
+ */
12
+ import * as React from 'react';
13
+ import { normalizeWavePeaks, waveformColumns } from '@/lib/waveform-columns';
14
+ import { mediaFetch } from '@/lib/media-queue';
15
+ import { useThemeChanges } from '@/lib/theme-changes';
16
+
17
+ /** A sound's envelope: the whole file's peaks and its length (to turn peak indexes into ms). */
18
+ interface Wave {
19
+ peaks: number[];
20
+ durationMs: number;
21
+ }
22
+
23
+ /** Peaks per second the server sends; the length when the answer has none. */
24
+ const PEAKS_PER_SECOND = 40;
25
+
26
+ /** At most this many device pixels per canvas: past the browser's limit a canvas goes blank. */
27
+ const MAX_CANVAS_PX = 4096;
28
+ /** A change this soon after the last drawing is drawn once the changes stop for as long (ms). */
29
+ const BURST_MS = 150;
30
+
31
+ /*
32
+ * Drawings waiting, done a few a frame (FRAME_BUDGET_MS of them at most): a zoom of a film with hundreds of sounds redrew
33
+ * them all in one go, a long freeze. A newer drawing of a canvas replaces its older one; the stretched old bitmap holds
34
+ * until it is done.
35
+ */
36
+ const FRAME_BUDGET_MS = 6;
37
+ const waiting = new Map<HTMLCanvasElement, () => void>();
38
+ let drawing = 0;
39
+ function drawSoon(el: HTMLCanvasElement, draw: () => void) {
40
+ waiting.set(el, draw);
41
+ if (!drawing) drawing = requestAnimationFrame(drawSome);
42
+ }
43
+ function drawSome() {
44
+ drawing = 0;
45
+ const start = performance.now();
46
+ for (const [el, draw] of waiting) {
47
+ waiting.delete(el);
48
+ draw();
49
+ if (performance.now() - start > FRAME_BUDGET_MS) break;
50
+ }
51
+ if (waiting.size) drawing = requestAnimationFrame(drawSome);
52
+ }
53
+
54
+ /* the color a waveform is drawn in, read once per color and theme: read from each canvas, each drawing laid out the
55
+ page's styles again */
56
+ const colors = new Map<string, string>();
57
+ function colorOf(el: HTMLCanvasElement, color: string, theme: unknown): string {
58
+ const key = `${String(theme)}\0${color}`;
59
+ let c = colors.get(key);
60
+ if (!c) {
61
+ c = getComputedStyle(el).color || color;
62
+ if (colors.size > 64) colors.clear();
63
+ colors.set(key, c);
64
+ }
65
+ return c;
66
+ }
67
+
68
+ /** Waveforms already fetched: the same sound is drawn again on every drag and zoom. */
69
+ const CACHE = new Map<string, Wave>();
70
+ const PENDING = new Map<string, Promise<Wave | null>>();
71
+
72
+ /**
73
+ * The waveform, asked again while the server says it is not ready yet (503: a file an agent has just written).
74
+ * About half a minute at most; after that the block stays plain until its address changes.
75
+ */
76
+ async function fetchWhenReady(url: string, tries = 30): Promise<Response> {
77
+ const res = await mediaFetch(url);
78
+ if (res.status !== 503 || tries <= 1) return res;
79
+ const wait = Math.min(Math.max(Number(res.headers.get('retry-after')) || 1, 0.5), 5) * 1000;
80
+ await new Promise((done) => setTimeout(done, wait));
81
+ return fetchWhenReady(url, tries - 1);
82
+ }
83
+
84
+ async function loadWave(url: string): Promise<Wave | null> {
85
+ if (CACHE.has(url)) return CACHE.get(url) ?? null;
86
+ const already = PENDING.get(url);
87
+ if (already) return already;
88
+ const task = fetchWhenReady(url)
89
+ .then(async (r) => {
90
+ if (!r.ok) return null;
91
+ const body = (await r.json()) as { peaks?: number[]; duration?: number; durationMs?: number };
92
+ if (!body.peaks?.length) return null;
93
+ const durationMs = typeof body.durationMs === 'number' ? body.durationMs
94
+ : typeof body.duration === 'number' ? body.duration * 1000
95
+ : (body.peaks.length / PEAKS_PER_SECOND) * 1000;
96
+ return { peaks: normalizeWavePeaks(body.peaks), durationMs };
97
+ })
98
+ .catch(() => null)
99
+ .then((wave) => {
100
+ if (wave) {
101
+ CACHE.set(url, wave);
102
+ while (CACHE.size > 256) CACHE.delete(CACHE.keys().next().value!);
103
+ }
104
+ PENDING.delete(url);
105
+ return wave;
106
+ });
107
+ PENDING.set(url, task);
108
+ return task;
109
+ }
110
+
111
+ export function BlockWaveform({
112
+ url, width, height, color, inMs = 0, durMs, visiblePx,
113
+ }: {
114
+ url: string;
115
+ width: number;
116
+ height: number;
117
+ /**
118
+ * The waveform's color; may be a CSS variable (`var(--tl-wave)`): a canvas cannot read variables, so it is set as
119
+ * the element's `color` and read back computed, following the theme.
120
+ */
121
+ color: string;
122
+ /**
123
+ * Draw only this stretch (px within the clip). Zoomed in, a clip can be tens of thousands of pixels wide and only a
124
+ * slice is on screen: a canvas over the whole clip would be stretched past the limit (blurry) or burn memory on what
125
+ * cannot be seen.
126
+ */
127
+ visiblePx?: { from: number; to: number };
128
+ /**
129
+ * Where in the source the clip starts and how much it plays. The envelope is the whole file's; without taking off
130
+ * the in point the whole file is squeezed into the clip and the waveform lies about where the sound is.
131
+ */
132
+ inMs?: number;
133
+ durMs: number;
134
+ }): React.ReactElement | null {
135
+ const ref = React.useRef<HTMLCanvasElement | null>(null);
136
+ const [loaded, setLoaded] = React.useState(() => ({ url, wave: CACHE.get(url) ?? null }));
137
+ // A new edit must never display the previous arrangement while its request is in flight.
138
+ const wave = loaded.url === url ? loaded.wave : CACHE.get(url) ?? null;
139
+
140
+ React.useEffect(() => {
141
+ let alive = true;
142
+ /* the same wave already in hand (from the cache) renders nothing: a clip drawn as it scrolls into view is not
143
+ drawn twice */
144
+ void loadWave(url).then((wave) => { if (alive) setLoaded((cur) => (cur.url === url && cur.wave === wave ? cur : { url, wave })); });
145
+ return () => { alive = false; };
146
+ }, [url]);
147
+
148
+ /* its color is read as it draws: a theme changed, it draws again */
149
+ const theme = useThemeChanges();
150
+ const from = Math.max(0, Math.min(width, Math.floor(visiblePx?.from ?? 0)));
151
+ const to = Math.max(from, Math.min(width, Math.ceil(visiblePx?.to ?? width)));
152
+ const sliceW = to - from;
153
+
154
+ /* when it was last drawn: a change soon after is part of a burst (a zoom, a scroll) */
155
+ const drawnAt = React.useRef(-Infinity);
156
+ React.useEffect(() => {
157
+ const el = ref.current;
158
+ if (!el || !wave?.peaks.length || sliceW <= 0 || height <= 0) return undefined;
159
+ /* drawn soon (see drawSoon); in a burst of zoom or scroll steps, once the burst stops (the stretched old bitmap
160
+ holds meanwhile): redrawn at every step, the waveforms were most of what a zoom cost */
161
+ const draw = () => {
162
+ drawnAt.current = performance.now();
163
+ /* capped in device pixels, not CSS pixels: capping CSS pixels would also drop Retina's 2×, and this layer is
164
+ thin enough that blur makes it useless. Full ratio while it fits, down to 1× at the cap. */
165
+ const drawW = Math.min(Math.round(sliceW), MAX_CANVAS_PX);
166
+ const dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, MAX_CANVAS_PX / drawW));
167
+ el.width = Math.max(1, Math.round(drawW * dpr));
168
+ el.height = Math.max(1, Math.round(height * dpr));
169
+ const ctx = el.getContext('2d');
170
+ if (!ctx) return;
171
+ ctx.scale(dpr, dpr);
172
+ ctx.clearRect(0, 0, drawW, height);
173
+ ctx.fillStyle = colorOf(el, color, theme);
174
+
175
+ /* one column per pixel: more peaks than pixels takes the maximum (sampling would miss a drum hit and draw
176
+ silence); fewer peaks than pixels interpolates (nearest-peak steps are the sampling grid, not the sound) */
177
+ const sliceIn = inMs + (width > 0 ? (from / width) * durMs : 0);
178
+ const sliceDur = width > 0 ? (sliceW / width) * durMs : durMs;
179
+ const columns = waveformColumns(wave, sliceIn, sliceDur, drawW);
180
+ /* one path, one fill: thousands of fillRect calls would be thousands of draws */
181
+ ctx.beginPath();
182
+ for (let x = 0; x < drawW; x += 1) {
183
+ const v = columns[x]!;
184
+ const h = Math.max(1, v * height);
185
+ ctx.rect(x, height - h, 1, h);
186
+ }
187
+ ctx.fill();
188
+ };
189
+ const timer = window.setTimeout(() => drawSoon(el, draw), performance.now() - drawnAt.current < BURST_MS ? BURST_MS : 0);
190
+ return () => { window.clearTimeout(timer); waiting.delete(el); };
191
+ }, [wave, width, height, color, inMs, durMs, from, sliceW, theme]);
192
+
193
+ if (!wave?.peaks.length || sliceW <= 0) return null;
194
+ return (
195
+ <canvas
196
+ ref={ref}
197
+ className="pointer-events-none absolute bottom-0"
198
+ style={{ left: from, width: sliceW, height, color }}
199
+ />
200
+ );
201
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Full-screen loading: the logo and a moving bar, while a project opens.
3
+ *
4
+ * Opening a project means finding (or making) it and then reading its film. Both involve a
5
+ * noticeable wait, and during that wait **the screen must change**: when the UI sits still,
6
+ * people do not think "it is loading", they think "did I miss the click?" and click again.
7
+ *
8
+ * So no skeleton is drawn here. A skeleton guesses what will appear, and a wrong guess is
9
+ * costly: a new project lands on an empty picture, while a skeleton would first show blocks
10
+ * that are not there, making the page flash when loading ends. A full-screen background with
11
+ * the logo guesses nothing; what lands is covered up, not corrected.
12
+ *
13
+ * Two places show it in turn: App while it finds the project to open, then ProjectView while
14
+ * that project loads. Both look identical, so the handoff has no visible seam; the user sees
15
+ * one loading screen that stays lit throughout, not two that flash in turn.
16
+ */
17
+ import { useT } from '@/i18n';
18
+ import { BrandLogo } from './BrandLogo';
19
+
20
+ export function BrandLoadingScreen({
21
+ labelKey,
22
+ }: {
23
+ /** What is being done (an i18n key): both callers pass the same one and get the same sentence. */
24
+ labelKey?: string;
25
+ }) {
26
+ const t = useT();
27
+ return (
28
+ <div
29
+ role="status"
30
+ aria-live="polite"
31
+ aria-busy
32
+ /* No entrance animation. This screen is mounted twice (App first, then ProjectView), and
33
+ the second mount would replay the entrance, flashing the whole screen at the handoff,
34
+ which is worse than having no loading screen. */
35
+ /* The background is --panel, the same as the workspace's when it lands, so when the loading
36
+ screen gives way the content just grows in and the base color never moves. */
37
+ className="workspace-shell z-[9000] flex flex-col items-center justify-center bg-[var(--panel)]"
38
+ >
39
+ {/* Kept small: an enlarged logo in the middle of the screen looks like a splash ad, while
40
+ this screen only means "one moment". The bar below is what moves. */}
41
+ <span className="text-[var(--text)]"><BrandLogo size={34} /></span>
42
+
43
+ {labelKey ? (
44
+ <p className="mt-6 text-[14px] font-medium tracking-[-0.01em] text-[var(--text)]">
45
+ {t(labelKey)}
46
+ </p>
47
+ ) : null}
48
+
49
+ {/* The bar is fake: opening a project cannot report a percentage. It only says one thing
50
+ here: something is still moving. A line frozen in one place would look stuck. */}
51
+ <div className="mt-5 h-[3px] w-[180px] overflow-hidden rounded-full bg-[var(--border)]">
52
+ <div className="openfilm-loading-bar h-full w-1/3 rounded-full bg-[var(--text)]/60" />
53
+ </div>
54
+ </div>
55
+ );
56
+ }
@@ -0,0 +1,25 @@
1
+ import React from 'react';
2
+
3
+ /*
4
+ * The mark: a playhead over two clips, forming an F, in the surrounding text's color. The clips are cut away around
5
+ * the playhead (a real gap, so whatever is behind shows through), which keeps the playhead's shape in one color.
6
+ */
7
+ const VIEWBOX = '8 6 84 92';
8
+ const PLAYHEAD = 'M17 12H37Q40 12 40 15V27Q40 29 38.5 30.5L31 37V88Q31 92 27 92Q23 92 23 88V37L15.5 30.5Q14 29 14 27V15Q14 12 17 12Z';
9
+
10
+ export function BrandLogo({ size = 18 }: { size?: number }) {
11
+ const gap = `openfilm-gap-${React.useId().replace(/:/g, '')}`;
12
+ return (
13
+ <svg width={size} height={size} viewBox={VIEWBOX} fill="currentColor" className="block shrink-0" role="img" aria-label="OpenFilm">
14
+ <mask id={gap} maskUnits="userSpaceOnUse" x="0" y="0" width="100" height="100">
15
+ <rect width="100" height="100" fill="#fff" />
16
+ <path d={PLAYHEAD} stroke="#000" strokeWidth="7" strokeLinejoin="round" />
17
+ </mask>
18
+ <g mask={`url(#${gap})`}>
19
+ <rect x="22" y="12" width="64" height="20" rx="5" />
20
+ <rect x="25" y="44" width="47" height="19" rx="5" />
21
+ </g>
22
+ <path d={PLAYHEAD} />
23
+ </svg>
24
+ );
25
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The one card shell (confirmations, notices) and its buttons: background and border follow the theme, radius 12,
3
+ * one button height (28) in three weights — primary (filled), secondary (outlined), quiet (text only) — plus danger.
4
+ */
5
+ import React from 'react';
6
+ import { X } from 'lucide-react';
7
+ import { useT } from '@/i18n';
8
+
9
+ /** The shell of things that float up (menus, dropdowns): the card's shadow on the floating surface. */
10
+ export const POPOVER_CHROME: React.CSSProperties = {
11
+ background: 'var(--bg-surface)',
12
+ border: '1px solid var(--border-strong)',
13
+ borderRadius: 12,
14
+ boxShadow: '0 16px 40px -8px rgba(0,0,0,0.4), 0 4px 12px rgba(0,0,0,0.18)',
15
+ };
16
+
17
+ export function Card({
18
+ icon,
19
+ title,
20
+ body,
21
+ onClose,
22
+ children,
23
+ role,
24
+ className = '',
25
+ }: {
26
+ icon?: React.ReactNode;
27
+ title: React.ReactNode;
28
+ body?: React.ReactNode;
29
+ onClose?: () => void;
30
+ children?: React.ReactNode;
31
+ role?: React.AriaRole;
32
+ className?: string;
33
+ }) {
34
+ const t = useT();
35
+ return (
36
+ <div
37
+ {...(role ? { role } : {})}
38
+ className={`relative rounded-[12px] border border-[var(--card-border)] bg-[var(--card-bg)] px-3.5 pb-3 pt-3 shadow-[0_8px_24px_-12px_rgba(0,0,0,0.35)] ${className}`}
39
+ >
40
+ {onClose ? (
41
+ <div className="absolute right-2 top-2 flex items-center gap-0.5">
42
+ <button
43
+ type="button"
44
+ onClick={onClose}
45
+ aria-label={t('project.close')}
46
+ className="flex h-6 w-6 items-center justify-center rounded-md text-[var(--text-faint)] transition hover:bg-[var(--bg-hover)] hover:text-[var(--text)]"
47
+ >
48
+ <X size={14} strokeWidth={1.75} />
49
+ </button>
50
+ </div>
51
+ ) : null}
52
+ <div className={`flex items-start gap-2 ${onClose ? 'pr-7' : ''}`}>
53
+ {icon ? <span className="mt-[2px] shrink-0 text-[var(--text-muted)]">{icon}</span> : null}
54
+ <div className="min-w-0 flex-1">
55
+ <h3 className="text-[13px] font-medium leading-snug text-[var(--text)]">{title}</h3>
56
+ {body ? <div className="mt-1 whitespace-pre-line break-words text-[12.5px] leading-relaxed text-[var(--text-muted)]">{body}</div> : null}
57
+ </div>
58
+ </div>
59
+ {children}
60
+ </div>
61
+ );
62
+ }
63
+
64
+ export function CardActions({ children, className = '' }: { children: React.ReactNode; className?: string }) {
65
+ return <div className={`mt-3 flex items-center justify-end gap-2 ${className}`}>{children}</div>;
66
+ }
67
+
68
+ type Weight = 'primary' | 'secondary' | 'quiet' | 'danger';
69
+
70
+ const WEIGHT: Record<Weight, string> = {
71
+ primary: 'bg-[var(--text)] text-[var(--panel)] hover:opacity-90',
72
+ secondary: 'text-[var(--text)] ring-1 ring-inset ring-[var(--border-strong)] hover:bg-[var(--bg-hover)]',
73
+ quiet: 'text-[var(--text-muted)] hover:text-[var(--text)]',
74
+ danger: 'bg-[var(--err)] text-white hover:brightness-110',
75
+ };
76
+
77
+ export const CardButton = React.forwardRef<
78
+ HTMLButtonElement,
79
+ React.ButtonHTMLAttributes<HTMLButtonElement> & { weight?: Weight }
80
+ >(function CardButton({ weight = 'secondary', className = '', type = 'button', ...props }, ref) {
81
+ return (
82
+ <button
83
+ {...props}
84
+ ref={ref}
85
+ type={type}
86
+ className={`inline-flex h-7 shrink-0 items-center justify-center gap-1.5 rounded-lg px-3 text-[12px] font-medium outline-none transition focus-visible:ring-2 focus-visible:ring-[var(--text)]/30 disabled:pointer-events-none disabled:opacity-50 ${WEIGHT[weight]} ${className}`}
87
+ />
88
+ );
89
+ });