@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,169 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount, onDestroy } from 'svelte';
|
|
3
|
+
import { fade } from 'svelte/transition';
|
|
4
|
+
import { browser } from '$frontend/lib/app-environment';
|
|
5
|
+
|
|
6
|
+
// Stores
|
|
7
|
+
import {
|
|
8
|
+
workspaceState,
|
|
9
|
+
initializeWorkspace,
|
|
10
|
+
} from '$frontend/lib/stores/ui/workspace.svelte';
|
|
11
|
+
import { appState, setAppLoading, setAppInitialized, restoreLastView } from '$frontend/lib/stores/core/app.svelte';
|
|
12
|
+
import { projectState } from '$frontend/lib/stores/core/projects.svelte';
|
|
13
|
+
import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
|
|
14
|
+
|
|
15
|
+
// Components
|
|
16
|
+
import DesktopLayout from './layout/DesktopLayout.svelte';
|
|
17
|
+
import MobileLayout from './layout/MobileLayout.svelte';
|
|
18
|
+
import LoadingScreen from '$frontend/lib/components/common/LoadingScreen.svelte';
|
|
19
|
+
import ModalProvider from '$frontend/lib/components/common/ModalProvider.svelte';
|
|
20
|
+
import SettingsModal from '$frontend/lib/components/settings/SettingsModal.svelte';
|
|
21
|
+
import HistoryModal from '$frontend/lib/components/history/HistoryModal.svelte';
|
|
22
|
+
|
|
23
|
+
// Services
|
|
24
|
+
import { initializeTheme } from '$frontend/lib/utils/theme';
|
|
25
|
+
import { initializeStore } from '$frontend/lib/stores/core/app.svelte';
|
|
26
|
+
import { initializeProjects } from '$frontend/lib/stores/core/projects.svelte';
|
|
27
|
+
import { initializeSessions } from '$frontend/lib/stores/core/sessions.svelte';
|
|
28
|
+
import { initializeNotifications } from '$frontend/lib/stores/ui/notification.svelte';
|
|
29
|
+
import { applyServerSettings } from '$frontend/lib/stores/features/settings.svelte';
|
|
30
|
+
import { userStore } from '$frontend/lib/stores/features/user.svelte';
|
|
31
|
+
import { initPresence } from '$frontend/lib/stores/core/presence.svelte';
|
|
32
|
+
import ws from '$frontend/lib/utils/ws';
|
|
33
|
+
import { debug } from '$shared/utils/logger';
|
|
34
|
+
|
|
35
|
+
const { children } = $props();
|
|
36
|
+
|
|
37
|
+
// Responsive state
|
|
38
|
+
let isMobile = $state(false);
|
|
39
|
+
let windowWidth = $state(0);
|
|
40
|
+
|
|
41
|
+
// Chat History modal state
|
|
42
|
+
let showHistoryModal = $state(false);
|
|
43
|
+
|
|
44
|
+
function closeHistoryModal() {
|
|
45
|
+
showHistoryModal = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Loading state
|
|
49
|
+
let loadingProgress = $state(0);
|
|
50
|
+
let loadingText = $state('Initializing Workspace...');
|
|
51
|
+
|
|
52
|
+
// Set progress directly — CSS transition in LoadingScreen handles smooth animation
|
|
53
|
+
function setProgress(value: number, text?: string) {
|
|
54
|
+
loadingProgress = value;
|
|
55
|
+
if (text) loadingText = text;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Responsive handler
|
|
59
|
+
function handleResize() {
|
|
60
|
+
if (browser) {
|
|
61
|
+
windowWidth = window.innerWidth;
|
|
62
|
+
isMobile = windowWidth < 1024;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Initialize
|
|
67
|
+
onMount(async () => {
|
|
68
|
+
handleResize();
|
|
69
|
+
window.addEventListener('resize', handleResize);
|
|
70
|
+
|
|
71
|
+
setAppLoading(true);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
// Step 1: Core initialization (theme, workspace, notifications — all sync/localStorage)
|
|
75
|
+
setProgress(10, 'Initializing core systems...');
|
|
76
|
+
initializeTheme();
|
|
77
|
+
initializeStore();
|
|
78
|
+
initializeNotifications();
|
|
79
|
+
initializeWorkspace();
|
|
80
|
+
|
|
81
|
+
// Step 2: Initialize user + wait for WebSocket in parallel
|
|
82
|
+
// userStore.initialize() reads localStorage (fast) and sets WS context locally.
|
|
83
|
+
// waitUntilConnected() waits for WS to connect and sync any pending context.
|
|
84
|
+
setProgress(20, 'Connecting...');
|
|
85
|
+
await Promise.all([
|
|
86
|
+
userStore.initialize(),
|
|
87
|
+
ws.waitUntilConnected(10000)
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
// Step 3: Restore user state from server
|
|
91
|
+
setProgress(30, 'Restoring state...');
|
|
92
|
+
let serverState: { currentProjectId: string | null; lastView: string | null; settings: any } | null = null;
|
|
93
|
+
try {
|
|
94
|
+
serverState = await ws.http('user:restore-state', {});
|
|
95
|
+
debug.log('workspace', 'Server state restored:', serverState);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
debug.warn('workspace', 'Failed to restore server state, using defaults:', err);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Step 4: Apply restored state + setup presence (sync operations)
|
|
101
|
+
setProgress(40);
|
|
102
|
+
if (serverState?.settings) {
|
|
103
|
+
applyServerSettings(serverState.settings);
|
|
104
|
+
}
|
|
105
|
+
restoreLastView(serverState?.lastView);
|
|
106
|
+
initPresence();
|
|
107
|
+
|
|
108
|
+
// Step 5: Load projects (with server-restored currentProjectId)
|
|
109
|
+
setProgress(50, 'Loading projects...');
|
|
110
|
+
await initializeProjects(serverState?.currentProjectId);
|
|
111
|
+
|
|
112
|
+
// Step 6: Load sessions
|
|
113
|
+
setProgress(70, 'Restoring sessions...');
|
|
114
|
+
await initializeSessions();
|
|
115
|
+
|
|
116
|
+
// Step 7: Ready
|
|
117
|
+
setProgress(100, 'Ready!');
|
|
118
|
+
} catch (error) {
|
|
119
|
+
debug.error('workspace', 'Initialization error:', error);
|
|
120
|
+
setProgress(100, 'Error during initialization');
|
|
121
|
+
} finally {
|
|
122
|
+
// Small delay for CSS transition to finish, then dismiss loading screen
|
|
123
|
+
setTimeout(() => {
|
|
124
|
+
setAppInitialized();
|
|
125
|
+
}, 100);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
onDestroy(() => {
|
|
130
|
+
if (browser) {
|
|
131
|
+
window.removeEventListener('resize', handleResize);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
</script>
|
|
135
|
+
|
|
136
|
+
<!-- Loading Screen -->
|
|
137
|
+
<LoadingScreen bind:isVisible={appState.isAppLoading} progress={loadingProgress} {loadingText} />
|
|
138
|
+
|
|
139
|
+
<!-- Main Workspace Layout -->
|
|
140
|
+
<div
|
|
141
|
+
class="h-screen w-screen overflow-hidden {isMobile ? 'bg-white/90 dark:bg-slate-900/98' : 'bg-slate-50 dark:bg-slate-900/70'} text-slate-900 dark:text-slate-100 font-sans"
|
|
142
|
+
>
|
|
143
|
+
<!-- Skip link for accessibility -->
|
|
144
|
+
<a
|
|
145
|
+
href="#main-content"
|
|
146
|
+
class="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:p-4 focus:bg-violet-600 focus:text-white"
|
|
147
|
+
>
|
|
148
|
+
Skip to main content
|
|
149
|
+
</a>
|
|
150
|
+
|
|
151
|
+
{#if isMobile}
|
|
152
|
+
<!-- Mobile Layout -->
|
|
153
|
+
<div class="flex flex-col h-full w-full" in:fade={{ duration: 200 }}>
|
|
154
|
+
<MobileLayout />
|
|
155
|
+
</div>
|
|
156
|
+
{:else}
|
|
157
|
+
<!-- Desktop Layout -->
|
|
158
|
+
<DesktopLayout />
|
|
159
|
+
{/if}
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<!-- Modal Provider -->
|
|
163
|
+
<ModalProvider />
|
|
164
|
+
|
|
165
|
+
<!-- Settings Modal -->
|
|
166
|
+
<SettingsModal />
|
|
167
|
+
|
|
168
|
+
<!-- History Modal -->
|
|
169
|
+
<HistoryModal bind:isOpen={showHistoryModal} onClose={closeHistoryModal} />
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { workspaceState } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
3
|
+
import DesktopNavigator from '../DesktopNavigator.svelte';
|
|
4
|
+
import Layout from './split-pane/Layout.svelte';
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div class="flex h-full w-full">
|
|
8
|
+
<!-- Desktop Navigator -->
|
|
9
|
+
<DesktopNavigator />
|
|
10
|
+
|
|
11
|
+
<!-- Main Workspace with Split Pane Layout -->
|
|
12
|
+
<main id="workspace-main" class="flex-1 h-full min-w-0 relative overflow-hidden p-3 box-border">
|
|
13
|
+
<Layout node={workspaceState.layout} />
|
|
14
|
+
</main>
|
|
15
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { workspaceState } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
3
|
+
import PanelContainer from '../PanelContainer.svelte';
|
|
4
|
+
import MobileNavigator from '../MobileNavigator.svelte';
|
|
5
|
+
|
|
6
|
+
const activePanelId = $derived(workspaceState.activeMobilePanel);
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<div class="flex flex-col h-full w-full">
|
|
10
|
+
<MobileNavigator />
|
|
11
|
+
|
|
12
|
+
<main id="main-content" class="flex-1 overflow-hidden">
|
|
13
|
+
{#if activePanelId}
|
|
14
|
+
<PanelContainer panelId={activePanelId} />
|
|
15
|
+
{/if}
|
|
16
|
+
</main>
|
|
17
|
+
</div>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SplitDirection, SplitNode } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
3
|
+
import Layout from './Layout.svelte';
|
|
4
|
+
import Handle from './Handle.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
direction: SplitDirection;
|
|
8
|
+
ratio: number;
|
|
9
|
+
path: number[];
|
|
10
|
+
child1: SplitNode;
|
|
11
|
+
child2: SplitNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { direction, ratio, path, child1, child2 }: Props = $props();
|
|
15
|
+
|
|
16
|
+
// Calculate sizes for flex basis
|
|
17
|
+
const child1Size = $derived(`${ratio}%`);
|
|
18
|
+
const child2Size = $derived(`${100 - ratio}%`);
|
|
19
|
+
|
|
20
|
+
// Calculate sizes for container
|
|
21
|
+
const size = 'calc(100% - (var(--spacing) * 3))';
|
|
22
|
+
const width = $derived(direction === 'horizontal' ? '100%' : size);
|
|
23
|
+
const height = $derived(direction === 'horizontal' ? size : '100%');
|
|
24
|
+
|
|
25
|
+
// Flex direction based on split direction
|
|
26
|
+
const flexDirection = $derived(direction === 'horizontal' ? 'flex-col' : 'flex-row');
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<div class="split-pane-container flex {flexDirection} gap-0" style="width: {width}; height: {height};">
|
|
30
|
+
<!-- Child 1 -->
|
|
31
|
+
<div class="split-pane-child overflow-hidden min-w-0" style="flex: 0 0 {child1Size};">
|
|
32
|
+
<Layout node={child1} path={[...path, 0]} />
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<!-- Resize Handle -->
|
|
36
|
+
<Handle {direction} {path} currentRatio={ratio} />
|
|
37
|
+
|
|
38
|
+
<!-- Child 2 -->
|
|
39
|
+
<div class="split-pane-child overflow-hidden min-w-0" style="flex: 0 0 {child2Size};">
|
|
40
|
+
<Layout node={child2} path={[...path, 1]} />
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SplitDirection } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
3
|
+
import { setSplitRatio, workspaceState } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
direction: SplitDirection;
|
|
7
|
+
path: number[];
|
|
8
|
+
currentRatio: number; // Get from parent
|
|
9
|
+
onResize?: (newRatio: number) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { direction, path, currentRatio, onResize }: Props = $props();
|
|
13
|
+
|
|
14
|
+
// Drag state
|
|
15
|
+
let isDragging = $state(false);
|
|
16
|
+
let dragStartPos = $state(0);
|
|
17
|
+
let dragStartRatio = $state(0);
|
|
18
|
+
let containerRect = $state<DOMRect | null>(null);
|
|
19
|
+
|
|
20
|
+
function handleResizeStart(event: MouseEvent) {
|
|
21
|
+
event.preventDefault();
|
|
22
|
+
|
|
23
|
+
isDragging = true;
|
|
24
|
+
dragStartPos = direction === 'horizontal' ? event.clientY : event.clientX;
|
|
25
|
+
dragStartRatio = currentRatio; // Save current ratio at drag start
|
|
26
|
+
|
|
27
|
+
// Get container dimensions
|
|
28
|
+
const container = (event.target as HTMLElement).closest('.split-pane-container');
|
|
29
|
+
if (container) {
|
|
30
|
+
containerRect = container.getBoundingClientRect();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
document.addEventListener('mousemove', handleDragMove);
|
|
34
|
+
document.addEventListener('mouseup', handleDragEnd);
|
|
35
|
+
document.body.style.cursor = direction === 'horizontal' ? 'row-resize' : 'col-resize';
|
|
36
|
+
document.body.style.userSelect = 'none';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function handleDragMove(event: MouseEvent) {
|
|
40
|
+
if (!isDragging || !containerRect) return;
|
|
41
|
+
|
|
42
|
+
const currentPos = direction === 'horizontal' ? event.clientY : event.clientX;
|
|
43
|
+
const delta = currentPos - dragStartPos;
|
|
44
|
+
|
|
45
|
+
// Calculate delta as percentage of container size
|
|
46
|
+
const containerSize =
|
|
47
|
+
direction === 'horizontal' ? containerRect.height : containerRect.width;
|
|
48
|
+
const deltaPercent = (delta / containerSize) * 100;
|
|
49
|
+
|
|
50
|
+
// Calculate new ratio (clamped between 10% and 90%)
|
|
51
|
+
const newRatio = Math.max(10, Math.min(90, dragStartRatio + deltaPercent));
|
|
52
|
+
|
|
53
|
+
// Update ratio via store
|
|
54
|
+
setSplitRatio(path, newRatio);
|
|
55
|
+
|
|
56
|
+
// Call optional callback
|
|
57
|
+
if (onResize) {
|
|
58
|
+
onResize(newRatio);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function handleDragEnd() {
|
|
63
|
+
isDragging = false;
|
|
64
|
+
containerRect = null;
|
|
65
|
+
document.removeEventListener('mousemove', handleDragMove);
|
|
66
|
+
document.removeEventListener('mouseup', handleDragEnd);
|
|
67
|
+
document.body.style.cursor = '';
|
|
68
|
+
document.body.style.userSelect = '';
|
|
69
|
+
}
|
|
70
|
+
</script>
|
|
71
|
+
|
|
72
|
+
<button
|
|
73
|
+
type="button"
|
|
74
|
+
class="shrink-0 relative z-10 transition-colors duration-150
|
|
75
|
+
{direction === 'horizontal' ? 'h-3 w-full cursor-row-resize' : 'w-3 h-full cursor-col-resize'}
|
|
76
|
+
{isDragging ? 'bg-violet-500/10' : 'hover:bg-violet-500/5'}"
|
|
77
|
+
aria-label="Resize panels"
|
|
78
|
+
onmousedown={handleResizeStart}
|
|
79
|
+
>
|
|
80
|
+
<div
|
|
81
|
+
class="transition-all duration-150 bg-violet-500/20 rounded
|
|
82
|
+
{direction === 'horizontal' ? 'h-0.5 w-12 mx-auto' : 'w-0.5 h-12 my-auto mx-auto'}
|
|
83
|
+
{isDragging ? 'bg-violet-600 scale-110' : ''}"
|
|
84
|
+
></div>
|
|
85
|
+
</button>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { SplitNode } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
3
|
+
import PanelContainer from '../../PanelContainer.svelte';
|
|
4
|
+
import Container from './Container.svelte';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
node: SplitNode;
|
|
8
|
+
path?: number[]; // Path in tree for resize updates
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { node, path = [] }: Props = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
{#if node.type === 'panel'}
|
|
15
|
+
<!-- Panel Leaf: Render panel wrapper -->
|
|
16
|
+
{#if node.panelId}
|
|
17
|
+
<div class="split-pane-panel h-full w-full overflow-hidden">
|
|
18
|
+
<PanelContainer panelId={node.panelId} />
|
|
19
|
+
</div>
|
|
20
|
+
{:else}
|
|
21
|
+
<!-- Empty slot -->
|
|
22
|
+
<div
|
|
23
|
+
class="split-pane-empty flex items-center justify-center h-full w-full bg-slate-100 dark:bg-slate-900/50 border-2 border-dashed border-slate-300 dark:border-slate-600 rounded-lg"
|
|
24
|
+
>
|
|
25
|
+
<span class="text-sm text-slate-400 dark:text-slate-500">Empty Panel</span>
|
|
26
|
+
</div>
|
|
27
|
+
{/if}
|
|
28
|
+
{:else if node.type === 'split'}
|
|
29
|
+
<!-- Split Container: Render split with two children -->
|
|
30
|
+
<Container
|
|
31
|
+
direction={node.direction}
|
|
32
|
+
ratio={node.ratio}
|
|
33
|
+
path={path}
|
|
34
|
+
child1={node.children[0]}
|
|
35
|
+
child2={node.children[1]}
|
|
36
|
+
/>
|
|
37
|
+
{/if}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { sessionState, setCurrentSession, createNewChatSession, clearMessages, loadMessagesForSession } from '$frontend/lib/stores/core/sessions.svelte';
|
|
3
|
+
import { projectState } from '$frontend/lib/stores/core/projects.svelte';
|
|
4
|
+
import { appState } from '$frontend/lib/stores/core/app.svelte';
|
|
5
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
6
|
+
import { onMount } from 'svelte';
|
|
7
|
+
import { fade } from 'svelte/transition';
|
|
8
|
+
import ChatMessages from '$frontend/lib/components/chat/message/ChatMessages.svelte';
|
|
9
|
+
import ChatInput from '$frontend/lib/components/chat/input/ChatInput.svelte';
|
|
10
|
+
import FloatingTodoList from '$frontend/lib/components/chat/widgets/FloatingTodoList.svelte';
|
|
11
|
+
import TimelineModal from '$frontend/lib/components/checkpoint/TimelineModal.svelte';
|
|
12
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
13
|
+
import Button from '$frontend/lib/components/common/Button.svelte';
|
|
14
|
+
import { debug } from '$shared/utils/logger';
|
|
15
|
+
import ws from '$frontend/lib/utils/ws';
|
|
16
|
+
import { chatService } from '$frontend/lib/services/chat/chat.service';
|
|
17
|
+
import { setSkipNextRestore } from '$frontend/lib/stores/ui/chat-input.svelte';
|
|
18
|
+
import { userStore } from '$frontend/lib/stores/features/user.svelte';
|
|
19
|
+
|
|
20
|
+
// Props
|
|
21
|
+
interface Props {
|
|
22
|
+
showMobileHeader?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { showMobileHeader = false }: Props = $props();
|
|
26
|
+
|
|
27
|
+
// Welcome state - don't show during restoration
|
|
28
|
+
const isWelcomeState = $derived(
|
|
29
|
+
sessionState.messages.length === 0 &&
|
|
30
|
+
!appState.isRestoring
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
// Check if we should show input (not during restoration)
|
|
34
|
+
const showInput = $derived(!appState.isRestoring);
|
|
35
|
+
|
|
36
|
+
// Project-aware state
|
|
37
|
+
const hasActiveProject = $derived(projectState.currentProject !== null);
|
|
38
|
+
|
|
39
|
+
// Scroll container
|
|
40
|
+
const scrollContainer: HTMLElement | undefined = $state();
|
|
41
|
+
|
|
42
|
+
// Checkpoints modal state
|
|
43
|
+
let showCheckpoints = $state(false);
|
|
44
|
+
|
|
45
|
+
function openCheckpoints() {
|
|
46
|
+
showCheckpoints = true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function closeCheckpoints() {
|
|
50
|
+
showCheckpoints = false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Extract text from message content
|
|
54
|
+
function extractMessageText(message: any): string {
|
|
55
|
+
if (!('message' in message) || !message.message?.content) {
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
const content = message.message.content;
|
|
59
|
+
|
|
60
|
+
if (typeof content === 'string') {
|
|
61
|
+
return content;
|
|
62
|
+
} else if (Array.isArray(content)) {
|
|
63
|
+
// Find text content in array
|
|
64
|
+
for (const item of content) {
|
|
65
|
+
if (typeof item === 'string') {
|
|
66
|
+
return item;
|
|
67
|
+
} else if (typeof item === 'object' && item !== null) {
|
|
68
|
+
if ('text' in item && typeof (item as any).text === 'string') {
|
|
69
|
+
return (item as any).text;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Process timeline messages with all necessary data
|
|
78
|
+
const timelineMessages = $derived(
|
|
79
|
+
sessionState.messages
|
|
80
|
+
.filter(m => {
|
|
81
|
+
if (m.type !== 'user') return false;
|
|
82
|
+
const text = extractMessageText(m);
|
|
83
|
+
return text.length > 0;
|
|
84
|
+
})
|
|
85
|
+
.map(msg => ({
|
|
86
|
+
id: msg.metadata?.message_id,
|
|
87
|
+
timestamp: msg.metadata?.created_at || '',
|
|
88
|
+
date: msg.metadata?.created_at ? new Date(msg.metadata.created_at).toLocaleDateString() : 'Unknown',
|
|
89
|
+
time: msg.metadata?.created_at ? new Date(msg.metadata.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }) : 'Unknown',
|
|
90
|
+
text: extractMessageText(msg)
|
|
91
|
+
}))
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
// Handle restore from timeline
|
|
95
|
+
async function handleTimelineRestore(messageId: string | undefined, messageTimestamp: string) {
|
|
96
|
+
if (!messageId) {
|
|
97
|
+
addNotification({
|
|
98
|
+
type: 'error',
|
|
99
|
+
title: 'Restore Failed',
|
|
100
|
+
message: 'Message ID not found',
|
|
101
|
+
duration: 3000
|
|
102
|
+
});
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
// Send restore request via WebSocket HTTP
|
|
108
|
+
await ws.http('snapshot:restore', {
|
|
109
|
+
messageId: messageId,
|
|
110
|
+
sessionId: sessionState.currentSession?.id || ''
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// Close modal
|
|
114
|
+
showCheckpoints = false;
|
|
115
|
+
|
|
116
|
+
// Reload messages from database to update UI
|
|
117
|
+
if (sessionState.currentSession?.id) {
|
|
118
|
+
await loadMessagesForSession(sessionState.currentSession.id);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
addNotification({
|
|
122
|
+
type: 'success',
|
|
123
|
+
title: 'Project Restored',
|
|
124
|
+
message: `Successfully restored to checkpoint at ${new Date(messageTimestamp).toLocaleTimeString()}`,
|
|
125
|
+
duration: 5000
|
|
126
|
+
});
|
|
127
|
+
} catch (error) {
|
|
128
|
+
debug.error('chat', 'Restore error:', error);
|
|
129
|
+
addNotification({
|
|
130
|
+
type: 'error',
|
|
131
|
+
title: 'Restore Failed',
|
|
132
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
133
|
+
duration: 5000
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function startNewChat() {
|
|
139
|
+
if (!hasActiveProject || !projectState.currentProject) {
|
|
140
|
+
addNotification({
|
|
141
|
+
type: 'warning',
|
|
142
|
+
title: 'No Project Selected',
|
|
143
|
+
message: 'Please select a project first',
|
|
144
|
+
duration: 3000
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Cancel active stream if running
|
|
150
|
+
if (appState.isLoading) {
|
|
151
|
+
chatService.cancelRequest();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Clear server input state and prevent stale restore on ChatInput remount
|
|
155
|
+
setSkipNextRestore(true);
|
|
156
|
+
const currentUserId = userStore.currentUser?.id;
|
|
157
|
+
const currentChatSessionId = sessionState.currentSession?.id;
|
|
158
|
+
if (currentUserId && currentChatSessionId) {
|
|
159
|
+
ws.emit('chat:input-sync', {
|
|
160
|
+
text: '',
|
|
161
|
+
senderId: currentUserId,
|
|
162
|
+
chatSessionId: currentChatSessionId,
|
|
163
|
+
attachments: []
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Clear messages for local view
|
|
168
|
+
clearMessages();
|
|
169
|
+
|
|
170
|
+
// Create a new session (existing sessions stay active for other users)
|
|
171
|
+
const newSession = await createNewChatSession(projectState.currentProject.id);
|
|
172
|
+
|
|
173
|
+
if (newSession) {
|
|
174
|
+
await setCurrentSession(newSession);
|
|
175
|
+
} else {
|
|
176
|
+
addNotification({
|
|
177
|
+
type: 'error',
|
|
178
|
+
title: 'Failed to Create Session',
|
|
179
|
+
message: 'Could not create a new chat session',
|
|
180
|
+
duration: 3000
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Check for active stream on mount only if needed
|
|
186
|
+
onMount(async () => {
|
|
187
|
+
debug.log('chat', 'Component mounted');
|
|
188
|
+
// WebSocket reconnection is handled automatically by ws client
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Export actions for DesktopPanel header
|
|
192
|
+
export const panelActions = {
|
|
193
|
+
checkpoints: openCheckpoints,
|
|
194
|
+
newChat: startNewChat,
|
|
195
|
+
hasMessages: () => sessionState.messages.length > 0
|
|
196
|
+
};
|
|
197
|
+
</script>
|
|
198
|
+
|
|
199
|
+
<div class="h-full flex flex-col bg-transparent">
|
|
200
|
+
{#if !hasActiveProject}
|
|
201
|
+
<div
|
|
202
|
+
class="flex-1 flex flex-col items-center justify-center gap-3 text-slate-600 dark:text-slate-500 text-sm"
|
|
203
|
+
>
|
|
204
|
+
<Icon name="lucide:bot" class="w-10 h-10 opacity-30" />
|
|
205
|
+
<span>No project selected</span>
|
|
206
|
+
</div>
|
|
207
|
+
{:else}
|
|
208
|
+
<div class="flex-1 flex flex-col overflow-hidden {showMobileHeader ? '' : '-m-3'}">
|
|
209
|
+
{#if isWelcomeState && !appState.isRestoring}
|
|
210
|
+
<!-- Welcome state with modern design -->
|
|
211
|
+
<div class="flex-1 overflow-y-auto overflow-x-hidden">
|
|
212
|
+
<div class="min-h-full flex items-center justify-center p-4">
|
|
213
|
+
<div class="w-full max-w-4xl space-y-6 md:space-y-8 lg:space-y-10">
|
|
214
|
+
<!-- Modern hero section -->
|
|
215
|
+
<div class="text-center space-y-3 md:space-y-4 px-6">
|
|
216
|
+
<div class="space-y-3 md:space-y-4">
|
|
217
|
+
<h1 class="text-3xl md:text-4xl font-semibold text-slate-900 dark:text-slate-100">
|
|
218
|
+
Build apps & websites with AI
|
|
219
|
+
</h1>
|
|
220
|
+
<p class="md:text-lg text-slate-600 dark:text-slate-400 max-w-2xl mx-auto">
|
|
221
|
+
Describe your idea. Get production-ready code.
|
|
222
|
+
</p>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<!-- Input area integrated in welcome state -->
|
|
227
|
+
{#if showInput}
|
|
228
|
+
<div class="w-full px-4 space-y-4" in:fade={{ duration: 200, delay: 100 }}>
|
|
229
|
+
<ChatInput />
|
|
230
|
+
</div>
|
|
231
|
+
{/if}
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
{:else}
|
|
236
|
+
<!-- Enhanced chat interface -->
|
|
237
|
+
<div class="flex-1 flex flex-col overflow-hidden">
|
|
238
|
+
<div class="flex-1 flex justify-center overflow-hidden">
|
|
239
|
+
<div class="w-full flex flex-col overflow-hidden">
|
|
240
|
+
<div class="flex-1 overflow-y-auto overflow-x-hidden">
|
|
241
|
+
<ChatMessages {scrollContainer} />
|
|
242
|
+
</div>
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<!-- Input area with SDK integration -->
|
|
248
|
+
{#if showInput}
|
|
249
|
+
<div
|
|
250
|
+
class="sticky bottom-0 flex-shrink-0 bg-gradient-to-t from-slate-50 via-slate-50 dark:from-slate-900 dark:via-slate-900 to-transparent"
|
|
251
|
+
in:fade={{ duration: 200, delay: 100 }}
|
|
252
|
+
>
|
|
253
|
+
<div class="flex justify-center">
|
|
254
|
+
<div class="w-full max-w-5xl px-4 pb-4 pt-2">
|
|
255
|
+
<ChatInput />
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
</div>
|
|
259
|
+
{/if}
|
|
260
|
+
{/if}
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
<!-- Floating TodoList (only shown when there's an active session with todos) -->
|
|
264
|
+
{#if sessionState.currentSession}
|
|
265
|
+
<FloatingTodoList />
|
|
266
|
+
{/if}
|
|
267
|
+
|
|
268
|
+
<!-- Checkpoint Modal -->
|
|
269
|
+
<TimelineModal
|
|
270
|
+
bind:isOpen={showCheckpoints}
|
|
271
|
+
onClose={closeCheckpoints}
|
|
272
|
+
/>
|
|
273
|
+
{/if}
|
|
274
|
+
</div>
|