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,422 @@
1
+ /**
2
+ * Context menu: a menu that pops up at the pointer.
3
+ *
4
+ * A few things that are not obvious but required:
5
+ * · **Portal to body.** An ancestor of the menu has `overflow: hidden` (the timeline's track
6
+ * area must clip whatever scrolls out of view); left in place, the menu would be half cut off.
7
+ * · **Flip to avoid the edge.** Pressed near the bottom-right corner of the screen, a menu
8
+ * growing toward the bottom right would be half off screen, and that corner is exactly
9
+ * where the timeline gets right-clicked most (the blocks at the end). Position after
10
+ * measuring the real size instead of guessing.
11
+ * · **Keyboard works.** Up/Down arrows, Home/End for the ends, Enter to run, Esc to close (only the menu: an Esc
12
+ * meant for it must not also close the dialog it was opened in). Right (or Enter) on an item with a submenu
13
+ * opens it and moves focus into it; Left or Esc closes it and comes back.
14
+ * The context menu is the only entry point for several things on the timeline; if it only
15
+ * worked with a mouse, those actions would not exist for keyboard users.
16
+ * · **Disabled items stay in place** and can say why (`hint`). If they vanished, the same
17
+ * spot would be "Delete" this time and "Duplicate" the next, and muscle memory could not keep up.
18
+ * · **Submenus** open as a separate panel so they do not stretch the parent's width.
19
+ * Clicking inside a submenu does not count as "clicking outside".
20
+ */
21
+ import * as React from 'react';
22
+ import { createPortal } from 'react-dom';
23
+
24
+ import { MENU_EDGE, menuPosition, menuStep } from '@/lib/menu-position';
25
+
26
+ export interface ContextMenuItem {
27
+ id: string;
28
+ label: string;
29
+ icon?: React.ReactNode;
30
+ /** Destructive action (delete). Red text. */
31
+ danger?: boolean;
32
+ disabled?: boolean;
33
+ /** Why it cannot be clicked. A grayed-out item must give a reason, otherwise users just keep clicking it. */
34
+ hint?: string;
35
+ /** A line under the label, in smaller type: what happens, or where to undo it (Hide row: where it comes back). */
36
+ note?: string;
37
+ /** Shortcut, drawn on the right. */
38
+ shortcut?: string;
39
+ /** Currently selected (the check mark in a sort list). `false` still reserves the space, to line up with checked items. */
40
+ checked?: boolean;
41
+ /** An item with a submenu does not run anything itself; it only opens the next panel. */
42
+ submenu?: readonly ContextMenuEntry[];
43
+ onSelect?: () => void;
44
+ }
45
+
46
+ export interface ContextMenuSeparator {
47
+ id: string;
48
+ separator: true;
49
+ }
50
+
51
+ export type ContextMenuEntry = ContextMenuItem | ContextMenuSeparator;
52
+
53
+ function isItem(e: ContextMenuEntry): e is ContextMenuItem {
54
+ return !('separator' in e);
55
+ }
56
+
57
+ export function ContextMenu({
58
+ x,
59
+ y,
60
+ items,
61
+ onClose,
62
+ dense = false,
63
+ align = 'start',
64
+ flipTo,
65
+ trigger,
66
+ }: {
67
+ /** Where it opens: the pointer, or the corner of the button it hangs from. */
68
+ x: number;
69
+ y: number;
70
+ items: readonly ContextMenuEntry[];
71
+ onClose: () => void;
72
+ /**
73
+ * One step tighter.
74
+ *
75
+ * A context menu answers "what can I do with this big area", with many items and long
76
+ * labels, so roomy spacing is right. The toolbar buttons instead open a short single-choice
77
+ * list (sort order, filter by type); at the same size, three or four short words would pop
78
+ * up something several times larger than the button itself, which looks like a misclick.
79
+ */
80
+ dense?: boolean;
81
+ /** 'end': its right edge at `x` (a "⋯" at the right of a card); see menuPosition. */
82
+ align?: 'start' | 'end';
83
+ /** Opened from a button: when it does not fit below, it flips to end here (the button's top). See menuPosition. */
84
+ flipTo?: number;
85
+ /** The button that opened it: a press on it is not an outside click (the button toggles the menu itself). */
86
+ trigger?: React.RefObject<HTMLElement | null>;
87
+ }) {
88
+ const ref = React.useRef<HTMLDivElement | null>(null);
89
+ const subRef = React.useRef<HTMLDivElement | null>(null);
90
+ const itemEls = React.useRef(new Map<string, HTMLButtonElement>());
91
+ const [pos, setPos] = React.useState<{ left: number; top: number } | null>(null);
92
+ const [openSub, setOpenSub] = React.useState<string | null>(null);
93
+ const openSubRef = React.useRef(openSub);
94
+ openSubRef.current = openSub;
95
+ const [subPos, setSubPos] = React.useState<{ left: number; top: number } | null>(null);
96
+ /* Which item the keyboard is on. -1 = the keyboard has not been used yet; no item should be
97
+ highlighted then, or mouse users would think that item is selected. */
98
+ const [active, setActive] = React.useState(-1);
99
+ /* The keyboard's item in the open submenu; -1 = focus is not in it (it was opened by the mouse, or not yet). */
100
+ const [subActive, setSubActive] = React.useState(-1);
101
+ /* `enter`: the keyboard opened it, so focus goes in, on its first item */
102
+ const showSub = React.useCallback((id: string | null, enter = false) => {
103
+ setOpenSub(id);
104
+ setSubActive(enter && id ? 0 : -1);
105
+ }, []);
106
+
107
+ const usable = React.useMemo(
108
+ () => items.filter((e): e is ContextMenuItem => isItem(e) && !e.disabled),
109
+ [items],
110
+ );
111
+ const subItems = openSub
112
+ ? (items.find((e) => isItem(e) && e.id === openSub) as ContextMenuItem | undefined)?.submenu
113
+ : undefined;
114
+ const subUsable = React.useMemo(
115
+ () => (subItems ?? []).filter((e): e is ContextMenuItem => isItem(e) && !e.disabled),
116
+ [subItems],
117
+ );
118
+ const inSub = subActive >= 0;
119
+
120
+ /* Position after measuring the real size. If it does not fit, grow the other way (flip); if
121
+ neither side fits, pin to the edge. Uses a layout effect: it is settled after layout and
122
+ before the browser paints, otherwise it would flash for one frame in the wrong place. */
123
+ React.useLayoutEffect(() => {
124
+ const el = ref.current;
125
+ if (!el) return;
126
+ const { width, height } = el.getBoundingClientRect();
127
+ const at = menuPosition({
128
+ at: { x, y },
129
+ size: { width, height },
130
+ viewport: { width: window.innerWidth, height: window.innerHeight },
131
+ align,
132
+ flipTo,
133
+ });
134
+ setPos({ left: at.left, top: at.top });
135
+ /* Pull focus in so arrow keys drive this menu rather than the timeline underneath. */
136
+ el.focus({ preventScroll: true });
137
+ }, [x, y, items, align, flipTo]);
138
+
139
+ React.useLayoutEffect(() => {
140
+ if (!openSub || !subRef.current) {
141
+ setSubPos(null);
142
+ return;
143
+ }
144
+ const anchor = itemEls.current.get(openSub)?.getBoundingClientRect();
145
+ if (!anchor) return;
146
+ const { width, height } = subRef.current.getBoundingClientRect();
147
+ const gap = 4;
148
+ const viewport = { width: window.innerWidth, height: window.innerHeight };
149
+ let left = anchor.right + gap;
150
+ if (left + width > viewport.width - 8) left = anchor.left - width - gap;
151
+ let top = anchor.top;
152
+ if (top + height > viewport.height - 8) top = Math.max(8, viewport.height - height - 8);
153
+ setSubPos({ left: Math.max(8, left), top });
154
+ }, [openSub, subItems]);
155
+
156
+ /* Into the submenu once it is placed (hidden until then, and a hidden element cannot take focus). */
157
+ React.useEffect(() => {
158
+ if (inSub && subPos) subRef.current?.focus({ preventScroll: true });
159
+ }, [inSub, subPos]);
160
+
161
+ React.useEffect(() => {
162
+ const close = (e: Event): void => {
163
+ /* A press inside the menu itself is not an outside click. Scrolling closes it (except the
164
+ menu's own, when it is taller than the window): the menu is pinned to screen coordinates,
165
+ and once what is underneath scrolls away it no longer points at what it was opened for. */
166
+ const node = e.target as Node;
167
+ if ((e.type === 'pointerdown' || e.type === 'scroll')
168
+ && (ref.current?.contains(node) || subRef.current?.contains(node))) return;
169
+ if (e.type === 'pointerdown' && trigger?.current?.contains(node)) return;
170
+ onClose();
171
+ };
172
+ /* Esc is the menu's wherever focus is (right after a right-click from the stage iframe it is not
173
+ inside the menu), and only the menu's: caught on the way down and stopped there, so the dialog
174
+ or editor under it does not take the same press (the Projects window would close). An open
175
+ submenu collapses first. */
176
+ const onEsc = (e: KeyboardEvent): void => {
177
+ if (e.key !== 'Escape') return;
178
+ e.preventDefault();
179
+ e.stopPropagation();
180
+ if (openSubRef.current) { showSub(null); ref.current?.focus({ preventScroll: true }); }
181
+ else onClose();
182
+ };
183
+ window.addEventListener('pointerdown', close, true);
184
+ window.addEventListener('scroll', close, true);
185
+ window.addEventListener('resize', close);
186
+ window.addEventListener('blur', close);
187
+ window.addEventListener('keydown', onEsc, true);
188
+ return () => {
189
+ window.removeEventListener('pointerdown', close, true);
190
+ window.removeEventListener('scroll', close, true);
191
+ window.removeEventListener('resize', close);
192
+ window.removeEventListener('blur', close);
193
+ window.removeEventListener('keydown', onEsc, true);
194
+ };
195
+ }, [onClose, trigger, showSub]);
196
+
197
+ const run = React.useCallback((item: ContextMenuItem) => {
198
+ if (item.disabled || item.submenu) return;
199
+ onClose();
200
+ item.onSelect?.();
201
+ }, [onClose]);
202
+
203
+ const onKeyDown = (e: React.KeyboardEvent): void => {
204
+ if (!usable.length) return;
205
+ const step = (d: number): void => {
206
+ e.preventDefault();
207
+ setActive((i) => {
208
+ const next = menuStep(i, d, usable.length);
209
+ const item = usable[next];
210
+ showSub(item?.submenu ? item.id : null);
211
+ return next;
212
+ });
213
+ };
214
+ if (e.key === 'ArrowDown') step(1);
215
+ else if (e.key === 'ArrowUp') step(-1);
216
+ else if (e.key === 'Home') { e.preventDefault(); setActive(0); }
217
+ else if (e.key === 'End') { e.preventDefault(); setActive(usable.length - 1); }
218
+ else if (e.key === 'ArrowRight') {
219
+ const item = usable[active];
220
+ if (item?.submenu) {
221
+ e.preventDefault();
222
+ showSub(item.id, true);
223
+ }
224
+ } else if (e.key === 'ArrowLeft') {
225
+ if (openSub) {
226
+ e.preventDefault();
227
+ showSub(null);
228
+ }
229
+ } else if (e.key === 'Enter' || e.key === ' ') {
230
+ const item = usable[active];
231
+ if (item) {
232
+ e.preventDefault();
233
+ if (item.submenu) showSub(item.id, true);
234
+ else run(item);
235
+ }
236
+ }
237
+ };
238
+
239
+ /* The submenu's own keys, while focus is in it; Left goes back to the item it hangs from. */
240
+ const onSubKeyDown = (e: React.KeyboardEvent): void => {
241
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
242
+ e.preventDefault();
243
+ setSubActive((i) => menuStep(i, e.key === 'ArrowDown' ? 1 : -1, subUsable.length));
244
+ } else if (e.key === 'Home') { e.preventDefault(); setSubActive(subUsable.length ? 0 : -1); }
245
+ else if (e.key === 'End') { e.preventDefault(); setSubActive(subUsable.length - 1); }
246
+ else if (e.key === 'ArrowLeft') {
247
+ e.preventDefault();
248
+ showSub(null);
249
+ ref.current?.focus({ preventScroll: true });
250
+ } else if (e.key === 'Enter' || e.key === ' ') {
251
+ const item = subUsable[subActive];
252
+ if (item) {
253
+ e.preventDefault();
254
+ run(item);
255
+ }
256
+ }
257
+ };
258
+
259
+ if (typeof document === 'undefined') return null;
260
+
261
+ /* Focus stays on the container (the arrow keys belong to it), so "which item is current"
262
+ must be announced separately; otherwise a screen reader would just announce a menu without
263
+ saying which entry it is on. */
264
+ const activeId = active >= 0 && usable[active] ? `ctxmenu-${usable[active]!.id}` : undefined;
265
+ const subActiveId = subActive >= 0 && subUsable[subActive] ? `ctxmenu-sub-${subUsable[subActive]!.id}` : undefined;
266
+
267
+ const panel = dense
268
+ ? 'min-w-[136px] rounded-lg py-[3px]'
269
+ : 'min-w-[184px] rounded-xl py-1';
270
+ const row = dense
271
+ ? 'gap-2 px-2.5 py-[5px] text-[12px]'
272
+ : 'gap-2.5 px-3 py-[7px] text-[13px]';
273
+
274
+ return createPortal(
275
+ <>
276
+ <div
277
+ ref={ref}
278
+ role="menu"
279
+ tabIndex={-1}
280
+ aria-orientation="vertical"
281
+ {...(activeId ? { 'aria-activedescendant': activeId } : {})}
282
+ onKeyDown={onKeyDown}
283
+ /* above every dialog it can be opened in (the Projects window is z-10050); taller than the window, it scrolls */
284
+ className={`fixed z-[10060] overflow-y-auto overflow-x-hidden border border-[var(--border)] bg-[var(--surface)] shadow-[var(--shadow-lg)] outline-none ${panel}`}
285
+ style={{
286
+ left: pos?.left ?? x,
287
+ top: pos?.top ?? y,
288
+ maxHeight: `calc(100vh - ${2 * MENU_EDGE}px)`,
289
+ /* Hidden until measured: the first frame would be painted at the un-adjusted position, and it would be seen flashing off screen. */
290
+ visibility: pos ? 'visible' : 'hidden',
291
+ animation: pos ? 'openfilm-rise 0.12s ease-out both' : undefined,
292
+ }}
293
+ >
294
+ {items.map((entry) => {
295
+ if (!isItem(entry)) {
296
+ return <div key={entry.id} className={`h-px bg-[var(--border)] ${dense ? 'my-[3px]' : 'my-1'}`} />;
297
+ }
298
+ const idx = usable.indexOf(entry);
299
+ const hasCheck = entry.checked !== undefined;
300
+ return (
301
+ <button
302
+ key={entry.id}
303
+ id={`ctxmenu-${entry.id}`}
304
+ ref={(el) => {
305
+ if (el) itemEls.current.set(entry.id, el);
306
+ else itemEls.current.delete(entry.id);
307
+ }}
308
+ type="button"
309
+ role="menuitem"
310
+ disabled={entry.disabled}
311
+ title={entry.disabled ? entry.hint : undefined}
312
+ aria-disabled={entry.disabled}
313
+ aria-haspopup={entry.submenu ? 'menu' : undefined}
314
+ aria-expanded={entry.submenu ? openSub === entry.id : undefined}
315
+ /* Moving the mouse over an item moves the keyboard position there too: if the two
316
+ pointers drifted apart, Enter would run a different item (the eye sees this one
317
+ lit while the keyboard sits elsewhere). */
318
+ onPointerEnter={() => {
319
+ setActive(idx);
320
+ if (openSub === entry.id) return;
321
+ /* the submenu the keyboard was in goes: focus back here, not lost with it */
322
+ if (inSub) ref.current?.focus({ preventScroll: true });
323
+ showSub(entry.submenu ? entry.id : null);
324
+ }}
325
+ onClick={() => {
326
+ if (entry.submenu) showSub(entry.id);
327
+ else run(entry);
328
+ }}
329
+ className={`flex w-full items-center text-left transition ${row} ${
330
+ entry.disabled
331
+ ? 'cursor-default text-[var(--text-faint)]'
332
+ : entry.danger
333
+ ? 'text-[var(--err)]'
334
+ : 'text-[var(--text)]'
335
+ } ${idx >= 0 && idx === active && !entry.disabled ? 'bg-[var(--bg-hover)]' : ''}`}
336
+ >
337
+ {hasCheck ? (
338
+ <span
339
+ className={`flex shrink-0 justify-center text-[var(--brand-wind-mid)] ${
340
+ dense ? 'w-[11px] text-[11px]' : 'w-[14px] text-[12px]'
341
+ }`}
342
+ aria-hidden
343
+ >
344
+ {entry.checked ? '✓' : ''}
345
+ </span>
346
+ ) : entry.icon ? (
347
+ <span className={`flex shrink-0 justify-center ${dense ? 'w-[13px]' : 'w-[15px]'}`}>
348
+ {entry.icon}
349
+ </span>
350
+ ) : null}
351
+ {entry.note ? (
352
+ <span className="flex min-w-0 max-w-[260px] flex-1 flex-col">
353
+ <span className="truncate">{entry.label}</span>
354
+ <span className="mt-0.5 whitespace-normal text-[11.5px] leading-snug text-[var(--text-faint)]">{entry.note}</span>
355
+ </span>
356
+ ) : (
357
+ <span className="min-w-0 flex-1 truncate">{entry.label}</span>
358
+ )}
359
+ {entry.shortcut ? (
360
+ <span className="shrink-0 text-[11px] text-[var(--text-faint)]">{entry.shortcut}</span>
361
+ ) : null}
362
+ {entry.submenu ? (
363
+ <span className="shrink-0 text-[12px] text-[var(--text-faint)]" aria-hidden>›</span>
364
+ ) : null}
365
+ </button>
366
+ );
367
+ })}
368
+ </div>
369
+ {subItems ? (
370
+ <div
371
+ ref={subRef}
372
+ role="menu"
373
+ tabIndex={-1}
374
+ aria-orientation="vertical"
375
+ {...(subActiveId ? { 'aria-activedescendant': subActiveId } : {})}
376
+ onKeyDown={onSubKeyDown}
377
+ className="fixed z-[10061] min-w-[168px] overflow-hidden rounded-xl border border-[var(--border)] bg-[var(--surface)] py-1 shadow-[var(--shadow-lg)] outline-none"
378
+ style={{
379
+ left: subPos?.left ?? 0,
380
+ top: subPos?.top ?? 0,
381
+ visibility: subPos ? 'visible' : 'hidden',
382
+ }}
383
+ >
384
+ {subItems.map((entry) => {
385
+ if (!isItem(entry)) {
386
+ return <div key={entry.id} className="my-1 h-px bg-[var(--border)]" />;
387
+ }
388
+ const idx = subUsable.indexOf(entry);
389
+ return (
390
+ <button
391
+ key={entry.id}
392
+ id={`ctxmenu-sub-${entry.id}`}
393
+ type="button"
394
+ role="menuitem"
395
+ disabled={entry.disabled}
396
+ title={entry.disabled ? entry.hint : undefined}
397
+ onPointerEnter={() => { if (inSub && idx >= 0) setSubActive(idx); }}
398
+ onClick={() => run(entry)}
399
+ className={`flex w-full items-center gap-2.5 px-3 py-[7px] text-left text-[13px] transition ${
400
+ entry.disabled
401
+ ? 'cursor-default text-[var(--text-faint)]'
402
+ : 'text-[var(--text)] hover:bg-[var(--bg-hover)]'
403
+ } ${inSub && idx >= 0 && idx === subActive ? 'bg-[var(--bg-hover)]' : ''}`}
404
+ >
405
+ {entry.checked !== undefined ? (
406
+ <span
407
+ className="flex w-[14px] shrink-0 justify-center text-[12px] text-[var(--brand-wind-mid)]"
408
+ aria-hidden
409
+ >
410
+ {entry.checked ? '✓' : ''}
411
+ </span>
412
+ ) : null}
413
+ <span className="min-w-0 flex-1 truncate">{entry.label}</span>
414
+ </button>
415
+ );
416
+ })}
417
+ </div>
418
+ ) : null}
419
+ </>,
420
+ document.body,
421
+ );
422
+ }