@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,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Terminal Service (WebSocket Version)
|
|
3
|
+
* Handles terminal operations using WebSocket bi-directional communication
|
|
4
|
+
* Replaces SSE-based streaming with WebSocket events
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TerminalLine, TerminalSession, TerminalCommand } from '$shared/types/terminal';
|
|
8
|
+
import { terminalSessionManager, type TerminalSessionState } from './session.service';
|
|
9
|
+
import { backgroundTerminalService } from './background';
|
|
10
|
+
import ws from '$frontend/lib/utils/ws';
|
|
11
|
+
import { debug } from '$shared/utils/logger';
|
|
12
|
+
|
|
13
|
+
export interface TerminalConnectOptions {
|
|
14
|
+
sessionId: string;
|
|
15
|
+
workingDirectory?: string;
|
|
16
|
+
projectPath?: string;
|
|
17
|
+
projectId?: string;
|
|
18
|
+
terminalSize?: { cols: number; rows: number };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface StreamingResponse {
|
|
22
|
+
type: 'output' | 'error' | 'directory' | 'exit' | 'complete' | 'clear-screen';
|
|
23
|
+
content?: string;
|
|
24
|
+
newDirectory?: string;
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
projectId?: string;
|
|
27
|
+
timestamp?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class TerminalService {
|
|
31
|
+
private resizeEndpointAvailable: boolean | null = true; // WebSocket always available
|
|
32
|
+
private activeListeners = new Map<string, Array<() => void>>();
|
|
33
|
+
private lastOutputSeq = new Map<string, number>();
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Connect to persistent PTY session with streaming output via WebSocket
|
|
37
|
+
*/
|
|
38
|
+
async connectToSession(
|
|
39
|
+
options: TerminalConnectOptions,
|
|
40
|
+
onData: (data: StreamingResponse) => void
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
const { sessionId, workingDirectory, projectPath, projectId, terminalSize } = options;
|
|
43
|
+
|
|
44
|
+
debug.log('terminal', `🔌 Connecting to PTY session via WebSocket: ${sessionId}`);
|
|
45
|
+
|
|
46
|
+
// CRITICAL: Cleanup existing listeners BEFORE creating new ones
|
|
47
|
+
// This prevents duplicate event handlers when reconnecting to the same session
|
|
48
|
+
// (e.g., when switching between projects and coming back)
|
|
49
|
+
this.cleanupListeners(sessionId);
|
|
50
|
+
|
|
51
|
+
// Reset sequence tracking for fresh deduplication
|
|
52
|
+
this.lastOutputSeq.delete(sessionId);
|
|
53
|
+
|
|
54
|
+
// Get or create session state
|
|
55
|
+
const session = terminalSessionManager.getOrCreateSession(
|
|
56
|
+
sessionId,
|
|
57
|
+
projectId,
|
|
58
|
+
projectPath,
|
|
59
|
+
workingDirectory
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Create unique stream ID for this connection
|
|
63
|
+
const streamId = `${sessionId}-${Date.now()}`;
|
|
64
|
+
|
|
65
|
+
// Get current output count to mark where new output starts
|
|
66
|
+
let outputStartIndex = 0;
|
|
67
|
+
if (typeof window !== 'undefined') {
|
|
68
|
+
try {
|
|
69
|
+
const terminalStoreModule = await import('$frontend/lib/stores/features/terminal.svelte');
|
|
70
|
+
const termSession = terminalStoreModule.terminalStore.getSession(sessionId);
|
|
71
|
+
if (termSession && termSession.lines) {
|
|
72
|
+
outputStartIndex = termSession.lines.length;
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
// Ignore error, use default 0
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Setup WebSocket listeners for this session
|
|
80
|
+
const listeners: Array<() => void> = [];
|
|
81
|
+
|
|
82
|
+
// Listen for ready event
|
|
83
|
+
const unsubReady = ws.on('terminal:ready', (data) => {
|
|
84
|
+
if (data.sessionId === sessionId) {
|
|
85
|
+
debug.log('terminal', `✅ PTY session ready: ${sessionId} (PID: ${data.pid})`);
|
|
86
|
+
// Update session state with stream ID
|
|
87
|
+
terminalSessionManager.updateSession(sessionId, {
|
|
88
|
+
streamId: data.streamId,
|
|
89
|
+
processId: data.pid,
|
|
90
|
+
isExecuting: true
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
listeners.push(unsubReady);
|
|
95
|
+
|
|
96
|
+
// Listen for output (with sequence-based deduplication)
|
|
97
|
+
const unsubOutput = ws.on('terminal:output', (data) => {
|
|
98
|
+
if (data.sessionId === sessionId) {
|
|
99
|
+
// Deduplicate: skip if we've already seen this sequence number
|
|
100
|
+
// Multiple WS connections in the same project room can deliver
|
|
101
|
+
// the same terminal:output event multiple times
|
|
102
|
+
if (data.seq !== undefined && data.seq !== null) {
|
|
103
|
+
const lastSeq = this.lastOutputSeq.get(sessionId) || 0;
|
|
104
|
+
if (data.seq <= lastSeq) return;
|
|
105
|
+
this.lastOutputSeq.set(sessionId, data.seq);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
onData({
|
|
109
|
+
type: 'output',
|
|
110
|
+
content: data.content,
|
|
111
|
+
sessionId: data.sessionId,
|
|
112
|
+
projectId: data.projectId,
|
|
113
|
+
timestamp: data.timestamp
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
listeners.push(unsubOutput);
|
|
118
|
+
|
|
119
|
+
// Listen for directory changes
|
|
120
|
+
const unsubDirectory = ws.on('terminal:directory', (data) => {
|
|
121
|
+
if (data.sessionId === sessionId) {
|
|
122
|
+
terminalSessionManager.updateWorkingDirectory(sessionId, data.newDirectory);
|
|
123
|
+
onData({
|
|
124
|
+
type: 'directory',
|
|
125
|
+
newDirectory: data.newDirectory,
|
|
126
|
+
sessionId: data.sessionId
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
listeners.push(unsubDirectory);
|
|
131
|
+
|
|
132
|
+
// Listen for exit
|
|
133
|
+
const unsubExit = ws.on('terminal:exit', (data) => {
|
|
134
|
+
if (data.sessionId === sessionId) {
|
|
135
|
+
debug.log('terminal', `🏁 PTY session exited: ${sessionId} (code: ${data.exitCode})`);
|
|
136
|
+
|
|
137
|
+
// Clear stream info
|
|
138
|
+
backgroundTerminalService.endStream(sessionId, true);
|
|
139
|
+
|
|
140
|
+
onData({
|
|
141
|
+
type: 'exit',
|
|
142
|
+
content: data.exitCode === 0 ? 'success' : 'error',
|
|
143
|
+
sessionId: data.sessionId
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Cleanup listeners
|
|
147
|
+
this.cleanupListeners(sessionId);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
listeners.push(unsubExit);
|
|
151
|
+
|
|
152
|
+
// Listen for errors
|
|
153
|
+
const unsubError = ws.on('terminal:error', (data) => {
|
|
154
|
+
if (data.sessionId === sessionId) {
|
|
155
|
+
debug.error('terminal', `❌ PTY error for ${sessionId}:`, data.error);
|
|
156
|
+
onData({
|
|
157
|
+
type: 'error',
|
|
158
|
+
content: data.error,
|
|
159
|
+
sessionId: data.sessionId
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
listeners.push(unsubError);
|
|
164
|
+
|
|
165
|
+
// Store listeners for cleanup
|
|
166
|
+
this.activeListeners.set(sessionId, listeners);
|
|
167
|
+
|
|
168
|
+
// Create terminal session (now using HTTP pattern)
|
|
169
|
+
try {
|
|
170
|
+
const response = await ws.http('terminal:create-session', {
|
|
171
|
+
sessionId,
|
|
172
|
+
streamId,
|
|
173
|
+
workingDirectory: session.workingDirectory,
|
|
174
|
+
projectPath,
|
|
175
|
+
cols: terminalSize?.cols || 80,
|
|
176
|
+
rows: terminalSize?.rows || 24,
|
|
177
|
+
outputStartIndex
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
debug.log('terminal', `✅ Terminal session created:`, response);
|
|
181
|
+
|
|
182
|
+
// Update session with response data
|
|
183
|
+
terminalSessionManager.updateSession(sessionId, {
|
|
184
|
+
streamId: response.streamId,
|
|
185
|
+
processId: response.pid,
|
|
186
|
+
isExecuting: true
|
|
187
|
+
});
|
|
188
|
+
} catch (error) {
|
|
189
|
+
debug.error('terminal', `❌ Failed to create terminal session:`, error);
|
|
190
|
+
// Cleanup listeners on error
|
|
191
|
+
this.cleanupListeners(sessionId);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Send Ctrl+C interrupt signal to a specific session
|
|
198
|
+
* Always sends the signal regardless of execution state for utility/accessibility
|
|
199
|
+
*/
|
|
200
|
+
async cancelCommand(sessionId: string): Promise<boolean> {
|
|
201
|
+
const session = terminalSessionManager.getSession(sessionId);
|
|
202
|
+
if (!session) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
// Step 1: Cancel client-side state (if executing)
|
|
208
|
+
if (session.isExecuting) {
|
|
209
|
+
terminalSessionManager.cancelExecution(sessionId);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Step 2: Always send Ctrl+C signal via WebSocket
|
|
213
|
+
// This is useful as a utility shortcut even when not executing
|
|
214
|
+
try {
|
|
215
|
+
const data = await ws.http('terminal:cancel', { sessionId }, 10000);
|
|
216
|
+
if (data.sessionId === sessionId) {
|
|
217
|
+
// Clear stream info only if was executing
|
|
218
|
+
if (session.isExecuting) {
|
|
219
|
+
backgroundTerminalService.endStream(sessionId, true);
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
} catch {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
debug.error('terminal', 'Error sending Ctrl+C signal:', error);
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Resize terminal for a specific session
|
|
235
|
+
*/
|
|
236
|
+
async resizeTerminal(sessionId: string, cols: number, rows: number): Promise<boolean> {
|
|
237
|
+
try {
|
|
238
|
+
debug.log('terminal', `🔧 Resizing terminal ${sessionId} to ${cols}x${rows}`);
|
|
239
|
+
|
|
240
|
+
// Send resize request via WebSocket HTTP
|
|
241
|
+
await ws.http('terminal:resize', { sessionId, cols, rows });
|
|
242
|
+
return true;
|
|
243
|
+
} catch (error) {
|
|
244
|
+
debug.error('terminal', 'Error resizing terminal:', error);
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Send input to terminal session
|
|
251
|
+
*/
|
|
252
|
+
sendInput(sessionId: string, data: string): void {
|
|
253
|
+
debug.log('terminal', `⌨️ Sending input to ${sessionId}:`, data);
|
|
254
|
+
ws.emit('terminal:input', { sessionId, data });
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Kill terminal session completely
|
|
259
|
+
*/
|
|
260
|
+
async killSession(sessionId: string): Promise<boolean> {
|
|
261
|
+
try {
|
|
262
|
+
debug.log('terminal', `💀 Killing terminal session: ${sessionId}`);
|
|
263
|
+
|
|
264
|
+
// Cleanup listeners first
|
|
265
|
+
this.cleanupListeners(sessionId);
|
|
266
|
+
|
|
267
|
+
// Send kill request and wait for confirmation
|
|
268
|
+
try {
|
|
269
|
+
const data = await ws.http('terminal:kill-session', { sessionId }, 5000);
|
|
270
|
+
return data.sessionId === sessionId;
|
|
271
|
+
} catch {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
} catch (error) {
|
|
275
|
+
debug.error('terminal', 'Error killing session:', error);
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Check shell availability
|
|
282
|
+
*/
|
|
283
|
+
async checkShellAvailability(): Promise<{
|
|
284
|
+
available: boolean;
|
|
285
|
+
path: string | null;
|
|
286
|
+
platform: string;
|
|
287
|
+
isWindows: boolean;
|
|
288
|
+
shellType: string;
|
|
289
|
+
}> {
|
|
290
|
+
try {
|
|
291
|
+
return await ws.http('terminal:check-shell', {}, 5000);
|
|
292
|
+
} catch {
|
|
293
|
+
return {
|
|
294
|
+
available: false,
|
|
295
|
+
path: null,
|
|
296
|
+
platform: 'unknown',
|
|
297
|
+
isWindows: false,
|
|
298
|
+
shellType: 'Unknown'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Get missed output for a session
|
|
305
|
+
*/
|
|
306
|
+
async getMissedOutput(
|
|
307
|
+
sessionId: string,
|
|
308
|
+
streamId?: string,
|
|
309
|
+
fromIndex: number = 0
|
|
310
|
+
): Promise<{
|
|
311
|
+
success: boolean;
|
|
312
|
+
output: string[];
|
|
313
|
+
outputCount: number;
|
|
314
|
+
status: string;
|
|
315
|
+
}> {
|
|
316
|
+
try {
|
|
317
|
+
const data = await ws.http('terminal:missed-output', { sessionId, streamId, fromIndex }, 5000);
|
|
318
|
+
if (data.sessionId === sessionId) {
|
|
319
|
+
return {
|
|
320
|
+
success: true,
|
|
321
|
+
output: data.output,
|
|
322
|
+
outputCount: data.outputCount,
|
|
323
|
+
status: data.status
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
success: false,
|
|
328
|
+
output: [],
|
|
329
|
+
outputCount: 0,
|
|
330
|
+
status: 'invalid_session'
|
|
331
|
+
};
|
|
332
|
+
} catch {
|
|
333
|
+
return {
|
|
334
|
+
success: false,
|
|
335
|
+
output: [],
|
|
336
|
+
outputCount: 0,
|
|
337
|
+
status: 'timeout'
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Cleanup listeners for a session
|
|
344
|
+
*/
|
|
345
|
+
cleanupListeners(sessionId: string): void {
|
|
346
|
+
const listeners = this.activeListeners.get(sessionId);
|
|
347
|
+
if (listeners) {
|
|
348
|
+
listeners.forEach(unsub => unsub());
|
|
349
|
+
this.activeListeners.delete(sessionId);
|
|
350
|
+
this.lastOutputSeq.delete(sessionId);
|
|
351
|
+
debug.log('terminal', `🧹 Cleaned up listeners for ${sessionId}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Cleanup all listeners (call on component unmount)
|
|
357
|
+
*/
|
|
358
|
+
cleanup(): void {
|
|
359
|
+
this.activeListeners.forEach((listeners, sessionId) => {
|
|
360
|
+
listeners.forEach(unsub => unsub());
|
|
361
|
+
});
|
|
362
|
+
this.activeListeners.clear();
|
|
363
|
+
this.lastOutputSeq.clear();
|
|
364
|
+
debug.log('terminal', '🧹 Cleaned up all terminal listeners');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Export singleton instance
|
|
369
|
+
export const terminalService = new TerminalService();
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import ws from '$frontend/lib/utils/ws';
|
|
2
|
+
import { debug } from '$shared/utils/logger';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Core Application Store
|
|
6
|
+
* Main app state: UI, navigation, loading, errors
|
|
7
|
+
*
|
|
8
|
+
* State persistence: lastView saved to server via user:save-state
|
|
9
|
+
* No localStorage usage for view state
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
interface PageInfo {
|
|
13
|
+
title: string;
|
|
14
|
+
description: string;
|
|
15
|
+
actions?: import('svelte').Snippet;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface AppState {
|
|
19
|
+
// UI Navigation
|
|
20
|
+
currentView: string;
|
|
21
|
+
isLoading: boolean;
|
|
22
|
+
isRestoring: boolean;
|
|
23
|
+
isCancelling: boolean;
|
|
24
|
+
error: string | null;
|
|
25
|
+
|
|
26
|
+
// Page Information
|
|
27
|
+
pageInfo: PageInfo;
|
|
28
|
+
|
|
29
|
+
// App Loading State
|
|
30
|
+
isAppLoading: boolean;
|
|
31
|
+
isAppInitialized: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Core app state using Svelte 5 runes
|
|
35
|
+
export const appState = $state<AppState>({
|
|
36
|
+
// UI Navigation
|
|
37
|
+
currentView: 'chat',
|
|
38
|
+
isLoading: false,
|
|
39
|
+
isRestoring: false,
|
|
40
|
+
isCancelling: false,
|
|
41
|
+
error: null,
|
|
42
|
+
|
|
43
|
+
// Page Information
|
|
44
|
+
pageInfo: {
|
|
45
|
+
title: 'Claude Code',
|
|
46
|
+
description: '',
|
|
47
|
+
actions: undefined
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// App Loading State
|
|
51
|
+
isAppLoading: true,
|
|
52
|
+
isAppInitialized: false
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ========================================
|
|
56
|
+
// UI STATE MANAGEMENT
|
|
57
|
+
// ========================================
|
|
58
|
+
|
|
59
|
+
export function setLoading(loading: boolean) {
|
|
60
|
+
appState.isLoading = loading;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function setCurrentView(view: string) {
|
|
64
|
+
appState.currentView = view;
|
|
65
|
+
// Save current view to server (fire-and-forget)
|
|
66
|
+
ws.http('user:save-state', { key: 'lastView', value: view }).catch(err => {
|
|
67
|
+
debug.error('workspace', 'Error saving lastView to server:', err);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function setPageInfo(title: string, description?: string, actions?: import('svelte').Snippet) {
|
|
72
|
+
appState.pageInfo.title = title;
|
|
73
|
+
appState.pageInfo.description = description || '';
|
|
74
|
+
appState.pageInfo.actions = actions;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function setError(error: string | null) {
|
|
78
|
+
appState.error = error;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function clearError() {
|
|
82
|
+
appState.error = null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// App loading state management
|
|
86
|
+
export function setAppLoading(loading: boolean) {
|
|
87
|
+
appState.isAppLoading = loading;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function setAppInitialized() {
|
|
91
|
+
appState.isAppInitialized = true;
|
|
92
|
+
appState.isAppLoading = false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Restore last view from server-provided state.
|
|
97
|
+
* Called during initialization with state from user:restore-state.
|
|
98
|
+
*/
|
|
99
|
+
export function restoreLastView(lastView?: string | null) {
|
|
100
|
+
if (lastView) {
|
|
101
|
+
const validViews = ['chat', 'files', 'terminal', 'history', 'settings'];
|
|
102
|
+
if (validViews.includes(lastView)) {
|
|
103
|
+
appState.currentView = lastView;
|
|
104
|
+
return lastView;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return 'chat'; // Default fallback
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ========================================
|
|
111
|
+
// INITIALIZATION
|
|
112
|
+
// ========================================
|
|
113
|
+
|
|
114
|
+
export function initializeStore() {
|
|
115
|
+
// Initialize core app store
|
|
116
|
+
// Any initialization logic can be added here
|
|
117
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Files Store
|
|
3
|
+
* File explorer state management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { FileNode } from '$shared/types/filesystem';
|
|
7
|
+
|
|
8
|
+
interface FileState {
|
|
9
|
+
files: FileNode[];
|
|
10
|
+
selectedFile: FileNode | null;
|
|
11
|
+
expandedFolders: Set<string>;
|
|
12
|
+
isLoading: boolean;
|
|
13
|
+
error: string | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// File state using Svelte 5 runes
|
|
17
|
+
export const fileState = $state<FileState>({
|
|
18
|
+
files: [],
|
|
19
|
+
selectedFile: null,
|
|
20
|
+
expandedFolders: new Set<string>(),
|
|
21
|
+
isLoading: false,
|
|
22
|
+
error: null
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// ========================================
|
|
26
|
+
// FILE MANAGEMENT
|
|
27
|
+
// ========================================
|
|
28
|
+
|
|
29
|
+
export function setFiles(files: FileNode[]) {
|
|
30
|
+
fileState.files = files;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function setSelectedFile(file: FileNode | null) {
|
|
34
|
+
fileState.selectedFile = file;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function toggleFolderExpansion(folderPath: string) {
|
|
38
|
+
if (fileState.expandedFolders.has(folderPath)) {
|
|
39
|
+
fileState.expandedFolders.delete(folderPath);
|
|
40
|
+
} else {
|
|
41
|
+
fileState.expandedFolders.add(folderPath);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function expandFolder(folderPath: string) {
|
|
46
|
+
fileState.expandedFolders.add(folderPath);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function collapseFolder(folderPath: string) {
|
|
50
|
+
fileState.expandedFolders.delete(folderPath);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function clearFiles() {
|
|
54
|
+
fileState.files = [];
|
|
55
|
+
fileState.selectedFile = null;
|
|
56
|
+
fileState.expandedFolders.clear();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ========================================
|
|
60
|
+
// STATE MANAGEMENT
|
|
61
|
+
// ========================================
|
|
62
|
+
|
|
63
|
+
export function setLoading(loading: boolean) {
|
|
64
|
+
fileState.isLoading = loading;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function setError(error: string | null) {
|
|
68
|
+
fileState.error = error;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function clearError() {
|
|
72
|
+
fileState.error = null;
|
|
73
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presence Store
|
|
3
|
+
* Shared reactive state for project presence (active users)
|
|
4
|
+
* Subscribes once to projectStatusService, shared across all components
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { onMount } from 'svelte';
|
|
8
|
+
import { projectStatusService, type ProjectStatus } from '$frontend/lib/services/project/status.service';
|
|
9
|
+
import { userStore } from '$frontend/lib/stores/features/user.svelte';
|
|
10
|
+
|
|
11
|
+
// Shared reactive state
|
|
12
|
+
export const presenceState = $state<{
|
|
13
|
+
statuses: Map<string, ProjectStatus>;
|
|
14
|
+
}>({
|
|
15
|
+
statuses: new Map()
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
let subscribed = false;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Initialize presence subscription (call once at app level)
|
|
22
|
+
* Automatically excludes the current user from activeUsers
|
|
23
|
+
*/
|
|
24
|
+
export function initPresence() {
|
|
25
|
+
if (subscribed) return;
|
|
26
|
+
subscribed = true;
|
|
27
|
+
|
|
28
|
+
projectStatusService.onStatusUpdate((statuses) => {
|
|
29
|
+
const currentUserId = userStore.currentUser?.id;
|
|
30
|
+
const statusMap = new Map<string, ProjectStatus>();
|
|
31
|
+
statuses.forEach((status) => {
|
|
32
|
+
statusMap.set(status.projectId, {
|
|
33
|
+
...status,
|
|
34
|
+
activeUsers: currentUserId
|
|
35
|
+
? status.activeUsers.filter((u) => u.userId !== currentUserId)
|
|
36
|
+
: status.activeUsers
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
presenceState.statuses = statusMap;
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get presence status for a specific project
|
|
45
|
+
*/
|
|
46
|
+
export function getProjectPresence(projectId: string): ProjectStatus | undefined {
|
|
47
|
+
return presenceState.statuses.get(projectId);
|
|
48
|
+
}
|