@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,615 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import { sessionState, removeSession, setCurrentSession } from '$frontend/lib/stores/core/sessions.svelte';
|
|
4
|
+
import { projectState, setCurrentProject } from '$frontend/lib/stores/core/projects.svelte';
|
|
5
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
6
|
+
import { setCurrentView } from '$frontend/lib/stores/core/app.svelte';
|
|
7
|
+
import ws from '$frontend/lib/utils/ws';
|
|
8
|
+
import type { ChatSession, Project } from '$shared/types/database/schema';
|
|
9
|
+
import type { SDKMessage } from '$shared/types/messaging';
|
|
10
|
+
import Input from '../common/Input.svelte';
|
|
11
|
+
import Select from '../common/Select.svelte';
|
|
12
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
13
|
+
import PageTemplate from '../common/PageTemplate.svelte';
|
|
14
|
+
import Button from '../common/Button.svelte';
|
|
15
|
+
import { showConfirm } from '$frontend/lib/stores/ui/dialog.svelte';
|
|
16
|
+
import Modal from '$frontend/lib/components/common/Modal.svelte';
|
|
17
|
+
import TimelineModal from '../checkpoint/TimelineModal.svelte';
|
|
18
|
+
import { debug } from '$shared/utils/logger';
|
|
19
|
+
|
|
20
|
+
// Use real session data from session store
|
|
21
|
+
const sessions = $derived(sessionState.sessions);
|
|
22
|
+
|
|
23
|
+
// Helper to get project name from project ID
|
|
24
|
+
function getProjectName(projectId: string): string {
|
|
25
|
+
const project = projectState.projects.find(p => p.id === projectId);
|
|
26
|
+
return project?.name || 'Unknown Project';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Helper to get relative time (last active)
|
|
30
|
+
function getRelativeTime(dateString: string): string {
|
|
31
|
+
const now = Date.now();
|
|
32
|
+
const date = new Date(dateString).getTime();
|
|
33
|
+
const diffMs = now - date;
|
|
34
|
+
const diffMins = Math.floor(diffMs / 1000 / 60);
|
|
35
|
+
const diffHours = Math.floor(diffMins / 60);
|
|
36
|
+
const diffDays = Math.floor(diffHours / 24);
|
|
37
|
+
|
|
38
|
+
if (diffMins < 1) return 'Just now';
|
|
39
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
40
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
41
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
42
|
+
if (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`;
|
|
43
|
+
if (diffDays < 365) return `${Math.floor(diffDays / 30)}mo ago`;
|
|
44
|
+
return `${Math.floor(diffDays / 365)}y ago`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Helper to get last activity timestamp
|
|
48
|
+
function getLastActive(session: ChatSession): string {
|
|
49
|
+
// Use ended_at if available, otherwise use started_at
|
|
50
|
+
const timestamp = session.ended_at && session.ended_at !== ''
|
|
51
|
+
? session.ended_at
|
|
52
|
+
: session.started_at;
|
|
53
|
+
return getRelativeTime(timestamp);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Helper to calculate session duration in minutes
|
|
57
|
+
function getSessionDuration(session: ChatSession): number {
|
|
58
|
+
const start = new Date(session.started_at).getTime();
|
|
59
|
+
const end = session.ended_at ? new Date(session.ended_at).getTime() : Date.now();
|
|
60
|
+
return Math.round((end - start) / 1000 / 60);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Cache for session data to avoid multiple API calls
|
|
64
|
+
const sessionDataCache = $state<Record<string, {
|
|
65
|
+
messages: SDKMessage[];
|
|
66
|
+
title: string;
|
|
67
|
+
summary: string;
|
|
68
|
+
count: number;
|
|
69
|
+
userCount: number;
|
|
70
|
+
assistantCount: number;
|
|
71
|
+
}>>({});
|
|
72
|
+
let loadingSessionData = $state(true);
|
|
73
|
+
|
|
74
|
+
// Helper to get session data from cache or API
|
|
75
|
+
async function getSessionData(sessionId: string) {
|
|
76
|
+
if (sessionDataCache[sessionId]) {
|
|
77
|
+
return sessionDataCache[sessionId];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// Get messages from current HEAD checkpoint (active branch only)
|
|
82
|
+
const messages = await ws.http('messages:list', { session_id: sessionId });
|
|
83
|
+
|
|
84
|
+
// Get title from first user message in current HEAD
|
|
85
|
+
const firstUserMessage = messages.find((m: SDKMessage) => m.type === 'user');
|
|
86
|
+
let title = 'New Conversation';
|
|
87
|
+
if (firstUserMessage) {
|
|
88
|
+
// Handle content properly - it can be string or array of content blocks
|
|
89
|
+
let textContent = '';
|
|
90
|
+
if (typeof firstUserMessage.message.content === 'string') {
|
|
91
|
+
textContent = firstUserMessage.message.content;
|
|
92
|
+
} else if (Array.isArray(firstUserMessage.message.content)) {
|
|
93
|
+
// Extract text from content blocks
|
|
94
|
+
const textBlocks = firstUserMessage.message.content.filter((c: any) => c.type === 'text');
|
|
95
|
+
textContent = textBlocks.map((b: any) => 'text' in b ? b.text : '').join(' ');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (textContent) {
|
|
99
|
+
title = textContent.slice(0, 60) + (textContent.length > 60 ? '...' : '');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Get summary from last assistant message in current HEAD checkpoint
|
|
104
|
+
const assistantMessages = messages.filter((m: SDKMessage) => m.type === 'assistant');
|
|
105
|
+
let summary = 'No messages yet';
|
|
106
|
+
if (assistantMessages.length > 0) {
|
|
107
|
+
const lastMessage = assistantMessages[assistantMessages.length - 1];
|
|
108
|
+
const textBlocks = lastMessage.message.content.filter((c: any) => c.type === 'text');
|
|
109
|
+
if (textBlocks.length > 0) {
|
|
110
|
+
const fullText = textBlocks.map((b: any) => 'text' in b ? b.text : '').join(' ');
|
|
111
|
+
const cleanText = fullText.replace(/```[\s\S]*?```/g, '').trim();
|
|
112
|
+
summary = cleanText.slice(0, 150) + (cleanText.length > 150 ? '...' : '');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Count user and assistant messages in current HEAD checkpoint
|
|
117
|
+
// Filter out empty user messages (same as ChatInterface.svelte timeline logic)
|
|
118
|
+
const userMessages = messages.filter((m: SDKMessage) => {
|
|
119
|
+
if (m.type !== 'user') return false;
|
|
120
|
+
// Extract text content
|
|
121
|
+
let textContent = '';
|
|
122
|
+
if (typeof m.message.content === 'string') {
|
|
123
|
+
textContent = m.message.content;
|
|
124
|
+
} else if (Array.isArray(m.message.content)) {
|
|
125
|
+
const textBlocks = m.message.content.filter(c => c.type === 'text');
|
|
126
|
+
textContent = textBlocks.map(b => 'text' in b ? b.text : '').join(' ');
|
|
127
|
+
}
|
|
128
|
+
return textContent.trim().length > 0;
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
const totalBubbles = userMessages.length + assistantMessages.length; // Total message bubbles in chat
|
|
132
|
+
|
|
133
|
+
const data = {
|
|
134
|
+
messages,
|
|
135
|
+
title,
|
|
136
|
+
summary,
|
|
137
|
+
count: totalBubbles, // Total bubbles (user + assistant with non-empty content)
|
|
138
|
+
userCount: userMessages.length, // Number of chat sessions/exchanges (non-empty user messages)
|
|
139
|
+
assistantCount: assistantMessages.length
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
sessionDataCache[sessionId] = data;
|
|
143
|
+
debug.log('session', `Loaded session ${sessionId}:`, {
|
|
144
|
+
title,
|
|
145
|
+
totalMessages: messages.length,
|
|
146
|
+
userCount: userMessages.length,
|
|
147
|
+
assistantCount: assistantMessages.length,
|
|
148
|
+
totalBubbles: totalBubbles,
|
|
149
|
+
summary: summary.substring(0, 50)
|
|
150
|
+
});
|
|
151
|
+
return data;
|
|
152
|
+
} catch (error) {
|
|
153
|
+
debug.error('session', 'Error fetching session data:', error);
|
|
154
|
+
return {
|
|
155
|
+
messages: [],
|
|
156
|
+
title: 'New Conversation',
|
|
157
|
+
summary: 'No messages yet',
|
|
158
|
+
count: 0,
|
|
159
|
+
userCount: 0,
|
|
160
|
+
assistantCount: 0
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Helper functions that use cached data
|
|
166
|
+
function getMessageCount(sessionId: string): number {
|
|
167
|
+
return sessionDataCache[sessionId]?.count || 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getUserMessageCount(sessionId: string): number {
|
|
171
|
+
return sessionDataCache[sessionId]?.userCount || 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getSessionTitle(sessionId: string): string {
|
|
175
|
+
return sessionDataCache[sessionId]?.title || 'New Conversation';
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getSessionSummary(sessionId: string): string {
|
|
179
|
+
return sessionDataCache[sessionId]?.summary || 'No messages yet';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Preload session data for visible sessions
|
|
183
|
+
async function preloadSessionData() {
|
|
184
|
+
loadingSessionData = true;
|
|
185
|
+
try {
|
|
186
|
+
// Load all sessions in parallel for better performance
|
|
187
|
+
await Promise.all(
|
|
188
|
+
sessions.slice(0, 20).map(session => getSessionData(session.id))
|
|
189
|
+
);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
debug.error('session', 'Error preloading session data:', error);
|
|
192
|
+
} finally {
|
|
193
|
+
loadingSessionData = false;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Initialize session data on mount
|
|
198
|
+
onMount(() => {
|
|
199
|
+
preloadSessionData();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// Reload session data when sessions change
|
|
203
|
+
$effect(() => {
|
|
204
|
+
if (sessions.length > 0 && !loadingSessionData) {
|
|
205
|
+
// Check if there are sessions without cached data
|
|
206
|
+
const uncachedSessions = sessions.filter(s => !sessionDataCache[s.id]);
|
|
207
|
+
if (uncachedSessions.length > 0) {
|
|
208
|
+
debug.log('session', `Found ${uncachedSessions.length} uncached sessions, loading...`);
|
|
209
|
+
preloadSessionData();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
let searchQuery = $state('');
|
|
215
|
+
let projectFilter = $state('all');
|
|
216
|
+
let showTimelineModal = $state(false);
|
|
217
|
+
let timelineSession = $state<ChatSession | null>(null);
|
|
218
|
+
|
|
219
|
+
function openTimelineModal(session: ChatSession) {
|
|
220
|
+
timelineSession = session;
|
|
221
|
+
showTimelineModal = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function closeTimelineModal() {
|
|
225
|
+
timelineSession = null;
|
|
226
|
+
showTimelineModal = false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Get unique projects
|
|
230
|
+
const projects = $derived(
|
|
231
|
+
Array.from(new Set(sessions.map(s => getProjectName(s.project_id))))
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Filtered sessions (sorted newest first)
|
|
235
|
+
const filteredSessions = $derived(
|
|
236
|
+
sessions
|
|
237
|
+
.filter(session => {
|
|
238
|
+
const title = getSessionTitle(session.id);
|
|
239
|
+
const summary = getSessionSummary(session.id);
|
|
240
|
+
const projectName = getProjectName(session.project_id);
|
|
241
|
+
const messageCount = getMessageCount(session.id);
|
|
242
|
+
|
|
243
|
+
// Filter out sessions with 0 messages
|
|
244
|
+
if (messageCount === 0) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const matchesSearch = searchQuery === '' ||
|
|
249
|
+
title.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
250
|
+
summary.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
|
251
|
+
projectName.toLowerCase().includes(searchQuery.toLowerCase());
|
|
252
|
+
|
|
253
|
+
const matchesProject = projectFilter === 'all' || projectName === projectFilter;
|
|
254
|
+
|
|
255
|
+
return matchesSearch && matchesProject;
|
|
256
|
+
})
|
|
257
|
+
.sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime())
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Session stats (only count sessions with messages)
|
|
261
|
+
const sessionsWithMessages = $derived(sessions.filter(s => getMessageCount(s.id) > 0));
|
|
262
|
+
|
|
263
|
+
const stats = $derived({
|
|
264
|
+
totalSessions: sessionsWithMessages.length,
|
|
265
|
+
totalChats: sessionsWithMessages.reduce((sum, s) => sum + getUserMessageCount(s.id), 0),
|
|
266
|
+
totalMessages: sessionsWithMessages.reduce((sum, s) => sum + getMessageCount(s.id), 0),
|
|
267
|
+
totalDuration: sessionsWithMessages.reduce((sum, s) => sum + getSessionDuration(s), 0)
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
function formatDuration(minutes: number): string {
|
|
271
|
+
const hours = Math.floor(minutes / 60);
|
|
272
|
+
const mins = minutes % 60;
|
|
273
|
+
if (hours > 0) {
|
|
274
|
+
return `${hours}h ${mins}m`;
|
|
275
|
+
}
|
|
276
|
+
return `${mins}m`;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function formatDate(dateString: string): string {
|
|
280
|
+
return new Date(dateString).toLocaleDateString('en-US', {
|
|
281
|
+
month: 'short',
|
|
282
|
+
day: 'numeric',
|
|
283
|
+
year: 'numeric'
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Helper to check if session is currently active
|
|
288
|
+
function isActiveSession(session: ChatSession): boolean {
|
|
289
|
+
return sessionState.currentSession?.id === session.id;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
async function resumeSession(session: ChatSession | null) {
|
|
293
|
+
if (!session) return;
|
|
294
|
+
|
|
295
|
+
const sessionData = await getSessionData(session.id);
|
|
296
|
+
const title = sessionData.title;
|
|
297
|
+
const projectName = getProjectName(session.project_id);
|
|
298
|
+
|
|
299
|
+
const confirmed = await showConfirm({
|
|
300
|
+
title: 'Resume Session',
|
|
301
|
+
message: `Resume session "${title}" from project "${projectName}"?\n\nThis will load the conversation and navigate to the chat view.`,
|
|
302
|
+
type: 'info',
|
|
303
|
+
confirmText: 'Resume',
|
|
304
|
+
cancelText: 'Cancel'
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
if (!confirmed) return;
|
|
308
|
+
|
|
309
|
+
try {
|
|
310
|
+
// Reactivate if ended (does NOT end other sessions — multi-session parallel)
|
|
311
|
+
let targetSession = session;
|
|
312
|
+
if (session.ended_at) {
|
|
313
|
+
const reactivatedSession = await ws.http('sessions:update', { id: session.id, reactivate: true });
|
|
314
|
+
if (reactivatedSession) {
|
|
315
|
+
const sessionIndex = sessionState.sessions.findIndex(s => s.id === session.id);
|
|
316
|
+
if (sessionIndex !== -1) {
|
|
317
|
+
sessionState.sessions[sessionIndex] = reactivatedSession;
|
|
318
|
+
}
|
|
319
|
+
targetSession = reactivatedSession;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Find the project
|
|
324
|
+
const project = projectState.projects.find(p => p.id === session.project_id);
|
|
325
|
+
if (project) {
|
|
326
|
+
// Set current project and session
|
|
327
|
+
setCurrentProject(project);
|
|
328
|
+
await setCurrentSession(targetSession);
|
|
329
|
+
// Navigate to chat view
|
|
330
|
+
setCurrentView('chat');
|
|
331
|
+
} else {
|
|
332
|
+
addNotification({
|
|
333
|
+
type: 'error',
|
|
334
|
+
title: 'Project Not Found',
|
|
335
|
+
message: 'The project for this session could not be found',
|
|
336
|
+
duration: 4000
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
} catch (error) {
|
|
340
|
+
debug.error('session', 'Error resuming session:', error);
|
|
341
|
+
addNotification({
|
|
342
|
+
type: 'error',
|
|
343
|
+
title: 'Resume Failed',
|
|
344
|
+
message: 'Failed to resume session',
|
|
345
|
+
duration: 5000
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function deleteSession(session: ChatSession) {
|
|
351
|
+
const sessionData = await getSessionData(session.id);
|
|
352
|
+
const title = sessionData.title;
|
|
353
|
+
|
|
354
|
+
const confirmed = await showConfirm({
|
|
355
|
+
title: 'Delete Session',
|
|
356
|
+
message: `Are you sure you want to delete session "${title}"? This action cannot be undone.`,
|
|
357
|
+
type: 'error',
|
|
358
|
+
confirmText: 'Delete',
|
|
359
|
+
cancelText: 'Cancel'
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
if (confirmed) {
|
|
363
|
+
try {
|
|
364
|
+
// Delete from database via WebSocket
|
|
365
|
+
await ws.http('sessions:delete', { id: session.id });
|
|
366
|
+
// Remove from local state
|
|
367
|
+
removeSession(session.id);
|
|
368
|
+
// Clear cache
|
|
369
|
+
delete sessionDataCache[session.id];
|
|
370
|
+
// User already knows session was deleted from UI update
|
|
371
|
+
} catch (error) {
|
|
372
|
+
addNotification({
|
|
373
|
+
|
|
374
|
+
type: 'error',
|
|
375
|
+
title: 'Error',
|
|
376
|
+
message: 'Failed to delete session',
|
|
377
|
+
duration: 5000
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function clearHistory() {
|
|
384
|
+
const confirmed = await showConfirm({
|
|
385
|
+
title: 'Clear All Session History',
|
|
386
|
+
message: 'Are you sure you want to clear all session history? This will delete all sessions. This action cannot be undone.',
|
|
387
|
+
type: 'error',
|
|
388
|
+
confirmText: 'Clear All',
|
|
389
|
+
cancelText: 'Cancel'
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
if (!confirmed) return;
|
|
393
|
+
|
|
394
|
+
if (sessions.length === 0) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
// Delete all sessions from database
|
|
400
|
+
const deletePromises = sessions.map(async (session) => {
|
|
401
|
+
try {
|
|
402
|
+
await ws.http('sessions:delete', { id: session.id });
|
|
403
|
+
// Remove from local state
|
|
404
|
+
removeSession(session.id);
|
|
405
|
+
// Clear cache
|
|
406
|
+
delete sessionDataCache[session.id];
|
|
407
|
+
return { success: true };
|
|
408
|
+
} catch (error) {
|
|
409
|
+
debug.error('session', `Error deleting session ${session.id}:`, error);
|
|
410
|
+
return { success: false, error: 'Failed to delete session' };
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
// Wait for all deletions to complete
|
|
415
|
+
const results = await Promise.all(deletePromises);
|
|
416
|
+
|
|
417
|
+
// Check if any deletions failed
|
|
418
|
+
const failed = results.filter(r => !r.success);
|
|
419
|
+
if (failed.length > 0) {
|
|
420
|
+
addNotification({
|
|
421
|
+
type: 'warning',
|
|
422
|
+
title: 'Partial Deletion',
|
|
423
|
+
message: `Failed to delete ${failed.length} session(s)`,
|
|
424
|
+
duration: 5000
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
} catch (error) {
|
|
428
|
+
debug.error('session', 'Error clearing history:', error);
|
|
429
|
+
addNotification({
|
|
430
|
+
type: 'error',
|
|
431
|
+
title: 'Clear Failed',
|
|
432
|
+
message: 'Failed to clear session history',
|
|
433
|
+
duration: 5000
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
</script>
|
|
438
|
+
|
|
439
|
+
<PageTemplate
|
|
440
|
+
title="Session History"
|
|
441
|
+
description="Previous conversation sessions"
|
|
442
|
+
>
|
|
443
|
+
{#snippet actions()}
|
|
444
|
+
<Button variant="outline" onclick={clearHistory} class="rounded-lg px-2 sm:px-4 py-1.5 sm:py-2">
|
|
445
|
+
<Icon name="lucide:trash-2" class="w-3.5 h-3.5 sm:w-4 sm:h-4 sm:mr-2" />
|
|
446
|
+
<span class="hidden sm:inline">Clear History</span>
|
|
447
|
+
</Button>
|
|
448
|
+
{/snippet}
|
|
449
|
+
|
|
450
|
+
<div class="flex flex-col h-full gap-6">
|
|
451
|
+
<!-- Modern Stats -->
|
|
452
|
+
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
453
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
|
|
454
|
+
<div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalSessions}</div>
|
|
455
|
+
<div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Sessions</div>
|
|
456
|
+
</div>
|
|
457
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
|
|
458
|
+
<div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalChats}</div>
|
|
459
|
+
<div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Chats</div>
|
|
460
|
+
</div>
|
|
461
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
|
|
462
|
+
<div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalMessages}</div>
|
|
463
|
+
<div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Messages</div>
|
|
464
|
+
</div>
|
|
465
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
|
|
466
|
+
<div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{formatDuration(stats.totalDuration)}</div>
|
|
467
|
+
<div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Duration</div>
|
|
468
|
+
</div>
|
|
469
|
+
</div>
|
|
470
|
+
|
|
471
|
+
<!-- Modern Filters -->
|
|
472
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4">
|
|
473
|
+
<div class="flex flex-col lg:flex-row gap-4">
|
|
474
|
+
<!-- Search -->
|
|
475
|
+
<div class="flex-1">
|
|
476
|
+
<Input
|
|
477
|
+
bind:value={searchQuery}
|
|
478
|
+
placeholder="Search sessions by title, summary, or project..."
|
|
479
|
+
type="search"
|
|
480
|
+
/>
|
|
481
|
+
</div>
|
|
482
|
+
|
|
483
|
+
<!-- Modern Project Filter -->
|
|
484
|
+
<Select
|
|
485
|
+
bind:value={projectFilter}
|
|
486
|
+
placeholder="All Projects"
|
|
487
|
+
options={[
|
|
488
|
+
{ value: "all", label: "All Projects" },
|
|
489
|
+
...projects.map(project => ({ value: project, label: project }))
|
|
490
|
+
]}
|
|
491
|
+
class="bg-slate-50 dark:bg-slate-800 border-slate-200 dark:border-slate-700 rounded-lg"
|
|
492
|
+
/>
|
|
493
|
+
</div>
|
|
494
|
+
</div>
|
|
495
|
+
|
|
496
|
+
<!-- Modern Sessions List -->
|
|
497
|
+
<div class="flex-1 overflow-auto">
|
|
498
|
+
{#if loadingSessionData}
|
|
499
|
+
<div class="flex items-center justify-center py-12">
|
|
500
|
+
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-violet-600"></div>
|
|
501
|
+
<span class="ml-3 text-slate-600 dark:text-slate-400">Loading session data...</span>
|
|
502
|
+
</div>
|
|
503
|
+
{:else if filteredSessions.length === 0}
|
|
504
|
+
<div class="flex items-center justify-center h-full">
|
|
505
|
+
<div class="text-center">
|
|
506
|
+
<div class="bg-slate-100 dark:bg-slate-800 rounded-full w-24 h-24 flex items-center justify-center mx-auto mb-6">
|
|
507
|
+
<Icon name="lucide:clock" class="w-12 h-12 text-slate-400" />
|
|
508
|
+
</div>
|
|
509
|
+
<h3 class="text-lg font-bold bg-gradient-to-r from-violet-600 to-violet-600 bg-clip-text text-transparent mb-2">No sessions found</h3>
|
|
510
|
+
<p class="text-slate-600 dark:text-slate-400">
|
|
511
|
+
{searchQuery ? 'Try adjusting your search criteria' : 'Start a new chat session to see it here'}
|
|
512
|
+
</p>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
{:else}
|
|
516
|
+
<div class="space-y-4">
|
|
517
|
+
{#each filteredSessions as session (session.id)}
|
|
518
|
+
<div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-6 transition-all duration-200">
|
|
519
|
+
<div class="flex flex-col lg:flex-row lg:items-center gap-6">
|
|
520
|
+
<!-- Session Info -->
|
|
521
|
+
<div class="flex-1 space-y-2">
|
|
522
|
+
<!-- Title and Badges -->
|
|
523
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
524
|
+
<h3 class="font-bold text-violet-700 dark:text-violet-500">
|
|
525
|
+
{getSessionTitle(session.id)}
|
|
526
|
+
</h3>
|
|
527
|
+
{#if isActiveSession(session)}
|
|
528
|
+
<span class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 border border-green-200 dark:border-green-700">
|
|
529
|
+
<Icon name="lucide:circle-check" class="w-3 h-3" />
|
|
530
|
+
Active
|
|
531
|
+
</span>
|
|
532
|
+
{/if}
|
|
533
|
+
<span class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300">
|
|
534
|
+
<Icon name="lucide:clock" class="w-3 h-3" />
|
|
535
|
+
{getLastActive(session)}
|
|
536
|
+
</span>
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
<!-- Metadata - Responsive Flex Wrap -->
|
|
540
|
+
<div class="flex flex-wrap gap-x-3 sm:gap-x-4 gap-y-1 text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
|
541
|
+
<span class="font-semibold text-slate-600 dark:text-slate-400">
|
|
542
|
+
{getProjectName(session.project_id)}
|
|
543
|
+
</span>
|
|
544
|
+
•
|
|
545
|
+
<span class="flex items-center gap-1">
|
|
546
|
+
<Icon name="lucide:calendar" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
|
547
|
+
{formatDate(session.started_at)}
|
|
548
|
+
</span>
|
|
549
|
+
<span class="flex items-center gap-1" title="Chat exchanges">
|
|
550
|
+
<Icon name="lucide:messages-square" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
|
551
|
+
{getUserMessageCount(session.id)} chats
|
|
552
|
+
</span>
|
|
553
|
+
<span class="flex items-center gap-1" title="Total message bubbles">
|
|
554
|
+
<Icon name="lucide:message-circle" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
|
555
|
+
{getMessageCount(session.id)} msgs
|
|
556
|
+
</span>
|
|
557
|
+
<span class="flex items-center gap-1">
|
|
558
|
+
<Icon name="lucide:timer" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
|
|
559
|
+
{formatDuration(getSessionDuration(session))}
|
|
560
|
+
</span>
|
|
561
|
+
</div>
|
|
562
|
+
|
|
563
|
+
<!-- Summary -->
|
|
564
|
+
<p class="text-sm text-slate-600 dark:text-slate-400 line-clamp-2">
|
|
565
|
+
{getSessionSummary(session.id)}
|
|
566
|
+
</p>
|
|
567
|
+
|
|
568
|
+
</div>
|
|
569
|
+
|
|
570
|
+
<!-- Modern Actions -->
|
|
571
|
+
<div class="flex flex-wrap sm:flex-nowrap items-center gap-2 sm:gap-3 w-full lg:w-auto">
|
|
572
|
+
<button
|
|
573
|
+
onclick={() => openTimelineModal(session)}
|
|
574
|
+
class="flex-1 sm:flex-initial px-3 sm:px-4 py-2 bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-700 text-purple-700 dark:text-purple-300 rounded-lg hover:bg-purple-100 dark:hover:bg-purple-900/30 transition-all duration-200 flex items-center justify-center gap-1.5 sm:gap-2 text-xs sm:text-sm"
|
|
575
|
+
title="View checkpoint timeline"
|
|
576
|
+
>
|
|
577
|
+
<Icon name="lucide:git-branch" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
|
578
|
+
<span>Timeline</span>
|
|
579
|
+
</button>
|
|
580
|
+
|
|
581
|
+
<button
|
|
582
|
+
onclick={() => resumeSession(session)}
|
|
583
|
+
class="flex-1 sm:flex-initial px-3 sm:px-4 py-2 bg-violet-600 dark:bg-violet-600 border border-violet-600 dark:border-violet-600 text-white rounded-lg hover:bg-violet-700 dark:hover:bg-violet-700 hover:border-violet-700 dark:hover:border-violet-700 transition-all duration-200 flex items-center justify-center gap-1.5 sm:gap-2 text-xs sm:text-sm"
|
|
584
|
+
>
|
|
585
|
+
<Icon name="lucide:message-circle-code" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
|
586
|
+
<span>Resume</span>
|
|
587
|
+
</button>
|
|
588
|
+
|
|
589
|
+
<button
|
|
590
|
+
onclick={() => deleteSession(session)}
|
|
591
|
+
class="flex p-2.5 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 hover:border-red-200 dark:hover:border-red-700 rounded-lg transition-all duration-200"
|
|
592
|
+
title="Delete session"
|
|
593
|
+
aria-label="Delete session"
|
|
594
|
+
>
|
|
595
|
+
<Icon name="lucide:trash-2" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
|
|
596
|
+
</button>
|
|
597
|
+
</div>
|
|
598
|
+
</div>
|
|
599
|
+
</div>
|
|
600
|
+
{/each}
|
|
601
|
+
</div>
|
|
602
|
+
{/if}
|
|
603
|
+
</div>
|
|
604
|
+
</div>
|
|
605
|
+
</PageTemplate>
|
|
606
|
+
|
|
607
|
+
<!-- Timeline Modal (Readonly) -->
|
|
608
|
+
{#if timelineSession}
|
|
609
|
+
<TimelineModal
|
|
610
|
+
bind:isOpen={showTimelineModal}
|
|
611
|
+
onClose={closeTimelineModal}
|
|
612
|
+
sessionIdOverride={timelineSession.id}
|
|
613
|
+
readonly={true}
|
|
614
|
+
/>
|
|
615
|
+
{/if}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Component exports
|
|
2
|
+
export { default as ThemeToggle } from './common/ThemeToggle.svelte';
|
|
3
|
+
export { default as LoadingSpinner } from './common/LoadingSpinner.svelte';
|
|
4
|
+
export { default as Button } from './common/Button.svelte';
|
|
5
|
+
export { default as Input } from './common/Input.svelte';
|
|
6
|
+
export { default as Card } from './common/Card.svelte';
|
|
7
|
+
export { default as ModelSelector } from './common/ModelSelector.svelte';
|
|
8
|
+
export { default as MonacoEditor } from './common/MonacoEditor.svelte';
|
|
9
|
+
export { default as PageTemplate } from './common/PageTemplate.svelte';
|
|
10
|
+
export { default as Modal } from './common/Modal.svelte';
|
|
11
|
+
|
|
12
|
+
// Chat components
|
|
13
|
+
export { default as ChatMessages } from './chat/message/ChatMessages.svelte';
|
|
14
|
+
export { default as ChatInterface } from './chat/ChatInterface.svelte';
|
|
15
|
+
export { default as ChatMessage } from './chat/message/ChatMessage.svelte';
|
|
16
|
+
export { default as ChatInput } from './chat/input/ChatInput.svelte';
|
|
17
|
+
export { default as DateSeparator } from './chat/message/DateSeparator.svelte';
|
|
18
|
+
|
|
19
|
+
// Tool Display components - exported for potential external use
|
|
20
|
+
export * from './chat/tools';
|
|
21
|
+
|
|
22
|
+
// File components
|
|
23
|
+
export { default as FileTree } from './files/FileTree.svelte';
|
|
24
|
+
export { default as FileNode } from './files/FileNode.svelte';
|
|
25
|
+
export { default as FileViewer } from './files/FileViewer.svelte';
|
|
26
|
+
|
|
27
|
+
// Terminal components
|
|
28
|
+
export { default as TerminalView } from './terminal/TerminalView.svelte';
|
|
29
|
+
export { default as Terminal } from './terminal/Terminal.svelte';
|
|
30
|
+
export { default as TerminalTabs } from './terminal/TerminalTabs.svelte';
|
|
31
|
+
|
|
32
|
+
// View components
|
|
33
|
+
export { default as HistoryView } from './history/HistoryView.svelte';
|
|
34
|
+
export { default as SettingsView } from './settings/SettingsView.svelte';
|