@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,403 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import Modal from '$frontend/lib/components/common/Modal.svelte';
|
|
4
|
+
import Dialog from '$frontend/lib/components/common/Dialog.svelte';
|
|
5
|
+
import {
|
|
6
|
+
workspaceState,
|
|
7
|
+
setActiveMobilePanel,
|
|
8
|
+
type PanelId
|
|
9
|
+
} from '$frontend/lib/stores/ui/workspace.svelte';
|
|
10
|
+
import { projectState, removeProject } from '$frontend/lib/stores/core/projects.svelte';
|
|
11
|
+
import { presenceState } from '$frontend/lib/stores/core/presence.svelte';
|
|
12
|
+
import { openSettingsModal } from '$frontend/lib/stores/ui/settings-modal.svelte';
|
|
13
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
14
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
15
|
+
import TunnelButton from '$frontend/lib/components/tunnel/TunnelButton.svelte';
|
|
16
|
+
import TunnelModal from '$frontend/lib/components/tunnel/TunnelModal.svelte';
|
|
17
|
+
import type { Project } from '$shared/types/database/schema';
|
|
18
|
+
import FolderBrowser from '$frontend/lib/components/common/FolderBrowser.svelte';
|
|
19
|
+
import ProjectUserAvatars from '$frontend/lib/components/common/ProjectUserAvatars.svelte';
|
|
20
|
+
import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
|
|
21
|
+
import ws from '$frontend/lib/utils/ws';
|
|
22
|
+
import { debug } from '$shared/utils/logger';
|
|
23
|
+
|
|
24
|
+
// Modal states
|
|
25
|
+
let showTunnelModal = $state(false);
|
|
26
|
+
|
|
27
|
+
// Project dropdown state
|
|
28
|
+
let showProjectMenu = $state(false);
|
|
29
|
+
let showFolderBrowser = $state(false);
|
|
30
|
+
let showDeleteDialog = $state(false);
|
|
31
|
+
let projectToDelete = $state<Project | null>(null);
|
|
32
|
+
let searchQuery = $state('');
|
|
33
|
+
|
|
34
|
+
// Get current project status from shared store
|
|
35
|
+
const currentProjectStatus = $derived(
|
|
36
|
+
projectState.currentProject?.id
|
|
37
|
+
? presenceState.statuses.get(projectState.currentProject.id)
|
|
38
|
+
: undefined
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const panels: { id: PanelId; icon: IconName; label: string }[] = [
|
|
42
|
+
{ id: 'chat', icon: 'lucide:bot', label: 'AI' },
|
|
43
|
+
{ id: 'files', icon: 'lucide:folder', label: 'Files' },
|
|
44
|
+
{ id: 'git', icon: 'lucide:git-branch', label: 'Source Control' },
|
|
45
|
+
{ id: 'terminal', icon: 'lucide:terminal', label: 'Terminal' },
|
|
46
|
+
{ id: 'preview', icon: 'lucide:globe', label: 'Preview' }
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Filtered projects based on search query
|
|
50
|
+
const filteredProjects = $derived(() => {
|
|
51
|
+
if (!searchQuery.trim()) return projectState.projects;
|
|
52
|
+
const query = searchQuery.toLowerCase();
|
|
53
|
+
return projectState.projects.filter(
|
|
54
|
+
(p) => p.name.toLowerCase().includes(query) || p.path.toLowerCase().includes(query)
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
function selectPanel(panelId: PanelId) {
|
|
59
|
+
setActiveMobilePanel(panelId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toggleProjectMenu() {
|
|
63
|
+
showProjectMenu = !showProjectMenu;
|
|
64
|
+
if (!showProjectMenu) {
|
|
65
|
+
searchQuery = '';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get status color from presence data (single source of truth)
|
|
70
|
+
// Green only when the project is the CURRENT project AND the current chat session has an active stream
|
|
71
|
+
function getStatusColor(projectId: string): string {
|
|
72
|
+
const currentProjectId = projectState.currentProject?.id;
|
|
73
|
+
if (projectId !== currentProjectId) return 'bg-slate-500/30';
|
|
74
|
+
const status = presenceState.statuses.get(projectId);
|
|
75
|
+
const currentChatSessionId = sessionState.currentSession?.id;
|
|
76
|
+
if (!status?.streams || !currentChatSessionId) return 'bg-slate-500/30';
|
|
77
|
+
const hasActiveForSession = status.streams.some(
|
|
78
|
+
(s: any) => s.status === 'active' && s.chatSessionId === currentChatSessionId
|
|
79
|
+
);
|
|
80
|
+
if (hasActiveForSession) return 'bg-emerald-500';
|
|
81
|
+
return 'bg-slate-500/30';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function openAddProject() {
|
|
85
|
+
showProjectMenu = false;
|
|
86
|
+
showFolderBrowser = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function closeFolderBrowser() {
|
|
90
|
+
showFolderBrowser = false;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function closeProjectMenu() {
|
|
94
|
+
showProjectMenu = false;
|
|
95
|
+
searchQuery = '';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function handleDeleteClick(project: Project, event: MouseEvent) {
|
|
99
|
+
event.stopPropagation();
|
|
100
|
+
projectToDelete = project;
|
|
101
|
+
showDeleteDialog = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function confirmDeleteProject() {
|
|
105
|
+
if (!projectToDelete) return;
|
|
106
|
+
const deleteId = projectToDelete.id!;
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await ws.http('projects:delete', { id: deleteId });
|
|
110
|
+
removeProject(deleteId);
|
|
111
|
+
showDeleteDialog = false;
|
|
112
|
+
projectToDelete = null;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
debug.error('workspace', 'Failed to delete project:', error);
|
|
115
|
+
addNotification({
|
|
116
|
+
type: 'error',
|
|
117
|
+
title: 'Error',
|
|
118
|
+
message: 'Failed to delete project',
|
|
119
|
+
duration: 5000
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function closeDeleteDialog() {
|
|
125
|
+
showDeleteDialog = false;
|
|
126
|
+
projectToDelete = null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async function createProjectFromFolder(folderPath: string, folderName: string) {
|
|
130
|
+
try {
|
|
131
|
+
showFolderBrowser = false;
|
|
132
|
+
|
|
133
|
+
const projects = await ws.http('projects:list', {});
|
|
134
|
+
|
|
135
|
+
const existingProject = projects
|
|
136
|
+
? projects.find((p: any) => p.path === folderPath)
|
|
137
|
+
: null;
|
|
138
|
+
|
|
139
|
+
if (existingProject) {
|
|
140
|
+
const { setCurrentProject } = await import('$frontend/lib/stores/core/projects.svelte');
|
|
141
|
+
setCurrentProject(existingProject);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const newProject = await ws.http('projects:create', { name: folderName, path: folderPath });
|
|
146
|
+
|
|
147
|
+
if (newProject) {
|
|
148
|
+
const { setCurrentProject } = await import('$frontend/lib/stores/core/projects.svelte');
|
|
149
|
+
setCurrentProject(newProject);
|
|
150
|
+
}
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error('Failed to create project:', error);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
</script>
|
|
156
|
+
|
|
157
|
+
<header
|
|
158
|
+
class="flex items-center bg-white/90 dark:bg-slate-900/98 py-2 px-3 gap-2 relative z-30"
|
|
159
|
+
>
|
|
160
|
+
<!-- Project Selector -->
|
|
161
|
+
<button
|
|
162
|
+
type="button"
|
|
163
|
+
class="flex items-center gap-2 px-3 py-2.5 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-lg text-slate-900 dark:text-slate-100 text-sm font-medium cursor-pointer transition-all duration-150 flex-1 min-w-0 active:bg-violet-500/10"
|
|
164
|
+
onclick={toggleProjectMenu}
|
|
165
|
+
aria-expanded={showProjectMenu}
|
|
166
|
+
aria-haspopup="menu"
|
|
167
|
+
>
|
|
168
|
+
<div class="relative shrink-0"><Icon name="lucide:folder-open" class="w-4 h-4" /><span class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-slate-100 dark:border-slate-800 {getStatusColor(projectState.currentProject?.id ?? '')}"></span></div>
|
|
169
|
+
<span class="flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap">
|
|
170
|
+
{projectState.currentProject?.name ?? 'No Project'}
|
|
171
|
+
</span>
|
|
172
|
+
<div class="shrink-0" onclick={(e) => e.stopPropagation()}>
|
|
173
|
+
<ProjectUserAvatars projectStatus={currentProjectStatus} maxVisible={2} />
|
|
174
|
+
</div>
|
|
175
|
+
<Icon name="lucide:chevron-down" class="w-3 h-3 opacity-60 shrink-0" />
|
|
176
|
+
</button>
|
|
177
|
+
|
|
178
|
+
<!-- Panel Tabs (Icon Only) -->
|
|
179
|
+
<div
|
|
180
|
+
class="flex gap-1 bg-slate-100/80 dark:bg-slate-800/50 p-1 border border-slate-200 dark:border-slate-800 rounded-lg"
|
|
181
|
+
role="tablist"
|
|
182
|
+
aria-label="Panel Tabs"
|
|
183
|
+
>
|
|
184
|
+
{#each panels as panel}
|
|
185
|
+
<button
|
|
186
|
+
type="button"
|
|
187
|
+
class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150
|
|
188
|
+
{workspaceState.activeMobilePanel === panel.id
|
|
189
|
+
? 'bg-violet-500/10 dark:bg-violet-500/20 text-slate-900 dark:text-slate-100 shadow-violet-500/20'
|
|
190
|
+
: 'active:bg-violet-500/10'}"
|
|
191
|
+
role="tab"
|
|
192
|
+
aria-selected={workspaceState.activeMobilePanel === panel.id}
|
|
193
|
+
aria-controls={`panel-${panel.id}`}
|
|
194
|
+
aria-label={panel.label}
|
|
195
|
+
title={panel.label}
|
|
196
|
+
onclick={() => selectPanel(panel.id)}
|
|
197
|
+
>
|
|
198
|
+
<Icon name={panel.icon} class="w-5 h-5" />
|
|
199
|
+
</button>
|
|
200
|
+
{/each}
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
<!-- Action Buttons -->
|
|
204
|
+
<div
|
|
205
|
+
class="flex gap-1 bg-slate-100/80 dark:bg-slate-800/50 p-1 border border-slate-200 dark:border-slate-800 rounded-lg"
|
|
206
|
+
role="tablist"
|
|
207
|
+
aria-label="Action Buttons"
|
|
208
|
+
>
|
|
209
|
+
<!-- Tunnel Button -->
|
|
210
|
+
<button
|
|
211
|
+
type="button"
|
|
212
|
+
class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150 active:bg-violet-500/10"
|
|
213
|
+
role="tab"
|
|
214
|
+
onclick={() => (showTunnelModal = true)}
|
|
215
|
+
aria-label="Public Tunnel"
|
|
216
|
+
title="Public Tunnel"
|
|
217
|
+
>
|
|
218
|
+
<Icon name="lucide:cloud-upload" class="w-5 h-5" />
|
|
219
|
+
</button>
|
|
220
|
+
|
|
221
|
+
<!-- Settings Button -->
|
|
222
|
+
<button
|
|
223
|
+
type="button"
|
|
224
|
+
class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150 active:bg-violet-500/10"
|
|
225
|
+
role="tab"
|
|
226
|
+
onclick={() => openSettingsModal()}
|
|
227
|
+
aria-label="Settings"
|
|
228
|
+
title="Settings"
|
|
229
|
+
>
|
|
230
|
+
<Icon name="lucide:settings" class="w-5 h-5" />
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
</header>
|
|
234
|
+
|
|
235
|
+
<!-- Project Selection Modal -->
|
|
236
|
+
<Modal bind:isOpen={showProjectMenu} onClose={closeProjectMenu} size="md">
|
|
237
|
+
{#snippet header()}
|
|
238
|
+
<div class="flex items-center justify-between px-4 py-3 md:px-6 md:py-4">
|
|
239
|
+
<h2 class="text-base md:text-lg font-bold text-slate-900 dark:text-slate-100">Projects</h2>
|
|
240
|
+
<div class="flex items-center gap-2">
|
|
241
|
+
<button
|
|
242
|
+
type="button"
|
|
243
|
+
class="flex items-center justify-center w-8 h-8 bg-violet-500/10 dark:bg-violet-500/15 border border-violet-500/20 rounded-lg text-violet-600 dark:text-violet-400 cursor-pointer transition-all duration-150 hover:bg-violet-500/20"
|
|
244
|
+
onclick={openAddProject}
|
|
245
|
+
aria-label="Add project"
|
|
246
|
+
title="Add project"
|
|
247
|
+
>
|
|
248
|
+
<Icon name="lucide:plus" class="w-4 h-4" />
|
|
249
|
+
</button>
|
|
250
|
+
<button
|
|
251
|
+
type="button"
|
|
252
|
+
class="p-1.5 md:p-2 rounded-lg text-slate-500 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-violet-500/10 transition-colors"
|
|
253
|
+
onclick={closeProjectMenu}
|
|
254
|
+
aria-label="Close modal"
|
|
255
|
+
>
|
|
256
|
+
<svg class="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
257
|
+
<path
|
|
258
|
+
stroke-linecap="round"
|
|
259
|
+
stroke-linejoin="round"
|
|
260
|
+
stroke-width="2"
|
|
261
|
+
d="M6 18L18 6M6 6l12 12"
|
|
262
|
+
/>
|
|
263
|
+
</svg>
|
|
264
|
+
</button>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
{/snippet}
|
|
268
|
+
|
|
269
|
+
{#snippet children()}
|
|
270
|
+
<!-- Search Box -->
|
|
271
|
+
{#if projectState.projects.length > 0}
|
|
272
|
+
<div class="mb-4">
|
|
273
|
+
<div
|
|
274
|
+
class="flex items-center gap-2 py-2.5 px-3.5 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-lg"
|
|
275
|
+
>
|
|
276
|
+
<Icon name="lucide:search" class="w-4 h-4 text-slate-500 dark:text-slate-400 shrink-0" />
|
|
277
|
+
<input
|
|
278
|
+
type="text"
|
|
279
|
+
bind:value={searchQuery}
|
|
280
|
+
placeholder="Search projects..."
|
|
281
|
+
class="flex-1 bg-transparent border-none outline-none text-slate-900 dark:text-slate-100 text-sm placeholder:text-slate-500 dark:placeholder:text-slate-400"
|
|
282
|
+
/>
|
|
283
|
+
{#if searchQuery}
|
|
284
|
+
<button
|
|
285
|
+
type="button"
|
|
286
|
+
class="flex items-center justify-center w-5 h-5 bg-transparent border-none rounded text-slate-400 cursor-pointer transition-all duration-150 hover:text-slate-600 dark:hover:text-slate-300"
|
|
287
|
+
onclick={() => (searchQuery = '')}
|
|
288
|
+
aria-label="Clear search"
|
|
289
|
+
>
|
|
290
|
+
<Icon name="lucide:x" class="w-3.5 h-3.5" />
|
|
291
|
+
</button>
|
|
292
|
+
{/if}
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
{/if}
|
|
296
|
+
|
|
297
|
+
{#if projectState.projects.length === 0}
|
|
298
|
+
<div class="flex flex-col items-center gap-3 py-8 text-slate-600 dark:text-slate-500 text-sm">
|
|
299
|
+
<Icon name="lucide:folder-x" class="w-12 h-12 text-slate-400 opacity-40" />
|
|
300
|
+
<p class="font-medium">No projects yet</p>
|
|
301
|
+
<p class="text-xs text-slate-500 dark:text-slate-500">Create your first project below</p>
|
|
302
|
+
</div>
|
|
303
|
+
{:else}
|
|
304
|
+
<div class="space-y-2">
|
|
305
|
+
{#each filteredProjects() as project (project.id)}
|
|
306
|
+
{@const isActive = projectState.currentProject?.id === project.id}
|
|
307
|
+
<div
|
|
308
|
+
class="flex items-center gap-2 w-full p-3 bg-transparent border border-slate-200 dark:border-slate-700 rounded-lg text-slate-900 dark:text-slate-100 text-sm text-left transition-all duration-150
|
|
309
|
+
{isActive
|
|
310
|
+
? 'border-violet-300 dark:border-violet-600 bg-violet-50 dark:bg-violet-900/10'
|
|
311
|
+
: ''}"
|
|
312
|
+
>
|
|
313
|
+
<button
|
|
314
|
+
type="button"
|
|
315
|
+
class="flex items-center gap-3 flex-1 min-w-0 bg-transparent border-none cursor-pointer text-left"
|
|
316
|
+
onclick={() => {
|
|
317
|
+
import('$frontend/lib/stores/core/projects.svelte').then((m) =>
|
|
318
|
+
m.setCurrentProject(project)
|
|
319
|
+
);
|
|
320
|
+
closeProjectMenu();
|
|
321
|
+
}}
|
|
322
|
+
>
|
|
323
|
+
<div
|
|
324
|
+
class="relative w-8 h-8 {isActive
|
|
325
|
+
? 'bg-violet-200 dark:bg-violet-800/30'
|
|
326
|
+
: 'bg-violet-100 dark:bg-violet-900/20'} rounded-lg flex items-center justify-center flex-shrink-0"
|
|
327
|
+
>
|
|
328
|
+
<Icon name="lucide:folder" class="text-violet-600 dark:text-violet-400 w-4 h-4" />
|
|
329
|
+
<span
|
|
330
|
+
class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-white dark:border-slate-900 {getStatusColor(project.id ?? '')}"
|
|
331
|
+
></span>
|
|
332
|
+
</div>
|
|
333
|
+
<div class="flex-1 min-w-0">
|
|
334
|
+
<div class="flex items-center gap-2">
|
|
335
|
+
<p class="font-semibold text-slate-900 dark:text-slate-100 truncate">
|
|
336
|
+
{project.name}
|
|
337
|
+
</p>
|
|
338
|
+
{#if isActive}
|
|
339
|
+
<span
|
|
340
|
+
class="inline-flex items-center gap-1 px-2 py-0.5 bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-300 text-xs font-medium rounded-full"
|
|
341
|
+
>
|
|
342
|
+
<Icon name="lucide:circle-check" class="w-3 h-3" />
|
|
343
|
+
Active
|
|
344
|
+
</span>
|
|
345
|
+
{/if}
|
|
346
|
+
</div>
|
|
347
|
+
<p class="text-xs text-slate-500 dark:text-slate-400 truncate font-mono">
|
|
348
|
+
{project.path}
|
|
349
|
+
</p>
|
|
350
|
+
</div>
|
|
351
|
+
</button>
|
|
352
|
+
<ProjectUserAvatars projectStatus={presenceState.statuses.get(project.id ?? '')} maxVisible={2} />
|
|
353
|
+
<button
|
|
354
|
+
type="button"
|
|
355
|
+
class="flex items-center justify-center w-8 h-8 bg-transparent border-none rounded-lg text-slate-400 dark:text-slate-500 cursor-pointer transition-all duration-150 hover:bg-red-500/15 hover:text-red-500 shrink-0"
|
|
356
|
+
onclick={(e) => handleDeleteClick(project, e)}
|
|
357
|
+
aria-label="Delete project"
|
|
358
|
+
title="Delete"
|
|
359
|
+
>
|
|
360
|
+
<Icon name="lucide:trash-2" class="w-4 h-4" />
|
|
361
|
+
</button>
|
|
362
|
+
</div>
|
|
363
|
+
{:else}
|
|
364
|
+
<div
|
|
365
|
+
class="flex flex-col items-center gap-2 py-8 text-slate-500 dark:text-slate-400 text-sm"
|
|
366
|
+
>
|
|
367
|
+
<Icon name="lucide:search-x" class="w-10 h-10 opacity-40" />
|
|
368
|
+
<p class="font-medium">No projects found</p>
|
|
369
|
+
<button
|
|
370
|
+
type="button"
|
|
371
|
+
class="text-xs text-violet-600 dark:text-violet-400 underline cursor-pointer hover:text-violet-700 dark:hover:text-violet-300"
|
|
372
|
+
onclick={() => (searchQuery = '')}
|
|
373
|
+
>
|
|
374
|
+
Clear search
|
|
375
|
+
</button>
|
|
376
|
+
</div>
|
|
377
|
+
{/each}
|
|
378
|
+
</div>
|
|
379
|
+
{/if}
|
|
380
|
+
{/snippet}
|
|
381
|
+
</Modal>
|
|
382
|
+
|
|
383
|
+
<!-- Folder Browser -->
|
|
384
|
+
<FolderBrowser
|
|
385
|
+
bind:isOpen={showFolderBrowser}
|
|
386
|
+
onClose={closeFolderBrowser}
|
|
387
|
+
onSelect={createProjectFromFolder}
|
|
388
|
+
/>
|
|
389
|
+
|
|
390
|
+
<!-- Delete Confirmation Dialog -->
|
|
391
|
+
<Dialog
|
|
392
|
+
bind:isOpen={showDeleteDialog}
|
|
393
|
+
onClose={closeDeleteDialog}
|
|
394
|
+
type="error"
|
|
395
|
+
title="Delete Project"
|
|
396
|
+
message='This will remove "{projectToDelete?.name}" from your project list. The actual project files on disk will not be deleted.'
|
|
397
|
+
confirmText="Delete"
|
|
398
|
+
cancelText="Cancel"
|
|
399
|
+
onConfirm={confirmDeleteProject}
|
|
400
|
+
/>
|
|
401
|
+
|
|
402
|
+
<!-- Tunnel Modal -->
|
|
403
|
+
<TunnelModal bind:isOpen={showTunnelModal} onClose={() => (showTunnelModal = false)} />
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { browser } from '$frontend/lib/app-environment';
|
|
3
|
+
import { onMount, onDestroy } from 'svelte';
|
|
4
|
+
import PanelHeader from './PanelHeader.svelte';
|
|
5
|
+
import ChatPanel from './panels/ChatPanel.svelte';
|
|
6
|
+
import PreviewPanel from './panels/PreviewPanel.svelte';
|
|
7
|
+
import FilesPanel from './panels/FilesPanel.svelte';
|
|
8
|
+
import TerminalPanel from './panels/TerminalPanel.svelte';
|
|
9
|
+
import GitPanel from './panels/GitPanel.svelte';
|
|
10
|
+
import HistoryModal from '$frontend/lib/components/history/HistoryModal.svelte';
|
|
11
|
+
import { workspaceState, type PanelId } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
panelId: PanelId;
|
|
15
|
+
noPadding?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { panelId, noPadding = false }: Props = $props();
|
|
19
|
+
|
|
20
|
+
const panel = $derived(workspaceState.panels[panelId]);
|
|
21
|
+
const isMinimized = $derived(panel?.minimized ?? false);
|
|
22
|
+
|
|
23
|
+
// Panel refs for actions
|
|
24
|
+
let chatPanelRef: any = $state();
|
|
25
|
+
let filesPanelRef: any = $state();
|
|
26
|
+
let terminalPanelRef: any = $state();
|
|
27
|
+
let previewPanelRef: any = $state();
|
|
28
|
+
let gitPanelRef: any = $state();
|
|
29
|
+
// History modal state
|
|
30
|
+
let showHistoryModal = $state(false);
|
|
31
|
+
|
|
32
|
+
// Mobile detection
|
|
33
|
+
let isMobile = $state(false);
|
|
34
|
+
|
|
35
|
+
function handleResize() {
|
|
36
|
+
if (browser) {
|
|
37
|
+
isMobile = window.innerWidth < 1024;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function openHistoryModal() {
|
|
42
|
+
showHistoryModal = true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function closeHistoryModal() {
|
|
46
|
+
showHistoryModal = false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
onMount(() => {
|
|
50
|
+
handleResize();
|
|
51
|
+
if (browser) {
|
|
52
|
+
window.addEventListener('resize', handleResize);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
onDestroy(() => {
|
|
57
|
+
if (browser) {
|
|
58
|
+
window.removeEventListener('resize', handleResize);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<div
|
|
64
|
+
class="flex flex-col h-full {isMobile
|
|
65
|
+
? 'bg-transparent'
|
|
66
|
+
: 'bg-white/90 dark:bg-slate-900/60 backdrop-blur-3 border border-slate-200 dark:border-slate-800 rounded-xl'} overflow-hidden"
|
|
67
|
+
>
|
|
68
|
+
<!-- Panel Header -->
|
|
69
|
+
<PanelHeader
|
|
70
|
+
{panelId}
|
|
71
|
+
{chatPanelRef}
|
|
72
|
+
{filesPanelRef}
|
|
73
|
+
{terminalPanelRef}
|
|
74
|
+
{previewPanelRef}
|
|
75
|
+
{gitPanelRef}
|
|
76
|
+
onHistoryOpen={openHistoryModal}
|
|
77
|
+
/>
|
|
78
|
+
|
|
79
|
+
<!-- Panel Content -->
|
|
80
|
+
{#if !isMinimized}
|
|
81
|
+
<div class="flex-1 overflow-hidden {noPadding ? '' : panelId === 'chat' ? 'p-3' : ''}">
|
|
82
|
+
{#if panelId === 'chat'}
|
|
83
|
+
<ChatPanel bind:this={chatPanelRef} />
|
|
84
|
+
{:else if panelId === 'preview'}
|
|
85
|
+
<PreviewPanel bind:this={previewPanelRef} />
|
|
86
|
+
{:else if panelId === 'files'}
|
|
87
|
+
<FilesPanel bind:this={filesPanelRef} />
|
|
88
|
+
{:else if panelId === 'terminal'}
|
|
89
|
+
<TerminalPanel bind:this={terminalPanelRef} />
|
|
90
|
+
{:else if panelId === 'git'}
|
|
91
|
+
<GitPanel bind:this={gitPanelRef} />
|
|
92
|
+
{/if}
|
|
93
|
+
</div>
|
|
94
|
+
{/if}
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<!-- History Modal (only for chat panel) -->
|
|
98
|
+
{#if panelId === 'chat'}
|
|
99
|
+
<HistoryModal bind:isOpen={showHistoryModal} onClose={closeHistoryModal} />
|
|
100
|
+
{/if}
|