@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,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for visualizing tree hierarchy structure
|
|
3
|
+
* Used for debugging and analyzing checkpoint/message tree structure
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DatabaseMessage } from '$shared/types/database/schema';
|
|
7
|
+
import { debug } from '$shared/utils/logger';
|
|
8
|
+
|
|
9
|
+
export interface TreeNode {
|
|
10
|
+
id: string;
|
|
11
|
+
messageText: string;
|
|
12
|
+
timestamp: string;
|
|
13
|
+
branchId: string | null;
|
|
14
|
+
parentId: string | null;
|
|
15
|
+
children: TreeNode[];
|
|
16
|
+
level: number; // Tree level (0 = main, 1 = branch from main, 2 = branch from branch, etc.)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build tree structure from messages
|
|
21
|
+
*/
|
|
22
|
+
export function buildTreeFromMessages(messages: DatabaseMessage[]): TreeNode[] {
|
|
23
|
+
const nodeMap = new Map<string, TreeNode>();
|
|
24
|
+
const roots: TreeNode[] = [];
|
|
25
|
+
|
|
26
|
+
// Create nodes
|
|
27
|
+
messages.forEach(msg => {
|
|
28
|
+
const sdkMessage = JSON.parse(msg.sdk_message);
|
|
29
|
+
|
|
30
|
+
// Only process user messages (checkpoints)
|
|
31
|
+
if (sdkMessage.type !== 'user') return;
|
|
32
|
+
|
|
33
|
+
const messageText = extractMessageText(sdkMessage);
|
|
34
|
+
if (!messageText.trim()) return;
|
|
35
|
+
|
|
36
|
+
nodeMap.set(msg.id, {
|
|
37
|
+
id: msg.id,
|
|
38
|
+
messageText: messageText.trim().slice(0, 50),
|
|
39
|
+
timestamp: msg.timestamp,
|
|
40
|
+
branchId: msg.branch_id || null,
|
|
41
|
+
parentId: msg.parent_message_id || null,
|
|
42
|
+
children: [],
|
|
43
|
+
level: 0
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Build tree structure
|
|
48
|
+
nodeMap.forEach(node => {
|
|
49
|
+
if (node.parentId && nodeMap.has(node.parentId)) {
|
|
50
|
+
const parent = nodeMap.get(node.parentId)!;
|
|
51
|
+
parent.children.push(node);
|
|
52
|
+
node.level = parent.level + (node.branchId ? 1 : 0);
|
|
53
|
+
} else {
|
|
54
|
+
roots.push(node);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Sort children by timestamp
|
|
59
|
+
nodeMap.forEach(node => {
|
|
60
|
+
node.children.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
roots.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
64
|
+
|
|
65
|
+
return roots;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Extract message text from SDK message
|
|
70
|
+
*/
|
|
71
|
+
function extractMessageText(sdkMessage: any): string {
|
|
72
|
+
if ('message' in sdkMessage && sdkMessage.message?.content) {
|
|
73
|
+
const content = sdkMessage.message.content;
|
|
74
|
+
if (typeof content === 'string') {
|
|
75
|
+
return content;
|
|
76
|
+
} else if (Array.isArray(content)) {
|
|
77
|
+
const textBlock = content.find((item: any) => typeof item === 'object' && 'text' in item);
|
|
78
|
+
if (textBlock && 'text' in textBlock) {
|
|
79
|
+
return textBlock.text;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Generate ASCII tree visualization
|
|
88
|
+
*/
|
|
89
|
+
export function generateTreeASCII(roots: TreeNode[]): string {
|
|
90
|
+
const lines: string[] = [];
|
|
91
|
+
|
|
92
|
+
function traverse(node: TreeNode, prefix: string = '', isLast: boolean = true) {
|
|
93
|
+
// Node line
|
|
94
|
+
const connector = isLast ? '└─' : '├─';
|
|
95
|
+
const branchTag = node.branchId ? ` [branch:${node.branchId}]` : '';
|
|
96
|
+
const levelTag = ` (level ${node.level})`;
|
|
97
|
+
lines.push(`${prefix}${connector} ${node.messageText}${branchTag}${levelTag}`);
|
|
98
|
+
|
|
99
|
+
// Children
|
|
100
|
+
const childPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
101
|
+
node.children.forEach((child, index) => {
|
|
102
|
+
const isLastChild = index === node.children.length - 1;
|
|
103
|
+
traverse(child, childPrefix, isLastChild);
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
roots.forEach((root, index) => {
|
|
108
|
+
const isLast = index === roots.length - 1;
|
|
109
|
+
traverse(root, '', isLast);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
return lines.join('\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generate compact list format (like your expected structure)
|
|
117
|
+
*/
|
|
118
|
+
export function generateTreeList(roots: TreeNode[]): string {
|
|
119
|
+
const lines: string[] = [];
|
|
120
|
+
|
|
121
|
+
function traverse(node: TreeNode, indent: number = 0) {
|
|
122
|
+
const indentStr = ' '.repeat(indent);
|
|
123
|
+
const branchTag = node.branchId ? ` [branch:${node.branchId}]` : '';
|
|
124
|
+
lines.push(`${indentStr}- ${node.messageText}${branchTag}`);
|
|
125
|
+
|
|
126
|
+
node.children.forEach(child => {
|
|
127
|
+
// Increase indent if child has different branchId (is a branch)
|
|
128
|
+
const childIndent = child.branchId && child.branchId !== node.branchId
|
|
129
|
+
? indent + 1
|
|
130
|
+
: indent;
|
|
131
|
+
traverse(child, childIndent);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
roots.forEach(root => {
|
|
136
|
+
traverse(root, 0);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return lines.join('\n');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Log tree structure with detailed analysis
|
|
144
|
+
*/
|
|
145
|
+
export function logTreeStructure(
|
|
146
|
+
sessionId: string,
|
|
147
|
+
messages: DatabaseMessage[],
|
|
148
|
+
currentHead: string | null,
|
|
149
|
+
label: string = 'Tree Structure'
|
|
150
|
+
) {
|
|
151
|
+
debug.log('checkpoint', `${label} - Session: ${sessionId}`);
|
|
152
|
+
debug.log('checkpoint', `Current HEAD: ${currentHead || 'none'}`);
|
|
153
|
+
debug.log('checkpoint', `Total messages: ${messages.length}`);
|
|
154
|
+
|
|
155
|
+
const roots = buildTreeFromMessages(messages);
|
|
156
|
+
|
|
157
|
+
debug.log('checkpoint', 'Tree Structure (ASCII):');
|
|
158
|
+
debug.log('checkpoint', generateTreeASCII(roots));
|
|
159
|
+
|
|
160
|
+
debug.log('checkpoint', '\nTree Structure (List format):');
|
|
161
|
+
debug.log('checkpoint', generateTreeList(roots));
|
|
162
|
+
|
|
163
|
+
// Additional analysis
|
|
164
|
+
const mainTimelineCount = roots.length;
|
|
165
|
+
const branchCount = messages.filter(m => m.branch_id && m.branch_id !== '').length;
|
|
166
|
+
const uniqueBranches = new Set(messages.map(m => m.branch_id).filter(Boolean)).size;
|
|
167
|
+
|
|
168
|
+
debug.log('checkpoint', '\nTree Statistics:');
|
|
169
|
+
debug.log('checkpoint', `- Main timeline nodes: ${mainTimelineCount}`);
|
|
170
|
+
debug.log('checkpoint', `- Total branch messages: ${branchCount}`);
|
|
171
|
+
debug.log('checkpoint', `- Unique branches: ${uniqueBranches}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Log parent-child relationships
|
|
176
|
+
*/
|
|
177
|
+
export function logParentChildRelationships(messages: DatabaseMessage[]) {
|
|
178
|
+
debug.log('checkpoint', '\nParent-Child Relationships:');
|
|
179
|
+
|
|
180
|
+
messages.forEach(msg => {
|
|
181
|
+
const sdkMessage = JSON.parse(msg.sdk_message);
|
|
182
|
+
if (sdkMessage.type !== 'user') return;
|
|
183
|
+
|
|
184
|
+
const messageText = extractMessageText(sdkMessage).trim().slice(0, 30);
|
|
185
|
+
const parentId = msg.parent_message_id || 'none';
|
|
186
|
+
const branchId = msg.branch_id || 'none';
|
|
187
|
+
|
|
188
|
+
debug.log('checkpoint', `${messageText}`);
|
|
189
|
+
debug.log('checkpoint', ` ID: ${msg.id}`);
|
|
190
|
+
debug.log('checkpoint', ` Parent: ${parentId}`);
|
|
191
|
+
debug.log('checkpoint', ` Branch: ${branchId}`);
|
|
192
|
+
debug.log('checkpoint', ` Timestamp: ${msg.timestamp}`);
|
|
193
|
+
debug.log('checkpoint', '');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
debug.log('checkpoint', '-'.repeat(80) + '\n');
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Validate tree structure and report issues
|
|
201
|
+
*/
|
|
202
|
+
export function validateTreeStructure(
|
|
203
|
+
messages: DatabaseMessage[],
|
|
204
|
+
expectedStructure?: string
|
|
205
|
+
): { valid: boolean; issues: string[] } {
|
|
206
|
+
const issues: string[] = [];
|
|
207
|
+
const messageMap = new Map(messages.map(m => [m.id, m]));
|
|
208
|
+
|
|
209
|
+
// Check for orphaned nodes (parent_id points to non-existent message)
|
|
210
|
+
messages.forEach(msg => {
|
|
211
|
+
if (msg.parent_message_id && !messageMap.has(msg.parent_message_id)) {
|
|
212
|
+
issues.push(`Message ${msg.id} has non-existent parent ${msg.parent_message_id}`);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Check for circular references
|
|
217
|
+
const visited = new Set<string>();
|
|
218
|
+
const recursionStack = new Set<string>();
|
|
219
|
+
|
|
220
|
+
function detectCycle(msgId: string): boolean {
|
|
221
|
+
if (recursionStack.has(msgId)) {
|
|
222
|
+
issues.push(`Circular reference detected at message ${msgId}`);
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
if (visited.has(msgId)) return false;
|
|
226
|
+
|
|
227
|
+
visited.add(msgId);
|
|
228
|
+
recursionStack.add(msgId);
|
|
229
|
+
|
|
230
|
+
const msg = messageMap.get(msgId);
|
|
231
|
+
if (msg && msg.parent_message_id) {
|
|
232
|
+
detectCycle(msg.parent_message_id);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
recursionStack.delete(msgId);
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
messages.forEach(msg => {
|
|
240
|
+
if (!visited.has(msg.id)) {
|
|
241
|
+
detectCycle(msg.id);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Compare with expected structure if provided
|
|
246
|
+
if (expectedStructure) {
|
|
247
|
+
const roots = buildTreeFromMessages(messages);
|
|
248
|
+
const actualStructure = generateTreeList(roots);
|
|
249
|
+
|
|
250
|
+
// Normalize whitespace for comparison
|
|
251
|
+
const normalizedExpected = expectedStructure.trim().replace(/\s+/g, ' ');
|
|
252
|
+
const normalizedActual = actualStructure.trim().replace(/\s+/g, ' ');
|
|
253
|
+
|
|
254
|
+
if (normalizedExpected !== normalizedActual) {
|
|
255
|
+
issues.push('Tree structure does not match expected structure');
|
|
256
|
+
debug.log('checkpoint', '\nExpected structure:');
|
|
257
|
+
debug.log('checkpoint', expectedStructure);
|
|
258
|
+
debug.log('checkpoint', '\nActual structure:');
|
|
259
|
+
debug.log('checkpoint', actualStructure);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
valid: issues.length === 0,
|
|
265
|
+
issues
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Compare two tree states and report differences
|
|
271
|
+
*/
|
|
272
|
+
export function compareTreeStates(
|
|
273
|
+
beforeMessages: DatabaseMessage[],
|
|
274
|
+
afterMessages: DatabaseMessage[],
|
|
275
|
+
operationLabel: string
|
|
276
|
+
) {
|
|
277
|
+
debug.log('checkpoint', `Tree State Comparison - ${operationLabel}`);
|
|
278
|
+
|
|
279
|
+
debug.log('checkpoint', 'BEFORE:');
|
|
280
|
+
const beforeRoots = buildTreeFromMessages(beforeMessages);
|
|
281
|
+
debug.log('checkpoint', generateTreeList(beforeRoots));
|
|
282
|
+
|
|
283
|
+
debug.log('checkpoint', '\nAFTER:');
|
|
284
|
+
const afterRoots = buildTreeFromMessages(afterMessages);
|
|
285
|
+
debug.log('checkpoint', generateTreeList(afterRoots));
|
|
286
|
+
|
|
287
|
+
// Analyze changes
|
|
288
|
+
const beforeIds = new Set(beforeMessages.map(m => m.id));
|
|
289
|
+
const afterIds = new Set(afterMessages.map(m => m.id));
|
|
290
|
+
|
|
291
|
+
const added = afterMessages.filter(m => !beforeIds.has(m.id));
|
|
292
|
+
const removed = beforeMessages.filter(m => !afterIds.has(m.id));
|
|
293
|
+
|
|
294
|
+
const modified = afterMessages.filter(m => {
|
|
295
|
+
const before = beforeMessages.find(bm => bm.id === m.id);
|
|
296
|
+
if (!before) return false;
|
|
297
|
+
return before.branch_id !== m.branch_id || before.parent_message_id !== m.parent_message_id;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
debug.log('checkpoint', '\nChanges:');
|
|
301
|
+
debug.log('checkpoint', `- Added: ${added.length}`);
|
|
302
|
+
debug.log('checkpoint', `- Removed: ${removed.length}`);
|
|
303
|
+
debug.log('checkpoint', `- Modified: ${modified.length}`);
|
|
304
|
+
|
|
305
|
+
if (modified.length > 0) {
|
|
306
|
+
debug.log('checkpoint', '\nModified messages:');
|
|
307
|
+
modified.forEach(m => {
|
|
308
|
+
const before = beforeMessages.find(bm => bm.id === m.id)!;
|
|
309
|
+
debug.log('checkpoint', ` ${m.id}:`);
|
|
310
|
+
if (before.parent_message_id !== m.parent_message_id) {
|
|
311
|
+
debug.log('checkpoint', ` Parent: ${before.parent_message_id} → ${m.parent_message_id}`);
|
|
312
|
+
}
|
|
313
|
+
if (before.branch_id !== m.branch_id) {
|
|
314
|
+
debug.log('checkpoint', ` Branch: ${before.branch_id || 'none'} → ${m.branch_id || 'none'}`);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
debug.log('checkpoint', `${'='.repeat(80)}\n`);
|
|
320
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket Client Instance
|
|
3
|
+
* Provides a singleton WebSocket client for real-time communication
|
|
4
|
+
* with automatic user/project context synchronization
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WSClient } from '$shared/utils/ws-client';
|
|
8
|
+
import type { WSAPI } from '$backend/ws';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get WebSocket URL based on environment
|
|
12
|
+
*/
|
|
13
|
+
function getWebSocketUrl(): string {
|
|
14
|
+
// Both dev and production: Use current host (Vite proxies /ws to backend in dev)
|
|
15
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
16
|
+
const host = window.location.host;
|
|
17
|
+
return `${protocol}//${host}/ws`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const ws = new WSClient<WSAPI>(getWebSocketUrl(), {
|
|
21
|
+
autoReconnect: true,
|
|
22
|
+
maxReconnectAttempts: 0, // Infinite reconnect
|
|
23
|
+
reconnectDelay: 1000,
|
|
24
|
+
maxReconnectDelay: 30000
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// CRITICAL: Handle Vite HMR to prevent WebSocket connection accumulation
|
|
28
|
+
// Without this, each HMR update creates a new WSClient+connection without
|
|
29
|
+
// closing the old one, causing duplicate connections on the server
|
|
30
|
+
if (import.meta.hot) {
|
|
31
|
+
import.meta.hot.dispose(() => {
|
|
32
|
+
ws.disconnect();
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Close WebSocket cleanly on page unload (refresh, tab close, navigate away).
|
|
37
|
+
// Without this, the browser may keep the old connection's HTTP upgrade request
|
|
38
|
+
// as "pending" during the page transition, causing the loading indicator to
|
|
39
|
+
// spin indefinitely after a refresh.
|
|
40
|
+
window.addEventListener('beforeunload', () => {
|
|
41
|
+
ws.disconnect();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export default ws;
|
package/frontend/main.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clopen - SPA Entry Point
|
|
3
|
+
* Mounts the Svelte application to the DOM (Svelte 5)
|
|
4
|
+
*/
|
|
5
|
+
import { mount } from 'svelte';
|
|
6
|
+
import './app.css';
|
|
7
|
+
import App from './App.svelte';
|
|
8
|
+
|
|
9
|
+
const app = mount(App, {
|
|
10
|
+
target: document.getElementById('app')!
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export default app;
|
package/index.html
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<link rel="icon" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
+
<meta
|
|
8
|
+
name="description"
|
|
9
|
+
content="Clopen - Modern web UI for Claude Code & OpenCode with real browser preview, git management, multi-account support, file management, checkpoints, collaboration, and integrated terminal. Built with Bun and Svelte 5."
|
|
10
|
+
/>
|
|
11
|
+
<meta name="theme-color" content="#0a0a0a" />
|
|
12
|
+
<title>Clopen</title>
|
|
13
|
+
|
|
14
|
+
<!-- DM Sans - Local self-hosted font -->
|
|
15
|
+
<link rel="stylesheet" href="/fonts/dm-sans.css">
|
|
16
|
+
|
|
17
|
+
<!-- Prevent FOUC (Flash of Unstyled Content) for theme -->
|
|
18
|
+
<script>
|
|
19
|
+
(function() {
|
|
20
|
+
// Check for saved theme preference
|
|
21
|
+
try {
|
|
22
|
+
const savedTheme = localStorage.getItem('claude-theme');
|
|
23
|
+
const isManualTheme = localStorage.getItem('claude-theme-manual') === 'true';
|
|
24
|
+
|
|
25
|
+
let isDark = false;
|
|
26
|
+
|
|
27
|
+
if (savedTheme && isManualTheme) {
|
|
28
|
+
// Use saved manual theme preference
|
|
29
|
+
const theme = JSON.parse(savedTheme);
|
|
30
|
+
isDark = theme.mode === 'dark';
|
|
31
|
+
} else {
|
|
32
|
+
// Follow system preference
|
|
33
|
+
isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Apply dark class immediately to prevent flash
|
|
37
|
+
if (isDark) {
|
|
38
|
+
document.documentElement.classList.add('dark');
|
|
39
|
+
} else {
|
|
40
|
+
document.documentElement.classList.remove('dark');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Set color scheme for browser integration
|
|
44
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
45
|
+
|
|
46
|
+
// Update meta theme-color for mobile browsers
|
|
47
|
+
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
|
|
48
|
+
if (metaThemeColor) {
|
|
49
|
+
metaThemeColor.setAttribute('content', isDark ? '#0a0a0a' : '#ffffff');
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
// Fallback to system preference if anything fails
|
|
53
|
+
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
54
|
+
document.documentElement.classList.add('dark');
|
|
55
|
+
document.documentElement.style.colorScheme = 'dark';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
</script>
|
|
60
|
+
</head>
|
|
61
|
+
<body
|
|
62
|
+
class="min-h-screen bg-white text-slate-900 dark:bg-slate-950 dark:text-slate-100 transition-colors duration-200"
|
|
63
|
+
>
|
|
64
|
+
<!-- App mount point -->
|
|
65
|
+
<div id="app"></div>
|
|
66
|
+
|
|
67
|
+
<!-- Vite entry point -->
|
|
68
|
+
<script type="module" src="/frontend/main.ts"></script>
|
|
69
|
+
</body>
|
|
70
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@myrialabs/clopen",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "All-in-one web workspace for Claude Code & OpenCode — chat, terminal, git, browser preview, checkpoints, and real-time collaboration",
|
|
5
|
+
"author": "Myria Labs",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"clopen": "./bin/clopen.ts"
|
|
10
|
+
},
|
|
11
|
+
"main": "./bin/clopen.ts",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/myrialabs/clopen.git"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/myrialabs/clopen#readme",
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/myrialabs/clopen/issues"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"claude",
|
|
22
|
+
"claude-code",
|
|
23
|
+
"opencode",
|
|
24
|
+
"ai",
|
|
25
|
+
"assistant",
|
|
26
|
+
"web-ui",
|
|
27
|
+
"bun",
|
|
28
|
+
"svelte",
|
|
29
|
+
"puppeteer",
|
|
30
|
+
"collaboration",
|
|
31
|
+
"terminal",
|
|
32
|
+
"browser-preview",
|
|
33
|
+
"file-management",
|
|
34
|
+
"file-editor",
|
|
35
|
+
"git",
|
|
36
|
+
"source-control",
|
|
37
|
+
"multi-account",
|
|
38
|
+
"anthropic",
|
|
39
|
+
"checkpoint",
|
|
40
|
+
"workspace",
|
|
41
|
+
"cloudflare-tunnel",
|
|
42
|
+
"mcp"
|
|
43
|
+
],
|
|
44
|
+
"engines": {
|
|
45
|
+
"bun": ">=1.2.12"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"dev": "bun --watch backend/index.ts",
|
|
49
|
+
"build": "vite build",
|
|
50
|
+
"start": "bun backend/index.ts",
|
|
51
|
+
"check": "svelte-check --tsconfig ./tsconfig.json",
|
|
52
|
+
"lint": "eslint .",
|
|
53
|
+
"lint:fix": "eslint . --fix",
|
|
54
|
+
"generate-icons": "bun scripts/generate-icons.ts"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@eslint/js": "^9.31.0",
|
|
58
|
+
"@sveltejs/vite-plugin-svelte": "^6.0.0",
|
|
59
|
+
"@tailwindcss/vite": "^4.1.11",
|
|
60
|
+
"@types/bun": "^1.2.18",
|
|
61
|
+
"@types/qrcode": "^1.5.6",
|
|
62
|
+
"@types/xterm": "^3.0.0",
|
|
63
|
+
"eslint": "^9.31.0",
|
|
64
|
+
"eslint-plugin-svelte": "^3.10.1",
|
|
65
|
+
"globals": "^16.3.0",
|
|
66
|
+
"svelte": "^5.0.0",
|
|
67
|
+
"svelte-check": "^4.0.0",
|
|
68
|
+
"tailwindcss": "^4.1.11",
|
|
69
|
+
"typescript": "^5.0.0",
|
|
70
|
+
"typescript-eslint": "^8.37.0",
|
|
71
|
+
"vite": "^7.0.4"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@anthropic-ai/claude-agent-sdk": "^0.2.7",
|
|
75
|
+
"@anthropic-ai/sdk": "^0.62.0",
|
|
76
|
+
"@elysiajs/cors": "^1.4.0",
|
|
77
|
+
"@elysiajs/static": "^1.4.7",
|
|
78
|
+
"@iconify-json/lucide": "^1.2.57",
|
|
79
|
+
"@iconify-json/material-icon-theme": "^1.2.16",
|
|
80
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
81
|
+
"@monaco-editor/loader": "^1.5.0",
|
|
82
|
+
"@opencode-ai/sdk": "^1.2.4",
|
|
83
|
+
"@tailwindcss/typography": "^0.5.16",
|
|
84
|
+
"@types/marked": "^5.0.2",
|
|
85
|
+
"@types/node": "^24.0.14",
|
|
86
|
+
"@xterm/addon-fit": "^0.10.0",
|
|
87
|
+
"@xterm/addon-web-links": "^0.11.0",
|
|
88
|
+
"bun-pty": "^0.4.2",
|
|
89
|
+
"canvas": "^3.1.2",
|
|
90
|
+
"chokidar": "^4.0.3",
|
|
91
|
+
"cloudflared": "^0.7.1",
|
|
92
|
+
"devtools-detector": "^2.0.23",
|
|
93
|
+
"elysia": "^1.4.19",
|
|
94
|
+
"eruda": "^3.4.3",
|
|
95
|
+
"file-type": "^21.0.0",
|
|
96
|
+
"is-text-path": "^3.0.0",
|
|
97
|
+
"marked": "^16.1.1",
|
|
98
|
+
"monaco-editor": "^0.52.2",
|
|
99
|
+
"nanoid": "^5.1.6",
|
|
100
|
+
"puppeteer": "^24.33.0",
|
|
101
|
+
"puppeteer-cluster": "^0.25.0",
|
|
102
|
+
"qrcode": "^1.5.4",
|
|
103
|
+
"sharp": "^0.34.3",
|
|
104
|
+
"werift": "^0.22.2",
|
|
105
|
+
"xterm": "^5.3.0"
|
|
106
|
+
},
|
|
107
|
+
"trustedDependencies": [
|
|
108
|
+
"cloudflared",
|
|
109
|
+
"puppeteer"
|
|
110
|
+
]
|
|
111
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
|
|
5
|
+
async function generateLucideIcons() {
|
|
6
|
+
console.log('Generating Lucide icons...');
|
|
7
|
+
|
|
8
|
+
// Read all lucide icons data using Bun.file
|
|
9
|
+
const lucideJsonFile = Bun.file(join(process.cwd(), 'node_modules/@iconify-json/lucide/icons.json'));
|
|
10
|
+
const lucideData = await lucideJsonFile.json() as { icons: Record<string, any> };
|
|
11
|
+
const iconNames = Object.keys(lucideData.icons);
|
|
12
|
+
|
|
13
|
+
// Generate import statement
|
|
14
|
+
const imports = `import lucideIcons from '@iconify-json/lucide/icons.json';`;
|
|
15
|
+
|
|
16
|
+
// Generate registry entries for ALL icons
|
|
17
|
+
const registryEntries = iconNames.map(iconName => {
|
|
18
|
+
return `\t'lucide:${iconName}': icons['${iconName}'],`;
|
|
19
|
+
}).join('\n');
|
|
20
|
+
|
|
21
|
+
// Create the lucide-icons.ts file
|
|
22
|
+
const lucideIconsContent = `// AUTO-GENERATED FILE: All Lucide Icons (${iconNames.length} icons)
|
|
23
|
+
// Generated by generate-icons.ts
|
|
24
|
+
// DO NOT EDIT MANUALLY
|
|
25
|
+
|
|
26
|
+
${imports}
|
|
27
|
+
|
|
28
|
+
// Type the icons object properly
|
|
29
|
+
const icons = lucideIcons.icons as Record<string, { body: string; width?: number; height?: number; top?: number }>;
|
|
30
|
+
|
|
31
|
+
export const lucideIconRegistry = {
|
|
32
|
+
${registryEntries}
|
|
33
|
+
} as const;
|
|
34
|
+
|
|
35
|
+
export type LucideIconName = keyof typeof lucideIconRegistry;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
// Write the file using Bun.write
|
|
39
|
+
await Bun.write('src/lib/components/common/lucide-icons.ts', lucideIconsContent);
|
|
40
|
+
console.log(`Generated lucide-icons.ts with ${iconNames.length} icons`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function generateMaterialIcons() {
|
|
44
|
+
console.log('Generating Material Icon Theme icons...');
|
|
45
|
+
|
|
46
|
+
// Read all Material Icon Theme icons from the icons.json file using Bun.file
|
|
47
|
+
const iconsJsonFile = Bun.file(join(process.cwd(), 'node_modules/@iconify-json/material-icon-theme/icons.json'));
|
|
48
|
+
const iconsData = await iconsJsonFile.json() as { icons: Record<string, any> };
|
|
49
|
+
const iconNames = Object.keys(iconsData.icons);
|
|
50
|
+
|
|
51
|
+
// Generate import statements
|
|
52
|
+
const imports = `import materialIcons from '@iconify-json/material-icon-theme/icons.json';`;
|
|
53
|
+
|
|
54
|
+
// Generate registry entries
|
|
55
|
+
const registryEntries = iconNames.map(iconName => {
|
|
56
|
+
return `\t'material:${iconName}': icons['${iconName}'],`;
|
|
57
|
+
}).join('\n');
|
|
58
|
+
|
|
59
|
+
// Create the material-icons.ts file
|
|
60
|
+
const materialIconsContent = `// AUTO-GENERATED FILE: All Material Icon Theme Icons (${iconNames.length} icons)
|
|
61
|
+
// Generated by generate-icons.ts
|
|
62
|
+
// DO NOT EDIT MANUALLY
|
|
63
|
+
|
|
64
|
+
${imports}
|
|
65
|
+
|
|
66
|
+
// Type the icons object properly
|
|
67
|
+
const icons = materialIcons.icons as Record<string, { body: string; width?: number; height?: number; top?: number }>;
|
|
68
|
+
|
|
69
|
+
// Registry mapping for Icon.svelte
|
|
70
|
+
export const materialIconRegistry = {
|
|
71
|
+
${registryEntries}
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
export type MaterialIconName = keyof typeof materialIconRegistry;
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
// Write the file using Bun.write
|
|
78
|
+
await Bun.write('src/lib/components/common/material-icons.ts', materialIconsContent);
|
|
79
|
+
console.log(`Generated material-icons.ts with ${iconNames.length} icons`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Generate both icon sets
|
|
83
|
+
console.log('Starting icon generation...\n');
|
|
84
|
+
await generateLucideIcons();
|
|
85
|
+
console.log('');
|
|
86
|
+
await generateMaterialIcons();
|
|
87
|
+
console.log('\nIcon generation complete!');
|