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,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an agent points at in Studio, in its reply. The agent writes a token in its text (src/agents.mjs tells it how)
|
|
3
|
+
* and the chat draws it as a pill, the same pill the person's own references are, which opens the thing in Studio:
|
|
4
|
+
*
|
|
5
|
+
* [[clip:<id>]] a clip, by its film.html id
|
|
6
|
+
* [[t:<seconds>]] a moment of the film
|
|
7
|
+
* [[range:<start>-<end>]] a stretch of the film, in seconds (start before end)
|
|
8
|
+
* [[track:<index>]] a track, by its place in film.html (0 is the first)
|
|
9
|
+
* [[file:<path>]] a file of the project, by its path in the project
|
|
10
|
+
*
|
|
11
|
+
* Anything else between double brackets (an unknown kind, a number that is not one, a path out of the project) stays
|
|
12
|
+
* as the agent wrote it: a pill that leads nowhere would be worse than the words.
|
|
13
|
+
*
|
|
14
|
+
* Pure: no React, no bridge, so `node --test` runs it (src/agent-refs.test.mjs).
|
|
15
|
+
*/
|
|
16
|
+
import type { PromptReference as StoredPromptReference } from '@openfilm/shared';
|
|
17
|
+
import type { StudioRef } from '../app-bridge';
|
|
18
|
+
import { kindOf, soundRole } from 'openfilm/film-doc';
|
|
19
|
+
import { refPill } from '../studio-refs.ts';
|
|
20
|
+
|
|
21
|
+
export type AgentRefSegment = { kind: 'text'; text: string } | { kind: 'ref'; ref: StudioRef; raw: string };
|
|
22
|
+
|
|
23
|
+
const TOKEN = /\[\[([a-z]+):([^\[\]\n]{1,300})\]\]/g;
|
|
24
|
+
const SECONDS = /^\d+(?:\.\d+)?$/;
|
|
25
|
+
|
|
26
|
+
/** The reference a token's kind and value name, or null when they name none. */
|
|
27
|
+
function refOf(kind: string, value: string): StudioRef | null {
|
|
28
|
+
const v = value.trim();
|
|
29
|
+
switch (kind) {
|
|
30
|
+
case 'clip':
|
|
31
|
+
return /^[^\s"'<>`]{1,120}$/.test(v)
|
|
32
|
+
? { kind: 'clip', id: v, loc: null, label: v, clipKind: 'mg', src: null, start: 0, end: 0 }
|
|
33
|
+
: null;
|
|
34
|
+
case 't':
|
|
35
|
+
return SECONDS.test(v) ? { kind: 'time', time: Number(v) } : null;
|
|
36
|
+
case 'range': {
|
|
37
|
+
/* an agent writes an en dash as often as a hyphen */
|
|
38
|
+
const m = /^(\d+(?:\.\d+)?)\s*[-–]\s*(\d+(?:\.\d+)?)$/.exec(v);
|
|
39
|
+
if (!m) return null;
|
|
40
|
+
const start = Number(m[1]);
|
|
41
|
+
const end = Number(m[2]);
|
|
42
|
+
return end > start ? { kind: 'range', start, end, clipIds: [] } : null;
|
|
43
|
+
}
|
|
44
|
+
case 'track':
|
|
45
|
+
return /^\d{1,3}$/.test(v) ? { kind: 'track', track: Number(v), label: '', clipIds: [] } : null;
|
|
46
|
+
case 'file': {
|
|
47
|
+
/* a path in the project: not from the disk's root, not out of the folder, not an address */
|
|
48
|
+
const path = v.replace(/^\.\//, '');
|
|
49
|
+
if (!path || path.startsWith('/') || /^[a-z]:[\\/]/i.test(path) || /^[a-z][a-z0-9+.-]*:/i.test(path)) return null;
|
|
50
|
+
if (path.split(/[\\/]/).some((part) => part === '..')) return null;
|
|
51
|
+
return { kind: 'file', path, fileKind: '' };
|
|
52
|
+
}
|
|
53
|
+
default:
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A piece of the agent's text, split into its words and the references in it. */
|
|
59
|
+
export function parseAgentRefs(text: string): AgentRefSegment[] {
|
|
60
|
+
const out: AgentRefSegment[] = [];
|
|
61
|
+
const pushText = (chunk: string) => {
|
|
62
|
+
if (!chunk) return;
|
|
63
|
+
const last = out[out.length - 1];
|
|
64
|
+
if (last?.kind === 'text') last.text += chunk;
|
|
65
|
+
else out.push({ kind: 'text', text: chunk });
|
|
66
|
+
};
|
|
67
|
+
let cursor = 0;
|
|
68
|
+
for (const match of text.matchAll(TOKEN)) {
|
|
69
|
+
const ref = refOf(match[1]!, match[2]!);
|
|
70
|
+
if (!ref) continue;
|
|
71
|
+
pushText(text.slice(cursor, match.index));
|
|
72
|
+
out.push({ kind: 'ref', ref, raw: match[0] });
|
|
73
|
+
cursor = match.index! + match[0].length;
|
|
74
|
+
}
|
|
75
|
+
pushText(text.slice(cursor));
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The pill for a reference an agent wrote. A clip's kind (and so its icon) comes from its file, when the film it is in
|
|
81
|
+
* is known (`clipSrc`); a clip the film does not have gets the neutral icon. `trackLabel` names a track in the chat's
|
|
82
|
+
* language.
|
|
83
|
+
*/
|
|
84
|
+
export function agentRefPill(ref: StudioRef, { clipSrc, trackLabel }: { clipSrc?: string | null; trackLabel?: (track: number) => string } = {}): StoredPromptReference {
|
|
85
|
+
if (ref.kind === 'clip') {
|
|
86
|
+
const target: StudioRef = { ...ref, ...(clipSrc ? { src: clipSrc } : {}) };
|
|
87
|
+
return { id: `agent:clip:${ref.id}`, kind: clipSrc ? clipPillKind(clipSrc) : 'clip', label: ref.id ?? ref.label, target: target as unknown as Record<string, unknown> };
|
|
88
|
+
}
|
|
89
|
+
const pill = refPill(undefined, ref);
|
|
90
|
+
return { ...pill, id: `agent:${pill.id}`, ...(ref.kind === 'track' && trackLabel ? { label: trackLabel(ref.track) } : {}) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The kind of pill (and so its icon) for a clip of this file, as the timeline colors it: a page is a motion graphic,
|
|
95
|
+
* a video or a still is a picture, a sound is a voice, a sound effect or music by where it lives.
|
|
96
|
+
*/
|
|
97
|
+
export function clipPillKind(src: string, sound = false): 'clipMg' | 'clipVideo' | 'clipVoice' | 'clipSfx' | 'clipMusic' {
|
|
98
|
+
const kind = kindOf({ src, ...(sound ? { sound: true } : {}) });
|
|
99
|
+
if (kind === 'page') return 'clipMg';
|
|
100
|
+
if (kind === 'sound') {
|
|
101
|
+
const role = soundRole(src);
|
|
102
|
+
return role === 'sfx' ? 'clipSfx' : role === 'music' ? 'clipMusic' : 'clipVoice';
|
|
103
|
+
}
|
|
104
|
+
return 'clipVideo';
|
|
105
|
+
}
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the agent did this turn: what it said and what it did.
|
|
3
|
+
*
|
|
4
|
+
* Two sources feed one shape:
|
|
5
|
+
* - the running turn: agent-text / agent-act / agent-act-done from the public SSE stream
|
|
6
|
+
* - finished turns: the server-projected PublicProgressSnapshot.steps
|
|
7
|
+
* Both reduce to the same item, so the panel has one rendering path, not one for live and one for
|
|
8
|
+
* history, which are two readings of the same process.
|
|
9
|
+
*
|
|
10
|
+
* This layer has no filtering and should not: tool names, inputs, command lines and reasoning are
|
|
11
|
+
* already left out of the events on the server (see agent-activity and public-stream in
|
|
12
|
+
* @openfilm/shared). Filtering again on the client would leave the raw fields in the network panel:
|
|
13
|
+
* hidden, not absent.
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
TRANSCRIPT_TEXT_MAX,
|
|
17
|
+
clipTranscriptText,
|
|
18
|
+
mergeStreamText,
|
|
19
|
+
type AgentAct,
|
|
20
|
+
type AgentActAsset,
|
|
21
|
+
type AgentAsk,
|
|
22
|
+
type PublicGenerationEvent,
|
|
23
|
+
type PublicProgressSnapshot,
|
|
24
|
+
type PublicTimelineStep,
|
|
25
|
+
} from '@openfilm/shared';
|
|
26
|
+
import type { AgentActivityItem } from '@/components/AgentActivityRow';
|
|
27
|
+
|
|
28
|
+
export interface TranscriptItem {
|
|
29
|
+
/** Merge key: text by partId, acts by callId. */
|
|
30
|
+
id: string;
|
|
31
|
+
/**
|
|
32
|
+
* `thought` / `plan` only come from an agent on the user's machine (Codex / Claude Code, see
|
|
33
|
+
* local-agent-run): the built-in agent's reasoning is not sent, and it has no plan tool.
|
|
34
|
+
*/
|
|
35
|
+
kind: 'text' | 'act' | 'notice' | 'thought' | 'plan';
|
|
36
|
+
/** What it is doing (a closed set); absent for text / notice. */
|
|
37
|
+
act?: AgentAct;
|
|
38
|
+
/** The short visible text after the act (search query / picture description), picked by the server. */
|
|
39
|
+
query?: string;
|
|
40
|
+
/**
|
|
41
|
+
* The short tail after a local agent's file or command act: the file name or the command. Local
|
|
42
|
+
* sources only: that is the user's own agent working in the user's own folder.
|
|
43
|
+
*/
|
|
44
|
+
detail?: string;
|
|
45
|
+
/** The plan checklist (each ACP plan update is the whole list, replaced in place). */
|
|
46
|
+
plan?: TranscriptPlanEntry[];
|
|
47
|
+
/** The `chat.ask` row: the question and options the agent stopped to ask. */
|
|
48
|
+
ask?: AgentAsk;
|
|
49
|
+
/** The asset this step made (image / sound / shot), seen and heard right in the conversation. Picked by the server. */
|
|
50
|
+
asset?: AgentActAsset;
|
|
51
|
+
/**
|
|
52
|
+
* The notice row's i18n key (the full token, resolved by t() when rendering).
|
|
53
|
+
*
|
|
54
|
+
* These rows are not the agent speaking but a note the platform leaves in the conversation; today
|
|
55
|
+
* there is one: earlier messages were summarized. It belongs in the conversation, because what it
|
|
56
|
+
* says is that the agent no longer has the original of the text above it.
|
|
57
|
+
*
|
|
58
|
+
* A local agent's own notice (ACP `notice`, such as a Codex warning) has no key: its words are in `text`.
|
|
59
|
+
*/
|
|
60
|
+
noticeKey?: string;
|
|
61
|
+
text?: string;
|
|
62
|
+
turnNo?: number;
|
|
63
|
+
/** Still running: text not final, or an act sent but not yet back. */
|
|
64
|
+
pending: boolean;
|
|
65
|
+
/** When the client first saw it, to time running rows; history has none. */
|
|
66
|
+
startedAt?: number;
|
|
67
|
+
/** When thinking ended (thought only: "Thought for 6s"). */
|
|
68
|
+
endedAt?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface TranscriptPlanEntry {
|
|
72
|
+
content: string;
|
|
73
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Merges one public stream event into the record.
|
|
78
|
+
*
|
|
79
|
+
* Pure, returning a new array: on an SSE reconnect the server replays from the start, so this must be
|
|
80
|
+
* idempotent; the same partId / callId again updates in place rather than adding a row.
|
|
81
|
+
*/
|
|
82
|
+
export function reduceTranscript(
|
|
83
|
+
items: TranscriptItem[],
|
|
84
|
+
event: PublicGenerationEvent,
|
|
85
|
+
now: number = Date.now(),
|
|
86
|
+
): TranscriptItem[] {
|
|
87
|
+
switch (event.type) {
|
|
88
|
+
case 'agent-text': {
|
|
89
|
+
const id = `text:${event.partId ?? event.turnNo}`;
|
|
90
|
+
const at = items.findIndex((item) => item.id === id);
|
|
91
|
+
// No partId (older servers) means the whole text came at once: final.
|
|
92
|
+
const pending = event.partId != null && event.done !== true;
|
|
93
|
+
if (at >= 0) {
|
|
94
|
+
const next = [...items];
|
|
95
|
+
next[at] = {
|
|
96
|
+
...next[at]!,
|
|
97
|
+
text: mergeStreamText(next[at]!.text ?? '', event.text),
|
|
98
|
+
pending,
|
|
99
|
+
};
|
|
100
|
+
return next;
|
|
101
|
+
}
|
|
102
|
+
return [...items, {
|
|
103
|
+
id,
|
|
104
|
+
kind: 'text',
|
|
105
|
+
text: event.text,
|
|
106
|
+
turnNo: event.turnNo,
|
|
107
|
+
pending,
|
|
108
|
+
startedAt: now,
|
|
109
|
+
}];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
case 'agent-act': {
|
|
113
|
+
const id = `act:${event.callId}`;
|
|
114
|
+
const at = items.findIndex((item) => item.id === id);
|
|
115
|
+
if (at >= 0) {
|
|
116
|
+
const next = [...items];
|
|
117
|
+
next[at] = {
|
|
118
|
+
...next[at]!,
|
|
119
|
+
act: event.act,
|
|
120
|
+
...(event.query !== undefined ? { query: event.query } : {}),
|
|
121
|
+
...(event.ask ? { ask: event.ask } : {}),
|
|
122
|
+
};
|
|
123
|
+
return next;
|
|
124
|
+
}
|
|
125
|
+
return [...items, {
|
|
126
|
+
id,
|
|
127
|
+
kind: 'act',
|
|
128
|
+
act: event.act,
|
|
129
|
+
...(event.query ? { query: event.query } : {}),
|
|
130
|
+
...(event.ask ? { ask: event.ask } : {}),
|
|
131
|
+
turnNo: event.turnNo,
|
|
132
|
+
pending: true,
|
|
133
|
+
startedAt: now,
|
|
134
|
+
}];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Only stops that row.
|
|
138
|
+
A callId with no match is normal: the server sends this for every tool_result, and most of those
|
|
139
|
+
acts (read / grep / glob) drew no row. Then do nothing; never stop another row instead. */
|
|
140
|
+
case 'agent-act-done': {
|
|
141
|
+
const at = event.callId
|
|
142
|
+
? items.findIndex((item) => item.id === `act:${event.callId}`)
|
|
143
|
+
: findLastPendingAct(items);
|
|
144
|
+
if (at < 0 || items[at]!.kind !== 'act') return items;
|
|
145
|
+
const next = [...items];
|
|
146
|
+
next[at] = { ...next[at]!, pending: false, ...(event.asset ? { asset: event.asset } : {}) };
|
|
147
|
+
return next;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Thinking only drives the status line underneath. Drafts are not sent and vanish when done: not in the conversation, not in the summary. */
|
|
151
|
+
case 'agent-thinking':
|
|
152
|
+
return items;
|
|
153
|
+
|
|
154
|
+
/* The agent's question settled (answered / skipped / timed out): write it back to its row, so the conversation
|
|
155
|
+
reads "asked what · answered what". The question's id is not the tool call's callId, so match the latest
|
|
156
|
+
unsettled question; a turn asks at most once. */
|
|
157
|
+
case 'ask-settled': {
|
|
158
|
+
for (let at = items.length - 1; at >= 0; at -= 1) {
|
|
159
|
+
const item = items[at]!;
|
|
160
|
+
if (item.kind !== 'act' || item.act !== 'chat.ask' || !item.ask || item.ask.status) continue;
|
|
161
|
+
const next = [...items];
|
|
162
|
+
next[at] = { ...item, ask: { ...item.ask, status: event.status, ...(event.answer ? { answer: event.answer } : {}) } };
|
|
163
|
+
return next;
|
|
164
|
+
}
|
|
165
|
+
return items;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/* Most context events only update the percentage (the caller reads it) and leave the record alone.
|
|
169
|
+
Only "just compacted" leaves a mark in the conversation, with a fixed id so a replay never adds a second. */
|
|
170
|
+
case 'context': {
|
|
171
|
+
if (!event.compacted) return items;
|
|
172
|
+
const id = 'notice:context-compacted';
|
|
173
|
+
if (items.some((item) => item.id === id)) return items;
|
|
174
|
+
return [...items, {
|
|
175
|
+
id,
|
|
176
|
+
kind: 'notice',
|
|
177
|
+
noticeKey: 'agent.contextCompacted',
|
|
178
|
+
pending: false,
|
|
179
|
+
startedAt: now,
|
|
180
|
+
}];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
default:
|
|
184
|
+
return items;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function findLastPendingAct(items: TranscriptItem[]): number {
|
|
189
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
190
|
+
if (items[index]!.kind === 'act' && items[index]!.pending) return index;
|
|
191
|
+
}
|
|
192
|
+
return -1;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Server-projected snapshot → the same items. History has no timing, so no startedAt.
|
|
197
|
+
*
|
|
198
|
+
* No second trim here: it was projected before it was sent (publicProgressSnapshot), and the shape
|
|
199
|
+
* this function receives has no tool name or input fields to cut.
|
|
200
|
+
*/
|
|
201
|
+
export function transcriptFromSteps(
|
|
202
|
+
steps: PublicTimelineStep[] | undefined,
|
|
203
|
+
): TranscriptItem[] {
|
|
204
|
+
if (!steps?.length) return [];
|
|
205
|
+
return steps.map((step) => {
|
|
206
|
+
if (step.kind === 'act') {
|
|
207
|
+
return {
|
|
208
|
+
id: step.id,
|
|
209
|
+
kind: 'act' as const,
|
|
210
|
+
act: step.act,
|
|
211
|
+
...(step.query ? { query: step.query } : {}),
|
|
212
|
+
...(step.asset ? { asset: step.asset } : {}),
|
|
213
|
+
...(step.ask ? { ask: step.ask } : {}),
|
|
214
|
+
...(step.turnNo != null ? { turnNo: step.turnNo } : {}),
|
|
215
|
+
pending: step.running === true,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
id: step.id,
|
|
220
|
+
kind: 'text' as const,
|
|
221
|
+
text: clipTranscriptText(step.text, TRANSCRIPT_TEXT_MAX),
|
|
222
|
+
...(step.turnNo != null ? { turnNo: step.turnNo } : {}),
|
|
223
|
+
pending: step.running === true,
|
|
224
|
+
};
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export function transcriptFromSnapshot(
|
|
229
|
+
snapshot: PublicProgressSnapshot | null | undefined,
|
|
230
|
+
): TranscriptItem[] {
|
|
231
|
+
return transcriptFromSteps(snapshot?.steps);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Rows already mapped, keyed by source object identity.
|
|
236
|
+
*
|
|
237
|
+
* This is what lets the whole list memoize. `reduceTranscript` replaces only the row a delta touched
|
|
238
|
+
* and passes the rest through, so which rows did not change is already exact in the data. If this step
|
|
239
|
+
* built new objects every time, that would be lost: two hundred rows with new identities, `React.memo`
|
|
240
|
+
* stops none, and every token is a full re-render.
|
|
241
|
+
*
|
|
242
|
+
* A WeakMap rather than a Map: the keys are the rows themselves, so the cache is collected with them.
|
|
243
|
+
*/
|
|
244
|
+
const TRACE_ITEMS = new WeakMap<TranscriptItem, AgentActivityItem>();
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Render adapter: AgentActivityList already draws these rows; this only maps the field names.
|
|
248
|
+
*
|
|
249
|
+
* The output must be cacheable: the same TranscriptItem must always give the same object (see the map
|
|
250
|
+
* above). So this depends only on its input, never on the clock or randomness, which would quietly
|
|
251
|
+
* corrupt the cache.
|
|
252
|
+
*/
|
|
253
|
+
export function toTraceLogItems(items: TranscriptItem[]): AgentActivityItem[] {
|
|
254
|
+
return items.map((item) => {
|
|
255
|
+
const cached = TRACE_ITEMS.get(item);
|
|
256
|
+
if (cached) return cached;
|
|
257
|
+
const mapped = traceLogItem(item);
|
|
258
|
+
TRACE_ITEMS.set(item, mapped);
|
|
259
|
+
return mapped;
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function traceLogItem(item: TranscriptItem): AgentActivityItem {
|
|
264
|
+
if (item.kind === 'notice') {
|
|
265
|
+
return { kind: 'notice' as const, id: item.id, label: item.noticeKey ?? '', ...(item.text ? { text: item.text } : {}) };
|
|
266
|
+
}
|
|
267
|
+
if (item.kind === 'thought') {
|
|
268
|
+
return {
|
|
269
|
+
kind: 'thought' as const,
|
|
270
|
+
id: item.id,
|
|
271
|
+
label: 'thought',
|
|
272
|
+
text: item.text ?? '',
|
|
273
|
+
pending: item.pending,
|
|
274
|
+
...(item.startedAt != null ? { startedAt: item.startedAt } : {}),
|
|
275
|
+
...(item.endedAt != null ? { endedAt: item.endedAt } : {}),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (item.kind === 'plan') {
|
|
279
|
+
return { kind: 'plan' as const, id: item.id, label: 'plan', plan: item.plan ?? [], pending: item.pending };
|
|
280
|
+
}
|
|
281
|
+
if (item.kind === 'act') {
|
|
282
|
+
return {
|
|
283
|
+
kind: 'act' as const,
|
|
284
|
+
id: item.id,
|
|
285
|
+
// The copy key is the act itself. An unknown key renders an empty row, and acts are a closed set,
|
|
286
|
+
// so a missing line of copy is a slip tests can catch, not a leak in production.
|
|
287
|
+
label: `act.${item.act ?? 'working'}`,
|
|
288
|
+
...(item.query ? { query: item.query } : {}),
|
|
289
|
+
...(item.detail ? { detail: item.detail } : {}),
|
|
290
|
+
...(item.ask ? { ask: item.ask } : {}),
|
|
291
|
+
...(item.asset ? { asset: item.asset } : {}),
|
|
292
|
+
...(item.turnNo != null ? { turnNo: item.turnNo } : {}),
|
|
293
|
+
pending: item.pending,
|
|
294
|
+
...(item.startedAt != null ? { startedAt: item.startedAt } : {}),
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
kind: 'text' as const,
|
|
299
|
+
id: item.id,
|
|
300
|
+
label: 'text',
|
|
301
|
+
text: item.text ?? '',
|
|
302
|
+
...(item.turnNo != null ? { turnNo: item.turnNo } : {}),
|
|
303
|
+
pending: item.pending,
|
|
304
|
+
...(item.startedAt != null ? { startedAt: item.startedAt } : {}),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Whether the line underneath should show.
|
|
310
|
+
*
|
|
311
|
+
* Do not give each activity its own caption. While an act runs, its row is lit with its own label;
|
|
312
|
+
* while text streams, the words appear on screen. A caption underneath would repeat what the user
|
|
313
|
+
* already sees.
|
|
314
|
+
*
|
|
315
|
+
* The one case that needs the line is when nothing runs: the message is sent and the model has not
|
|
316
|
+
* started. Then the screen really is empty, and without a word it looks frozen.
|
|
317
|
+
*
|
|
318
|
+
* Returns true when something is running, so no line is needed.
|
|
319
|
+
*/
|
|
320
|
+
export function transcriptBusy(items: TranscriptItem[]): boolean {
|
|
321
|
+
return items.some((item) => item.pending);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Whether the agent has finished talking this turn: is the last item a finished paragraph.
|
|
326
|
+
*
|
|
327
|
+
* A turn does not end with the agent's last sentence: the platform still wraps up for some seconds,
|
|
328
|
+
* with the transcript still and the task still running. Without this check a "planning the next step"
|
|
329
|
+
* line would hang there after the agent has signed off; the work left is the platform's and not worth
|
|
330
|
+
* announcing.
|
|
331
|
+
*
|
|
332
|
+
* When the last item is a finished act (or there is none), the model is about to speak, and that really
|
|
333
|
+
* is waiting for it.
|
|
334
|
+
*/
|
|
335
|
+
export function transcriptDoneTalking(items: TranscriptItem[]): boolean {
|
|
336
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
337
|
+
const item = items[index]!;
|
|
338
|
+
// Marks such as dividers are not the agent's doing: look at the item before.
|
|
339
|
+
if (item.kind === 'notice') continue;
|
|
340
|
+
return item.kind === 'text' && !item.pending;
|
|
341
|
+
}
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Reconciles the live cache with the saved transcript, keeping what is more complete.
|
|
347
|
+
*
|
|
348
|
+
* Comparing counts alone fails in one concrete case: a Q&A turn is usually one paragraph; if SSE stops
|
|
349
|
+
* mid-way, the live copy is half a sentence and the saved one the full text, both with a count of 1, so
|
|
350
|
+
* keeping the live copy leaves the client stopped mid-sentence. Merge by id instead, text through
|
|
351
|
+
* mergeStreamText: a longer prefix wins.
|
|
352
|
+
*
|
|
353
|
+
* The ids must be aligned too: live uses `text:4:1` / `act:call`, saved uses `text-4:1` / `tool-call`.
|
|
354
|
+
* Unaligned, they never merge and the half and the full text show side by side.
|
|
355
|
+
*/
|
|
356
|
+
export function preferTranscript(live: TranscriptItem[], disk: TranscriptItem[]): TranscriptItem[] {
|
|
357
|
+
if (live.length === 0) return disk;
|
|
358
|
+
if (disk.length === 0) return live;
|
|
359
|
+
/* Order follows the saved copy (written in the order things happened), so steps the live copy missed
|
|
360
|
+
land where they belong, not at the end. Items only in the live copy follow the item before them there. */
|
|
361
|
+
const liveByKey = new Map<string, TranscriptItem>();
|
|
362
|
+
for (const item of live) {
|
|
363
|
+
const key = transcriptMergeKey(item);
|
|
364
|
+
const prev = liveByKey.get(key);
|
|
365
|
+
liveByKey.set(key, prev ? pickRicherTranscript(prev, item) : item);
|
|
366
|
+
}
|
|
367
|
+
const diskKeys = new Set(disk.map(transcriptMergeKey));
|
|
368
|
+
const trailing = new Map<string | null, TranscriptItem[]>();
|
|
369
|
+
let anchor: string | null = null;
|
|
370
|
+
const placed = new Set<string>();
|
|
371
|
+
for (const item of live) {
|
|
372
|
+
const key = transcriptMergeKey(item);
|
|
373
|
+
if (diskKeys.has(key)) { anchor = key; continue; }
|
|
374
|
+
if (placed.has(key)) continue;
|
|
375
|
+
placed.add(key);
|
|
376
|
+
const list = trailing.get(anchor) ?? [];
|
|
377
|
+
list.push(liveByKey.get(key)!);
|
|
378
|
+
trailing.set(anchor, list);
|
|
379
|
+
}
|
|
380
|
+
const out: TranscriptItem[] = [...(trailing.get(null) ?? [])];
|
|
381
|
+
const seen = new Set<string>();
|
|
382
|
+
for (const item of disk) {
|
|
383
|
+
const key = transcriptMergeKey(item);
|
|
384
|
+
if (seen.has(key)) continue;
|
|
385
|
+
seen.add(key);
|
|
386
|
+
const prev = liveByKey.get(key);
|
|
387
|
+
out.push(prev ? pickRicherTranscript(prev, item) : item);
|
|
388
|
+
out.push(...(trailing.get(key) ?? []));
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function transcriptMergeKey(item: TranscriptItem): string {
|
|
394
|
+
if (item.kind === 'text') return item.id.replace(/^text[-:]/, 'text:');
|
|
395
|
+
if (item.kind === 'act') return item.id.replace(/^(?:act:|tool-)/, 'act:');
|
|
396
|
+
return item.id;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function pickRicherTranscript(a: TranscriptItem, b: TranscriptItem): TranscriptItem {
|
|
400
|
+
/* Same content: return the original. Downstream memoizes by object identity (each row, each run), so a new object with the same content re-renders the whole turn. */
|
|
401
|
+
if (a === b) return a;
|
|
402
|
+
if (a.kind === 'text' && b.kind === 'text') {
|
|
403
|
+
if ((a.text ?? '') === (b.text ?? '') && Boolean(a.pending) === Boolean(b.pending)) return a;
|
|
404
|
+
const text = mergeStreamText(a.text ?? '', b.text ?? '');
|
|
405
|
+
const base = (b.text ?? '').length > (a.text ?? '').length ? b : a;
|
|
406
|
+
return { ...base, text, pending: a.pending && b.pending };
|
|
407
|
+
}
|
|
408
|
+
if (a.kind === 'act' && b.kind === 'act') {
|
|
409
|
+
if (a.pending && !b.pending) return b;
|
|
410
|
+
if (b.pending && !a.pending) return a;
|
|
411
|
+
// The live copy often lacks the asset (its receipt was missed); the saved copy has it.
|
|
412
|
+
if (!a.asset && b.asset) return { ...a, asset: b.asset };
|
|
413
|
+
}
|
|
414
|
+
return a;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Finalizes the text when the turn ends.
|
|
419
|
+
*
|
|
420
|
+
* `replied.text` is the agent's whole last message. Snapshots stream in 80-byte steps and the last
|
|
421
|
+
* cumulative one may miss the live cache, but the final event carries the full text. An empty string
|
|
422
|
+
* must not overwrite: a reply the server rebuilds from storage is often empty, which means the text is
|
|
423
|
+
* in the process record, not that the agent said nothing.
|
|
424
|
+
*/
|
|
425
|
+
export function sealTranscriptText(items: TranscriptItem[], text: string): TranscriptItem[] {
|
|
426
|
+
const sealed = items.some((item) => item.pending)
|
|
427
|
+
? items.map((item) => (item.pending ? { ...item, pending: false } : item))
|
|
428
|
+
: items;
|
|
429
|
+
if (!text.trim()) return sealed;
|
|
430
|
+
for (let i = sealed.length - 1; i >= 0; i -= 1) {
|
|
431
|
+
const item = sealed[i]!;
|
|
432
|
+
if (item.kind !== 'text') continue;
|
|
433
|
+
const merged = mergeStreamText(item.text ?? '', text);
|
|
434
|
+
if (merged === (item.text ?? '')) return sealed;
|
|
435
|
+
const next = [...sealed];
|
|
436
|
+
next[i] = { ...item, text: merged, pending: false };
|
|
437
|
+
return next;
|
|
438
|
+
}
|
|
439
|
+
return [...sealed, { id: 'text:reply', kind: 'text', text, pending: false }];
|
|
440
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Merging the lists of turns on the client.
|
|
5
|
+
*
|
|
6
|
+
* A server snapshot and local optimistic entries live side by side; every view must merge them the same way, or
|
|
7
|
+
* a just-created item shows in one place and vanishes in another.
|
|
8
|
+
*/
|
|
9
|
+
import type { Turn } from '@openfilm/shared';
|
|
10
|
+
import { shareListById } from '@/lib/structural-share';
|
|
11
|
+
|
|
12
|
+
/** Merges the server list with optimistic local entries, so a refresh doesn't wipe a just-created turn. */
|
|
13
|
+
export function mergeAssetLists(server: Turn[], local: Turn[]): Turn[] {
|
|
14
|
+
const map = new Map<string, Turn>();
|
|
15
|
+
for (const a of local) map.set(a.id, a);
|
|
16
|
+
for (const a of server) {
|
|
17
|
+
const prev = map.get(a.id);
|
|
18
|
+
map.set(
|
|
19
|
+
a.id,
|
|
20
|
+
prev
|
|
21
|
+
? {
|
|
22
|
+
...prev,
|
|
23
|
+
...a,
|
|
24
|
+
title: a.title || prev.title,
|
|
25
|
+
sessionTitle: a.sessionTitle || prev.sessionTitle,
|
|
26
|
+
prompt: a.prompt || prev.prompt,
|
|
27
|
+
}
|
|
28
|
+
: a,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
/* Unchanged entries keep their reference, and an unchanged list returns `local`, so polling doesn't re-render it. */
|
|
32
|
+
return shareListById(local, [...map.values()].sort(
|
|
33
|
+
(a, b) => Date.parse(b.createdAt) - Date.parse(a.createdAt),
|
|
34
|
+
));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function makePlaceholder(
|
|
38
|
+
assetId: string,
|
|
39
|
+
modality: Turn['modality'],
|
|
40
|
+
prompt?: string,
|
|
41
|
+
ids?: { projectId: string; sessionId: string },
|
|
42
|
+
): Turn {
|
|
43
|
+
const now = new Date().toISOString();
|
|
44
|
+
return {
|
|
45
|
+
id: assetId,
|
|
46
|
+
ownerId: '',
|
|
47
|
+
projectId: ids?.projectId ?? assetId,
|
|
48
|
+
sessionId: ids?.sessionId ?? assetId,
|
|
49
|
+
sessionTitle: '',
|
|
50
|
+
baseVideoId: null,
|
|
51
|
+
modality,
|
|
52
|
+
title: '',
|
|
53
|
+
status: 'generating',
|
|
54
|
+
prompt: prompt?.trim() ?? '',
|
|
55
|
+
result: null,
|
|
56
|
+
createdAt: now,
|
|
57
|
+
updatedAt: now,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A turn that has no real id yet.
|
|
63
|
+
*
|
|
64
|
+
* Between Send and the server's answer there is a real wait. Showing nothing reads as "nothing happened", and
|
|
65
|
+
* the person clicks again. So a client-only entry goes up at once, is swapped for the real id when the answer
|
|
66
|
+
* comes, and is removed on failure.
|
|
67
|
+
*
|
|
68
|
+
* The prefix must be recognizable: the entry doesn't exist on the server, so anything that asks the server by id
|
|
69
|
+
* (the progress stream) must skip it, or every send starts with a 404.
|
|
70
|
+
*/
|
|
71
|
+
const PENDING_ASSET_PREFIX = 'pending:';
|
|
72
|
+
|
|
73
|
+
export function isPendingAssetId(id: string): boolean {
|
|
74
|
+
return id.startsWith(PENDING_ASSET_PREFIX);
|
|
75
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an attached folder is, short enough for its card: the folder it sits in, the home folder written as "~", and
|
|
3
|
+
* only the last two folders of a deep path ("…/Projects/2026"). Empty for a folder at the top of a disk.
|
|
4
|
+
*/
|
|
5
|
+
export function attachmentPlace(path: string): string {
|
|
6
|
+
const windows = /^[A-Za-z]:[\\/]/.test(path) || path.includes('\\');
|
|
7
|
+
const sep = windows ? '\\' : '/';
|
|
8
|
+
const trimmed = path.replace(/[\\/]+$/, '');
|
|
9
|
+
const cut = Math.max(trimmed.lastIndexOf('/'), trimmed.lastIndexOf('\\'));
|
|
10
|
+
if (cut <= 0) return '';
|
|
11
|
+
const parent = trimmed.slice(0, cut);
|
|
12
|
+
const home = parent.match(/^(?:\/Users\/[^/]+|\/home\/[^/]+|[A-Za-z]:[\\/]Users[\\/][^\\/]+)(?=$|[\\/])/);
|
|
13
|
+
const rest = (home ? parent.slice(home[0].length) : parent).split(/[\\/]/).filter(Boolean);
|
|
14
|
+
if (rest.length > 2) return ['…', ...rest.slice(-2)].join(sep);
|
|
15
|
+
if (home) return ['~', ...rest].join(sep);
|
|
16
|
+
return `${windows ? '' : '/'}${rest.join(sep)}`;
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A turn of the app's own agent that stopped because the service its model runs on has no balance left (the shell
|
|
3
|
+
* says so: src/pi-config.mjs `outOfBalance`), the same for every service: which one, and its page to top it up (null
|
|
4
|
+
* when the service is not one the app knows). Kept with the saved turn, so its footer still says it.
|
|
5
|
+
*/
|
|
6
|
+
export interface Balance { provider: string; url: string | null }
|
|
7
|
+
|
|
8
|
+
/** What a saved turn keeps of this (lib/chat-store's Turn, extended here: older turns have none). */
|
|
9
|
+
export interface TurnBalance { balance?: Balance }
|
|
10
|
+
|
|
11
|
+
/** A balance as the shell or a saved turn gives it, or null. */
|
|
12
|
+
export function readBalance(value: unknown): Balance | null {
|
|
13
|
+
const v = value as Partial<Balance> | null | undefined;
|
|
14
|
+
if (!v || typeof v.provider !== 'string' || !v.provider) return null;
|
|
15
|
+
return { provider: v.provider, url: typeof v.url === 'string' && /^https:\/\//.test(v.url) ? v.url : null };
|
|
16
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the chat draws what floats over the window (menus, tooltips, previews): its own layer at the end of the page,
|
|
3
|
+
* inside the chat's styles (`.of-chat`), so a menu opened from the composer can reach over Studio and still look like
|
|
4
|
+
* the chat's. The chat itself sits in Studio's page (apps/desktop/shell), not on a page of its own.
|
|
5
|
+
*/
|
|
6
|
+
export const chatLayer = (): HTMLElement => document.getElementById('of-chat-layer') ?? document.body;
|
|
7
|
+
|
|
8
|
+
/** Whether a key or pointer event happened in the chat (its column or its layer), not in Studio around it. */
|
|
9
|
+
export const inChat = (target: EventTarget | null): boolean =>
|
|
10
|
+
Boolean(target && (target as Element).closest?.('.of-chat'));
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether a menu of the chat's is open (the model menu, the @ list…). While the project's assets are open the chat's
|
|
3
|
+
* view keeps only its composer, and a menu opening above it needs the whole column back: the app is told (main.mjs
|
|
4
|
+
* `assets.menu`). Counted, since one menu can open from another.
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
let open = 0;
|
|
9
|
+
const tell = () => (window as unknown as { openfilmDesktop?: { setMenuOpen?: (open: boolean) => void } }).openfilmDesktop?.setMenuOpen?.(open > 0);
|
|
10
|
+
|
|
11
|
+
export function useChatMenu(shown: boolean) {
|
|
12
|
+
React.useEffect(() => {
|
|
13
|
+
if (!shown) return undefined;
|
|
14
|
+
open += 1;
|
|
15
|
+
tell();
|
|
16
|
+
return () => { open -= 1; tell(); };
|
|
17
|
+
}, [shown]);
|
|
18
|
+
}
|