@myrialabs/clopen 0.0.1
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/.env.example +6 -0
- package/.github/workflows/release.yml +60 -0
- package/.github/workflows/test.yml +40 -0
- package/CONTRIBUTING.md +499 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/backend/index.ts +156 -0
- package/backend/lib/chat/helpers.ts +42 -0
- package/backend/lib/chat/index.ts +2 -0
- package/backend/lib/chat/stream-manager.ts +1126 -0
- package/backend/lib/database/README.md +77 -0
- package/backend/lib/database/index.ts +119 -0
- package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
- package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
- package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
- package/backend/lib/database/migrations/index.ts +154 -0
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
- package/backend/lib/database/queries/engine-queries.ts +75 -0
- package/backend/lib/database/queries/index.ts +9 -0
- package/backend/lib/database/queries/message-queries.ts +472 -0
- package/backend/lib/database/queries/project-queries.ts +117 -0
- package/backend/lib/database/queries/session-queries.ts +271 -0
- package/backend/lib/database/queries/settings-queries.ts +34 -0
- package/backend/lib/database/queries/snapshot-queries.ts +326 -0
- package/backend/lib/database/queries/utils-queries.ts +59 -0
- package/backend/lib/database/seeders/index.ts +13 -0
- package/backend/lib/database/seeders/settings_seeder.ts +84 -0
- package/backend/lib/database/utils/connection.ts +174 -0
- package/backend/lib/database/utils/index.ts +4 -0
- package/backend/lib/database/utils/migration-runner.ts +118 -0
- package/backend/lib/database/utils/seeder-runner.ts +121 -0
- package/backend/lib/engine/adapters/claude/environment.ts +164 -0
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
- package/backend/lib/engine/adapters/claude/index.ts +1 -0
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
- package/backend/lib/engine/adapters/claude/stream.ts +177 -0
- package/backend/lib/engine/adapters/opencode/index.ts +2 -0
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
- package/backend/lib/engine/adapters/opencode/server.ts +104 -0
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
- package/backend/lib/engine/index.ts +196 -0
- package/backend/lib/engine/types.ts +58 -0
- package/backend/lib/files/file-operations.ts +478 -0
- package/backend/lib/files/file-reading.ts +308 -0
- package/backend/lib/files/file-watcher.ts +383 -0
- package/backend/lib/files/path-browsing.ts +382 -0
- package/backend/lib/git/git-executor.ts +88 -0
- package/backend/lib/git/git-parser.ts +411 -0
- package/backend/lib/git/git-service.ts +505 -0
- package/backend/lib/mcp/README.md +1144 -0
- package/backend/lib/mcp/config.ts +316 -0
- package/backend/lib/mcp/index.ts +35 -0
- package/backend/lib/mcp/project-context.ts +236 -0
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
- package/backend/lib/mcp/servers/helper.ts +143 -0
- package/backend/lib/mcp/servers/index.ts +45 -0
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
- package/backend/lib/mcp/servers/weather/index.ts +31 -0
- package/backend/lib/mcp/stdio-server.ts +103 -0
- package/backend/lib/mcp/types.ts +65 -0
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
- package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
- package/backend/lib/preview/browser/browser-pool.ts +357 -0
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
- package/backend/lib/preview/browser/types.ts +359 -0
- package/backend/lib/preview/index.ts +24 -0
- package/backend/lib/project/index.ts +2 -0
- package/backend/lib/project/status-manager.ts +182 -0
- package/backend/lib/shared/index.ts +2 -0
- package/backend/lib/shared/port-utils.ts +25 -0
- package/backend/lib/shared/process-manager.ts +281 -0
- package/backend/lib/snapshot/blob-store.ts +227 -0
- package/backend/lib/snapshot/gitignore.ts +307 -0
- package/backend/lib/snapshot/helpers.ts +397 -0
- package/backend/lib/snapshot/snapshot-service.ts +483 -0
- package/backend/lib/terminal/helpers.ts +14 -0
- package/backend/lib/terminal/index.ts +8 -0
- package/backend/lib/terminal/pty-manager.ts +4 -0
- package/backend/lib/terminal/pty-session-manager.ts +387 -0
- package/backend/lib/terminal/shell-utils.ts +313 -0
- package/backend/lib/terminal/stream-manager.ts +293 -0
- package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
- package/backend/lib/user/helpers.ts +87 -0
- package/backend/lib/utils/ws.ts +944 -0
- package/backend/lib/vite-dev.ts +353 -0
- package/backend/middleware/cors.ts +15 -0
- package/backend/middleware/error-handler.ts +49 -0
- package/backend/middleware/logger.ts +9 -0
- package/backend/types/api.ts +24 -0
- package/backend/ws/README.md +1505 -0
- package/backend/ws/chat/background.ts +198 -0
- package/backend/ws/chat/index.ts +21 -0
- package/backend/ws/chat/stream.ts +707 -0
- package/backend/ws/engine/claude/accounts.ts +401 -0
- package/backend/ws/engine/claude/index.ts +13 -0
- package/backend/ws/engine/claude/status.ts +43 -0
- package/backend/ws/engine/index.ts +14 -0
- package/backend/ws/engine/opencode/index.ts +11 -0
- package/backend/ws/engine/opencode/status.ts +30 -0
- package/backend/ws/engine/utils.ts +36 -0
- package/backend/ws/files/index.ts +30 -0
- package/backend/ws/files/read.ts +189 -0
- package/backend/ws/files/search.ts +453 -0
- package/backend/ws/files/watch.ts +124 -0
- package/backend/ws/files/write.ts +143 -0
- package/backend/ws/git/branch.ts +106 -0
- package/backend/ws/git/commit.ts +39 -0
- package/backend/ws/git/conflict.ts +68 -0
- package/backend/ws/git/diff.ts +69 -0
- package/backend/ws/git/index.ts +24 -0
- package/backend/ws/git/log.ts +41 -0
- package/backend/ws/git/remote.ts +214 -0
- package/backend/ws/git/staging.ts +84 -0
- package/backend/ws/git/status.ts +90 -0
- package/backend/ws/index.ts +69 -0
- package/backend/ws/mcp/index.ts +61 -0
- package/backend/ws/messages/crud.ts +74 -0
- package/backend/ws/messages/index.ts +14 -0
- package/backend/ws/preview/browser/cleanup.ts +129 -0
- package/backend/ws/preview/browser/console.ts +114 -0
- package/backend/ws/preview/browser/interact.ts +513 -0
- package/backend/ws/preview/browser/mcp.ts +129 -0
- package/backend/ws/preview/browser/native-ui.ts +235 -0
- package/backend/ws/preview/browser/stats.ts +55 -0
- package/backend/ws/preview/browser/tab-info.ts +126 -0
- package/backend/ws/preview/browser/tab.ts +166 -0
- package/backend/ws/preview/browser/webcodecs.ts +293 -0
- package/backend/ws/preview/index.ts +146 -0
- package/backend/ws/projects/crud.ts +113 -0
- package/backend/ws/projects/index.ts +25 -0
- package/backend/ws/projects/presence.ts +46 -0
- package/backend/ws/projects/status.ts +116 -0
- package/backend/ws/sessions/crud.ts +327 -0
- package/backend/ws/sessions/index.ts +33 -0
- package/backend/ws/settings/crud.ts +112 -0
- package/backend/ws/settings/index.ts +14 -0
- package/backend/ws/snapshot/index.ts +17 -0
- package/backend/ws/snapshot/restore.ts +173 -0
- package/backend/ws/snapshot/timeline.ts +141 -0
- package/backend/ws/system/index.ts +14 -0
- package/backend/ws/system/operations.ts +49 -0
- package/backend/ws/terminal/index.ts +40 -0
- package/backend/ws/terminal/persistence.ts +153 -0
- package/backend/ws/terminal/session.ts +382 -0
- package/backend/ws/terminal/stream.ts +79 -0
- package/backend/ws/tunnel/index.ts +14 -0
- package/backend/ws/tunnel/operations.ts +91 -0
- package/backend/ws/types.ts +20 -0
- package/backend/ws/user/crud.ts +156 -0
- package/backend/ws/user/index.ts +14 -0
- package/bin/clopen.ts +307 -0
- package/bun.lock +1352 -0
- package/frontend/App.svelte +34 -0
- package/frontend/app.css +313 -0
- package/frontend/lib/app-environment.ts +10 -0
- package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
- package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
- package/frontend/lib/components/chat/formatters/index.ts +3 -0
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
- package/frontend/lib/components/chat/shared/index.ts +2 -0
- package/frontend/lib/components/chat/shared/utils.ts +116 -0
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
- package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
- package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
- package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
- package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
- package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
- package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
- package/frontend/lib/components/chat/tools/components/index.ts +7 -0
- package/frontend/lib/components/chat/tools/index.ts +26 -0
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
- package/frontend/lib/components/common/Alert.svelte +139 -0
- package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
- package/frontend/lib/components/common/Button.svelte +71 -0
- package/frontend/lib/components/common/Card.svelte +102 -0
- package/frontend/lib/components/common/Checkbox.svelte +48 -0
- package/frontend/lib/components/common/Dialog.svelte +249 -0
- package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
- package/frontend/lib/components/common/Icon.svelte +58 -0
- package/frontend/lib/components/common/Input.svelte +72 -0
- package/frontend/lib/components/common/Lightbox.svelte +233 -0
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
- package/frontend/lib/components/common/Modal.svelte +177 -0
- package/frontend/lib/components/common/ModalProvider.svelte +28 -0
- package/frontend/lib/components/common/ModelSelector.svelte +110 -0
- package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
- package/frontend/lib/components/common/NotificationToast.svelte +113 -0
- package/frontend/lib/components/common/PageTemplate.svelte +76 -0
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
- package/frontend/lib/components/common/Select.svelte +98 -0
- package/frontend/lib/components/common/Textarea.svelte +80 -0
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
- package/frontend/lib/components/common/lucide-icons.ts +1642 -0
- package/frontend/lib/components/common/material-icons.ts +1082 -0
- package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
- package/frontend/lib/components/common/xterm/index.ts +16 -0
- package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
- package/frontend/lib/components/common/xterm/types.ts +31 -0
- package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
- package/frontend/lib/components/files/FileNode.svelte +384 -0
- package/frontend/lib/components/files/FileTree.svelte +681 -0
- package/frontend/lib/components/files/FileViewer.svelte +728 -0
- package/frontend/lib/components/files/SearchResults.svelte +303 -0
- package/frontend/lib/components/git/BranchManager.svelte +458 -0
- package/frontend/lib/components/git/ChangesSection.svelte +107 -0
- package/frontend/lib/components/git/CommitForm.svelte +76 -0
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
- package/frontend/lib/components/git/DiffViewer.svelte +364 -0
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
- package/frontend/lib/components/git/GitButton.svelte +33 -0
- package/frontend/lib/components/git/GitLog.svelte +361 -0
- package/frontend/lib/components/git/GitModal.svelte +80 -0
- package/frontend/lib/components/history/HistoryModal.svelte +563 -0
- package/frontend/lib/components/history/HistoryView.svelte +615 -0
- package/frontend/lib/components/index.ts +34 -0
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
- package/frontend/lib/components/preview/index.ts +2 -0
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
- package/frontend/lib/components/settings/SettingsView.svelte +36 -0
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
- package/frontend/lib/components/terminal/Terminal.svelte +368 -0
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
- package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
- package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
- package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
- package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
- package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
- package/frontend/lib/constants/preview.ts +45 -0
- package/frontend/lib/services/chat/chat.service.ts +704 -0
- package/frontend/lib/services/chat/index.ts +7 -0
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
- package/frontend/lib/services/notification/index.ts +8 -0
- package/frontend/lib/services/notification/push.service.ts +144 -0
- package/frontend/lib/services/notification/sound.service.ts +127 -0
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
- package/frontend/lib/services/preview/index.ts +23 -0
- package/frontend/lib/services/project/index.ts +8 -0
- package/frontend/lib/services/project/status.service.ts +159 -0
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
- package/frontend/lib/services/terminal/background/index.ts +130 -0
- package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
- package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
- package/frontend/lib/services/terminal/index.ts +14 -0
- package/frontend/lib/services/terminal/persistence.service.ts +260 -0
- package/frontend/lib/services/terminal/project.service.ts +953 -0
- package/frontend/lib/services/terminal/session.service.ts +364 -0
- package/frontend/lib/services/terminal/terminal.service.ts +369 -0
- package/frontend/lib/stores/core/app.svelte.ts +117 -0
- package/frontend/lib/stores/core/files.svelte.ts +73 -0
- package/frontend/lib/stores/core/presence.svelte.ts +48 -0
- package/frontend/lib/stores/core/projects.svelte.ts +317 -0
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
- package/frontend/lib/stores/features/models.svelte.ts +89 -0
- package/frontend/lib/stores/features/settings.svelte.ts +87 -0
- package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
- package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
- package/frontend/lib/stores/features/user.svelte.ts +96 -0
- package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
- package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
- package/frontend/lib/types/native-ui.ts +73 -0
- package/frontend/lib/utils/chat/date-separator.ts +39 -0
- package/frontend/lib/utils/chat/message-grouper.ts +219 -0
- package/frontend/lib/utils/chat/message-processor.ts +135 -0
- package/frontend/lib/utils/chat/tool-handler.ts +161 -0
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
- package/frontend/lib/utils/click-outside.ts +20 -0
- package/frontend/lib/utils/context-manager.ts +257 -0
- package/frontend/lib/utils/file-icon-mappings.ts +769 -0
- package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
- package/frontend/lib/utils/git-status.ts +68 -0
- package/frontend/lib/utils/platform.ts +113 -0
- package/frontend/lib/utils/port-check.ts +65 -0
- package/frontend/lib/utils/terminalFormatter.ts +207 -0
- package/frontend/lib/utils/theme.ts +6 -0
- package/frontend/lib/utils/tree-visualizer.ts +320 -0
- package/frontend/lib/utils/ws.ts +44 -0
- package/frontend/main.ts +13 -0
- package/index.html +70 -0
- package/package.json +111 -0
- package/scripts/generate-icons.ts +87 -0
- package/scripts/pre-publish-check.sh +142 -0
- package/scripts/setup-hooks.sh +134 -0
- package/scripts/validate-branch-name.sh +47 -0
- package/scripts/validate-commit-msg.sh +42 -0
- package/shared/constants/engines.ts +134 -0
- package/shared/types/database/connection.ts +16 -0
- package/shared/types/database/index.ts +6 -0
- package/shared/types/database/schema.ts +141 -0
- package/shared/types/engine/index.ts +45 -0
- package/shared/types/filesystem/index.ts +22 -0
- package/shared/types/git.ts +171 -0
- package/shared/types/messaging/index.ts +239 -0
- package/shared/types/messaging/tool.ts +526 -0
- package/shared/types/network/api.ts +18 -0
- package/shared/types/network/index.ts +5 -0
- package/shared/types/stores/app.ts +23 -0
- package/shared/types/stores/dialog.ts +21 -0
- package/shared/types/stores/index.ts +3 -0
- package/shared/types/stores/settings.ts +15 -0
- package/shared/types/terminal/index.ts +44 -0
- package/shared/types/ui/components.ts +61 -0
- package/shared/types/ui/icons.ts +23 -0
- package/shared/types/ui/index.ts +22 -0
- package/shared/types/ui/notifications.ts +14 -0
- package/shared/types/ui/theme.ts +12 -0
- package/shared/types/websocket/index.ts +43 -0
- package/shared/types/window.d.ts +13 -0
- package/shared/utils/anonymous-user.ts +168 -0
- package/shared/utils/async.ts +10 -0
- package/shared/utils/diff-calculator.ts +184 -0
- package/shared/utils/file-type-detection.ts +166 -0
- package/shared/utils/logger.ts +158 -0
- package/shared/utils/message-formatter.ts +79 -0
- package/shared/utils/path.ts +47 -0
- package/shared/utils/ws-client.ts +768 -0
- package/shared/utils/ws-server.ts +660 -0
- package/static/audio/notification.ogg +0 -0
- package/static/favicon.svg +8 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
- package/static/fonts/dm-sans.css +96 -0
- package/svelte.config.js +20 -0
- package/vite.config.ts +33 -0
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Modern SDK-Based Input Area with File Upload Support
|
|
3
|
+
|
|
4
|
+
Features:
|
|
5
|
+
- Server-Sent Events streaming
|
|
6
|
+
- Real-time message display
|
|
7
|
+
- Enhanced error handling
|
|
8
|
+
- Modern AI-first UI design
|
|
9
|
+
- Proper cancellation support
|
|
10
|
+
- File upload support (images, PDFs, documents)
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
<script lang="ts">
|
|
14
|
+
import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
|
|
15
|
+
import { projectState } from '$frontend/lib/stores/core/projects.svelte';
|
|
16
|
+
import { appState } from '$frontend/lib/stores/core/app.svelte';
|
|
17
|
+
import { settings } from '$frontend/lib/stores/features/settings.svelte';
|
|
18
|
+
import { onDestroy, untrack } from 'svelte';
|
|
19
|
+
import { ChatService } from '$frontend/lib/services/chat';
|
|
20
|
+
import { chatService } from '$frontend/lib/services/chat/chat.service';
|
|
21
|
+
import { getEngineInfo } from '$shared/constants/engines';
|
|
22
|
+
import { modelStore } from '$frontend/lib/stores/features/models.svelte';
|
|
23
|
+
import { chatModelState } from '$frontend/lib/stores/ui/chat-model.svelte';
|
|
24
|
+
import { presenceState } from '$frontend/lib/stores/core/presence.svelte';
|
|
25
|
+
import { editModeState } from '$frontend/lib/stores/ui/edit-mode.svelte';
|
|
26
|
+
import { claudeAccountsStore } from '$frontend/lib/stores/features/claude-accounts.svelte';
|
|
27
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
28
|
+
import ws from '$frontend/lib/utils/ws';
|
|
29
|
+
import { debug } from '$shared/utils/logger';
|
|
30
|
+
|
|
31
|
+
// Components
|
|
32
|
+
import FileAttachmentPreview from './components/FileAttachmentPreview.svelte';
|
|
33
|
+
import EditModeIndicator from './components/EditModeIndicator.svelte';
|
|
34
|
+
import ChatInputActions from './components/ChatInputActions.svelte';
|
|
35
|
+
import LoadingIndicator from './components/LoadingIndicator.svelte';
|
|
36
|
+
import DragDropOverlay from './components/DragDropOverlay.svelte';
|
|
37
|
+
import EngineModelPicker from './components/EngineModelPicker.svelte';
|
|
38
|
+
|
|
39
|
+
// Composables
|
|
40
|
+
import { useFileHandling } from './composables/use-file-handling.svelte';
|
|
41
|
+
import { usePlaceholderAnimation, useLoadingTextAnimation } from './composables/use-animations.svelte';
|
|
42
|
+
import { useTextareaResize } from './composables/use-textarea-resize.svelte';
|
|
43
|
+
import { useChatActions } from './composables/use-chat-actions.svelte';
|
|
44
|
+
import { useInputState } from './composables/use-input-state.svelte';
|
|
45
|
+
|
|
46
|
+
let messageText = $state('');
|
|
47
|
+
let textareaElement: HTMLTextAreaElement;
|
|
48
|
+
let fileInputElement: HTMLInputElement;
|
|
49
|
+
|
|
50
|
+
// Initialize composables
|
|
51
|
+
const fileHandling = useFileHandling();
|
|
52
|
+
const placeholderTexts = ChatService.placeholderTexts;
|
|
53
|
+
const loadingTexts = ChatService.loadingTexts;
|
|
54
|
+
const placeholderAnimation = usePlaceholderAnimation(placeholderTexts);
|
|
55
|
+
const loadingTextAnimation = useLoadingTextAnimation(loadingTexts);
|
|
56
|
+
const textareaResize = useTextareaResize();
|
|
57
|
+
|
|
58
|
+
// Helper functions for composables
|
|
59
|
+
const setMessageText = (text: string) => {
|
|
60
|
+
messageText = text;
|
|
61
|
+
};
|
|
62
|
+
const getTextareaElement = () => textareaElement;
|
|
63
|
+
const adjustTextareaHeight = () =>
|
|
64
|
+
textareaResize.adjustTextareaHeight(textareaElement, messageText);
|
|
65
|
+
const focusTextarea = () => textareaElement?.focus();
|
|
66
|
+
|
|
67
|
+
// Chat actions params
|
|
68
|
+
const chatActionsParams = {
|
|
69
|
+
get messageText() {
|
|
70
|
+
return messageText;
|
|
71
|
+
},
|
|
72
|
+
get attachedFiles() {
|
|
73
|
+
return fileHandling.attachedFiles;
|
|
74
|
+
},
|
|
75
|
+
clearAllAttachments: fileHandling.clearAllAttachments,
|
|
76
|
+
adjustTextareaHeight,
|
|
77
|
+
focusTextarea,
|
|
78
|
+
startLoadingAnimation: loadingTextAnimation.startAnimation,
|
|
79
|
+
stopLoadingAnimation: loadingTextAnimation.stopAnimation,
|
|
80
|
+
clearDraft: () => inputState.clearDraft()
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Initialize input state management (restoration, template loading, sync)
|
|
84
|
+
// Declared before chatActions so clearDraft is available
|
|
85
|
+
const inputState = useInputState({
|
|
86
|
+
setMessageText,
|
|
87
|
+
getTextareaElement,
|
|
88
|
+
adjustTextareaHeight,
|
|
89
|
+
focusTextarea,
|
|
90
|
+
setAttachedFiles: (files) => {
|
|
91
|
+
fileHandling.attachedFiles = files;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const chatActions = useChatActions(chatActionsParams);
|
|
96
|
+
|
|
97
|
+
// Enhanced model info based on user settings
|
|
98
|
+
const modelInfo = $derived.by(() => {
|
|
99
|
+
const selectedModel = settings.selectedModel;
|
|
100
|
+
const model = modelStore.getById(selectedModel);
|
|
101
|
+
const modelName = model?.name || 'AI Assistant';
|
|
102
|
+
const engineInfo = getEngineInfo(settings.selectedEngine);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
name: modelName,
|
|
106
|
+
description: engineInfo?.description || 'AI-powered development assistant',
|
|
107
|
+
icon: 'lucide:brain-circuit' as IconName,
|
|
108
|
+
model: selectedModel
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Check if the current model supports file attachments
|
|
113
|
+
const modelSupportsAttachments = $derived.by(() => {
|
|
114
|
+
const model = modelStore.getById(chatModelState.model);
|
|
115
|
+
if (!model) return true; // Default to enabled if model not found
|
|
116
|
+
return model.capabilities.some(c => c.toLowerCase() === 'attachments');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Check if we're in welcome state (no messages)
|
|
120
|
+
const isWelcomeState = $derived(sessionState.messages.length === 0);
|
|
121
|
+
|
|
122
|
+
// Project-aware state
|
|
123
|
+
const hasActiveProject = $derived(projectState.currentProject !== null);
|
|
124
|
+
|
|
125
|
+
// Reason why chat input is blocked (aside from isLoading / no project)
|
|
126
|
+
const chatBlockedReason = $derived.by(() => {
|
|
127
|
+
const engine = chatModelState.engine;
|
|
128
|
+
if (engine === 'claude-code') {
|
|
129
|
+
if (claudeAccountsStore.loaded && claudeAccountsStore.accounts.length === 0) {
|
|
130
|
+
return 'no-claude-account' as const;
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
if (!chatModelState.model) {
|
|
134
|
+
return 'no-model' as const;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const isInputDisabled = $derived(appState.isLoading || !hasActiveProject || !!chatBlockedReason);
|
|
141
|
+
|
|
142
|
+
const chatPlaceholder = $derived.by(() => {
|
|
143
|
+
if (chatBlockedReason === 'no-claude-account') {
|
|
144
|
+
return 'No Claude Code account connected. Configure it in Settings → AI Engine → Claude Code → Accounts.';
|
|
145
|
+
}
|
|
146
|
+
if (chatBlockedReason === 'no-model') {
|
|
147
|
+
return 'No model selected. Please select a model to start chatting.';
|
|
148
|
+
}
|
|
149
|
+
return placeholderAnimation.placeholderText;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Wrapper functions for event handlers
|
|
153
|
+
const handleTextareaInput = () => {
|
|
154
|
+
textareaResize.handleTextareaInput(textareaElement, messageText);
|
|
155
|
+
// Sync input text to other collaborators (includes draft save)
|
|
156
|
+
inputState.emitInputSync(messageText, fileHandling.attachedFiles);
|
|
157
|
+
};
|
|
158
|
+
const handleKeyDown = (event: KeyboardEvent) =>
|
|
159
|
+
textareaResize.handleKeyDown(event, textareaElement, messageText);
|
|
160
|
+
const handleKeyPress = (event: KeyboardEvent) =>
|
|
161
|
+
chatActions.handleKeyPress(event, messageText, setMessageText);
|
|
162
|
+
const handleSendMessage = () => {
|
|
163
|
+
chatActions.sendMessage(messageText, setMessageText);
|
|
164
|
+
};
|
|
165
|
+
const handleCancelEdit = () => {
|
|
166
|
+
chatActions.handleCancelEdit();
|
|
167
|
+
messageText = '';
|
|
168
|
+
fileHandling.clearAllAttachments();
|
|
169
|
+
adjustTextareaHeight();
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// Reactive effect for placeholder animation
|
|
173
|
+
$effect(() => {
|
|
174
|
+
if (isWelcomeState) {
|
|
175
|
+
placeholderAnimation.startAnimation();
|
|
176
|
+
} else {
|
|
177
|
+
placeholderAnimation.setStaticPlaceholder('Continue the conversation...');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// Sync appState.isLoading from presence data (single source of truth for all users)
|
|
182
|
+
// Also fetch partial text and reconnect to stream for late-joining users / refresh
|
|
183
|
+
let lastCatchupProjectId: string | undefined;
|
|
184
|
+
let lastPresenceProjectId: string | undefined;
|
|
185
|
+
$effect(() => {
|
|
186
|
+
const projectId = projectState.currentProject?.id;
|
|
187
|
+
const sessionId = sessionState.currentSession?.id; // Reactive dep: retry catchup when session loads
|
|
188
|
+
if (!projectId) return;
|
|
189
|
+
|
|
190
|
+
// When switching projects, clear isCancelling from the previous project.
|
|
191
|
+
// The cancel is project-scoped: cancelling Project A must NOT block Project B.
|
|
192
|
+
// Also, after project switch the WS room changes, so the chat:cancelled event
|
|
193
|
+
// for the old project will never arrive — we must clear it here.
|
|
194
|
+
if (projectId !== lastPresenceProjectId) {
|
|
195
|
+
if (lastPresenceProjectId && appState.isCancelling) {
|
|
196
|
+
appState.isCancelling = false;
|
|
197
|
+
}
|
|
198
|
+
lastPresenceProjectId = projectId;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const status = presenceState.statuses.get(projectId);
|
|
202
|
+
// Check if the active stream is for the CURRENT session (not just any session in the project)
|
|
203
|
+
const hasActiveForSession = status?.streams?.some(
|
|
204
|
+
(s: any) => s.status === 'active' && s.chatSessionId === sessionId
|
|
205
|
+
) ?? false;
|
|
206
|
+
if (hasActiveForSession && !appState.isLoading) {
|
|
207
|
+
// Don't re-enable loading if user just cancelled locally
|
|
208
|
+
if (appState.isCancelling) return;
|
|
209
|
+
|
|
210
|
+
appState.isLoading = true;
|
|
211
|
+
|
|
212
|
+
// Catch up on active stream's partial text for late-joining users
|
|
213
|
+
// Only do this once per project switch to avoid repeated fetches
|
|
214
|
+
// Only attempt if session is available (may not be on initial load)
|
|
215
|
+
if (projectId !== lastCatchupProjectId && sessionId) {
|
|
216
|
+
lastCatchupProjectId = projectId;
|
|
217
|
+
catchupActiveStream(status);
|
|
218
|
+
}
|
|
219
|
+
} else if (hasActiveForSession && appState.isLoading && sessionId && projectId !== lastCatchupProjectId) {
|
|
220
|
+
// Session became available after loading was already set (e.g. page refresh)
|
|
221
|
+
// The first run set isLoading=true but couldn't catch up because session wasn't loaded yet
|
|
222
|
+
lastCatchupProjectId = projectId;
|
|
223
|
+
catchupActiveStream(status);
|
|
224
|
+
} else if (!hasActiveForSession && appState.isLoading && !appState.isCancelling) {
|
|
225
|
+
// Only clear loading if not in the middle of a cancel operation
|
|
226
|
+
appState.isLoading = false;
|
|
227
|
+
lastCatchupProjectId = undefined;
|
|
228
|
+
} else if (!hasActiveForSession && !appState.isLoading) {
|
|
229
|
+
// No active streams for this session — just reset catchup tracking.
|
|
230
|
+
lastCatchupProjectId = undefined;
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Fetch current stream state, inject partial text, and reconnect to live events
|
|
236
|
+
* for late-joining users (browser refresh, project switch, long absence)
|
|
237
|
+
*/
|
|
238
|
+
async function catchupActiveStream(status: any) {
|
|
239
|
+
if (!status?.streams?.length || !sessionState.currentSession?.id) return;
|
|
240
|
+
|
|
241
|
+
// Find the active stream
|
|
242
|
+
const activeStream = status.streams.find((s: any) => s.status === 'active');
|
|
243
|
+
if (!activeStream) return;
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const streamState = await ws.http('chat:background-state', {
|
|
247
|
+
chatSessionId: sessionState.currentSession.id
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
if (streamState && streamState.status === 'active' && streamState.processId) {
|
|
251
|
+
// Check if we already have a streaming message for this processId
|
|
252
|
+
const hasStreamingMessage = sessionState.messages.some(
|
|
253
|
+
(m: any) => m.type === 'stream_event' && m.processId === streamState.processId
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
if (!hasStreamingMessage) {
|
|
257
|
+
// Inject streaming message with the current partial text from the server
|
|
258
|
+
const streamingMessage = {
|
|
259
|
+
type: 'stream_event' as const,
|
|
260
|
+
processId: streamState.processId,
|
|
261
|
+
partialText: streamState.currentPartialText || '',
|
|
262
|
+
timestamp: new Date().toISOString()
|
|
263
|
+
};
|
|
264
|
+
(sessionState.messages as any[]).push(streamingMessage);
|
|
265
|
+
} else {
|
|
266
|
+
// Update existing stream_event with latest partial text
|
|
267
|
+
const existingMsg = sessionState.messages.find(
|
|
268
|
+
(m: any) => m.type === 'stream_event' && m.processId === streamState.processId
|
|
269
|
+
);
|
|
270
|
+
if (existingMsg && streamState.currentPartialText) {
|
|
271
|
+
(existingMsg as any).partialText = streamState.currentPartialText;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Reconnect to live stream events so future partials/messages/complete flow in
|
|
276
|
+
chatService.reconnectToStream(
|
|
277
|
+
sessionState.currentSession.id,
|
|
278
|
+
streamState.processId
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
debug.log('chat', 'Caught up with active stream:', {
|
|
282
|
+
processId: streamState.processId,
|
|
283
|
+
partialLength: streamState.currentPartialText?.length || 0
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
debug.error('chat', 'Failed to catch up with active stream:', error);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Sync loading animation with appState.isLoading (works for all users, not just sender)
|
|
292
|
+
$effect(() => {
|
|
293
|
+
if (appState.isLoading) {
|
|
294
|
+
loadingTextAnimation.startAnimation();
|
|
295
|
+
} else {
|
|
296
|
+
loadingTextAnimation.stopAnimation();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// When edit mode exits (remote cancel, user cancel), reset input and attachments
|
|
301
|
+
// Skip during project transition (isRestoring) - server restore will handle it
|
|
302
|
+
// Use untrack for isRestoring to avoid this effect re-running when isRestoring changes
|
|
303
|
+
let wasEditing = false;
|
|
304
|
+
$effect(() => {
|
|
305
|
+
const isEditing = editModeState.isEditing;
|
|
306
|
+
if (wasEditing && !isEditing) {
|
|
307
|
+
const restoring = untrack(() => appState.isRestoring);
|
|
308
|
+
if (!restoring) {
|
|
309
|
+
messageText = '';
|
|
310
|
+
fileHandling.clearAllAttachments();
|
|
311
|
+
setTimeout(() => adjustTextareaHeight(), 0);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
wasEditing = isEditing;
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Sync file attachments to other collaborators when they change
|
|
318
|
+
// Use untrack for isRestoring so this effect ONLY re-runs when files change,
|
|
319
|
+
// not when isRestoring transitions (which would emit stale data to new project)
|
|
320
|
+
$effect(() => {
|
|
321
|
+
// Access attachedFiles to create reactive dependency
|
|
322
|
+
const files = fileHandling.attachedFiles;
|
|
323
|
+
const restoring = untrack(() => appState.isRestoring);
|
|
324
|
+
if (!restoring) {
|
|
325
|
+
// Emit attachment sync with current text
|
|
326
|
+
inputState.emitAttachmentSync(messageText, files);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
onDestroy(() => {
|
|
331
|
+
fileHandling.clearAllAttachments();
|
|
332
|
+
});
|
|
333
|
+
</script>
|
|
334
|
+
|
|
335
|
+
<div class="">
|
|
336
|
+
<!-- Hidden file input -->
|
|
337
|
+
<input
|
|
338
|
+
bind:this={fileInputElement}
|
|
339
|
+
type="file"
|
|
340
|
+
multiple
|
|
341
|
+
accept={[...fileHandling.SUPPORTED_IMAGE_TYPES, ...fileHandling.SUPPORTED_DOCUMENT_TYPES].join(
|
|
342
|
+
','
|
|
343
|
+
)}
|
|
344
|
+
onchange={fileHandling.handleFileInputChange}
|
|
345
|
+
class="hidden"
|
|
346
|
+
/>
|
|
347
|
+
|
|
348
|
+
<!-- Input container with modern design -->
|
|
349
|
+
<div class="{isWelcomeState ? 'max-w-3xl mx-auto' : 'max-w-4xl mx-auto'} relative">
|
|
350
|
+
<!-- File attachments preview -->
|
|
351
|
+
<FileAttachmentPreview
|
|
352
|
+
attachedFiles={fileHandling.attachedFiles}
|
|
353
|
+
onRemove={fileHandling.removeAttachment}
|
|
354
|
+
/>
|
|
355
|
+
|
|
356
|
+
<!-- Main input area with drag and drop support -->
|
|
357
|
+
<div
|
|
358
|
+
class="
|
|
359
|
+
relative z-10 flex items-end gap-3 lg:gap-4 overflow-hidden bg-white dark:bg-slate-900
|
|
360
|
+
border border-slate-200 dark:border-slate-700 rounded-xl transition-all duration-200
|
|
361
|
+
focus-within:ring-2 focus-within:ring-violet-500 {fileHandling.isDragging && 'ring-2 ring-violet-500'}"
|
|
362
|
+
role="region"
|
|
363
|
+
aria-label="Message input with file drop zone"
|
|
364
|
+
ondragover={fileHandling.handleDragOver}
|
|
365
|
+
ondragleave={fileHandling.handleDragLeave}
|
|
366
|
+
ondrop={fileHandling.handleDrop}
|
|
367
|
+
>
|
|
368
|
+
<div class="flex-1">
|
|
369
|
+
<!-- Engine/Model Picker -->
|
|
370
|
+
<EngineModelPicker />
|
|
371
|
+
|
|
372
|
+
<!-- Edit Mode Indicator -->
|
|
373
|
+
<EditModeIndicator onCancel={handleCancelEdit} />
|
|
374
|
+
|
|
375
|
+
<div class="relative">
|
|
376
|
+
<textarea
|
|
377
|
+
bind:this={textareaElement}
|
|
378
|
+
bind:value={messageText}
|
|
379
|
+
placeholder={chatPlaceholder}
|
|
380
|
+
class="flex w-full p-4 pr-24 border-0 bg-transparent resize-none focus:outline-none text-slate-900 dark:text-slate-100 placeholder-slate-500 dark:placeholder-slate-400 text-base leading-relaxed disabled:opacity-50 disabled:cursor-not-allowed"
|
|
381
|
+
rows="1"
|
|
382
|
+
style="max-height: 22.5rem; overflow-y: hidden;"
|
|
383
|
+
disabled={isInputDisabled}
|
|
384
|
+
oninput={handleTextareaInput}
|
|
385
|
+
onkeydown={handleKeyDown}
|
|
386
|
+
onkeypress={handleKeyPress}
|
|
387
|
+
onpaste={fileHandling.handlePaste}
|
|
388
|
+
oncompositionstart={chatActions.handleCompositionStart}
|
|
389
|
+
oncompositionend={chatActions.handleCompositionEnd}
|
|
390
|
+
autocomplete="off"
|
|
391
|
+
></textarea>
|
|
392
|
+
|
|
393
|
+
<!-- Action buttons -->
|
|
394
|
+
<ChatInputActions
|
|
395
|
+
isLoading={appState.isLoading}
|
|
396
|
+
hasActiveProject={hasActiveProject}
|
|
397
|
+
messageText={messageText}
|
|
398
|
+
attachedFiles={fileHandling.attachedFiles}
|
|
399
|
+
isProcessingFiles={fileHandling.isProcessingFiles}
|
|
400
|
+
{modelSupportsAttachments}
|
|
401
|
+
onSend={handleSendMessage}
|
|
402
|
+
onCancel={chatActions.cancelRequest}
|
|
403
|
+
onAttachFile={() => fileHandling.handleFileSelect(fileInputElement)}
|
|
404
|
+
/>
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
</div>
|
|
408
|
+
|
|
409
|
+
<!-- Overlays -->
|
|
410
|
+
<DragDropOverlay
|
|
411
|
+
isDragging={fileHandling.isDragging}
|
|
412
|
+
isProcessingFiles={fileHandling.isProcessingFiles}
|
|
413
|
+
/>
|
|
414
|
+
|
|
415
|
+
<!-- Loading indicator -->
|
|
416
|
+
<LoadingIndicator
|
|
417
|
+
visibleLoadingText={loadingTextAnimation.visibleLoadingText}
|
|
418
|
+
isWelcomeState={isWelcomeState}
|
|
419
|
+
/>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import type { FileAttachment } from '../composables/use-file-handling.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
isLoading: boolean;
|
|
7
|
+
hasActiveProject: boolean;
|
|
8
|
+
messageText: string;
|
|
9
|
+
attachedFiles: FileAttachment[];
|
|
10
|
+
isProcessingFiles: boolean;
|
|
11
|
+
modelSupportsAttachments: boolean;
|
|
12
|
+
onSend: () => void;
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
onAttachFile: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
isLoading,
|
|
19
|
+
hasActiveProject,
|
|
20
|
+
messageText,
|
|
21
|
+
attachedFiles,
|
|
22
|
+
isProcessingFiles,
|
|
23
|
+
modelSupportsAttachments,
|
|
24
|
+
onSend,
|
|
25
|
+
onCancel,
|
|
26
|
+
onAttachFile
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
const attachDisabled = $derived(
|
|
30
|
+
isProcessingFiles || !hasActiveProject || !modelSupportsAttachments
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const attachTitle = $derived(
|
|
34
|
+
!modelSupportsAttachments
|
|
35
|
+
? 'File attachments are not supported by this model'
|
|
36
|
+
: 'Attach files'
|
|
37
|
+
);
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<div class="absolute bottom-2 right-2 flex items-center gap-1.5">
|
|
41
|
+
{#if !isLoading}
|
|
42
|
+
<!-- Attach file button -->
|
|
43
|
+
<button
|
|
44
|
+
onclick={onAttachFile}
|
|
45
|
+
disabled={attachDisabled}
|
|
46
|
+
class="w-8 h-8 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg flex items-center justify-center transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed group relative"
|
|
47
|
+
title={attachTitle}
|
|
48
|
+
aria-label={attachTitle}
|
|
49
|
+
>
|
|
50
|
+
<Icon name="lucide:paperclip" class="text-slate-600 dark:text-slate-400 w-4.5 h-4.5" />
|
|
51
|
+
{#if attachedFiles.length > 0}
|
|
52
|
+
<span class="absolute -top-1 -right-1 w-4 h-4 bg-violet-500 text-white text-xs rounded-full flex items-center justify-center">
|
|
53
|
+
{attachedFiles.length}
|
|
54
|
+
</span>
|
|
55
|
+
{/if}
|
|
56
|
+
</button>
|
|
57
|
+
{/if}
|
|
58
|
+
|
|
59
|
+
<!-- Send/Cancel button -->
|
|
60
|
+
{#if isLoading}
|
|
61
|
+
<button
|
|
62
|
+
onclick={onCancel}
|
|
63
|
+
class="w-10 h-10 bg-red-500 hover:bg-red-600 rounded-xl flex items-center justify-center transition-all duration-200 group"
|
|
64
|
+
aria-label="Cancel request"
|
|
65
|
+
>
|
|
66
|
+
<Icon name="lucide:circle-stop" class="text-white w-5 h-5" />
|
|
67
|
+
</button>
|
|
68
|
+
{:else}
|
|
69
|
+
<button
|
|
70
|
+
onclick={onSend}
|
|
71
|
+
disabled={(!messageText.trim() && attachedFiles.length === 0) || !hasActiveProject || isProcessingFiles}
|
|
72
|
+
class="w-10 h-10 bg-gradient-to-r from-violet-600 to-violet-600 hover:from-violet-700 hover:to-violet-700 disabled:from-slate-300 disabled:to-slate-300 dark:disabled:from-slate-700 dark:disabled:to-slate-700 rounded-xl flex items-center justify-center transition-all duration-200 disabled:cursor-not-allowed group"
|
|
73
|
+
aria-label="Send message"
|
|
74
|
+
>
|
|
75
|
+
<Icon name="lucide:send-horizontal" class="text-white w-5 h-5" />
|
|
76
|
+
</button>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
isDragging: boolean;
|
|
4
|
+
isProcessingFiles: boolean;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const { isDragging, isProcessingFiles }: Props = $props();
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<!-- Drag and drop overlay -->
|
|
11
|
+
{#if isDragging}
|
|
12
|
+
<div class="absolute inset-0 bg-violet-500/10 rounded-xl flex items-center justify-center pointer-events-none">
|
|
13
|
+
<div class="bg-white dark:bg-slate-800 px-4 py-2 rounded-lg shadow-lg border border-violet-500">
|
|
14
|
+
<span class="text-sm font-medium text-violet-700 dark:text-violet-300">Drop files here to attach</span>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
{/if}
|
|
18
|
+
|
|
19
|
+
<!-- Processing files overlay -->
|
|
20
|
+
{#if isProcessingFiles}
|
|
21
|
+
<div class="absolute top-0 left-0 right-0 -mt-10 flex justify-center">
|
|
22
|
+
<div class="bg-white dark:bg-slate-800 px-3 py-1.5 rounded-lg shadow-lg border border-slate-200 dark:border-slate-700 flex items-center gap-2">
|
|
23
|
+
<svg class="animate-spin h-3 w-3 text-violet-500" viewBox="0 0 24 24">
|
|
24
|
+
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
|
|
25
|
+
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
|
26
|
+
</svg>
|
|
27
|
+
<span class="text-xs text-slate-600 dark:text-slate-400">Processing files...</span>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
{/if}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import { editModeState } from '$frontend/lib/stores/ui/edit-mode.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { onCancel }: Props = $props();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
{#if editModeState.isEditing}
|
|
13
|
+
<div class="px-4 py-2 bg-amber-50 dark:bg-amber-900/20 border-b border-slate-200 dark:border-slate-600">
|
|
14
|
+
<div class="flex items-center justify-between">
|
|
15
|
+
<div class="flex flex-col gap-1">
|
|
16
|
+
<div class="flex items-center gap-2">
|
|
17
|
+
<Icon name="lucide:pencil" class="w-4 h-4 text-amber-600 dark:text-amber-400" />
|
|
18
|
+
<span class="text-sm text-amber-700 dark:text-amber-300 font-medium">
|
|
19
|
+
Editing message
|
|
20
|
+
</span>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
<button
|
|
24
|
+
onclick={onCancel}
|
|
25
|
+
class="px-2 py-1 text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-800/40 rounded-md text-sm font-medium transition-colors flex items-center gap-1 flex-shrink-0"
|
|
26
|
+
aria-label="Cancel edit"
|
|
27
|
+
>
|
|
28
|
+
<Icon name="lucide:x" class="w-3.5 h-3.5" />
|
|
29
|
+
Cancel
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
{/if}
|