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,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings › Keyboard shortcuts: the editor's actions (lib/keymap) in tabs (Timeline, Player, Basic, Other), two
|
|
3
|
+
* columns of an action and its keys, and the scheme they come from: OpenFilm, Premiere Pro, Final Cut Pro X, or one of
|
|
4
|
+
* three custom ones.
|
|
5
|
+
*
|
|
6
|
+
* A built-in scheme is read-only: clicking a key there copies the scheme into a free custom slot first, and says so.
|
|
7
|
+
* In a custom scheme a click records the next keys pressed (Esc cancels); keys another action has are offered to it
|
|
8
|
+
* only after "Replace". Restore defaults sets the custom scheme back to the scheme it was copied from. Nothing is kept
|
|
9
|
+
* until Save; Cancel lets the changes go.
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import { X } from 'lucide-react';
|
|
13
|
+
import { useT } from '@/i18n';
|
|
14
|
+
import { studioHost } from '@/lib/host';
|
|
15
|
+
import {
|
|
16
|
+
ACTIONS, BUILT_IN_SCHEMES, comboCaps, comboOf, conflictOf, copyOf, CUSTOM_SLOTS, editable, FIXED_KEYS, isBuiltIn, KEY_TABS,
|
|
17
|
+
keymapFor, restoreDefaults, withKey, withoutKeys, type ActionId, type CustomSlot, type KeyPrefs, type KeyTab, type SchemeId,
|
|
18
|
+
} from '@/lib/keymap';
|
|
19
|
+
import { MAC, saveKeyPrefs, useKeyPrefs } from '@/lib/keymap-store';
|
|
20
|
+
import { SettingsSelect } from './SettingsSelect';
|
|
21
|
+
import { SettingsButton, SettingsPage, SettingsSegmented, settingsError } from './ui';
|
|
22
|
+
|
|
23
|
+
/** The built-in schemes are named for the app whose keys they give. */
|
|
24
|
+
const SCHEME_NAMES: Record<(typeof BUILT_IN_SCHEMES)[number], string> = { openfilm: 'OpenFilm', premiere: 'Premiere Pro', fcp: 'Final Cut Pro X' };
|
|
25
|
+
|
|
26
|
+
/** A row: its words and keys; `held`: combinations pressed while another is held (K, then J). */
|
|
27
|
+
type Row = { id: string; label: string; keys: readonly string[]; action?: ActionId; held?: readonly (readonly string[])[] };
|
|
28
|
+
|
|
29
|
+
/** A combination as key caps: its modifiers, then its key. */
|
|
30
|
+
function Caps({ combo }: { combo: string }) {
|
|
31
|
+
return (
|
|
32
|
+
<span className="inline-flex items-center gap-1">
|
|
33
|
+
{comboCaps(combo, MAC).map((cap, i) => (
|
|
34
|
+
<kbd key={i} className="inline-flex h-6 min-w-6 items-center justify-center rounded-[5px] border border-[var(--border)] bg-[var(--dock-pane)] px-1.5 font-sans text-[12.5px] leading-none text-[var(--text-dim)]">
|
|
35
|
+
{cap}
|
|
36
|
+
</kbd>
|
|
37
|
+
))}
|
|
38
|
+
</span>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** An action's keys, one combination after another. */
|
|
43
|
+
function KeyList({ keys, none }: { keys: readonly string[]; none: string }) {
|
|
44
|
+
if (!keys.length) return <span className="text-[13px] text-[var(--text-faint)]">{none}</span>;
|
|
45
|
+
return (
|
|
46
|
+
<span className="flex flex-wrap items-center justify-end gap-x-1.5 gap-y-1">
|
|
47
|
+
{keys.map((combo, i) => (
|
|
48
|
+
<React.Fragment key={combo}>
|
|
49
|
+
{i > 0 ? <span className="text-[12px] text-[var(--text-faint)]">/</span> : null}
|
|
50
|
+
<Caps combo={combo} />
|
|
51
|
+
</React.Fragment>
|
|
52
|
+
))}
|
|
53
|
+
</span>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function ShortcutsSection() {
|
|
58
|
+
const t = useT();
|
|
59
|
+
const saved = useKeyPrefs();
|
|
60
|
+
const [draft, setDraft] = React.useState<KeyPrefs>(saved);
|
|
61
|
+
const [tab, setTab] = React.useState<KeyTab>('timeline');
|
|
62
|
+
const [recording, setRecording] = React.useState<ActionId | null>(null);
|
|
63
|
+
const [pending, setPending] = React.useState<{ id: ActionId; combo: string; other: ActionId } | null>(null);
|
|
64
|
+
const [notice, setNotice] = React.useState<string | null>(null);
|
|
65
|
+
const [error, setError] = React.useState<string | null>(null);
|
|
66
|
+
const [saving, setSaving] = React.useState(false);
|
|
67
|
+
const dirty = JSON.stringify(draft) !== JSON.stringify(saved);
|
|
68
|
+
/* the schemes kept changed elsewhere while nothing here is changed: show them */
|
|
69
|
+
const dirtyRef = React.useRef(dirty);
|
|
70
|
+
dirtyRef.current = dirty;
|
|
71
|
+
React.useEffect(() => { if (!dirtyRef.current) setDraft(saved); }, [saved]);
|
|
72
|
+
|
|
73
|
+
const schemeName = React.useCallback((id: SchemeId) => (isBuiltIn(id) ? SCHEME_NAMES[id] : t('settings.shortcuts.customName').replace('{n}', id.slice(-1))), [t]);
|
|
74
|
+
const actionName = React.useCallback((id: ActionId) => t(`settings.shortcuts.actions.${id}`), [t]);
|
|
75
|
+
const keymap = React.useMemo(() => keymapFor(draft, draft.scheme, MAC), [draft]);
|
|
76
|
+
const custom = isBuiltIn(draft.scheme) ? null : draft.scheme as CustomSlot;
|
|
77
|
+
|
|
78
|
+
/* a key pressed while recording is the action's new key; Esc stops recording, a click anywhere else too */
|
|
79
|
+
const recordRef = React.useRef<(combo: string) => void>(() => {});
|
|
80
|
+
recordRef.current = (combo) => {
|
|
81
|
+
if (!recording || !custom) return;
|
|
82
|
+
setRecording(null);
|
|
83
|
+
const other = conflictOf(keymap, recording, combo, MAC);
|
|
84
|
+
if (other) setPending({ id: recording, combo, other });
|
|
85
|
+
else setDraft((d) => withKey(d, custom, recording, combo, MAC));
|
|
86
|
+
};
|
|
87
|
+
React.useEffect(() => {
|
|
88
|
+
if (!recording) return;
|
|
89
|
+
const onKey = (e: KeyboardEvent) => {
|
|
90
|
+
if (e.key === 'Tab') return;
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
e.stopImmediatePropagation();
|
|
93
|
+
if (e.key === 'Escape' && !e.metaKey && !e.ctrlKey && !e.altKey && !e.shiftKey) { setRecording(null); return; }
|
|
94
|
+
const combo = comboOf(e, MAC);
|
|
95
|
+
if (combo) recordRef.current(combo);
|
|
96
|
+
};
|
|
97
|
+
const onDown = (e: PointerEvent) => {
|
|
98
|
+
if (!(e.target as Element | null)?.closest?.(`[data-recording]`)) setRecording(null);
|
|
99
|
+
};
|
|
100
|
+
window.addEventListener('keydown', onKey, true);
|
|
101
|
+
window.addEventListener('pointerdown', onDown, true);
|
|
102
|
+
return () => { window.removeEventListener('keydown', onKey, true); window.removeEventListener('pointerdown', onDown, true); };
|
|
103
|
+
}, [recording]);
|
|
104
|
+
|
|
105
|
+
const chooseScheme = (id: SchemeId) => {
|
|
106
|
+
setRecording(null);
|
|
107
|
+
setPending(null);
|
|
108
|
+
setError(null);
|
|
109
|
+
if (!isBuiltIn(id) && !draft.custom[id]) {
|
|
110
|
+
/* an empty custom slot starts as a copy of the scheme shown */
|
|
111
|
+
setDraft({ scheme: id, custom: { ...draft.custom, [id]: copyOf(draft, draft.scheme, MAC) } });
|
|
112
|
+
setNotice(t('settings.shortcuts.started').replace('{to}', schemeName(id)).replace('{from}', schemeName(draft.scheme)));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
setDraft({ ...draft, scheme: id });
|
|
116
|
+
setNotice(null);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const startRecording = (id: ActionId) => {
|
|
120
|
+
setPending(null);
|
|
121
|
+
setError(null);
|
|
122
|
+
if (custom) { setNotice(null); setRecording(id); return; }
|
|
123
|
+
const ready = editable(draft, MAC);
|
|
124
|
+
if (!ready) { setNotice(t('settings.shortcuts.full')); return; }
|
|
125
|
+
setDraft(ready.prefs);
|
|
126
|
+
setNotice(t('settings.shortcuts.copied').replace('{from}', schemeName(draft.scheme)).replace('{to}', schemeName(ready.slot)));
|
|
127
|
+
setRecording(id);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const replace = () => {
|
|
131
|
+
if (!pending || !custom) return;
|
|
132
|
+
setDraft((d) => withKey(d, custom, pending.id, pending.combo, MAC));
|
|
133
|
+
setPending(null);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const restore = () => {
|
|
137
|
+
if (!custom) return;
|
|
138
|
+
setRecording(null);
|
|
139
|
+
setPending(null);
|
|
140
|
+
setNotice(null);
|
|
141
|
+
setDraft((d) => restoreDefaults(d, custom, MAC));
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const cancel = () => {
|
|
145
|
+
setDraft(saved);
|
|
146
|
+
setRecording(null);
|
|
147
|
+
setPending(null);
|
|
148
|
+
setNotice(null);
|
|
149
|
+
setError(null);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const save = async () => {
|
|
153
|
+
setRecording(null);
|
|
154
|
+
setPending(null);
|
|
155
|
+
setSaving(true);
|
|
156
|
+
try {
|
|
157
|
+
await saveKeyPrefs(draft);
|
|
158
|
+
setNotice(null);
|
|
159
|
+
setError(null);
|
|
160
|
+
} catch (e) {
|
|
161
|
+
setError(t('settings.shortcuts.saveFailed').replace('{error}', e instanceof Error ? e.message : String(e)));
|
|
162
|
+
} finally {
|
|
163
|
+
setSaving(false);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
const actionRows: Row[] = ACTIONS
|
|
168
|
+
.filter((a) => a.tab === tab && (a.id !== 'refer' || studioHost?.refer))
|
|
169
|
+
.map((a) => ({ id: a.id, label: actionName(a.id), keys: keymap[a.id], action: a.id }));
|
|
170
|
+
/* Other: the keys that are always the same, after the actions */
|
|
171
|
+
const fixedRows: Row[] = tab !== 'other' ? [] : [
|
|
172
|
+
...(studioHost?.settings ? [{ id: 'settings', label: t('settings.title'), keys: ['mod+,'] }] : []),
|
|
173
|
+
...(() => {
|
|
174
|
+
/* stop held, then back or forward: a frame that way */
|
|
175
|
+
const stop = keymap['shuttle-stop'][0];
|
|
176
|
+
const held = [keymap['shuttle-back'][0], keymap['shuttle-forward'][0]].flatMap((step) => (stop && step ? [[stop, step]] : []));
|
|
177
|
+
return [{ id: 'shuttle-hold', label: t('settings.shortcuts.shuttleHold'), keys: [], held }];
|
|
178
|
+
})(),
|
|
179
|
+
...FIXED_KEYS.map((row) => ({ id: row.label, label: t(`settings.shortcuts.${row.label}`), keys: row.keys })),
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
const renderRow = (row: Row) => {
|
|
183
|
+
const id = row.action;
|
|
184
|
+
const on = id && recording === id;
|
|
185
|
+
const caps = row.held
|
|
186
|
+
? (
|
|
187
|
+
<span className="flex flex-wrap items-center justify-end gap-x-1.5 gap-y-1">
|
|
188
|
+
{row.held.map((combos, i) => (
|
|
189
|
+
<React.Fragment key={combos.join(' ')}>
|
|
190
|
+
{i > 0 ? <span className="text-[12px] text-[var(--text-faint)]">/</span> : null}
|
|
191
|
+
<span className="inline-flex items-center gap-1">
|
|
192
|
+
{combos.flatMap((combo, j) => [
|
|
193
|
+
...(j > 0 ? [<span key={`p${j}`} className="text-[12px] text-[var(--text-faint)]">+</span>] : []),
|
|
194
|
+
<Caps key={j} combo={combo} />,
|
|
195
|
+
])}
|
|
196
|
+
</span>
|
|
197
|
+
</React.Fragment>
|
|
198
|
+
))}
|
|
199
|
+
</span>
|
|
200
|
+
)
|
|
201
|
+
: <KeyList keys={row.keys} none={t('settings.shortcuts.none')} />;
|
|
202
|
+
const conflict = pending && pending.id === id ? pending : null;
|
|
203
|
+
return (
|
|
204
|
+
<div key={row.id} className="py-1">
|
|
205
|
+
<div className="group flex min-h-[36px] items-center justify-between gap-4">
|
|
206
|
+
<span className="min-w-0 text-[14px] leading-snug text-[var(--text)]">{row.label}</span>
|
|
207
|
+
{id ? (
|
|
208
|
+
<span className="flex shrink-0 items-center gap-1">
|
|
209
|
+
{custom && row.keys.length && !on ? (
|
|
210
|
+
<button type="button" aria-label={`${t('settings.shortcuts.clear')}: ${row.label}`} title={t('settings.shortcuts.clear')}
|
|
211
|
+
onClick={() => { setPending(null); setDraft((d) => withoutKeys(d, custom, id, MAC)); }}
|
|
212
|
+
className="flex h-6 w-6 items-center justify-center rounded-[5px] text-[var(--text-faint)] opacity-0 transition hover:bg-[var(--bg-hover)] hover:text-[var(--text)] focus-visible:opacity-100 group-hover:opacity-100">
|
|
213
|
+
<X size={13} />
|
|
214
|
+
</button>
|
|
215
|
+
) : null}
|
|
216
|
+
<button type="button" data-recording={on ? '' : undefined}
|
|
217
|
+
aria-label={`${row.label}: ${custom ? t('settings.shortcuts.change') : t('settings.shortcuts.editCopy')}`}
|
|
218
|
+
title={custom ? t('settings.shortcuts.change') : t('settings.shortcuts.editCopy')}
|
|
219
|
+
onClick={() => (on ? setRecording(null) : startRecording(id))}
|
|
220
|
+
className={`flex min-h-8 max-w-[220px] items-center justify-end rounded-[7px] px-2 py-1 transition ${
|
|
221
|
+
on ? 'bg-[var(--bg-active)] ring-1 ring-[var(--accent)]' : pending?.id === id ? 'ring-1 ring-[var(--err)]' : 'hover:bg-[var(--bg-hover)]'
|
|
222
|
+
}`}>
|
|
223
|
+
{on ? <span className="text-[13px] text-[var(--text-muted)]">{t('settings.shortcuts.recording')}</span> : conflict ? <Caps combo={conflict.combo} /> : caps}
|
|
224
|
+
</button>
|
|
225
|
+
</span>
|
|
226
|
+
) : <span className="flex max-w-[220px] shrink-0 justify-end px-2">{caps}</span>}
|
|
227
|
+
</div>
|
|
228
|
+
{/* the keys pressed are another action's: replace, or keep things as they are */}
|
|
229
|
+
{conflict ? (
|
|
230
|
+
<div role="alert" className="mb-1 mt-1.5 flex flex-col gap-2 rounded-[8px] border border-[var(--border)] bg-[var(--dock-pane)] px-3 py-2.5">
|
|
231
|
+
<span className="text-[13.5px] leading-snug text-[var(--text)]">
|
|
232
|
+
{t('settings.shortcuts.conflict').replace('{keys}', comboCaps(conflict.combo, MAC).join(MAC ? '' : '+')).replace('{action}', actionName(conflict.other))}
|
|
233
|
+
</span>
|
|
234
|
+
<span className="flex justify-end gap-2">
|
|
235
|
+
<SettingsButton className="h-8" onClick={() => setPending(null)}>{t('settings.shortcuts.cancel')}</SettingsButton>
|
|
236
|
+
<SettingsButton className="h-8" variant="primary" onClick={replace}>{t('settings.shortcuts.replace')}</SettingsButton>
|
|
237
|
+
</span>
|
|
238
|
+
</div>
|
|
239
|
+
) : null}
|
|
240
|
+
</div>
|
|
241
|
+
);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/* two columns, filled top to bottom, the left one first */
|
|
245
|
+
const columns = (rows: Row[]) => {
|
|
246
|
+
const half = Math.ceil(rows.length / 2);
|
|
247
|
+
return [rows.slice(0, half), rows.slice(half)];
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const schemeOptions = [...BUILT_IN_SCHEMES, ...CUSTOM_SLOTS].map((id) => ({ value: id as SchemeId, label: schemeName(id) }));
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<SettingsPage note={t('settings.shortcuts.hint')}>
|
|
254
|
+
<div className="mb-4 flex flex-wrap items-center justify-between gap-3">
|
|
255
|
+
<SettingsSegmented<KeyTab>
|
|
256
|
+
label={t('settings.sections.shortcuts')}
|
|
257
|
+
value={tab}
|
|
258
|
+
onChange={(next) => { setTab(next); setRecording(null); }}
|
|
259
|
+
options={KEY_TABS.map((id) => ({ value: id, label: t(`settings.shortcuts.tabs.${id}`) }))}
|
|
260
|
+
/>
|
|
261
|
+
<SettingsSelect<SchemeId> label={t('settings.shortcuts.scheme')} value={draft.scheme} options={schemeOptions} onChange={chooseScheme} width={200} />
|
|
262
|
+
</div>
|
|
263
|
+
{notice ? <p role="status" className="mb-3 text-[13.5px] leading-relaxed text-[var(--text-muted)]">{notice}</p> : null}
|
|
264
|
+
<div className="@container">
|
|
265
|
+
<div className="grid grid-cols-1 gap-x-8 @xl:grid-cols-2">
|
|
266
|
+
{columns(actionRows).map((rows, i) => (
|
|
267
|
+
<div key={i} className={`flex flex-col ${i === 1 ? '@xl:border-l @xl:border-[var(--border)] @xl:pl-8' : ''}`}>{rows.map(renderRow)}</div>
|
|
268
|
+
))}
|
|
269
|
+
</div>
|
|
270
|
+
{fixedRows.length ? (
|
|
271
|
+
<>
|
|
272
|
+
<p className="mt-3 border-t border-[var(--border)] pt-3 text-[13px] text-[var(--text-faint)]">{t('settings.shortcuts.fixed')}</p>
|
|
273
|
+
<div className="grid grid-cols-1 gap-x-8 @xl:grid-cols-2">
|
|
274
|
+
{columns(fixedRows).map((rows, i) => (
|
|
275
|
+
<div key={i} className={`flex flex-col ${i === 1 ? '@xl:border-l @xl:border-[var(--border)] @xl:pl-8' : ''}`}>{rows.map(renderRow)}</div>
|
|
276
|
+
))}
|
|
277
|
+
</div>
|
|
278
|
+
</>
|
|
279
|
+
) : null}
|
|
280
|
+
</div>
|
|
281
|
+
{error ? <p className={`mt-3 ${settingsError}`}>{error}</p> : null}
|
|
282
|
+
<div className="mt-5 flex items-center justify-between gap-3">
|
|
283
|
+
<SettingsButton onClick={restore} disabled={!custom}>{t('settings.shortcuts.restore')}</SettingsButton>
|
|
284
|
+
<span className="flex gap-2">
|
|
285
|
+
<SettingsButton onClick={cancel} disabled={!dirty || saving}>{t('settings.shortcuts.cancel')}</SettingsButton>
|
|
286
|
+
<SettingsButton variant="primary" onClick={() => { void save(); }} disabled={!dirty || saving}>{t('settings.shortcuts.save')}</SettingsButton>
|
|
287
|
+
</span>
|
|
288
|
+
</div>
|
|
289
|
+
</SettingsPage>
|
|
290
|
+
);
|
|
291
|
+
}
|
package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/StorageGroup.tsx
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings → General → Storage: the projects library, the folder every project is a folder of (studio/server's
|
|
3
|
+
* locations, src/locations.mjs). It shows its folder and the free space on its disk, and can be changed (the system's
|
|
4
|
+
* folder dialog), opened in the file manager, or set back to its default. When projects are in it, a change asks
|
|
5
|
+
* whether they move too; left behind, they stay where they are, and in the list. Exports have no setting: the export
|
|
6
|
+
* dialog picks the folder and keeps the last one.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { useT } from '@/i18n';
|
|
10
|
+
import { api, type StudioLocation, type StudioLocations } from '@/api';
|
|
11
|
+
import { studioHost } from '@/lib/host';
|
|
12
|
+
import { ConfirmDialog } from '../ConfirmDialog';
|
|
13
|
+
import { SettingsButton, SettingsField, SettingsGroup, SettingsItem, settingsError } from './ui';
|
|
14
|
+
|
|
15
|
+
type Which = keyof StudioLocations;
|
|
16
|
+
|
|
17
|
+
/** Bytes as the person reads a disk's free space: GB, or MB below one. */
|
|
18
|
+
function size(bytes: number): string {
|
|
19
|
+
return bytes >= 1e9 ? `${(bytes / 1e9).toFixed(bytes >= 1e11 ? 0 : 1)} GB` : `${Math.max(1, Math.round(bytes / 1e6))} MB`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function StorageGroup() {
|
|
23
|
+
const t = useT();
|
|
24
|
+
const [where, setWhere] = React.useState<StudioLocations | null>(null);
|
|
25
|
+
const [busy, setBusy] = React.useState<Which | null>(null);
|
|
26
|
+
const [error, setError] = React.useState<{ which: Which; text: string } | null>(null);
|
|
27
|
+
/* where no folder dialog can open (a browser on another system), the path is typed */
|
|
28
|
+
const [typing, setTyping] = React.useState<{ which: Which; text: string } | null>(null);
|
|
29
|
+
/* a new folder for a library with projects in it: whether they move too */
|
|
30
|
+
const [asking, setAsking] = React.useState<{ which: Which; folder: string | null } | null>(null);
|
|
31
|
+
const [note, setNote] = React.useState<{ which: Which; text: string } | null>(null);
|
|
32
|
+
const load = React.useCallback(() => { void api.locations().then(setWhere).catch(() => {}); }, []);
|
|
33
|
+
React.useEffect(load, [load]);
|
|
34
|
+
if (!where) return null;
|
|
35
|
+
|
|
36
|
+
const apply = async (which: Which, folder: string | null, move: boolean) => {
|
|
37
|
+
setBusy(which);
|
|
38
|
+
setError(null);
|
|
39
|
+
setNote(null);
|
|
40
|
+
try {
|
|
41
|
+
const done = await api.moveLibrary(folder, move);
|
|
42
|
+
setTyping(null);
|
|
43
|
+
setWhere(done.locations);
|
|
44
|
+
if (done.failed.length) setError({ which, text: t('settings.storage.moveFailed').replace('{names}', done.failed.map((f) => f.name).join(', ')) });
|
|
45
|
+
if (done.moved) setNote({ which, text: t('settings.storage.moved').replace('{count}', String(done.moved)) });
|
|
46
|
+
} catch (e) {
|
|
47
|
+
setError({ which, text: e instanceof Error ? e.message : String(e) });
|
|
48
|
+
} finally {
|
|
49
|
+
setBusy(null);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const keep = (which: Which, folder: string | null) => {
|
|
53
|
+
const now = where[which];
|
|
54
|
+
if (now.count > 0 && (folder ?? now.default) !== now.path) setAsking({ which, folder });
|
|
55
|
+
else void apply(which, folder, false);
|
|
56
|
+
};
|
|
57
|
+
const change = async (which: Which) => {
|
|
58
|
+
const ask = { title: t(`settings.storage.${which}Pick`), button: t('settings.storage.pickButton'), at: where[which].path };
|
|
59
|
+
try {
|
|
60
|
+
const pick = studioHost?.folders?.pick;
|
|
61
|
+
const picked = pick ? await pick(ask) : await api.pickFolder(ask.title, ask.at);
|
|
62
|
+
if (picked) keep(which, picked);
|
|
63
|
+
} catch {
|
|
64
|
+
setTyping({ which, text: where[which].path });
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const row = (which: Which, place: StudioLocation) => (
|
|
69
|
+
<SettingsItem
|
|
70
|
+
key={which}
|
|
71
|
+
title={t(`settings.storage.${which}`)}
|
|
72
|
+
description={(
|
|
73
|
+
<>
|
|
74
|
+
{typing?.which === which ? (
|
|
75
|
+
<form className="mt-2 flex items-center gap-2" onSubmit={(e) => { e.preventDefault(); if (typing.text.trim()) keep(which, typing.text.trim()); }}>
|
|
76
|
+
<SettingsField autoFocus value={typing.text} onChange={(e) => setTyping({ which, text: e.target.value })} spellCheck={false}
|
|
77
|
+
aria-label={t(`settings.storage.${which}`)} className="font-mono text-[13px]"
|
|
78
|
+
onKeyDown={(e) => { if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); setTyping(null); } }} />
|
|
79
|
+
<SettingsButton type="submit" variant="primary" disabled={busy === which || !typing.text.trim()}>{t('settings.storage.save')}</SettingsButton>
|
|
80
|
+
</form>
|
|
81
|
+
) : (
|
|
82
|
+
<span className="mt-1 block break-all font-mono text-[13px]" title={place.path}>{place.path}</span>
|
|
83
|
+
)}
|
|
84
|
+
{place.free != null ? <span className="mt-0.5 block text-[13px]">{t('settings.storage.free').replace('{size}', size(place.free))}</span> : null}
|
|
85
|
+
{note?.which === which ? <span className="mt-1 block text-[13px]" role="status">{note.text}</span> : null}
|
|
86
|
+
{error?.which === which ? <span className={`mt-1 block ${settingsError}`} role="alert">{error.text}</span> : null}
|
|
87
|
+
</>
|
|
88
|
+
)}
|
|
89
|
+
control={(
|
|
90
|
+
<>
|
|
91
|
+
{place.chosen ? (
|
|
92
|
+
<SettingsButton variant="quiet" disabled={busy === which} onClick={() => keep(which, null)} title={place.default}>{t('settings.storage.reset')}</SettingsButton>
|
|
93
|
+
) : null}
|
|
94
|
+
<SettingsButton onClick={() => void api.openLocation(which).catch(() => {})}>{t('settings.storage.show')}</SettingsButton>
|
|
95
|
+
<SettingsButton disabled={busy === which} onClick={() => void change(which)}>{t('settings.storage.change')}</SettingsButton>
|
|
96
|
+
</>
|
|
97
|
+
)}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<SettingsGroup title={t('settings.storage.group')}>
|
|
103
|
+
{row('projects', where.projects)}
|
|
104
|
+
<ConfirmDialog
|
|
105
|
+
open={!!asking}
|
|
106
|
+
title={t('settings.storage.moveTitle')}
|
|
107
|
+
description={asking ? t('settings.storage.moveDesc')
|
|
108
|
+
.replace('{count}', String(where[asking.which].count))
|
|
109
|
+
.replace('{from}', where[asking.which].path)
|
|
110
|
+
.replace('{to}', asking.folder ?? where[asking.which].default) : undefined}
|
|
111
|
+
confirmLabel={t('settings.storage.moveYes')}
|
|
112
|
+
altLabel={t('settings.storage.moveNo')}
|
|
113
|
+
onConfirm={() => { const a = asking; setAsking(null); if (a) void apply(a.which, a.folder, true); }}
|
|
114
|
+
onAlt={() => { const a = asking; setAsking(null); if (a) void apply(a.which, a.folder, false); }}
|
|
115
|
+
onCancel={() => setAsking(null)}
|
|
116
|
+
/>
|
|
117
|
+
</SettingsGroup>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The theme: light, dark or the system's, dark until the person chooses. The choice is kept in localStorage `openfilm.theme` and shown as
|
|
3
|
+
* `<html data-theme>`; index.html applies it before the first paint, this keeps it current afterwards, in every page
|
|
4
|
+
* of Studio that is open.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
export type ThemeMode = 'light' | 'dark' | 'system';
|
|
9
|
+
|
|
10
|
+
const KEY = 'openfilm.theme';
|
|
11
|
+
const dark = () => window.matchMedia('(prefers-color-scheme: dark)');
|
|
12
|
+
|
|
13
|
+
function saved(): ThemeMode {
|
|
14
|
+
try {
|
|
15
|
+
const value = localStorage.getItem(KEY);
|
|
16
|
+
return value === 'light' || value === 'system' ? value : 'dark';
|
|
17
|
+
} catch { return 'dark'; }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function apply(mode: ThemeMode) {
|
|
21
|
+
document.documentElement.dataset.theme = mode === 'system' ? (dark().matches ? 'dark' : 'light') : mode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const listeners = new Set<() => void>();
|
|
25
|
+
|
|
26
|
+
/* "System" follows the system while Studio is open, not only when it starts */
|
|
27
|
+
if (typeof window !== 'undefined') dark().addEventListener('change', () => { if (saved() === 'system') apply('system'); });
|
|
28
|
+
/* a theme chosen in another of Studio's pages (another tab of the app, another window) is this one's too */
|
|
29
|
+
if (typeof window !== 'undefined') {
|
|
30
|
+
window.addEventListener('storage', (event) => {
|
|
31
|
+
if (event.key !== KEY) return;
|
|
32
|
+
apply(saved());
|
|
33
|
+
for (const listener of listeners) listener();
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function setThemeMode(mode: ThemeMode) {
|
|
38
|
+
try { localStorage.setItem(KEY, mode); } catch { /* not kept past this page */ }
|
|
39
|
+
apply(mode);
|
|
40
|
+
for (const listener of listeners) listener();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** The chosen theme and a way to change it; every user of it sees the change. */
|
|
44
|
+
export function useThemeMode(): [ThemeMode, (mode: ThemeMode) => void] {
|
|
45
|
+
const mode = React.useSyncExternalStore((on) => { listeners.add(on); return () => listeners.delete(on); }, saved, () => 'dark' as ThemeMode);
|
|
46
|
+
return [mode, setThemeMode];
|
|
47
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts every section of Settings is made of. A page reads like a document: titled groups with room between them,
|
|
3
|
+
* and in each a list of rows parted by hairlines, each its title and what it does on the left, its control on the
|
|
4
|
+
* right. Controls are quiet until pointed at: a choice of two or three is a segmented control, on/off a switch, a
|
|
5
|
+
* longer list a select.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
|
|
9
|
+
/** A section's body: an optional note, then its groups. */
|
|
10
|
+
export function SettingsPage({ note, children }: { note?: React.ReactNode; children: React.ReactNode }) {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex max-w-[880px] flex-col">
|
|
13
|
+
{note ? <p className="mb-8 text-[14.5px] leading-relaxed text-[var(--text-muted)]">{note}</p> : null}
|
|
14
|
+
{children}
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** A titled group of rows; `footnote` goes under it. */
|
|
20
|
+
export function SettingsGroup({ title, footnote, children }: { title?: React.ReactNode; footnote?: React.ReactNode; children: React.ReactNode }) {
|
|
21
|
+
return (
|
|
22
|
+
<section className="mb-10 last:mb-0">
|
|
23
|
+
{title ? <h3 className="mb-1 text-[17px] font-semibold tracking-[-0.01em] text-[var(--text)]">{title}</h3> : null}
|
|
24
|
+
<div className="flex flex-col divide-y divide-[var(--border)]">{children}</div>
|
|
25
|
+
{footnote ? <p className="pt-2 text-[13.5px] leading-relaxed text-[var(--text-faint)]">{footnote}</p> : null}
|
|
26
|
+
</section>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** One row: a mark, a title (with a badge beside it), what it does, its control; `children` open under it. */
|
|
31
|
+
export function SettingsItem({ mark, title, badge, description, control, children }: {
|
|
32
|
+
mark?: React.ReactNode;
|
|
33
|
+
title: React.ReactNode;
|
|
34
|
+
badge?: React.ReactNode;
|
|
35
|
+
description?: React.ReactNode;
|
|
36
|
+
control?: React.ReactNode;
|
|
37
|
+
children?: React.ReactNode;
|
|
38
|
+
}) {
|
|
39
|
+
return (
|
|
40
|
+
<div className="py-4">
|
|
41
|
+
<div className="flex min-h-10 items-center gap-4">
|
|
42
|
+
{mark ? <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-[8px] bg-[var(--fill-tsp)] text-[var(--text)]">{mark}</span> : null}
|
|
43
|
+
<div className="min-w-0 flex-1">
|
|
44
|
+
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5">
|
|
45
|
+
<span className="text-[15px] text-[var(--text)]">{title}</span>
|
|
46
|
+
{badge}
|
|
47
|
+
</div>
|
|
48
|
+
{description ? <div className="mt-1 text-[14px] leading-[1.55] text-[var(--text-muted)]">{description}</div> : null}
|
|
49
|
+
</div>
|
|
50
|
+
{control ? <div className="flex shrink-0 items-center gap-2">{control}</div> : null}
|
|
51
|
+
</div>
|
|
52
|
+
{children ? <div className="pt-3">{children}</div> : null}
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** "Connected", with a green dot and whatever tells which connection it is. */
|
|
58
|
+
export function SettingsStatus({ tone = 'ok', children }: { tone?: 'ok' | 'muted'; children: React.ReactNode }) {
|
|
59
|
+
return (
|
|
60
|
+
<span className="inline-flex items-center gap-1.5 text-[13.5px] text-[var(--text-muted)]">
|
|
61
|
+
<span className={`h-1.5 w-1.5 rounded-full ${tone === 'ok' ? 'bg-[var(--ok)]' : 'bg-[var(--text-faint)]'}`} aria-hidden />
|
|
62
|
+
{children}
|
|
63
|
+
</span>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const BUTTON = {
|
|
68
|
+
default: 'border border-[var(--border)] text-[var(--text)] hover:bg-[var(--bg-hover)]',
|
|
69
|
+
primary: 'border border-transparent bg-[var(--text)] text-[var(--bg)] hover:opacity-90',
|
|
70
|
+
quiet: 'border border-transparent text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text)]',
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & { variant?: keyof typeof BUTTON };
|
|
74
|
+
|
|
75
|
+
export function SettingsButton({ variant = 'default', className = '', ...props }: ButtonProps) {
|
|
76
|
+
return (
|
|
77
|
+
<button type="button" {...props}
|
|
78
|
+
className={`inline-flex h-9 shrink-0 items-center justify-center gap-1.5 rounded-[8px] px-3.5 text-[14px] font-medium transition disabled:pointer-events-none disabled:opacity-50 ${BUTTON[variant]} ${className}`} />
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** A link that looks like a button (an install page, a key page). */
|
|
83
|
+
export function SettingsLinkButton({ className = '', ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
|
|
84
|
+
return (
|
|
85
|
+
<a target="_blank" rel="noreferrer" {...props}
|
|
86
|
+
className={`inline-flex h-9 shrink-0 items-center justify-center gap-1.5 rounded-[8px] px-3.5 text-[14px] font-medium transition ${BUTTON.default} ${className}`} />
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** On or off. */
|
|
91
|
+
export function SettingsSwitch({ checked, onChange, label, disabled }: { checked: boolean; onChange: (v: boolean) => void; label: string; disabled?: boolean }) {
|
|
92
|
+
return (
|
|
93
|
+
<button type="button" role="switch" aria-checked={checked} aria-label={label} title={label} disabled={disabled} onClick={() => onChange(!checked)}
|
|
94
|
+
className={`relative h-6 w-10 shrink-0 rounded-full transition-colors disabled:opacity-50 ${checked ? 'bg-[var(--text)]' : 'bg-[var(--surface-3)]'}`}>
|
|
95
|
+
<span className={`absolute top-[3px] h-[18px] w-[18px] rounded-full bg-[var(--bg)] shadow-[0_1px_2px_rgba(0,0,0,0.2)] transition-all ${checked ? 'left-[19px]' : 'left-[3px]'}`} />
|
|
96
|
+
</button>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** One of two or three, all in view: the chosen one raised out of a quiet track. */
|
|
101
|
+
export function SettingsSegmented<T extends string>({ label, value, options, onChange }: {
|
|
102
|
+
label: string;
|
|
103
|
+
value: T;
|
|
104
|
+
options: Array<{ value: T; label: string; icon?: React.ReactNode }>;
|
|
105
|
+
onChange: (value: T) => void;
|
|
106
|
+
}) {
|
|
107
|
+
return (
|
|
108
|
+
<div role="radiogroup" aria-label={label} className="inline-flex shrink-0 gap-0.5 rounded-[10px] bg-[var(--fill-tsp)] p-[3px]">
|
|
109
|
+
{options.map((option) => {
|
|
110
|
+
const on = option.value === value;
|
|
111
|
+
return (
|
|
112
|
+
<button key={option.value} type="button" role="radio" aria-checked={on} onClick={() => { if (!on) onChange(option.value); }}
|
|
113
|
+
className={`inline-flex h-[30px] items-center gap-1.5 whitespace-nowrap rounded-[7px] px-3 text-[14px] transition ${
|
|
114
|
+
on ? 'bg-[var(--dock-pane)] text-[var(--text)] shadow-[0_1px_2px_rgba(0,0,0,0.08)] ring-1 ring-[var(--border)]' : 'text-[var(--text-muted)] hover:text-[var(--text)]'
|
|
115
|
+
}`}>
|
|
116
|
+
{option.icon ? <span className="flex shrink-0">{option.icon}</span> : null}
|
|
117
|
+
{option.label}
|
|
118
|
+
</button>
|
|
119
|
+
);
|
|
120
|
+
})}
|
|
121
|
+
</div>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** A text field. */
|
|
126
|
+
export const SettingsField = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(function SettingsField({ className = '', ...props }, ref) {
|
|
127
|
+
return (
|
|
128
|
+
<input ref={ref} {...props}
|
|
129
|
+
className={`h-10 w-full min-w-0 rounded-[8px] border border-[var(--border)] bg-transparent px-3 text-[14px] text-[var(--text)] outline-none transition placeholder:text-[var(--text-faint)] hover:border-[var(--border-strong)] focus:border-[var(--text-muted)] ${className}`} />
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
/** A form that opened under a row. */
|
|
134
|
+
export function SettingsInset({ children }: { children: React.ReactNode }) {
|
|
135
|
+
return <div className="flex flex-col gap-2.5 rounded-[10px] border border-[var(--border)] p-4">{children}</div>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export const settingsLink = 'inline-flex items-center gap-1 text-[var(--text-muted)] underline-offset-2 hover:text-[var(--text)] hover:underline';
|
|
139
|
+
export const settingsError = 'text-[13.5px] leading-relaxed text-[var(--err)]';
|
package/skill/assets/upstream/packages/openfilm/studio/ui/src/components/settings/use-settings.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { studioHost } from '../../lib/host';
|
|
3
|
+
import type { SettingsSection } from './SettingsModal';
|
|
4
|
+
|
|
5
|
+
/** Both editor and launcher follow the native Settings command; each page has its own dialog. */
|
|
6
|
+
export function useSettings() {
|
|
7
|
+
const [section, setSection] = React.useState<SettingsSection | null>(null);
|
|
8
|
+
React.useEffect(() => studioHost?.settings?.onOpen(() => setSection('general')), []);
|
|
9
|
+
return [section, setSection] as const;
|
|
10
|
+
}
|