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,148 @@
1
+ /**
2
+ * Subtitles over the picture.
3
+ *
4
+ * Subtitles are not part of the film: how they look, where they sit and whether they show is a preference (large
5
+ * type, two languages, none), and drawing them into the film would decide for everyone and mean rendering the film
6
+ * again for every change of look. So the film only says what is said when; the editor lays the lines over the
7
+ * picture here, and exports burn them in or write them as files from the same cues and the same CSS
8
+ * (studio/server/film-subtitle.mjs), so the preview is what the export shows.
9
+ *
10
+ * Moved by dragging, not by top/bottom presets: what a subtitle has to stay clear of differs in every film. Dragging
11
+ * shows exactly where it lands.
12
+ *
13
+ * Mount it as a direct child of the delivery frame's box (the `position: relative` box with `container-type: size`
14
+ * around the picture, letterbox included): positions are fractions of that box, and the font size is in its `cqmin`.
15
+ */
16
+ import * as React from 'react';
17
+
18
+ import {
19
+ cueAt,
20
+ cueText,
21
+ filmSubtitleWordCss,
22
+ filmSubtitleWords,
23
+ snapAxis,
24
+ SNAP_X,
25
+ SNAP_Y,
26
+ subtitleCss,
27
+ subtitleOpacity,
28
+ type SubtitleCue,
29
+ type SubtitleStyle,
30
+ } from '@/lib/subtitles';
31
+
32
+ export function SubtitleOverlay({
33
+ cues, timeMs, style, onMove, onPick,
34
+ }: {
35
+ /** The lines as shown (see useFilmSubtitles: the picked language already applied). */
36
+ cues: readonly SubtitleCue[];
37
+ timeMs: number;
38
+ /** The style for this frame (filmSubtitleStyleFor(style, frame)): off → nothing is drawn. */
39
+ style: SubtitleStyle;
40
+ /** Where it was dragged to (fractions of the frame). Without it the subtitle cannot be dragged (read only). */
41
+ onMove?: (pos: { x: number; y: number }) => void;
42
+ /** The subtitle pressed: its look is shown (the inspector), as a layer pressed shows its own. */
43
+ onPick?: () => void;
44
+ }): React.ReactElement | null {
45
+ const cue = React.useMemo(() => (style.on ? cueAt(cues, timeMs) : null), [cues, timeMs, style.on]);
46
+ const box = React.useRef<HTMLDivElement | null>(null);
47
+ const [dragging, setDragging] = React.useState(false);
48
+ /* while dragging the position lives here: handing every move up would re-render the whole editor each time.
49
+ It is handed up once, on release. */
50
+ const [livePos, setLivePos] = React.useState<{ x: number; y: number } | null>(null);
51
+ /** The line it is snapped to now (fraction of the frame); null = none, and no line drawn. */
52
+ const [guides, setGuides] = React.useState<{ x: number | null; y: number | null }>({ x: null, y: null });
53
+
54
+ const onPointerDown = React.useCallback((e: React.PointerEvent) => {
55
+ if (!onMove || e.button !== 0) return;
56
+ const frame = box.current?.parentElement;
57
+ if (!frame) return;
58
+ e.preventDefault();
59
+ e.stopPropagation();
60
+ onPick?.();
61
+ setDragging(true);
62
+ const rect = frame.getBoundingClientRect();
63
+ /* how far from the anchor it was grabbed, or it would jump under the pointer. The anchor is `pos` itself (the line
64
+ nearest the frame's edge, see filmSubtitleCss), not the block's center: with two lines they differ */
65
+ const dx = e.clientX - (rect.left + style.pos.x * rect.width);
66
+ const dy = e.clientY - (rect.top + style.pos.y * rect.height);
67
+
68
+ let last: { x: number; y: number } | null = null;
69
+ let raf = 0;
70
+ const move = (ev: PointerEvent): void => {
71
+ /* kept inside the picture: dragged out, it would vanish, and nobody thinks "it is still there, off the frame" */
72
+ const rawX = Math.min(0.98, Math.max(0.02, (ev.clientX - dx - rect.left) / rect.width));
73
+ const rawY = Math.min(0.98, Math.max(0.02, (ev.clientY - dy - rect.top) / rect.height));
74
+ const x = snapAxis(rawX, rect.width, SNAP_X);
75
+ const y = snapAxis(rawY, rect.height, SNAP_Y);
76
+ /* a line only while snapped: always drawn, the center lines would be decoration; this one says "aligned now" */
77
+ last = { x: x.value, y: y.value };
78
+ const guide = { x: x.guide, y: y.guide };
79
+ /* once a frame: pointer moves come far more often than the screen refreshes */
80
+ if (!raf) {
81
+ raf = requestAnimationFrame(() => {
82
+ raf = 0;
83
+ setGuides(guide);
84
+ setLivePos(last);
85
+ });
86
+ }
87
+ };
88
+ const up = (): void => {
89
+ window.removeEventListener('pointermove', move);
90
+ window.removeEventListener('pointerup', up);
91
+ if (raf) cancelAnimationFrame(raf);
92
+ setDragging(false);
93
+ setGuides({ x: null, y: null });
94
+ setLivePos(null);
95
+ if (last) onMove(last);
96
+ };
97
+ window.addEventListener('pointermove', move);
98
+ window.addEventListener('pointerup', up);
99
+ }, [onMove, onPick, style.pos.x, style.pos.y]);
100
+
101
+ if (!cue) return null;
102
+ /* position, size, color, stroke and box all come from the shared function; this layer adds only what editing has:
103
+ the move cursor and the dashed outline while dragging */
104
+ const css = subtitleCss(livePos ? { ...style, pos: livePos } : style);
105
+ return (
106
+ <>
107
+ {/* snap lines run across the whole frame: they say how the subtitle sits against the picture */}
108
+ {guides.x !== null ? <Guide axis="x" at={guides.x} /> : null}
109
+ {guides.y !== null ? <Guide axis="y" at={guides.y} /> : null}
110
+ <div
111
+ ref={box}
112
+ role={onMove ? 'presentation' : undefined}
113
+ onPointerDown={onPointerDown}
114
+ className={onMove ? 'cursor-move' : 'pointer-events-none'}
115
+ style={{ ...(css.box as React.CSSProperties), ...(cue && style.fadeMs ? { opacity: subtitleOpacity(cue, timeMs, style.fadeMs) } : {}) }}
116
+ >
117
+ <span
118
+ style={{
119
+ ...(css.text as React.CSSProperties),
120
+ /* subtitles have no edge of their own: the outline shows what is being held */
121
+ ...(dragging ? { outline: '1px dashed rgba(255,255,255,0.7)', outlineOffset: 3 } : {}),
122
+ }}
123
+ >
124
+ {style.karaoke ? (
125
+ filmSubtitleWords(cue, timeMs).map((w, i) => (
126
+ w.active
127
+ ? <span key={i} style={filmSubtitleWordCss(style) as React.CSSProperties}>{w.text}</span>
128
+ : <React.Fragment key={i}>{w.text}</React.Fragment>
129
+ ))
130
+ ) : cueText(cue)}
131
+ </span>
132
+ </div>
133
+ </>
134
+ );
135
+ }
136
+
137
+ /** A snap line. Magenta is what design tools use for these; it needs no explaining. */
138
+ function Guide({ axis, at }: { axis: 'x' | 'y'; at: number }) {
139
+ return (
140
+ <div
141
+ aria-hidden
142
+ className="pointer-events-none absolute z-10"
143
+ style={axis === 'x'
144
+ ? { left: `${at * 100}%`, top: 0, bottom: 0, width: 1, background: '#f0f', opacity: 0.9 }
145
+ : { top: `${at * 100}%`, left: 0, right: 0, height: 1, background: '#f0f', opacity: 0.9 }}
146
+ />
147
+ );
148
+ }
@@ -0,0 +1,419 @@
1
+ /**
2
+ * The subtitle row: the film's subtitles as blocks under the ruler, timed by hand.
3
+ *
4
+ * drag a cue's edge its start or end (never over a neighbor, snapping to clip edges, cues and the playhead)
5
+ * drag a cue moves it, its length kept
6
+ * double-click types its words, in place (Enter keeps them, Esc lets them go; empty takes it away)
7
+ * right-click split at the playhead, merge with the next, edit, delete; on empty space, add one
8
+ * ⌘B with the row targeted (the last thing pressed on the timeline): splits the cue at the playhead
9
+ * ⌫ / Delete, Enter the selected cue: deletes it, types its words
10
+ *
11
+ * Each edit goes back into the transcript the cue comes from, in one undo step (lib/use-subtitle-edits). The row is
12
+ * shown or hidden with its button in the toolbar (kept on this machine).
13
+ */
14
+ import React from 'react';
15
+ import { Captions, Combine, EyeOff, Pencil, Plus, Scissors, Trash2 } from 'lucide-react';
16
+ import { useT } from '@/i18n';
17
+ import { hintOf } from '@/lib/keymap';
18
+ import { currentKeymap, keyAction, MAC } from '@/lib/keymap-store';
19
+ import {
20
+ addSpan,
21
+ canMerge,
22
+ canSplitAt,
23
+ cueEnd,
24
+ cueIndexAt,
25
+ cueKey,
26
+ moveCue,
27
+ retimeCue,
28
+ rowOrder,
29
+ } from '@/lib/subtitle-cues';
30
+ import type { SubtitleCue } from '@/lib/subtitles';
31
+ import { snapToFrame } from '@/lib/timecode';
32
+ import { DRAG_THRESHOLD_PX, snapMs, type SnapTarget } from '@/lib/timeline-drag';
33
+ import type { SubtitleEdits } from '@/lib/use-subtitle-edits';
34
+ import { ContextMenu, type ContextMenuEntry } from './ContextMenu';
35
+ import { Tooltip } from './Tooltip';
36
+ import { isTypingTarget } from './typing-target';
37
+
38
+ export const SUBTITLE_ROW_H = 30;
39
+ /** How wide a cue's edge can be grabbed (px), less on a narrow cue so its middle can still be. */
40
+ const EDGE_PX = 6;
41
+
42
+ /** What the editor gives the timeline for the row. */
43
+ export interface SubtitleRowSpec {
44
+ /** The film's subtitles (GET captions), as the film shows them. */
45
+ cues: readonly SubtitleCue[];
46
+ edits: SubtitleEdits;
47
+ /** The clip a cue added at `ms` would be said by, or null: nothing heard there. */
48
+ speakerAt: (ms: number) => { clipId: string; src: string; endMs: number } | null;
49
+ /** A cue pressed: the subtitles' look is shown (the inspector). */
50
+ onPick?: () => void;
51
+ }
52
+
53
+ /* ── shown or not: kept on this machine ── */
54
+
55
+ const SHOWN_KEY = 'openfilm.timeline.subtitleRow';
56
+ const shownListeners = new Set<() => void>();
57
+ let shownNow: boolean | null = null;
58
+ function readShown(): boolean {
59
+ if (shownNow != null) return shownNow;
60
+ try { shownNow = localStorage.getItem(SHOWN_KEY) !== '0'; } catch { shownNow = true; }
61
+ return shownNow;
62
+ }
63
+ function setShown(on: boolean): void {
64
+ shownNow = on;
65
+ try { localStorage.setItem(SHOWN_KEY, on ? '1' : '0'); } catch { /* this session only */ }
66
+ for (const fn of shownListeners) fn();
67
+ }
68
+ /** "Hide the row", in the row's menus: where it comes back is said under it. */
69
+ const hideRowItem = (t: (key: string) => string): ContextMenuEntry => ({
70
+ id: 'hide', label: t('subtitleRow.hide'), note: t('subtitleRow.hideNote'), icon: <EyeOff size={14} />, onSelect: () => setShown(false),
71
+ });
72
+ export function useSubtitleRowShown(): [boolean, (on: boolean) => void] {
73
+ const on = React.useSyncExternalStore((fn) => { shownListeners.add(fn); return () => { shownListeners.delete(fn); }; }, readShown, () => true);
74
+ return [on, setShown];
75
+ }
76
+
77
+ /**
78
+ * The switch for the row, in the subtitle panel's Lines header: the lines edited on the timeline (timed, split,
79
+ * merged), or not shown there. Kept on this machine.
80
+ */
81
+ export function SubtitleRowToggle() {
82
+ const t = useT();
83
+ const [on, set] = useSubtitleRowShown();
84
+ return (
85
+ <Tooltip side="bottom" label={t(on ? 'subtitleRow.hideRow' : 'subtitleRow.showRow')}>
86
+ <button
87
+ type="button"
88
+ data-subtitle-row-toggle={on ? '1' : '0'}
89
+ onClick={() => set(!on)}
90
+ onMouseDown={(e) => e.preventDefault()}
91
+ aria-pressed={on}
92
+ className="flex h-5 shrink-0 items-center gap-1 rounded-[4px] px-1.5 text-[10.5px] font-medium outline-none transition focus-visible:ring-1 focus-visible:ring-[var(--border-strong)]"
93
+ style={on ? { color: '#fff', background: 'var(--tl-accent)' } : { color: 'var(--text-muted)', background: 'var(--fill-tsp)' }}
94
+ >
95
+ <Captions size={11} aria-hidden />
96
+ {t('subtitleRow.editOnTimeline')}
97
+ </button>
98
+ </Tooltip>
99
+ );
100
+ }
101
+
102
+ /**
103
+ * The row's head, in the timeline's head column: its name, and a cue added at the playhead. Its right-click menu adds
104
+ * one too, or hides the row (it comes back from the subtitle panel's "Edit on timeline").
105
+ */
106
+ export function SubtitleRowHead({ onAdd }: { onAdd: () => void }) {
107
+ const t = useT();
108
+ const [menu, setMenu] = React.useState<{ x: number; y: number } | null>(null);
109
+ const items: ContextMenuEntry[] = [
110
+ { id: 'add', label: t('subtitleRow.add'), icon: <Plus size={14} />, onSelect: onAdd },
111
+ { id: 'sep', separator: true },
112
+ hideRowItem(t),
113
+ ];
114
+ return (
115
+ <div className="flex items-center justify-between gap-1 border-b pl-2 pr-1" style={{ height: SUBTITLE_ROW_H, borderColor: 'var(--tl-line)' }}
116
+ data-subtitle-row-head=""
117
+ onContextMenu={(e) => { e.preventDefault(); e.stopPropagation(); setMenu({ x: e.clientX, y: e.clientY }); }}>
118
+ <span className="truncate text-[10.5px] font-medium" style={{ color: 'var(--tl-text)' }}>{t('subtitleRow.name')}</span>
119
+ <Tooltip side="bottom" label={t('subtitleRow.add')}>
120
+ <button type="button" onClick={onAdd} onMouseDown={(e) => e.preventDefault()} aria-label={t('subtitleRow.add')}
121
+ className="flex h-5 w-5 shrink-0 items-center justify-center rounded-[4px] text-[var(--tl-faint)] hover:bg-black/5 hover:text-[var(--tl-text)] dark:hover:bg-white/10">
122
+ <Plus size={13} />
123
+ </button>
124
+ </Tooltip>
125
+ {menu ? <ContextMenu x={menu.x} y={menu.y} items={items} onClose={() => setMenu(null)} /> : null}
126
+ </div>
127
+ );
128
+ }
129
+
130
+ type Drag = { key: string; startMs: number; endMs: number; guideMs: number | null };
131
+
132
+ export interface SubtitleRowHandle {
133
+ /** Add a cue at the playhead (the head's "+"). */
134
+ addAtPlayhead: () => void;
135
+ }
136
+
137
+ export const SubtitleRow = React.forwardRef<SubtitleRowHandle, {
138
+ spec: SubtitleRowSpec;
139
+ pxPerMs: number;
140
+ scrollLeft: number;
141
+ /** The width of the time laid out (px). */
142
+ width: number;
143
+ /** The playhead now (ms). */
144
+ playheadMs: () => number;
145
+ snap: boolean;
146
+ /** Where the row's drags snap besides other cues: 0, the playhead, every clip's edges. */
147
+ snapTargets: () => SnapTarget[];
148
+ /** Why an edit could not be done, said in the timeline's toolbar. */
149
+ onError: (text: string) => void;
150
+ }>(function SubtitleRow({ spec, pxPerMs, scrollLeft, width, playheadMs, snap, snapTargets, onError }, ref) {
151
+ const t = useT();
152
+ const cues = React.useMemo(() => rowOrder(spec.cues), [spec.cues]);
153
+ const [selected, setSelected] = React.useState<string | null>(null);
154
+ const [drag, setDrag] = React.useState<Drag | null>(null);
155
+ /** Times an edit was sent with, shown until the lines come back. */
156
+ const [held, setHeld] = React.useState<{ key: string; startMs: number; endMs: number } | null>(null);
157
+ const [editing, setEditing] = React.useState<{ key: string; text: string } | null>(null);
158
+ const [menu, setMenu] = React.useState<{ x: number; y: number; items: ContextMenuEntry[] } | null>(null);
159
+ /** The cue to select (and maybe type into) once the lines come back from an edit, by its start. */
160
+ const [after, setAfter] = React.useState<{ startMs: number; edit: boolean } | null>(null);
161
+ const rowRef = React.useRef<HTMLDivElement | null>(null);
162
+ const innerRef = React.useRef<HTMLDivElement | null>(null);
163
+ const targeted = React.useRef(false);
164
+ const cuesRef = React.useRef(cues);
165
+ cuesRef.current = cues;
166
+
167
+ /* the lines came back: what an edit made is picked */
168
+ React.useEffect(() => {
169
+ if (!after) return;
170
+ const found = cues.find((c) => Math.abs(c.startMs - after.startMs) <= 2);
171
+ if (!found) return;
172
+ setSelected(cueKey(found));
173
+ if (after.edit) setEditing({ key: cueKey(found), text: found.text });
174
+ setAfter(null);
175
+ }, [cues, after]);
176
+ /* the selection follows the lines: one gone is let go */
177
+ React.useEffect(() => {
178
+ if (selected && !cues.some((c) => cueKey(c) === selected) && !after) setSelected(null);
179
+ }, [cues, selected, after]);
180
+
181
+ const run = React.useCallback(async (job: Promise<string | null>, then?: { startMs: number; edit?: boolean }) => {
182
+ const why = await job;
183
+ setHeld(null);
184
+ if (why) { onError(why); return; }
185
+ if (then) setAfter({ startMs: then.startMs, edit: then.edit ?? false });
186
+ }, [onError]);
187
+
188
+ const msAt = (clientX: number) => {
189
+ const box = innerRef.current?.getBoundingClientRect();
190
+ return box && pxPerMs > 0 ? Math.max(0, (clientX - box.left) / pxPerMs) : 0;
191
+ };
192
+
193
+ /* ── the edits ── */
194
+ const split = React.useCallback((cue: SubtitleCue, atMs: number) => {
195
+ if (!canSplitAt(cue, atMs)) { onError(t('subtitleRow.splitWhere')); return; }
196
+ void run(spec.edits.split(cue, snapToFrame(atMs)), { startMs: cue.startMs });
197
+ }, [spec.edits, run, onError, t]);
198
+ const merge = React.useCallback((cue: SubtitleCue) => {
199
+ const list = cuesRef.current;
200
+ const next = list[list.indexOf(cue) + 1];
201
+ if (!next || !canMerge(cue, next)) return;
202
+ void run(spec.edits.merge(cue, next), { startMs: cue.startMs });
203
+ }, [spec.edits, run]);
204
+ const remove = React.useCallback((cue: SubtitleCue) => {
205
+ setSelected(null);
206
+ void run(spec.edits.remove(cue));
207
+ }, [spec.edits, run]);
208
+ const addAt = React.useCallback((atMs: number) => {
209
+ const at = snapToFrame(atMs);
210
+ const speaker = spec.speakerAt(at);
211
+ if (!speaker) { onError(t('subtitleRow.nothingHeard')); return; }
212
+ const span = addSpan(cuesRef.current, at, speaker.endMs);
213
+ if (!span) { onError(t('subtitleRow.noRoom')); return; }
214
+ void run(spec.edits.add(speaker.clipId, speaker.src, span.startMs, span.endMs, t('subtitleRow.newText')), { startMs: span.startMs, edit: true });
215
+ }, [spec, run, onError, t]);
216
+ React.useImperativeHandle(ref, () => ({ addAtPlayhead: () => addAt(playheadMs()) }), [addAt, playheadMs]);
217
+ const commitText = React.useCallback(() => {
218
+ const now = editing;
219
+ setEditing(null);
220
+ if (!now) return;
221
+ const cue = cuesRef.current.find((c) => cueKey(c) === now.key);
222
+ if (!cue || now.text.trim() === cue.text.trim()) return;
223
+ void run(now.text.trim() ? spec.edits.setText(cue, now.text) : spec.edits.remove(cue), { startMs: cue.startMs });
224
+ }, [editing, spec.edits, run]);
225
+
226
+ /* ── keys, while the row is the timeline's target ── */
227
+ React.useEffect(() => {
228
+ const onDown = (e: PointerEvent) => {
229
+ const el = e.target as Element | null;
230
+ if (rowRef.current?.contains(el)) { targeted.current = true; return; }
231
+ /* another row of the timeline pressed: it is the target now */
232
+ if (el?.closest?.('[data-timeline] .tl-scroll')) { targeted.current = false; setSelected(null); }
233
+ };
234
+ const onKey = (e: KeyboardEvent) => {
235
+ if (!targeted.current || isTypingTarget(e.target) || (e.target as Element | null)?.closest?.('[role="dialog"]')) return;
236
+ const list = cuesRef.current;
237
+ const picked = list.find((c) => cueKey(c) === selected);
238
+ const mod = e.metaKey || e.ctrlKey;
239
+ const take = () => { e.preventDefault(); e.stopPropagation(); };
240
+ const action = keyAction(e);
241
+ if (action === 'split') {
242
+ take();
243
+ if (e.repeat) return;
244
+ const at = playheadMs();
245
+ const cue = picked && canSplitAt(picked, at) ? picked : list[cueIndexAt(list, at)];
246
+ if (cue) split(cue, at);
247
+ return;
248
+ }
249
+ if (!picked) return;
250
+ if (action === 'remove' || action === 'ripple-delete') { take(); remove(picked); return; }
251
+ if (mod || e.altKey) return;
252
+ if (e.key === 'Enter') { take(); setEditing({ key: cueKey(picked), text: picked.text }); return; }
253
+ if (e.key === 'Escape') { setSelected(null); }
254
+ };
255
+ window.addEventListener('pointerdown', onDown, true);
256
+ window.addEventListener('keydown', onKey, true);
257
+ return () => { window.removeEventListener('pointerdown', onDown, true); window.removeEventListener('keydown', onKey, true); };
258
+ }, [selected, split, remove, playheadMs]);
259
+
260
+ /* ── dragging: an edge, or the whole cue ── */
261
+ const startDrag = (e: React.PointerEvent, cue: SubtitleCue, how: 'start' | 'end' | 'move') => {
262
+ if (e.button !== 0) return;
263
+ e.preventDefault();
264
+ e.stopPropagation();
265
+ const i = cuesRef.current.indexOf(cue);
266
+ const key = cueKey(cue);
267
+ setSelected(key);
268
+ targeted.current = true;
269
+ spec.onPick?.();
270
+ const fromX = e.clientX;
271
+ const grabMs = msAt(e.clientX) - cue.startMs;
272
+ const others = cuesRef.current.flatMap((c, j) => (j === i ? [] : [{ ms: c.startMs, kind: 'edge' as const }, { ms: cueEnd(c), kind: 'edge' as const }]));
273
+ const targets = [...snapTargets(), ...others];
274
+ const snapped = (ms: number) => {
275
+ if (!snap) return { ms: snapToFrame(ms), guide: null };
276
+ const hit = snapMs(ms, targets, pxPerMs);
277
+ return hit.target ? { ms: hit.ms, guide: hit.ms } : { ms: snapToFrame(ms), guide: null };
278
+ };
279
+ let moved = false;
280
+ let last: Drag | null = null;
281
+ const move = (ev: PointerEvent) => {
282
+ if (!moved && Math.abs(ev.clientX - fromX) < DRAG_THRESHOLD_PX) return;
283
+ moved = true;
284
+ const ms = msAt(ev.clientX);
285
+ let next: Drag;
286
+ if (how === 'move') {
287
+ /* the start or the end snaps, whichever is nearer something */
288
+ const start = snapped(ms - grabMs);
289
+ const end = snapped(ms - grabMs + cue.durMs);
290
+ const byEnd = !start.guide && end.guide != null;
291
+ const at = moveCue(cuesRef.current, i, byEnd ? end.ms - cue.durMs : start.ms);
292
+ next = { key, ...at, guideMs: byEnd ? end.guide : start.guide };
293
+ } else {
294
+ const s = snapped(ms);
295
+ next = { key, ...retimeCue(cuesRef.current, i, how, s.ms), guideMs: s.guide };
296
+ }
297
+ last = next;
298
+ setDrag(next);
299
+ };
300
+ const up = () => {
301
+ window.removeEventListener('pointermove', move);
302
+ window.removeEventListener('pointerup', up);
303
+ window.removeEventListener('pointercancel', up);
304
+ setDrag(null);
305
+ const done = last;
306
+ if (!moved || !done || (done.startMs === cue.startMs && done.endMs === cueEnd(cue))) return;
307
+ setHeld({ key, startMs: done.startMs, endMs: done.endMs });
308
+ void run(spec.edits.retime(cue, done.startMs, done.endMs), { startMs: done.startMs });
309
+ };
310
+ window.addEventListener('pointermove', move);
311
+ window.addEventListener('pointerup', up);
312
+ window.addEventListener('pointercancel', up);
313
+ };
314
+
315
+ const openMenu = (e: React.MouseEvent, cue: SubtitleCue | null) => {
316
+ e.preventDefault();
317
+ e.stopPropagation();
318
+ targeted.current = true;
319
+ const at = playheadMs();
320
+ if (!cue) {
321
+ setSelected(null);
322
+ setMenu({ x: e.clientX, y: e.clientY, items: [
323
+ { id: 'add', label: t('subtitleRow.add'), icon: <Plus size={14} />, onSelect: () => addAt(at) },
324
+ { id: 'sep', separator: true },
325
+ hideRowItem(t),
326
+ ] });
327
+ return;
328
+ }
329
+ setSelected(cueKey(cue));
330
+ const list = cuesRef.current;
331
+ const next = list[list.indexOf(cue) + 1];
332
+ setMenu({
333
+ x: e.clientX, y: e.clientY, items: [
334
+ { id: 'split', label: t('subtitleRow.split'), icon: <Scissors size={14} />, shortcut: hintOf(currentKeymap(), 'split', MAC),
335
+ disabled: !canSplitAt(cue, at), hint: t('subtitleRow.splitWhere'), onSelect: () => split(cue, at) },
336
+ { id: 'merge', label: t('subtitleRow.merge'), icon: <Combine size={14} />, disabled: !canMerge(cue, next), hint: t('subtitleRow.mergeWhy'), onSelect: () => merge(cue) },
337
+ { id: 'edit', label: t('subtitleRow.edit'), icon: <Pencil size={14} />, shortcut: '↵', onSelect: () => setEditing({ key: cueKey(cue), text: cue.text }) },
338
+ { id: 'sep', separator: true },
339
+ { id: 'delete', label: t('subtitleRow.delete'), icon: <Trash2 size={14} />, danger: true, shortcut: hintOf(currentKeymap(), 'remove', MAC), onSelect: () => remove(cue) },
340
+ ],
341
+ });
342
+ };
343
+
344
+ return (
345
+ <div
346
+ ref={rowRef}
347
+ data-subtitle-row=""
348
+ className="relative shrink-0 overflow-hidden border-b select-none"
349
+ style={{ height: SUBTITLE_ROW_H, background: 'var(--tl-lane)', borderColor: 'var(--tl-line)' }}
350
+ onPointerDown={(e) => { if (e.button === 0) { setSelected(null); targeted.current = true; } }}
351
+ onContextMenu={(e) => openMenu(e, null)}
352
+ >
353
+ <div ref={innerRef} className="absolute inset-y-0 left-0" style={{ width, transform: `translate3d(${-scrollLeft}px,0,0)` }}>
354
+ {/* scaled while a zoom gesture goes on (see Timeline's zoom layers) */}
355
+ <div data-zoom-layer="content" className="absolute inset-0">
356
+ {cues.map((cue) => {
357
+ const key = cueKey(cue);
358
+ const shown = drag?.key === key ? drag : held?.key === key ? held : { startMs: cue.startMs, endMs: cueEnd(cue) };
359
+ const left = shown.startMs * pxPerMs;
360
+ const w = Math.max(2, (shown.endMs - shown.startMs) * pxPerMs);
361
+ const picked = selected === key;
362
+ const edge = Math.min(EDGE_PX, w / 4);
363
+ return (
364
+ <div
365
+ key={key}
366
+ data-subtitle-cue={cue.startMs}
367
+ title={cue.text}
368
+ className="absolute top-[3px] bottom-[3px] cursor-grab overflow-hidden rounded-[3px] active:cursor-grabbing"
369
+ style={{
370
+ left, width: w, background: 'var(--tl-caption)',
371
+ boxShadow: picked ? '0 0 0 1.5px var(--tl-select)' : undefined,
372
+ zIndex: picked ? 2 : 1,
373
+ }}
374
+ onPointerDown={(e) => startDrag(e, cue, 'move')}
375
+ onDoubleClick={(e) => { e.stopPropagation(); setEditing({ key, text: cue.text }); }}
376
+ onContextMenu={(e) => openMenu(e, cue)}
377
+ >
378
+ <span className="pointer-events-none absolute inset-0 flex items-center truncate px-1.5 text-[10.5px] leading-none text-white/95">
379
+ {w > 14 ? cue.text : null}
380
+ </span>
381
+ <span role="presentation" data-trim="start" className="absolute inset-y-0 left-0 z-[2] cursor-ew-resize" style={{ width: edge }}
382
+ onPointerDown={(e) => startDrag(e, cue, 'start')} />
383
+ <span role="presentation" data-trim="end" className="absolute inset-y-0 right-0 z-[2] cursor-ew-resize" style={{ width: edge }}
384
+ onPointerDown={(e) => startDrag(e, cue, 'end')} />
385
+ </div>
386
+ );
387
+ })}
388
+ {editing ? (() => {
389
+ const cue = cues.find((c) => cueKey(c) === editing.key);
390
+ if (!cue) return null;
391
+ return (
392
+ <input
393
+ autoFocus
394
+ data-subtitle-edit=""
395
+ aria-label={t('subtitleRow.edit')}
396
+ value={editing.text}
397
+ onChange={(e) => setEditing({ ...editing, text: e.target.value })}
398
+ onFocus={(e) => e.currentTarget.select()}
399
+ onKeyDown={(e) => {
400
+ e.stopPropagation();
401
+ if (e.key === 'Enter') { e.preventDefault(); commitText(); }
402
+ if (e.key === 'Escape') { e.preventDefault(); setEditing(null); }
403
+ }}
404
+ onBlur={commitText}
405
+ onPointerDown={(e) => e.stopPropagation()}
406
+ className="absolute top-[3px] z-10 h-[24px] rounded-[3px] bg-[var(--bg)] px-1.5 text-[11.5px] text-[var(--text)] outline-none ring-1 ring-[var(--tl-accent)]"
407
+ style={{ left: cue.startMs * pxPerMs, width: Math.max(180, cue.durMs * pxPerMs) }}
408
+ />
409
+ );
410
+ })() : null}
411
+ {drag?.guideMs != null ? (
412
+ <div aria-hidden className="pointer-events-none absolute inset-y-0 z-[3] w-px bg-[var(--tl-accent)]" style={{ left: drag.guideMs * pxPerMs }} />
413
+ ) : null}
414
+ </div>
415
+ </div>
416
+ {menu ? <ContextMenu x={menu.x} y={menu.y} items={menu.items} onClose={() => setMenu(null)} /> : null}
417
+ </div>
418
+ );
419
+ });