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.
- package/LICENSE +21 -0
- package/README.md +29 -2
- package/bin/install.mjs +66 -0
- package/package.json +29 -4
- package/skill/SKILL.md +64 -0
- package/skill/agents/openai.yaml +4 -0
- package/skill/assets/examples/ApprovalCard.tsx +8 -0
- package/skill/assets/examples/BasicApp.tsx +38 -0
- package/skill/assets/examples/BriefingCard.tsx +8 -0
- package/skill/assets/examples/ChatControls.tsx +49 -0
- package/skill/assets/examples/ChatPanel.tsx +73 -0
- package/skill/assets/examples/Checkbox.tsx +13 -0
- package/skill/assets/examples/Combobox.tsx +14 -0
- package/skill/assets/examples/Drawer.tsx +10 -0
- package/skill/assets/examples/MemoryCard.tsx +9 -0
- package/skill/assets/examples/MessageList.tsx +8 -0
- package/skill/assets/examples/Theme.tsx +14 -0
- package/skill/assets/examples/ToolCall.tsx +8 -0
- package/skill/assets/examples/Tree.tsx +15 -0
- package/skill/assets/examples/examples.css +11 -0
- package/skill/assets/library/auricui-0.1.0-preview.1.tgz +0 -0
- package/skill/assets/library/auricui-0.1.0-preview.3.tgz +0 -0
- package/skill/assets/library/auricui-0.1.0-preview.4.tgz +0 -0
- package/skill/assets/upstream/LICENSE +21 -0
- package/skill/assets/upstream/apps/desktop/chat/src/App.tsx +1099 -0
- package/skill/assets/upstream/apps/desktop/chat/src/app-bridge.ts +154 -0
- package/skill/assets/upstream/apps/desktop/chat/src/chat.css +1812 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/AddSkill.tsx +223 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/AgentActivityRow.tsx +716 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/AgentIcons.tsx +46 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/AttachmentCard.tsx +34 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/AttachmentPreview.tsx +331 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/BalanceNotice.tsx +22 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/BrandLogo.tsx +35 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/ComposerCard.tsx +182 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/ConversationSessionBar.tsx +292 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/CopyDiagnostics.tsx +24 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/Divider.tsx +47 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/FileChangesList.tsx +119 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/Markdown.tsx +198 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/ModelPopover.tsx +280 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/NoticeCard.tsx +87 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/Popover.tsx +195 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/PreviewFrame.tsx +129 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/ProjectConversation.tsx +852 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/ProjectStarters.tsx +118 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/Tooltip.tsx +191 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/TurnLog.tsx +583 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/TurnQuestionCard.tsx +133 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/dock-pane-bar.ts +14 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/project-version.ts +37 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/MentionMenuPlugin.tsx +372 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptEditor.tsx +476 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptPill.tsx +399 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/PromptReferenceNode.tsx +164 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/index.ts +31 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/mention-menu.ts +206 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/prompt-text.ts +171 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/useStickToBottom.ts +320 -0
- package/skill/assets/upstream/apps/desktop/chat/src/components/useTicker.ts +55 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/index.ts +42 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/en-US.ts +408 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/es.ts +407 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/ja.ts +407 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/ko.ts +403 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/words.ts +6 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/locales/zh-CN.ts +407 -0
- package/skill/assets/upstream/apps/desktop/chat/src/i18n/lookup.ts +12 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-activity-runs.ts +154 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-menu-rows.ts +41 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-refs.ts +105 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/agent-transcript.ts +440 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/asset-lists.ts +75 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/attachment-place.ts +17 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/balance.ts +16 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-layer.ts +10 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-menu.ts +18 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/chat-store.ts +101 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/composer-attachments.ts +189 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/desktop-attachments.ts +178 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/desktop-bridge.ts +291 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/developer.ts +39 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/esc-stop.ts +23 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/file-changes.ts +32 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/live-activity.ts +20 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/local-agent-run.ts +1174 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/local-sessions.ts +25 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/model-calls.ts +87 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/pretty-json.ts +12 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/preview-top.ts +15 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/project-resources.ts +66 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/project-sessions.ts +66 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/project-versions.ts +116 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/ref-pictures.ts +119 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/relative-time.ts +46 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/skill-pages.ts +29 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/step-file.ts +23 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/structural-share.ts +52 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/studio-film.ts +44 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/studio-transfer.ts +40 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/unprompted.ts +27 -0
- package/skill/assets/upstream/apps/desktop/chat/src/lib/upload-limits.ts +53 -0
- package/skill/assets/upstream/apps/desktop/chat/src/mount.tsx +19 -0
- package/skill/assets/upstream/apps/desktop/chat/src/state/local-agent-store.ts +296 -0
- package/skill/assets/upstream/apps/desktop/chat/src/studio-refs.ts +307 -0
- package/skill/assets/upstream/apps/desktop/package.json +51 -0
- package/skill/assets/upstream/apps/desktop/shell/bridge.ts +415 -0
- package/skill/assets/upstream/apps/desktop/shell/index.html +27 -0
- package/skill/assets/upstream/apps/desktop/shell/main.tsx +17 -0
- package/skill/assets/upstream/apps/desktop/shell/shell.css +17 -0
- package/skill/assets/upstream/apps/desktop/shell/vite.config.ts +65 -0
- package/skill/assets/upstream/apps/desktop/src/agent-definitions.mjs +31 -0
- package/skill/assets/upstream/packages/openfilm/LICENSE +21 -0
- package/skill/assets/upstream/packages/openfilm/package.json +88 -0
- package/skill/assets/upstream/packages/openfilm/src/film-doc.d.mts +125 -0
- package/skill/assets/upstream/packages/openfilm/src/film-doc.mjs +986 -0
- package/skill/assets/upstream/packages/openfilm/src/overrides.mjs +73 -0
- package/skill/assets/upstream/packages/openfilm/src/timeline.mjs +1280 -0
- package/skill/assets/upstream/packages/openfilm/studio/server/bridge.js +209 -0
- package/skill/assets/upstream/packages/openfilm/studio/server/film-subtitle.mjs +785 -0
- package/skill/assets/upstream/packages/openfilm/studio/server/ops.mjs +403 -0
- package/skill/assets/upstream/packages/openfilm/studio/server/stage.js +1649 -0
- package/skill/assets/upstream/packages/openfilm/studio/server/track-layout.mjs +43 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/App.tsx +156 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/api.ts +328 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/AudioMeters.tsx +188 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BlockThumbs.tsx +84 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BlockWaveform.tsx +201 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BrandLoadingScreen.tsx +56 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/BrandLogo.tsx +25 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Card.tsx +89 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ColorPicker.tsx +204 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ConfirmDialog.tsx +96 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ContextMenu.tsx +422 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/DesktopDialog.tsx +271 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ExamplesGallery.tsx +372 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmFullscreenBar.tsx +147 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmInspector.tsx +2310 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FilmStageSelect.tsx +3296 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FocusNav.tsx +28 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FontFamilyPicker.tsx +293 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/FramePicker.tsx +69 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/InspectorPanel.tsx +25 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Launcher.tsx +38 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/LayersPanel.tsx +277 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/LiveTime.tsx +101 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MediaPool.tsx +1413 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MediaPreview.tsx +188 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MenuSelect.tsx +161 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/MgOutline.tsx +24 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/OperationHistory.tsx +184 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PaneDivider.tsx +292 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PasteAttrsDialog.tsx +69 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PlaybackMenu.tsx +110 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Popover.tsx +159 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectCover.tsx +60 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectHistoryControls.tsx +748 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectLayoutMenu.tsx +82 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectMenu.tsx +111 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectSources.tsx +587 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectView.tsx +3222 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectsLibrary.tsx +567 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ProjectsView.tsx +1593 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/PropertiesSection.tsx +418 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ResourceInspector.tsx +100 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SelectionToolbar.tsx +157 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SkillsLibrary.tsx +504 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SourcePreview.tsx +464 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SpendAsks.tsx +88 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/StageMaskOverlay.tsx +299 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/StageTransformFrame.tsx +246 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleInspector.module.css +46 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleInspector.tsx +884 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleOverlay.tsx +148 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/SubtitleRow.tsx +419 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/TabStrip.tsx +188 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Timeline.tsx +4911 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/TimelineMarkers.tsx +329 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Toast.tsx +55 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/Tooltip.tsx +193 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/UpdateButton.tsx +104 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/ViewerBar.tsx +98 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/WhatsNew.tsx +147 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/dock-pane-bar.ts +16 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/export/ExportCenter.tsx +1502 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/export/ExportTray.tsx +284 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/film-stage-hit.ts +18 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/inspector-fields.tsx +863 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/inspector-look.tsx +1357 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/player-controls/controls-bar.tsx +397 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/player-controls/transport.tsx +294 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/replace-drop.ts +20 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/AgentsSection.tsx +613 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/CustomServices.tsx +104 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/DiagnosticsGroup.tsx +202 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ModelsSection.tsx +179 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ProviderMark.tsx +24 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ProvidersSection.tsx +177 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/SettingsModal.tsx +511 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/SettingsSelect.tsx +84 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ShortcutsSection.tsx +291 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/StorageGroup.tsx +119 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/theme.ts +47 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/ui.tsx +139 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/use-settings.ts +10 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/stage-menu.tsx +283 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/timeline-head-extras.tsx +91 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/typing-target.ts +45 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-editor-tools.tsx +408 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-page-changes.ts +48 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/use-project-history.ts +113 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/useBlockDrag.ts +639 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/Preview.tsx +426 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/player.ts +558 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/preview-frames.ts +36 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/steps.ts +36 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/undo.ts +156 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/use-film.ts +212 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/editor/use-preview-renders.ts +89 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/globals.css +559 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/en.ts +1901 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/es.ts +1900 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/index.ts +130 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/ja.ts +1900 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/ko.ts +1896 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/words.ts +6 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/i18n/zh-CN.ts +1901 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/agent-row.ts +21 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/asset-timeline.ts +469 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/audio-levels.ts +60 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/batch-map.ts +28 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/browser.ts +10 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/canvas-fit.ts +116 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/chat-marks.ts +114 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-attrs.ts +102 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-fade.ts +70 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-look.ts +520 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-mask.ts +536 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/clip-replace.ts +60 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/component-props.ts +128 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/context-length.ts +12 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/desktop-build.ts +44 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/diagnostics.ts +68 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/double-press.ts +22 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/dropped-overrides.ts +139 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/export-jobs.ts +191 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/export-spec.ts +261 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/field-input.ts +155 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-clip-edits.ts +82 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-frame.ts +134 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-overrides.ts +107 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film-shape.ts +172 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/film.ts +80 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/font-menu.ts +236 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/font-styles.ts +129 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/freeze-frame.ts +49 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/host.ts +438 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/inspect-scrub.ts +26 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/keymap-store.ts +74 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/keymap.ts +529 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/layers-tree.ts +133 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/look.ts +83 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/loudness.ts +58 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/markers.ts +152 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-bar.ts +58 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-folders.ts +45 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-place.ts +102 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-queue.ts +107 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/media-selection.ts +64 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/menu-position.ts +77 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/mg-transform.ts +111 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/mixed.ts +3 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/page-changes.ts +45 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/picture-files.ts +19 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/preview-prefs.ts +46 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/preview-renders.ts +71 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-delete.ts +20 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-folders.ts +144 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-history.ts +188 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/project-sources.ts +283 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/projects-page.ts +40 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/provider-marks.ts +35 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/providers.ts +163 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/resource-drag.ts +131 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/retry-image.ts +17 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/saved-insert.ts +40 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/saved.ts +125 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/selection-toolbar.ts +158 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/shortcut-hint.ts +21 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/similar-layers.ts +46 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/solo.ts +19 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-clip.ts +60 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-crop.ts +232 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-gesture.ts +277 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-layers.ts +71 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-mask.ts +176 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-measure.ts +53 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/stage-types.ts +155 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/studio-refs.ts +248 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/studio-version.ts +41 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/subtitle-cues.ts +238 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/subtitles.ts +263 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/tab-menu.ts +6 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/template-controls.ts +131 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/theme-changes.ts +31 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-cache.ts +63 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-cell.ts +8 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-frame.ts +76 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/thumb-pick.ts +42 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timecode.ts +161 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-clipboard.ts +279 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-drag.ts +589 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-edit.ts +1138 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-keys.ts +63 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-layout.ts +1038 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-nav.ts +160 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-prefs.ts +73 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-split.ts +40 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/timeline-view.ts +105 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/title.ts +39 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/track-heights.ts +55 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/track-lock.ts +27 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/transitions.ts +459 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/type-metrics.ts +83 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-film-subtitles.ts +111 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-fonts.ts +181 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-loaded-thumbs.ts +91 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-edits.ts +134 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-font.ts +51 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/use-subtitle-style.ts +68 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/waveform-columns.ts +48 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/web-update.ts +41 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/whats-new.ts +401 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/lib/workspace-resources.ts +373 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/src/main.tsx +10 -0
- package/skill/assets/upstream/packages/openfilm/studio/ui/vite.config.ts +12 -0
- package/skill/assets/upstream/packages/shared/package.json +45 -0
- package/skill/assets/upstream/packages/shared/src/agent-activity.ts +263 -0
- package/skill/assets/upstream/packages/shared/src/contract.ts +285 -0
- package/skill/assets/upstream/packages/shared/src/index.ts +17 -0
- package/skill/assets/upstream/packages/shared/src/markdown-blocks.ts +252 -0
- package/skill/assets/upstream/packages/shared/src/modality.ts +4 -0
- package/skill/assets/upstream/packages/shared/src/prompt-reference.ts +113 -0
- package/skill/assets/upstream/packages/shared/src/title.ts +76 -0
- package/skill/assets/upstream/packages/shared/src/turn.ts +68 -0
- package/skill/assets/upstream/packages/shared/src/video.ts +26 -0
- package/skill/references/agent-components.md +33 -0
- package/skill/references/api-index.json +2693 -0
- package/skill/references/chat-components.md +65 -0
- package/skill/references/components.md +23 -0
- package/skill/references/core-components.md +44 -0
- package/skill/references/dependencies.md +22 -0
- package/skill/references/esm-package.md +48 -0
- package/skill/references/integration.md +43 -0
- package/skill/references/library-manifest.json +12 -0
- package/skill/references/source-manifest.json +325 -0
- package/skill/references/studio-components.md +24 -0
- package/skill/references/theme-motion.md +49 -0
- package/skill/references/troubleshooting.md +27 -0
- package/skill/references/verification.md +13 -0
- package/skill/scripts/check_dependencies.py +109 -0
- package/skill/scripts/inspect_api.py +60 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A question from the agent (ask_user), shown above the composer where error cards appear.
|
|
5
|
+
*
|
|
6
|
+
* As with Claude Code's AskUserQuestion and Codex's request_user_input, the turn waits here: a picked option (or
|
|
7
|
+
* number key), or a line written under "Other", is the result of that tool call and the agent carries on, in the
|
|
8
|
+
* same turn, with no new bubble. The "asked you…" row in the conversation then shows the answer. Closing skips the
|
|
9
|
+
* question and the agent goes on by its own judgment.
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { ArrowUp, MessageCircleQuestion } from 'lucide-react';
|
|
13
|
+
import type { AgentAsk } from '@openfilm/shared';
|
|
14
|
+
import type { LocalAgentAnswer, LocalAgentQuestion } from '@/lib/desktop-bridge';
|
|
15
|
+
import { ChoiceRow, ComposerCard, useNumberKeys } from './ComposerCard';
|
|
16
|
+
import { useT } from '@/i18n';
|
|
17
|
+
|
|
18
|
+
export function TurnQuestionCard({
|
|
19
|
+
ask,
|
|
20
|
+
onAnswer,
|
|
21
|
+
onSkip,
|
|
22
|
+
allowOther = true,
|
|
23
|
+
}: {
|
|
24
|
+
ask: Pick<AgentAsk, 'question' | 'options'>;
|
|
25
|
+
/** Whether a free answer is allowed (some local agent questions take options only). A question without options always allows one. */
|
|
26
|
+
allowOther?: boolean;
|
|
27
|
+
/** The answer (the picked option's text, or what was written). */
|
|
28
|
+
onAnswer: (text: string) => void;
|
|
29
|
+
/** Closing skips the question. */
|
|
30
|
+
onSkip: () => void;
|
|
31
|
+
}) {
|
|
32
|
+
const t = useT();
|
|
33
|
+
const [other, setOther] = React.useState('');
|
|
34
|
+
/* Collapsed: out of the way, but the turn still waits; the card stays as one line with the question */
|
|
35
|
+
const [collapsed, setCollapsed] = React.useState(false);
|
|
36
|
+
const freeTextOnly = ask.options.length === 0;
|
|
37
|
+
const [otherOpen, setOtherOpen] = React.useState(freeTextOnly);
|
|
38
|
+
const canWrite = allowOther || freeTextOnly;
|
|
39
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
40
|
+
|
|
41
|
+
const send = React.useCallback((text: string) => {
|
|
42
|
+
const value = text.trim();
|
|
43
|
+
if (value) onAnswer(value);
|
|
44
|
+
}, [onAnswer]);
|
|
45
|
+
const pickByIndex = React.useCallback((index: number) => {
|
|
46
|
+
const option = ask.options[index];
|
|
47
|
+
if (option) send(option.label);
|
|
48
|
+
else if (canWrite && index === ask.options.length) { setOtherOpen(true); window.setTimeout(() => inputRef.current?.focus(), 0); }
|
|
49
|
+
}, [ask.options, canWrite, send]);
|
|
50
|
+
useNumberKeys(ask.options.length + (canWrite ? 1 : 0), collapsed ? null : pickByIndex);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ComposerCard
|
|
54
|
+
role="dialog"
|
|
55
|
+
icon={<MessageCircleQuestion size={14} />}
|
|
56
|
+
title={ask.question}
|
|
57
|
+
onClose={onSkip}
|
|
58
|
+
collapse={{ collapsed, onToggle: () => setCollapsed((v) => !v), label: t(collapsed ? 'turnAsk.expand' : 'turnAsk.collapse') }}
|
|
59
|
+
>
|
|
60
|
+
<div className="-mx-1 mt-2 flex flex-col gap-0.5">
|
|
61
|
+
{ask.options.map((option, index) => (
|
|
62
|
+
<ChoiceRow
|
|
63
|
+
key={`${index}-${option.label}`}
|
|
64
|
+
index={index + 1}
|
|
65
|
+
label={option.label}
|
|
66
|
+
description={option.description}
|
|
67
|
+
onPick={() => send(option.label)}
|
|
68
|
+
/>
|
|
69
|
+
))}
|
|
70
|
+
{otherOpen ? (
|
|
71
|
+
<form
|
|
72
|
+
className="flex items-center gap-2 px-2 py-1"
|
|
73
|
+
onSubmit={(event) => { event.preventDefault(); send(other); }}
|
|
74
|
+
>
|
|
75
|
+
<input
|
|
76
|
+
ref={inputRef}
|
|
77
|
+
autoFocus={freeTextOnly}
|
|
78
|
+
value={other}
|
|
79
|
+
onChange={(event) => setOther(event.target.value)}
|
|
80
|
+
onKeyDown={(event) => { if (event.key === 'Escape' && !freeTextOnly) setOtherOpen(false); }}
|
|
81
|
+
placeholder={t('turnAsk.otherPlaceholder')}
|
|
82
|
+
className="h-7 min-w-0 flex-1 rounded-lg bg-[var(--surface-2)] px-2.5 text-[13px] text-[var(--text)] outline-none placeholder:text-[var(--text-faint)] focus:ring-1 focus:ring-[var(--border-strong)]"
|
|
83
|
+
/>
|
|
84
|
+
<button
|
|
85
|
+
type="submit"
|
|
86
|
+
disabled={!other.trim()}
|
|
87
|
+
aria-label={t('turnAsk.send')}
|
|
88
|
+
className="flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-[var(--text)] text-[var(--panel)] transition hover:opacity-90 disabled:opacity-30"
|
|
89
|
+
>
|
|
90
|
+
<ArrowUp size={14} />
|
|
91
|
+
</button>
|
|
92
|
+
</form>
|
|
93
|
+
) : canWrite ? (
|
|
94
|
+
<ChoiceRow
|
|
95
|
+
index={ask.options.length + 1}
|
|
96
|
+
label={<span className="text-[var(--text-muted)]">{t('turnAsk.other')}</span>}
|
|
97
|
+
onPick={() => pickByIndex(ask.options.length)}
|
|
98
|
+
/>
|
|
99
|
+
) : null}
|
|
100
|
+
</div>
|
|
101
|
+
</ComposerCard>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Questions from a local Claude Code or Codex (see LocalAgentTurn in local-agent-run): possibly several at once.
|
|
107
|
+
* One card steps through them and returns all answers after the last. Closing skips them all.
|
|
108
|
+
*/
|
|
109
|
+
export function LocalAgentQuestionCard({
|
|
110
|
+
questions,
|
|
111
|
+
onDone,
|
|
112
|
+
}: {
|
|
113
|
+
questions: LocalAgentQuestion[];
|
|
114
|
+
onDone: (answers: Record<string, LocalAgentAnswer> | null) => void;
|
|
115
|
+
}) {
|
|
116
|
+
const [at, setAt] = React.useState(0);
|
|
117
|
+
const answers = React.useRef<Record<string, LocalAgentAnswer>>({});
|
|
118
|
+
const q = questions[at];
|
|
119
|
+
if (!q) return null;
|
|
120
|
+
return (
|
|
121
|
+
<TurnQuestionCard
|
|
122
|
+
key={q.id}
|
|
123
|
+
ask={{ question: q.question, options: q.options }}
|
|
124
|
+
allowOther={q.customId != null}
|
|
125
|
+
onAnswer={(text) => {
|
|
126
|
+
answers.current[q.id] = q.options.some((o) => o.label === text) ? { label: text } : { text };
|
|
127
|
+
if (at + 1 < questions.length) setAt(at + 1);
|
|
128
|
+
else onDone(answers.current);
|
|
129
|
+
}}
|
|
130
|
+
onSkip={() => onDone(null)}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One style for the top bar of the workspace's three panes; the conversation pane's is the original.
|
|
3
|
+
*
|
|
4
|
+
* If one pane changed its type, button boxes or spacing, the three would stop lining up, so it lives in one place.
|
|
5
|
+
*/
|
|
6
|
+
export const PANE_BAR = 'flex h-8 shrink-0 items-center gap-1 px-1.5';
|
|
7
|
+
export const PANE_TITLE = 'min-w-0 truncate text-[12px] font-medium text-[var(--text-muted)]';
|
|
8
|
+
export const PANE_BTN =
|
|
9
|
+
'flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded-md text-[var(--text-dim)] transition hover:bg-[var(--bg-hover)] hover:text-[var(--text)]';
|
|
10
|
+
export const PANE_BTN_ON = 'bg-[var(--bg-hover)] text-[var(--text)]';
|
|
11
|
+
export const PANE_ICON = 14;
|
|
12
|
+
/** The viewer's two panel switches (assets, inspector): the buttons a person reaches for most, one size up. */
|
|
13
|
+
export const PANE_TOGGLE = PANE_BTN.replace('h-[22px] w-[22px]', 'h-7 w-7');
|
|
14
|
+
export const PANE_TOGGLE_ICON = 16;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { PromptDisplay, TurnAttachment } from '@openfilm/shared';
|
|
2
|
+
import type { FileChange } from '@/lib/file-changes';
|
|
3
|
+
import type { Balance } from '@/lib/balance';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One turn of the conversation as the chat draws it: what the person said, what they brought, who answered, how
|
|
7
|
+
* long it took and how it ended.
|
|
8
|
+
*/
|
|
9
|
+
export interface ProjectVersion {
|
|
10
|
+
assetId: string;
|
|
11
|
+
label: string;
|
|
12
|
+
title: string;
|
|
13
|
+
prompt: string;
|
|
14
|
+
/** The message as written in the composer (with pill markers); without it, `prompt` is rendered. */
|
|
15
|
+
promptDisplay?: PromptDisplay;
|
|
16
|
+
/** What came with the message (names and thumbnails). */
|
|
17
|
+
attachments: TurnAttachment[];
|
|
18
|
+
posterUrl?: string;
|
|
19
|
+
status: 'ready' | 'active' | 'failed';
|
|
20
|
+
current: boolean;
|
|
21
|
+
/** The turn's frame (who answered, which model, how long, how it stopped); see project-versions. */
|
|
22
|
+
agent?: string;
|
|
23
|
+
model?: string;
|
|
24
|
+
durationMs?: number;
|
|
25
|
+
outcome?: 'stopped' | 'failed' | 'balance';
|
|
26
|
+
/** outcome 'balance': which service had no balance left, and its page to top it up */
|
|
27
|
+
balance?: Balance;
|
|
28
|
+
/** outcome 'failed': why, as the turn kept it (on its footer's Interrupted, on hover) */
|
|
29
|
+
error?: string;
|
|
30
|
+
/** When it was sent / settled (ISO): hover says "N minutes ago". */
|
|
31
|
+
createdAt?: string;
|
|
32
|
+
settledAt?: string;
|
|
33
|
+
/** what the turn changed in the project's files (lib/file-changes); none when nothing did */
|
|
34
|
+
fileChanges?: FileChange[];
|
|
35
|
+
/** 'background': the agent started it by itself, a background task of its own having come back (no message) */
|
|
36
|
+
origin?: 'background';
|
|
37
|
+
}
|
package/skill/assets/upstream/apps/desktop/chat/src/components/prompt-editor/MentionMenuPlugin.tsx
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type `@` in the sentence to pick something and insert it.
|
|
5
|
+
*
|
|
6
|
+
* The host's providers decide what can be picked (see mention-menu). This plugin
|
|
7
|
+
* recognizes the `@`, places the menu above the composer, and replaces the typed `@xxx`
|
|
8
|
+
* with a pill.
|
|
9
|
+
*
|
|
10
|
+
* Built on Lexical's LexicalTypeaheadMenuPlugin, which handles anchoring, repositioning
|
|
11
|
+
* on scroll, arrow keys and Enter. Only the trigger is our own: Lexical's requires
|
|
12
|
+
* whitespace or line start before `@`, which does not work in text without spaces
|
|
13
|
+
* (Chinese, Japanese).
|
|
14
|
+
*/
|
|
15
|
+
import React from 'react';
|
|
16
|
+
import { createPortal } from 'react-dom';
|
|
17
|
+
import { useChatMenu } from '@/lib/chat-menu';
|
|
18
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
19
|
+
import {
|
|
20
|
+
LexicalTypeaheadMenuPlugin,
|
|
21
|
+
MenuOption,
|
|
22
|
+
type MenuTextMatch,
|
|
23
|
+
} from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
24
|
+
import {
|
|
25
|
+
$createTextNode,
|
|
26
|
+
$insertNodes,
|
|
27
|
+
COMMAND_PRIORITY_CRITICAL,
|
|
28
|
+
KEY_ESCAPE_COMMAND,
|
|
29
|
+
type LexicalEditor,
|
|
30
|
+
type TextNode,
|
|
31
|
+
} from 'lexical';
|
|
32
|
+
import { FileText, Image as ImageIcon, Music, Video } from 'lucide-react';
|
|
33
|
+
import { PILL_ICON } from './PromptPill';
|
|
34
|
+
import { $createPromptReferenceNode } from './PromptReferenceNode';
|
|
35
|
+
import {
|
|
36
|
+
buildMentionMenu,
|
|
37
|
+
matchMentionTrigger,
|
|
38
|
+
type MentionEntry,
|
|
39
|
+
type MentionProvider,
|
|
40
|
+
type MentionTrigger,
|
|
41
|
+
} from './mention-menu';
|
|
42
|
+
import { chatLayer } from '@/lib/chat-layer';
|
|
43
|
+
|
|
44
|
+
export interface MentionMenuConfig {
|
|
45
|
+
/** What `@` can pick, in menu group order. */
|
|
46
|
+
providers: MentionProvider[];
|
|
47
|
+
/** The menu's accessible name (it is a listbox). */
|
|
48
|
+
label: string;
|
|
49
|
+
/**
|
|
50
|
+
* Called when the menu opens.
|
|
51
|
+
*
|
|
52
|
+
* The file list is fetched only then: most sessions never mention a file.
|
|
53
|
+
*/
|
|
54
|
+
onOpen?: () => void;
|
|
55
|
+
/** When typing opens it: an `@` (the default), or a `/` at the start (the agent's commands, matchSlashTrigger). */
|
|
56
|
+
trigger?: (textBeforeCursor: string) => MentionTrigger | null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Lexical requires options to subclass MenuOption (it attaches a ref for scrolling). */
|
|
60
|
+
class MentionMenuOption extends MenuOption {
|
|
61
|
+
readonly entry: MentionEntry;
|
|
62
|
+
|
|
63
|
+
constructor(entry: MentionEntry) {
|
|
64
|
+
super(entry.item.id);
|
|
65
|
+
this.entry = entry;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function MentionMenuPlugin({ providers, label, onOpen, trigger: match = matchMentionTrigger }: MentionMenuConfig) {
|
|
70
|
+
const [editor] = useLexicalComposerContext();
|
|
71
|
+
const [query, setQuery] = React.useState('');
|
|
72
|
+
|
|
73
|
+
const options = React.useMemo(
|
|
74
|
+
() => buildMentionMenu(providers, query).map((entry) => new MentionMenuOption(entry)),
|
|
75
|
+
[providers, query],
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
/* These callbacks must be stable. The host re-renders often (20 times a second during
|
|
79
|
+
playback) and Lexical uses them as effect dependencies, so new functions would
|
|
80
|
+
re-subscribe every render. The trigger likewise depends only on the typed text, not
|
|
81
|
+
on a new providers array. */
|
|
82
|
+
const providersRef = React.useRef(providers);
|
|
83
|
+
providersRef.current = providers;
|
|
84
|
+
const onOpenRef = React.useRef(onOpen);
|
|
85
|
+
onOpenRef.current = onOpen;
|
|
86
|
+
const handleOpen = React.useCallback(() => onOpenRef.current?.(), []);
|
|
87
|
+
const handleQueryChange = React.useCallback((value: string | null) => setQuery(value ?? ''), []);
|
|
88
|
+
|
|
89
|
+
const matchRef = React.useRef(match);
|
|
90
|
+
matchRef.current = match;
|
|
91
|
+
const triggerFn = React.useCallback((text: string): MenuTextMatch | null => {
|
|
92
|
+
const trigger = matchRef.current(text);
|
|
93
|
+
if (!trigger) return null;
|
|
94
|
+
/* No matches, no menu: an open menu takes the arrow keys, so an empty one would lock
|
|
95
|
+
them while the user probably wants to move the caret. */
|
|
96
|
+
if (buildMentionMenu(providersRef.current, trigger.query).length === 0) return null;
|
|
97
|
+
return {
|
|
98
|
+
leadOffset: trigger.leadOffset,
|
|
99
|
+
matchingString: trigger.query,
|
|
100
|
+
replaceableString: trigger.replaceableString,
|
|
101
|
+
};
|
|
102
|
+
}, []);
|
|
103
|
+
|
|
104
|
+
const onSelectOption = React.useCallback(
|
|
105
|
+
(option: MentionMenuOption, nodeToReplace: TextNode | null, closeMenu: () => void) => {
|
|
106
|
+
const { item } = option.entry;
|
|
107
|
+
const picked = item.disabled ? null : item.select();
|
|
108
|
+
/* a command goes in as its words, with a space for what follows */
|
|
109
|
+
if (picked && 'text' in picked) {
|
|
110
|
+
editor.update(() => {
|
|
111
|
+
const node = $createTextNode(`${picked.text} `);
|
|
112
|
+
if (nodeToReplace) nodeToReplace.replace(node);
|
|
113
|
+
else $insertNodes([node]);
|
|
114
|
+
node.select();
|
|
115
|
+
closeMenu();
|
|
116
|
+
});
|
|
117
|
+
keepCaretInComposer(editor);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const references = picked ? [picked].flat() : [];
|
|
121
|
+
/* A disabled item yields nothing, but the menu still closes: an open menu takes
|
|
122
|
+
Enter, so the next Enter would not send. Same when select() returns nothing
|
|
123
|
+
(too many references). */
|
|
124
|
+
if (!references.length) {
|
|
125
|
+
closeMenu();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
editor.update(() => {
|
|
129
|
+
/* A space after each pill: the caret cannot enter a pill, so the next typed text
|
|
130
|
+
needs somewhere to go apart from it. */
|
|
131
|
+
const nodes = references.flatMap((reference) => [$createPromptReferenceNode(reference), $createTextNode(' ')]);
|
|
132
|
+
// nodeToReplace is the typed `@xxx`; without it, insert at the caret.
|
|
133
|
+
if (nodeToReplace) {
|
|
134
|
+
nodeToReplace.replace(nodes[0]!);
|
|
135
|
+
let last = nodes[0]!;
|
|
136
|
+
for (const node of nodes.slice(1)) { last.insertAfter(node); last = node; }
|
|
137
|
+
} else $insertNodes(nodes);
|
|
138
|
+
(nodes.at(-1) as TextNode).select();
|
|
139
|
+
closeMenu();
|
|
140
|
+
});
|
|
141
|
+
keepCaretInComposer(editor);
|
|
142
|
+
},
|
|
143
|
+
[editor],
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<LexicalTypeaheadMenuPlugin<MentionMenuOption>
|
|
148
|
+
options={options}
|
|
149
|
+
onQueryChange={handleQueryChange}
|
|
150
|
+
onSelectOption={onSelectOption}
|
|
151
|
+
onOpen={handleOpen}
|
|
152
|
+
triggerFn={triggerFn}
|
|
153
|
+
/* The menu must get Enter first: the composer's Enter-to-send is HIGH, so without
|
|
154
|
+
CRITICAL, Enter with the menu open would send the unfinished message. */
|
|
155
|
+
commandPriority={COMMAND_PRIORITY_CRITICAL}
|
|
156
|
+
menuRenderFn={(anchorRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => (
|
|
157
|
+
anchorRef.current && options.length > 0 && typeof document !== 'undefined'
|
|
158
|
+
? createPortal(
|
|
159
|
+
<MentionMenu
|
|
160
|
+
label={label}
|
|
161
|
+
options={options}
|
|
162
|
+
selectedIndex={selectedIndex}
|
|
163
|
+
onPick={selectOptionAndCleanUp}
|
|
164
|
+
onHighlight={setHighlightedIndex}
|
|
165
|
+
shell={composerShell(editor.getRootElement())}
|
|
166
|
+
editorRoot={editor.getRootElement()}
|
|
167
|
+
onDismiss={() => editor.dispatchCommand(KEY_ESCAPE_COMMAND, new KeyboardEvent('keydown', { key: 'Escape' }))}
|
|
168
|
+
/>,
|
|
169
|
+
chatLayer(),
|
|
170
|
+
)
|
|
171
|
+
: null
|
|
172
|
+
)}
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* After a pick (Enter, Tab or click), the caret must stay in the composer, after the
|
|
179
|
+
* pill's space.
|
|
180
|
+
*
|
|
181
|
+
* The pick also re-renders the host, unmounts the menu and mounts the pill's remove
|
|
182
|
+
* button. If any of that takes focus, the next keystrokes go to the page as editor
|
|
183
|
+
* shortcuts. So once the frame settles, focus returns if it left; Lexical's focus
|
|
184
|
+
* restores the stored selection, which is right after the pill.
|
|
185
|
+
*/
|
|
186
|
+
function keepCaretInComposer(editor: LexicalEditor): void {
|
|
187
|
+
if (typeof window === 'undefined') return;
|
|
188
|
+
window.requestAnimationFrame(() => {
|
|
189
|
+
const root = editor.getRootElement();
|
|
190
|
+
if (!root?.isConnected || !editor.isEditable()) return;
|
|
191
|
+
if (root.ownerDocument.activeElement === root) return;
|
|
192
|
+
editor.focus();
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** The box the menu aligns to: the host's `data-prompt-shell`, or else the editor's parent. */
|
|
197
|
+
function composerShell(root: HTMLElement | null): HTMLElement | null {
|
|
198
|
+
if (!root) return null;
|
|
199
|
+
return root.closest<HTMLElement>('[data-prompt-shell]') ?? root.parentElement ?? root;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** A file's broad type from its extension, only to pick an icon, so images and audio look different. */
|
|
203
|
+
const IMAGE_EXT = /\.(png|jpe?g|gif|webp|avif|svg|heic|bmp)$/i;
|
|
204
|
+
const VIDEO_EXT = /\.(mp4|mov|webm|m4v|mkv|avi)$/i;
|
|
205
|
+
const AUDIO_EXT = /\.(mp3|wav|m4a|aac|ogg|flac|opus)$/i;
|
|
206
|
+
function fileIcon(label: string): React.ComponentType<{ size?: number; className?: string }> {
|
|
207
|
+
if (IMAGE_EXT.test(label)) return ImageIcon;
|
|
208
|
+
if (VIDEO_EXT.test(label)) return Video;
|
|
209
|
+
if (AUDIO_EXT.test(label)) return Music;
|
|
210
|
+
return FileText;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* The menu.
|
|
215
|
+
*
|
|
216
|
+
* Placed above the whole composer, left-aligned and as wide (at most 360), not at the
|
|
217
|
+
* caret, where it would cover the text just typed. Below only when there is no room
|
|
218
|
+
* above. A click anywhere outside the menu and the editor closes it, since an open menu
|
|
219
|
+
* takes the arrow keys and Enter.
|
|
220
|
+
*/
|
|
221
|
+
function MentionMenu({
|
|
222
|
+
label,
|
|
223
|
+
options,
|
|
224
|
+
selectedIndex,
|
|
225
|
+
onPick,
|
|
226
|
+
onHighlight,
|
|
227
|
+
shell,
|
|
228
|
+
editorRoot,
|
|
229
|
+
onDismiss,
|
|
230
|
+
}: {
|
|
231
|
+
label: string;
|
|
232
|
+
options: MentionMenuOption[];
|
|
233
|
+
selectedIndex: number | null;
|
|
234
|
+
onPick: (option: MentionMenuOption) => void;
|
|
235
|
+
onHighlight: (index: number) => void;
|
|
236
|
+
shell: HTMLElement | null;
|
|
237
|
+
editorRoot: HTMLElement | null;
|
|
238
|
+
onDismiss: () => void;
|
|
239
|
+
}) {
|
|
240
|
+
const ref = React.useRef<HTMLDivElement>(null);
|
|
241
|
+
const [style, setStyle] = React.useState<React.CSSProperties | null>(null);
|
|
242
|
+
useChatMenu(true);
|
|
243
|
+
|
|
244
|
+
React.useLayoutEffect(() => {
|
|
245
|
+
if (!shell) return undefined;
|
|
246
|
+
const place = () => {
|
|
247
|
+
const r = shell.getBoundingClientRect();
|
|
248
|
+
const width = Math.min(360, Math.max(260, r.width));
|
|
249
|
+
const left = Math.max(8, Math.min(r.left, window.innerWidth - width - 8));
|
|
250
|
+
const gap = 8;
|
|
251
|
+
const roomAbove = r.top - gap - 8;
|
|
252
|
+
const roomBelow = window.innerHeight - r.bottom - gap - 8;
|
|
253
|
+
const up = roomAbove >= Math.min(320, roomBelow) || roomAbove >= 200;
|
|
254
|
+
setStyle({
|
|
255
|
+
position: 'fixed',
|
|
256
|
+
left,
|
|
257
|
+
width,
|
|
258
|
+
maxHeight: Math.max(160, Math.min(340, up ? roomAbove : roomBelow)),
|
|
259
|
+
...(up ? { bottom: window.innerHeight - r.top + gap } : { top: r.bottom + gap }),
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
place();
|
|
263
|
+
window.addEventListener('resize', place);
|
|
264
|
+
window.addEventListener('scroll', place, true);
|
|
265
|
+
return () => {
|
|
266
|
+
window.removeEventListener('resize', place);
|
|
267
|
+
window.removeEventListener('scroll', place, true);
|
|
268
|
+
};
|
|
269
|
+
}, [shell]);
|
|
270
|
+
|
|
271
|
+
/* Capture phase: some elements stop pointerdown before it bubbles to window. A click
|
|
272
|
+
into an iframe sends no event but blurs the window, which also counts as outside. */
|
|
273
|
+
const dismissRef = React.useRef(onDismiss);
|
|
274
|
+
dismissRef.current = onDismiss;
|
|
275
|
+
React.useEffect(() => {
|
|
276
|
+
const outside = (event: PointerEvent) => {
|
|
277
|
+
const target = event.target as Node;
|
|
278
|
+
if (ref.current?.contains(target) || editorRoot?.contains(target)) return;
|
|
279
|
+
dismissRef.current();
|
|
280
|
+
};
|
|
281
|
+
const blur = () => dismissRef.current();
|
|
282
|
+
window.addEventListener('pointerdown', outside, true);
|
|
283
|
+
window.addEventListener('blur', blur);
|
|
284
|
+
return () => {
|
|
285
|
+
window.removeEventListener('pointerdown', outside, true);
|
|
286
|
+
window.removeEventListener('blur', blur);
|
|
287
|
+
};
|
|
288
|
+
}, [editorRoot]);
|
|
289
|
+
|
|
290
|
+
if (!style) return null;
|
|
291
|
+
return (
|
|
292
|
+
<div
|
|
293
|
+
ref={ref}
|
|
294
|
+
role="listbox"
|
|
295
|
+
aria-label={label}
|
|
296
|
+
/* Marks the open mention menu. */
|
|
297
|
+
data-prompt-menu=""
|
|
298
|
+
style={style}
|
|
299
|
+
className="z-[10000] flex flex-col overflow-hidden rounded-[14px] border border-[var(--border)] bg-[var(--surface)] shadow-[0_18px_48px_-12px_rgba(0,0,0,0.45),0_2px_8px_rgba(0,0,0,0.12)]"
|
|
300
|
+
>
|
|
301
|
+
<div className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden p-1.5">
|
|
302
|
+
{options.map((option, index) => {
|
|
303
|
+
const { item, groupLabel, firstOfGroup } = option.entry;
|
|
304
|
+
const Icon = item.icon ?? (item.kind === 'file' ? fileIcon(item.label) : PILL_ICON[item.kind]);
|
|
305
|
+
const selected = index === selectedIndex;
|
|
306
|
+
return (
|
|
307
|
+
<React.Fragment key={option.key}>
|
|
308
|
+
{firstOfGroup ? (
|
|
309
|
+
<div
|
|
310
|
+
role="presentation"
|
|
311
|
+
className={`px-2 pb-1 text-[11px] font-medium text-[var(--text-faint)] ${index === 0 ? 'pt-1' : 'mt-1 border-t border-[var(--border)] pt-2.5'}`}
|
|
312
|
+
>
|
|
313
|
+
{groupLabel}
|
|
314
|
+
</div>
|
|
315
|
+
) : null}
|
|
316
|
+
<div
|
|
317
|
+
role="option"
|
|
318
|
+
/* Lexical points aria-activedescendant at this id (its own renderer uses
|
|
319
|
+
the same scheme), so screen readers follow the keyboard selection. */
|
|
320
|
+
id={`typeahead-item-${index}`}
|
|
321
|
+
aria-selected={selected}
|
|
322
|
+
aria-disabled={item.disabled ? true : undefined}
|
|
323
|
+
ref={option.setRefElement}
|
|
324
|
+
onMouseEnter={() => onHighlight(index)}
|
|
325
|
+
/* Keep focus in the editor on mouse down, so typing continues after the pick. */
|
|
326
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
327
|
+
onClick={() => onPick(option)}
|
|
328
|
+
className={`flex items-center gap-2.5 rounded-lg px-2 py-1.5 text-[13px] ${
|
|
329
|
+
item.disabled
|
|
330
|
+
? 'cursor-not-allowed opacity-50'
|
|
331
|
+
: `cursor-pointer ${selected ? 'bg-[var(--bg-hover)]' : ''}`
|
|
332
|
+
}`}
|
|
333
|
+
>
|
|
334
|
+
<span className={`flex h-6 w-6 shrink-0 items-center justify-center rounded-md ${selected && !item.disabled ? 'bg-[var(--surface)] text-[var(--text)]' : 'bg-[var(--surface-2)] text-[var(--text-muted)]'}`}>
|
|
335
|
+
<Icon size={13} />
|
|
336
|
+
</span>
|
|
337
|
+
{/* A disabled item keeps its full name and the hint shrinks instead. */}
|
|
338
|
+
<span className={`truncate text-[var(--text)] ${item.disabled || item.describes ? 'max-w-[60%] shrink-0' : 'min-w-0 flex-1'}`}>{item.label}</span>
|
|
339
|
+
{/* Disabled: why. Otherwise: the file size or timecode. */}
|
|
340
|
+
{item.disabled && item.disabledHint ? (
|
|
341
|
+
<span className="min-w-0 flex-1 truncate text-right text-[11px] text-[var(--text-faint)]" title={item.disabledHint}>
|
|
342
|
+
{item.disabledHint}
|
|
343
|
+
</span>
|
|
344
|
+
) : item.detail && item.describes ? (
|
|
345
|
+
<span className="min-w-0 flex-1 truncate text-[11.5px] text-[var(--text-faint)]" title={item.detail}>{item.detail}</span>
|
|
346
|
+
) : item.detail ? (
|
|
347
|
+
<span className="shrink-0 tabular-nums text-[11.5px] text-[var(--text-faint)]">
|
|
348
|
+
{item.detail}
|
|
349
|
+
</span>
|
|
350
|
+
) : null}
|
|
351
|
+
</div>
|
|
352
|
+
</React.Fragment>
|
|
353
|
+
);
|
|
354
|
+
})}
|
|
355
|
+
</div>
|
|
356
|
+
{/* Key hints: the menu is keyboard-first */}
|
|
357
|
+
<div aria-hidden className="flex shrink-0 items-center gap-3 border-t border-[var(--border)] px-3 py-1.5 text-[11px] text-[var(--text-faint)]">
|
|
358
|
+
<span><Kbd>↑</Kbd><Kbd>↓</Kbd></span>
|
|
359
|
+
<span><Kbd>↵</Kbd></span>
|
|
360
|
+
<span className="ml-auto"><Kbd>esc</Kbd></span>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function Kbd({ children }: { children: React.ReactNode }) {
|
|
367
|
+
return (
|
|
368
|
+
<kbd className="mr-0.5 inline-flex h-[18px] min-w-[18px] items-center justify-center rounded border border-[var(--border)] bg-[var(--surface-2)] px-1 font-sans text-[10.5px] text-[var(--text-muted)]">
|
|
369
|
+
{children}
|
|
370
|
+
</kbd>
|
|
371
|
+
);
|
|
372
|
+
}
|