@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,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Tab Manager
|
|
3
|
+
* Manages tab state and operations for BrowserPreview
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { debug } from '$shared/utils/logger';
|
|
7
|
+
import type { DeviceSize, Rotation } from '$frontend/lib/constants/preview';
|
|
8
|
+
|
|
9
|
+
// Console message type (temporary placeholder)
|
|
10
|
+
type ConsoleMessage = any;
|
|
11
|
+
|
|
12
|
+
// Tab interface
|
|
13
|
+
export interface PreviewTab {
|
|
14
|
+
id: string;
|
|
15
|
+
url: string;
|
|
16
|
+
title: string;
|
|
17
|
+
sessionId: string | null;
|
|
18
|
+
sessionInfo: any;
|
|
19
|
+
isConnected: boolean;
|
|
20
|
+
isStreamReady: boolean;
|
|
21
|
+
isLoading: boolean;
|
|
22
|
+
isLaunchingBrowser: boolean;
|
|
23
|
+
isNavigating: boolean; // True when navigating within same session (e.g., clicking a link)
|
|
24
|
+
deviceSize: DeviceSize;
|
|
25
|
+
rotation: Rotation;
|
|
26
|
+
consoleLogs: ConsoleMessage[];
|
|
27
|
+
canvasAPI: any;
|
|
28
|
+
previewDimensions: any;
|
|
29
|
+
lastFrameData: any;
|
|
30
|
+
errorMessage: string | null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Helper function to get tab title from URL
|
|
35
|
+
*/
|
|
36
|
+
export function getTabTitle(url: string): string {
|
|
37
|
+
if (!url) return 'New Tab';
|
|
38
|
+
try {
|
|
39
|
+
return new URL(url).hostname;
|
|
40
|
+
} catch {
|
|
41
|
+
return url.length > 30 ? url.slice(0, 30) + '...' : url;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Create browser tab manager state
|
|
47
|
+
*/
|
|
48
|
+
export function createTabManager() {
|
|
49
|
+
let tabs = $state<PreviewTab[]>([]);
|
|
50
|
+
let activeTabId = $state<string | null>(null);
|
|
51
|
+
let nextTabId = $state(1);
|
|
52
|
+
|
|
53
|
+
// Get active tab (derived)
|
|
54
|
+
const activeTab = $derived.by(() => tabs.find(tab => tab.id === activeTabId));
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Create a new tab
|
|
58
|
+
*/
|
|
59
|
+
function createTab(tabUrl: string = ''): string {
|
|
60
|
+
const tabId = `tab-${nextTabId++}`;
|
|
61
|
+
debug.log('preview', `📁 Creating new tab: ${tabId} with URL: ${tabUrl || '(empty)'}`);
|
|
62
|
+
|
|
63
|
+
// Default device size is laptop
|
|
64
|
+
const deviceSize: DeviceSize = 'laptop';
|
|
65
|
+
// Default rotation: landscape for laptop (matches new default)
|
|
66
|
+
const rotation: Rotation = 'landscape';
|
|
67
|
+
|
|
68
|
+
const newTab: PreviewTab = {
|
|
69
|
+
id: tabId,
|
|
70
|
+
url: tabUrl,
|
|
71
|
+
title: getTabTitle(tabUrl),
|
|
72
|
+
sessionId: null,
|
|
73
|
+
sessionInfo: null,
|
|
74
|
+
isConnected: false,
|
|
75
|
+
isStreamReady: false,
|
|
76
|
+
isLoading: false,
|
|
77
|
+
isLaunchingBrowser: false,
|
|
78
|
+
isNavigating: false,
|
|
79
|
+
deviceSize,
|
|
80
|
+
rotation,
|
|
81
|
+
consoleLogs: [],
|
|
82
|
+
canvasAPI: null,
|
|
83
|
+
previewDimensions: { scale: 1 },
|
|
84
|
+
lastFrameData: null,
|
|
85
|
+
errorMessage: null
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
tabs = [...tabs, newTab];
|
|
89
|
+
activeTabId = tabId;
|
|
90
|
+
|
|
91
|
+
return tabId;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Switch to a specific tab
|
|
96
|
+
*/
|
|
97
|
+
function switchTab(tabId: string): PreviewTab | null {
|
|
98
|
+
const tab = tabs.find(t => t.id === tabId);
|
|
99
|
+
if (!tab || activeTabId === tabId) return null;
|
|
100
|
+
|
|
101
|
+
debug.log('preview', `🔄 Switching tab from ${activeTabId} to ${tabId}`);
|
|
102
|
+
activeTabId = tabId;
|
|
103
|
+
|
|
104
|
+
return tab;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Close a tab
|
|
109
|
+
*/
|
|
110
|
+
function closeTab(tabId: string): { removedTab: PreviewTab | null; newActiveTab: PreviewTab | null } {
|
|
111
|
+
const tabIndex = tabs.findIndex(tab => tab.id === tabId);
|
|
112
|
+
if (tabIndex === -1) return { removedTab: null, newActiveTab: null };
|
|
113
|
+
|
|
114
|
+
const removedTab = tabs[tabIndex];
|
|
115
|
+
tabs = tabs.filter(t => t.id !== tabId);
|
|
116
|
+
|
|
117
|
+
let newActiveTab: PreviewTab | null = null;
|
|
118
|
+
|
|
119
|
+
// Switch to adjacent tab if closing active tab
|
|
120
|
+
if (activeTabId === tabId && tabs.length > 0) {
|
|
121
|
+
const newIndex = tabIndex < tabs.length ? tabIndex : tabs.length - 1;
|
|
122
|
+
newActiveTab = tabs[newIndex];
|
|
123
|
+
if (newActiveTab) {
|
|
124
|
+
activeTabId = newActiveTab.id;
|
|
125
|
+
}
|
|
126
|
+
} else if (tabs.length === 0) {
|
|
127
|
+
activeTabId = null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { removedTab, newActiveTab };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Update tab state
|
|
135
|
+
*/
|
|
136
|
+
function updateTab(tabId: string, updates: Partial<PreviewTab>): void {
|
|
137
|
+
tabs = tabs.map(tab => {
|
|
138
|
+
if (tab.id === tabId) {
|
|
139
|
+
return { ...tab, ...updates };
|
|
140
|
+
}
|
|
141
|
+
return tab;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Update active tab
|
|
147
|
+
*/
|
|
148
|
+
function updateActiveTab(updates: Partial<PreviewTab>): void {
|
|
149
|
+
if (!activeTabId) return;
|
|
150
|
+
updateTab(activeTabId, updates);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get tab by ID
|
|
155
|
+
*/
|
|
156
|
+
function getTab(tabId: string): PreviewTab | undefined {
|
|
157
|
+
return tabs.find(t => t.id === tabId);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get all tabs
|
|
162
|
+
*/
|
|
163
|
+
function getAllTabs(): PreviewTab[] {
|
|
164
|
+
return tabs;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Get active tab ID
|
|
169
|
+
*/
|
|
170
|
+
function getActiveTabId(): string | null {
|
|
171
|
+
return activeTabId;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Set tabs (for external state sync)
|
|
176
|
+
*/
|
|
177
|
+
function setTabs(newTabs: PreviewTab[]): void {
|
|
178
|
+
tabs = newTabs;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Clear all tabs (used when switching projects)
|
|
183
|
+
*/
|
|
184
|
+
function clearAllTabs(): void {
|
|
185
|
+
debug.log('preview', '🧹 Clearing all tabs');
|
|
186
|
+
tabs = [];
|
|
187
|
+
activeTabId = null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
// Getters
|
|
192
|
+
get tabs() { return tabs; },
|
|
193
|
+
get activeTabId() { return activeTabId; },
|
|
194
|
+
get activeTab() { return activeTab; },
|
|
195
|
+
|
|
196
|
+
// Methods
|
|
197
|
+
createTab,
|
|
198
|
+
switchTab,
|
|
199
|
+
closeTab,
|
|
200
|
+
updateTab,
|
|
201
|
+
updateActiveTab,
|
|
202
|
+
getTab,
|
|
203
|
+
getAllTabs,
|
|
204
|
+
getActiveTabId,
|
|
205
|
+
setTabs,
|
|
206
|
+
clearAllTabs
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export type TabManager = ReturnType<typeof createTabManager>;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser Tab Operations
|
|
3
|
+
*
|
|
4
|
+
* Handles browser session lifecycle (launch, navigate, destroy) via WebSocket.
|
|
5
|
+
* All operations work with active tab on backend (tab-centric architecture).
|
|
6
|
+
*
|
|
7
|
+
* Session Persistence:
|
|
8
|
+
* - Sessions survive page refresh
|
|
9
|
+
* - On page load, existing sessions can be recovered via getExistingTabs()
|
|
10
|
+
* - Frontend reconnects to backend tabs and restarts streaming
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import ws from '$frontend/lib/utils/ws';
|
|
14
|
+
import { debug } from '$shared/utils/logger';
|
|
15
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
16
|
+
import type { DeviceSize, Rotation } from '$frontend/lib/constants/preview';
|
|
17
|
+
|
|
18
|
+
export interface BrowserSessionInfo {
|
|
19
|
+
quality: string;
|
|
20
|
+
url: string;
|
|
21
|
+
deviceSize?: DeviceSize;
|
|
22
|
+
rotation?: Rotation;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface LaunchResult {
|
|
26
|
+
success: boolean;
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
sessionInfo?: BrowserSessionInfo;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface NavigateResult {
|
|
33
|
+
success: boolean;
|
|
34
|
+
finalUrl?: string;
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ExistingTabInfo {
|
|
39
|
+
tabId: string;
|
|
40
|
+
url: string;
|
|
41
|
+
title: string;
|
|
42
|
+
quality: string;
|
|
43
|
+
isStreaming: boolean;
|
|
44
|
+
deviceSize: string;
|
|
45
|
+
rotation: string;
|
|
46
|
+
isActive: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ExistingTabsResult {
|
|
50
|
+
tabs: ExistingTabInfo[];
|
|
51
|
+
activeTabId: string | null;
|
|
52
|
+
count: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Launch browser for active tab
|
|
57
|
+
*/
|
|
58
|
+
export async function launchBrowser(
|
|
59
|
+
url: string,
|
|
60
|
+
deviceSize: DeviceSize,
|
|
61
|
+
rotation: Rotation,
|
|
62
|
+
projectId: string,
|
|
63
|
+
mcpSessionId?: string
|
|
64
|
+
): Promise<LaunchResult> {
|
|
65
|
+
debug.log('preview', `🌐 launchBrowser - URL: ${url}, device: ${deviceSize}, rotation: ${rotation}, projectId: ${projectId}${mcpSessionId ? `, mcpSessionId: ${mcpSessionId}` : ''}`);
|
|
66
|
+
|
|
67
|
+
if (!url) {
|
|
68
|
+
debug.error('preview', '❌ launchBrowser: No URL provided');
|
|
69
|
+
addNotification({
|
|
70
|
+
type: 'error',
|
|
71
|
+
title: 'URL Required',
|
|
72
|
+
message: 'Please enter a URL to launch browser preview'
|
|
73
|
+
});
|
|
74
|
+
return { success: false, error: 'URL is required' };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!projectId) {
|
|
78
|
+
debug.error('preview', '❌ launchBrowser: No projectId provided');
|
|
79
|
+
addNotification({
|
|
80
|
+
type: 'error',
|
|
81
|
+
title: 'Project Required',
|
|
82
|
+
message: 'Please select a project first'
|
|
83
|
+
});
|
|
84
|
+
return { success: false, error: 'Project ID is required' };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
debug.log('preview', `📡 Sending browser:launch via WebSocket...`);
|
|
89
|
+
|
|
90
|
+
// Backend will create tab automatically with projectId
|
|
91
|
+
const data = await ws.http(
|
|
92
|
+
'preview:browser-tab-open',
|
|
93
|
+
{ url, deviceSize, rotation },
|
|
94
|
+
60000
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
debug.log('preview', `✅ Browser launched successfully - sessionId: ${data.tabId}`);
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
success: true,
|
|
101
|
+
sessionId: data.tabId,
|
|
102
|
+
sessionInfo: {
|
|
103
|
+
quality: data.quality,
|
|
104
|
+
url: data.url,
|
|
105
|
+
deviceSize,
|
|
106
|
+
rotation
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
debug.error('preview', '💥 Error launching browser:', error);
|
|
111
|
+
addNotification({
|
|
112
|
+
type: 'error',
|
|
113
|
+
title: 'Launch Failed',
|
|
114
|
+
message: error instanceof Error ? error.message : 'Failed to launch browser preview'
|
|
115
|
+
});
|
|
116
|
+
return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Navigate active tab to new URL
|
|
125
|
+
*/
|
|
126
|
+
export async function navigateBrowser(newUrl: string, projectId: string): Promise<NavigateResult> {
|
|
127
|
+
if (!newUrl) {
|
|
128
|
+
return { success: false, error: 'No URL provided' };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (!projectId) {
|
|
132
|
+
return { success: false, error: 'Project ID is required' };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Backend uses active tab automatically
|
|
137
|
+
const data = await ws.http('preview:browser-tab-navigate', { url: newUrl }, 30000);
|
|
138
|
+
|
|
139
|
+
return { success: true, finalUrl: data.finalUrl };
|
|
140
|
+
} catch (error) {
|
|
141
|
+
addNotification({
|
|
142
|
+
type: 'error',
|
|
143
|
+
title: 'Navigation Failed',
|
|
144
|
+
message: error instanceof Error ? error.message : 'Failed to navigate'
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
success: false,
|
|
148
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Destroy browser session for active tab
|
|
155
|
+
*/
|
|
156
|
+
export async function destroyBrowser(projectId: string): Promise<void> {
|
|
157
|
+
if (!projectId) {
|
|
158
|
+
debug.error('preview', `❌ destroyBrowser: No projectId provided`);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
// Backend uses active tab automatically
|
|
164
|
+
await ws.http('preview:browser-tab-close', {});
|
|
165
|
+
debug.log('preview', `✅ Browser session destroyed`);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
debug.error('preview', `❌ Error destroying browser:`, error);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Destroy browser session for a specific tab
|
|
173
|
+
*/
|
|
174
|
+
export async function destroyBrowserTab(tabId: string, projectId: string): Promise<void> {
|
|
175
|
+
if (!projectId) {
|
|
176
|
+
debug.error('preview', `❌ destroyBrowserTab: No projectId provided`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
await ws.http('preview:browser-tab-close', { tabId });
|
|
182
|
+
debug.log('preview', `✅ Browser tab ${tabId} destroyed`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
debug.error('preview', `❌ Error destroying browser tab ${tabId}:`, error);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get all existing tabs from backend (for session recovery after refresh)
|
|
190
|
+
*/
|
|
191
|
+
export async function getExistingTabs(projectId: string): Promise<ExistingTabsResult | null> {
|
|
192
|
+
if (!projectId) {
|
|
193
|
+
debug.error('preview', `❌ getExistingTabs: No projectId provided`);
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
debug.log('preview', `🔍 Checking for existing browser tabs (project: ${projectId})...`);
|
|
199
|
+
|
|
200
|
+
const data = await ws.http('preview:browser-tabs-list', {}, 5000);
|
|
201
|
+
|
|
202
|
+
if (data.count > 0) {
|
|
203
|
+
debug.log('preview', `✅ Found ${data.count} existing browser tabs`);
|
|
204
|
+
} else {
|
|
205
|
+
debug.log('preview', `📭 No existing browser tabs found`);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
tabs: data.tabs,
|
|
210
|
+
activeTabId: data.activeTabId,
|
|
211
|
+
count: data.count
|
|
212
|
+
};
|
|
213
|
+
} catch (error) {
|
|
214
|
+
debug.warn('preview', `⚠️ Could not get existing tabs:`, error);
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Switch to a specific tab on backend
|
|
221
|
+
*/
|
|
222
|
+
export async function switchToBackendTab(tabId: string, projectId: string): Promise<boolean> {
|
|
223
|
+
if (!projectId) {
|
|
224
|
+
debug.error('preview', `❌ switchToBackendTab: No projectId provided`);
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
debug.log('preview', `🔄 Switching to backend tab: ${tabId} (project: ${projectId})`);
|
|
230
|
+
|
|
231
|
+
await ws.http('preview:browser-tab-switch', { tabId }, 5000);
|
|
232
|
+
|
|
233
|
+
debug.log('preview', `✅ Switched to backend tab: ${tabId}`);
|
|
234
|
+
return true;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
debug.error('preview', `❌ Error switching to backend tab:`, error);
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fade, scale, fly } from 'svelte/transition';
|
|
3
|
+
import { cubicOut } from 'svelte/easing';
|
|
4
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
5
|
+
import {
|
|
6
|
+
settingsModalState,
|
|
7
|
+
closeSettingsModal,
|
|
8
|
+
setActiveSection,
|
|
9
|
+
settingsSections,
|
|
10
|
+
type SettingsSection
|
|
11
|
+
} from '$frontend/lib/stores/ui/settings-modal.svelte';
|
|
12
|
+
|
|
13
|
+
// Import settings components
|
|
14
|
+
import ModelSettings from './model/ModelSettings.svelte';
|
|
15
|
+
import AIEnginesSettings from './engines/AIEnginesSettings.svelte';
|
|
16
|
+
import AppearanceSettings from './appearance/AppearanceSettings.svelte';
|
|
17
|
+
import UserSettings from './user/UserSettings.svelte';
|
|
18
|
+
import NotificationSettings from './notifications/NotificationSettings.svelte';
|
|
19
|
+
import GeneralSettings from './general/GeneralSettings.svelte';
|
|
20
|
+
import pkg from '../../../../package.json';
|
|
21
|
+
|
|
22
|
+
// Responsive state
|
|
23
|
+
let isMobileMenuOpen = $state(false);
|
|
24
|
+
let windowWidth = $state(typeof window !== 'undefined' ? window.innerWidth : 1024);
|
|
25
|
+
|
|
26
|
+
const isMobile = $derived(windowWidth < 768);
|
|
27
|
+
const isOpen = $derived(settingsModalState.isOpen);
|
|
28
|
+
const activeSection = $derived(settingsModalState.activeSection);
|
|
29
|
+
|
|
30
|
+
// Handle section change
|
|
31
|
+
function handleSectionChange(section: SettingsSection) {
|
|
32
|
+
setActiveSection(section);
|
|
33
|
+
if (isMobile) {
|
|
34
|
+
isMobileMenuOpen = false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Handle backdrop click
|
|
39
|
+
function handleBackdropClick(event: MouseEvent) {
|
|
40
|
+
if (event.target === event.currentTarget) {
|
|
41
|
+
closeSettingsModal();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle keyboard events
|
|
46
|
+
function handleKeydown(event: KeyboardEvent) {
|
|
47
|
+
if (event.key === 'Escape') {
|
|
48
|
+
closeSettingsModal();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Get current section info
|
|
53
|
+
const currentSectionInfo = $derived(
|
|
54
|
+
settingsSections.find((s) => s.id === activeSection) || settingsSections[0]
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// Update window width on resize
|
|
58
|
+
function handleResize() {
|
|
59
|
+
windowWidth = window.innerWidth;
|
|
60
|
+
if (!isMobile) {
|
|
61
|
+
isMobileMenuOpen = false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<svelte:window on:keydown={handleKeydown} on:resize={handleResize} />
|
|
67
|
+
|
|
68
|
+
{#if isOpen}
|
|
69
|
+
<!-- Backdrop -->
|
|
70
|
+
<div
|
|
71
|
+
class="fixed inset-0 z-[100] flex items-center justify-center md:p-4 bg-black/60 backdrop-blur-sm"
|
|
72
|
+
role="dialog"
|
|
73
|
+
aria-modal="true"
|
|
74
|
+
aria-labelledby="settings-title"
|
|
75
|
+
tabindex="-1"
|
|
76
|
+
onclick={handleBackdropClick}
|
|
77
|
+
onkeydown={handleKeydown}
|
|
78
|
+
in:fade={{ duration: 200, easing: cubicOut }}
|
|
79
|
+
out:fade={{ duration: 150, easing: cubicOut }}
|
|
80
|
+
>
|
|
81
|
+
<!-- Modal Container -->
|
|
82
|
+
<div
|
|
83
|
+
role="dialog"
|
|
84
|
+
aria-labelledby="settings-title"
|
|
85
|
+
tabindex="-1"
|
|
86
|
+
class="flex flex-col w-full max-w-225 h-[85vh] max-h-175 bg-slate-50 dark:bg-slate-950 border border-violet-500/20 rounded-2xl overflow-hidden shadow-[0_25px_50px_-12px_rgba(0,0,0,0.25)] dark:shadow-[0_25px_50px_-12px_rgba(0,0,0,0.5)] max-md:max-w-full max-md:h-screen max-md:max-h-screen max-md:rounded-none"
|
|
87
|
+
onclick={(e) => e.stopPropagation()}
|
|
88
|
+
onkeydown={(e) => e.stopPropagation()}
|
|
89
|
+
in:scale={{ duration: 250, easing: cubicOut, start: 0.95 }}
|
|
90
|
+
out:scale={{ duration: 150, easing: cubicOut, start: 0.95 }}
|
|
91
|
+
>
|
|
92
|
+
<!-- Mobile Header -->
|
|
93
|
+
{#if isMobile}
|
|
94
|
+
<header
|
|
95
|
+
class="flex items-center justify-between py-3 px-4 bg-slate-100 dark:bg-slate-900/95 border-b border-slate-200 dark:border-slate-800"
|
|
96
|
+
>
|
|
97
|
+
<button
|
|
98
|
+
type="button"
|
|
99
|
+
class="flex items-center justify-center w-9 h-9 bg-transparent border-none rounded-lg text-slate-500 cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
100
|
+
onclick={() => (isMobileMenuOpen = !isMobileMenuOpen)}
|
|
101
|
+
aria-label="Toggle menu"
|
|
102
|
+
>
|
|
103
|
+
<Icon name={isMobileMenuOpen ? 'lucide:x' : 'lucide:menu'} class="w-5 h-5" />
|
|
104
|
+
</button>
|
|
105
|
+
<h2
|
|
106
|
+
id="settings-title"
|
|
107
|
+
class="flex items-center gap-2 text-base font-semibold text-slate-900 dark:text-slate-100 m-0"
|
|
108
|
+
>
|
|
109
|
+
<Icon name={currentSectionInfo.icon} class="w-5 h-5" />
|
|
110
|
+
{currentSectionInfo.label}
|
|
111
|
+
</h2>
|
|
112
|
+
<button
|
|
113
|
+
type="button"
|
|
114
|
+
class="flex items-center justify-center w-9 h-9 bg-transparent border-none rounded-lg text-slate-500 cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
115
|
+
onclick={closeSettingsModal}
|
|
116
|
+
aria-label="Close settings"
|
|
117
|
+
>
|
|
118
|
+
<Icon name="lucide:x" class="w-5 h-5" />
|
|
119
|
+
</button>
|
|
120
|
+
</header>
|
|
121
|
+
{/if}
|
|
122
|
+
|
|
123
|
+
<div class="flex flex-1 min-h-0 relative">
|
|
124
|
+
<!-- Sidebar -->
|
|
125
|
+
<aside
|
|
126
|
+
class="flex flex-col w-68 shrink-0 bg-white dark:bg-slate-900/98 border-r border-slate-200 dark:border-slate-800
|
|
127
|
+
{isMobile
|
|
128
|
+
? 'absolute left-0 top-0 bottom-0 z-10 w-70 shadow-[4px_0_20px_rgba(0,0,0,0.15)] dark:shadow-[4px_0_20px_rgba(0,0,0,0.3)] transition-transform duration-[250ms] ease-out'
|
|
129
|
+
: ''}
|
|
130
|
+
{isMobile && !isMobileMenuOpen ? '-translate-x-full' : 'translate-x-0'}"
|
|
131
|
+
>
|
|
132
|
+
{#if !isMobile}
|
|
133
|
+
<header
|
|
134
|
+
class="flex items-center justify-between py-5 px-4 pl-6.5 border-b border-slate-200 dark:border-slate-800"
|
|
135
|
+
>
|
|
136
|
+
<div
|
|
137
|
+
class="flex items-center gap-2.5 text-lg font-bold text-slate-900 dark:text-slate-100"
|
|
138
|
+
>
|
|
139
|
+
<span>Settings</span>
|
|
140
|
+
</div>
|
|
141
|
+
<button
|
|
142
|
+
type="button"
|
|
143
|
+
class="flex items-center justify-center w-9 h-9 bg-transparent border-none rounded-lg text-slate-500 cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
144
|
+
onclick={closeSettingsModal}
|
|
145
|
+
aria-label="Close settings"
|
|
146
|
+
>
|
|
147
|
+
<Icon name="lucide:x" class="w-5 h-5" />
|
|
148
|
+
</button>
|
|
149
|
+
</header>
|
|
150
|
+
{/if}
|
|
151
|
+
|
|
152
|
+
<nav class="flex-1 overflow-y-auto p-3">
|
|
153
|
+
{#each settingsSections as section (section.id)}
|
|
154
|
+
<button
|
|
155
|
+
type="button"
|
|
156
|
+
class="flex items-start gap-3 w-full py-3 px-3.5 bg-transparent border-none rounded-lg text-slate-500 text-sm text-left cursor-pointer transition-all duration-150 mb-1
|
|
157
|
+
hover:bg-violet-500/10 hover:text-slate-600 dark:hover:text-slate-400
|
|
158
|
+
{activeSection === section.id
|
|
159
|
+
? 'bg-violet-500/10 dark:bg-violet-500/20 text-slate-900 dark:text-slate-100'
|
|
160
|
+
: ''}"
|
|
161
|
+
onclick={() => handleSectionChange(section.id)}
|
|
162
|
+
>
|
|
163
|
+
<Icon
|
|
164
|
+
name={section.icon}
|
|
165
|
+
class="w-5 h-5 shrink-0 mt-0.5 {activeSection === section.id
|
|
166
|
+
? 'text-violet-600'
|
|
167
|
+
: ''}"
|
|
168
|
+
/>
|
|
169
|
+
<div class="flex flex-col gap-0.5">
|
|
170
|
+
<span class="font-semibold">{section.label}</span>
|
|
171
|
+
<span
|
|
172
|
+
class="text-xs text-slate-600 dark:text-slate-500 leading-tight {activeSection ===
|
|
173
|
+
section.id
|
|
174
|
+
? 'text-violet-600 dark:text-violet-400'
|
|
175
|
+
: ''}">{section.description}</span
|
|
176
|
+
>
|
|
177
|
+
</div>
|
|
178
|
+
</button>
|
|
179
|
+
{/each}
|
|
180
|
+
</nav>
|
|
181
|
+
|
|
182
|
+
<footer class="p-4 border-t border-slate-200 dark:border-slate-800">
|
|
183
|
+
<div class="flex items-center gap-2 text-xs text-slate-600 dark:text-slate-500">
|
|
184
|
+
<Icon name="lucide:info" class="w-4 h-4" />
|
|
185
|
+
<span>Clopen v{pkg.version}</span>
|
|
186
|
+
</div>
|
|
187
|
+
</footer>
|
|
188
|
+
</aside>
|
|
189
|
+
|
|
190
|
+
<!-- Mobile Menu Overlay -->
|
|
191
|
+
{#if isMobile && isMobileMenuOpen}
|
|
192
|
+
<button
|
|
193
|
+
type="button"
|
|
194
|
+
class="absolute inset-0 z-[5] bg-black/40 border-none p-0 cursor-default"
|
|
195
|
+
onclick={() => (isMobileMenuOpen = false)}
|
|
196
|
+
aria-label="Close menu"
|
|
197
|
+
in:fade={{ duration: 150 }}
|
|
198
|
+
out:fade={{ duration: 100 }}
|
|
199
|
+
></button>
|
|
200
|
+
{/if}
|
|
201
|
+
|
|
202
|
+
<!-- Content Area -->
|
|
203
|
+
<main class="flex-1 flex flex-col min-w-0 overflow-hidden">
|
|
204
|
+
<div class="flex-1 overflow-y-auto p-4 md:p-5">
|
|
205
|
+
{#if activeSection === 'model'}
|
|
206
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
207
|
+
<ModelSettings />
|
|
208
|
+
</div>
|
|
209
|
+
{:else if activeSection === 'engines'}
|
|
210
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
211
|
+
<AIEnginesSettings />
|
|
212
|
+
</div>
|
|
213
|
+
{:else if activeSection === 'appearance'}
|
|
214
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
215
|
+
<AppearanceSettings />
|
|
216
|
+
</div>
|
|
217
|
+
{:else if activeSection === 'user'}
|
|
218
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
219
|
+
<UserSettings />
|
|
220
|
+
</div>
|
|
221
|
+
{:else if activeSection === 'notifications'}
|
|
222
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
223
|
+
<NotificationSettings />
|
|
224
|
+
</div>
|
|
225
|
+
{:else if activeSection === 'general'}
|
|
226
|
+
<div in:fly={{ x: 20, duration: 200 }}>
|
|
227
|
+
<GeneralSettings />
|
|
228
|
+
</div>
|
|
229
|
+
{/if}
|
|
230
|
+
</div>
|
|
231
|
+
</main>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
{/if}
|