@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,382 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onMount } from 'svelte';
|
|
3
|
+
import { fade } from 'svelte/transition';
|
|
4
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
5
|
+
import { projectState, setCurrentProject, removeProject } from '$frontend/lib/stores/core/projects.svelte';
|
|
6
|
+
import { workspaceState, toggleNavigator } from '$frontend/lib/stores/ui/workspace.svelte';
|
|
7
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
8
|
+
import { openSettingsModal } from '$frontend/lib/stores/ui/settings-modal.svelte';
|
|
9
|
+
import { projectStatusService } from '$frontend/lib/services/project';
|
|
10
|
+
import { presenceState } from '$frontend/lib/stores/core/presence.svelte';
|
|
11
|
+
import type { Project } from '$shared/types/database/schema';
|
|
12
|
+
import { debug } from '$shared/utils/logger';
|
|
13
|
+
import FolderBrowser from '$frontend/lib/components/common/FolderBrowser.svelte';
|
|
14
|
+
import Dialog from '$frontend/lib/components/common/Dialog.svelte';
|
|
15
|
+
import ViewMenu from '$frontend/lib/components/workspace/ViewMenu.svelte';
|
|
16
|
+
import TunnelButton from '$frontend/lib/components/tunnel/TunnelButton.svelte';
|
|
17
|
+
import TunnelModal from '$frontend/lib/components/tunnel/TunnelModal.svelte';
|
|
18
|
+
import ProjectUserAvatars from '$frontend/lib/components/common/ProjectUserAvatars.svelte';
|
|
19
|
+
import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
|
|
20
|
+
import ws from '$frontend/lib/utils/ws';
|
|
21
|
+
|
|
22
|
+
// State
|
|
23
|
+
let existingProjects = $state<Project[]>([]);
|
|
24
|
+
let showFolderBrowser = $state(false);
|
|
25
|
+
let showDeleteDialog = $state(false);
|
|
26
|
+
let projectToDelete = $state<Project | null>(null);
|
|
27
|
+
let searchQuery = $state('');
|
|
28
|
+
let showTunnelModal = $state(false);
|
|
29
|
+
|
|
30
|
+
// Derived
|
|
31
|
+
const isCollapsed = $derived(workspaceState.navigatorCollapsed);
|
|
32
|
+
const currentProjectId = $derived(projectState.currentProject?.id);
|
|
33
|
+
const navigatorWidth = $derived(
|
|
34
|
+
workspaceState.navigatorCollapsed ? 48 : workspaceState.navigatorWidth
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const filteredProjects = $derived(() => {
|
|
38
|
+
if (!searchQuery.trim()) return existingProjects;
|
|
39
|
+
const query = searchQuery.toLowerCase();
|
|
40
|
+
return existingProjects.filter(
|
|
41
|
+
(p) => p.name.toLowerCase().includes(query) || p.path.toLowerCase().includes(query)
|
|
42
|
+
);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Load projects
|
|
46
|
+
async function loadProjects() {
|
|
47
|
+
try {
|
|
48
|
+
const projects = await ws.http('projects:list', {});
|
|
49
|
+
if (Array.isArray(projects)) {
|
|
50
|
+
existingProjects = projects;
|
|
51
|
+
}
|
|
52
|
+
} catch (error) {
|
|
53
|
+
debug.error('workspace', 'Failed to load projects:', error);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Select project
|
|
58
|
+
async function selectProject(project: Project) {
|
|
59
|
+
await setCurrentProject(project);
|
|
60
|
+
await projectStatusService.startTracking(project.id);
|
|
61
|
+
|
|
62
|
+
// Update last opened (handled by projects:get in setCurrentProject)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Create project from folder
|
|
66
|
+
async function createProjectFromFolder(folderPath: string, folderName: string) {
|
|
67
|
+
try {
|
|
68
|
+
showFolderBrowser = false;
|
|
69
|
+
|
|
70
|
+
// Check if already exists
|
|
71
|
+
const existing = existingProjects.find((p) => p.path === folderPath);
|
|
72
|
+
if (existing) {
|
|
73
|
+
await selectProject(existing);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const newProject = await ws.http('projects:create', { name: folderName, path: folderPath });
|
|
78
|
+
|
|
79
|
+
await setCurrentProject(newProject);
|
|
80
|
+
await loadProjects();
|
|
81
|
+
} catch (error) {
|
|
82
|
+
debug.error('workspace', 'Failed to create project:', error);
|
|
83
|
+
addNotification({
|
|
84
|
+
type: 'error',
|
|
85
|
+
title: 'Error',
|
|
86
|
+
message: 'Failed to create project',
|
|
87
|
+
duration: 5000
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Delete project
|
|
93
|
+
async function confirmDeleteProject() {
|
|
94
|
+
if (!projectToDelete) return;
|
|
95
|
+
const deleteId = projectToDelete.id!;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await ws.http('projects:delete', { id: deleteId });
|
|
99
|
+
removeProject(deleteId);
|
|
100
|
+
existingProjects = existingProjects.filter(p => p.id !== deleteId);
|
|
101
|
+
showDeleteDialog = false;
|
|
102
|
+
projectToDelete = null;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
debug.error('workspace', 'Failed to delete project:', error);
|
|
105
|
+
addNotification({
|
|
106
|
+
type: 'error',
|
|
107
|
+
title: 'Error',
|
|
108
|
+
message: 'Failed to delete project',
|
|
109
|
+
duration: 5000
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Get status color from presence data (single source of truth)
|
|
115
|
+
// Green only when the project is the CURRENT project AND the current chat session has an active stream
|
|
116
|
+
function getStatusColor(projectId: string): string {
|
|
117
|
+
if (projectId !== currentProjectId) return 'bg-slate-500/30';
|
|
118
|
+
const status = presenceState.statuses.get(projectId);
|
|
119
|
+
const currentChatSessionId = sessionState.currentSession?.id;
|
|
120
|
+
if (!status?.streams || !currentChatSessionId) return 'bg-slate-500/30';
|
|
121
|
+
const hasActiveForSession = status.streams.some(
|
|
122
|
+
(s: any) => s.status === 'active' && s.chatSessionId === currentChatSessionId
|
|
123
|
+
);
|
|
124
|
+
if (hasActiveForSession) return 'bg-emerald-500';
|
|
125
|
+
return 'bg-slate-500/30';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Close folder browser
|
|
129
|
+
function closeFolderBrowser() {
|
|
130
|
+
showFolderBrowser = false;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Close delete dialog
|
|
134
|
+
function closeDeleteDialog() {
|
|
135
|
+
showDeleteDialog = false;
|
|
136
|
+
projectToDelete = null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Handle delete button click
|
|
140
|
+
function handleDeleteClick(project: Project, event: MouseEvent) {
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
projectToDelete = project;
|
|
143
|
+
showDeleteDialog = true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
onMount(async () => {
|
|
147
|
+
await loadProjects();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Get project initials (max 2 characters)
|
|
151
|
+
function getProjectInitials(name: string): string {
|
|
152
|
+
const words = name.trim().split(/[\s-_]+/);
|
|
153
|
+
if (words.length >= 2) {
|
|
154
|
+
// Multiple words: take first letter of first 2 words
|
|
155
|
+
return (words[0][0] + words[1][0]).toUpperCase();
|
|
156
|
+
}
|
|
157
|
+
// Single word: take first 2 letters
|
|
158
|
+
return name.substring(0, 2).toUpperCase();
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<!-- Project Navigator Sidebar -->
|
|
163
|
+
<aside
|
|
164
|
+
class="shrink-0 h-full bg-white dark:bg-slate-900/95 border-r border-slate-200 dark:border-slate-800 transition-[width] duration-200 z-20"
|
|
165
|
+
style="width: {navigatorWidth}px"
|
|
166
|
+
aria-label="Project Navigator"
|
|
167
|
+
>
|
|
168
|
+
<nav
|
|
169
|
+
class="flex flex-col h-full bg-slate-50 dark:bg-slate-900/95 transition-all duration-200 {isCollapsed
|
|
170
|
+
? 'items-center'
|
|
171
|
+
: ''}"
|
|
172
|
+
>
|
|
173
|
+
<!-- Header -->
|
|
174
|
+
<header
|
|
175
|
+
class="flex items-center justify-between p-4 border-b border-slate-200 dark:border-slate-800 {isCollapsed
|
|
176
|
+
? 'justify-center px-2'
|
|
177
|
+
: ''}"
|
|
178
|
+
>
|
|
179
|
+
{#if !isCollapsed}
|
|
180
|
+
<div class="flex items-center gap-2.5" in:fade={{ duration: 150 }}>
|
|
181
|
+
<img src="/favicon.svg" alt="Clopen" class="w-8 h-8 rounded-lg" />
|
|
182
|
+
<span class="text-base font-semibold text-slate-900 dark:text-slate-100">Clopen</span>
|
|
183
|
+
</div>
|
|
184
|
+
{/if}
|
|
185
|
+
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
class="flex items-center justify-center w-8 h-8 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"
|
|
189
|
+
onclick={toggleNavigator}
|
|
190
|
+
aria-label={isCollapsed ? 'Expand navigator' : 'Collapse navigator'}
|
|
191
|
+
title={isCollapsed ? 'Expand' : 'Collapse'}
|
|
192
|
+
>
|
|
193
|
+
<Icon
|
|
194
|
+
name={isCollapsed ? 'lucide:panel-left-open' : 'lucide:panel-left-close'}
|
|
195
|
+
class="w-5 h-5"
|
|
196
|
+
/>
|
|
197
|
+
</button>
|
|
198
|
+
</header>
|
|
199
|
+
|
|
200
|
+
{#if !isCollapsed}
|
|
201
|
+
<!-- Search -->
|
|
202
|
+
<div
|
|
203
|
+
class="flex items-center gap-2.5 mx-4 my-3 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"
|
|
204
|
+
in:fade={{ duration: 150 }}
|
|
205
|
+
>
|
|
206
|
+
<Icon name="lucide:search" class="w-4 h-4 text-slate-600 dark:text-slate-500 shrink-0" />
|
|
207
|
+
<input
|
|
208
|
+
type="text"
|
|
209
|
+
bind:value={searchQuery}
|
|
210
|
+
placeholder="Search projects..."
|
|
211
|
+
class="flex-1 bg-transparent border-none outline-none text-slate-900 dark:text-slate-100 text-sm placeholder:text-slate-600 dark:placeholder:text-slate-500"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<!-- Projects List -->
|
|
216
|
+
<div class="flex-1 flex flex-col min-h-0 px-3" in:fade={{ duration: 150 }}>
|
|
217
|
+
<div
|
|
218
|
+
class="flex items-center justify-between py-2 px-1 text-xs font-semibold text-slate-600 dark:text-slate-500 uppercase tracking-wider"
|
|
219
|
+
>
|
|
220
|
+
<span>Projects</span>
|
|
221
|
+
<button
|
|
222
|
+
type="button"
|
|
223
|
+
class="flex items-center justify-center w-6 h-6 bg-transparent border-none rounded-md text-slate-600 dark:text-slate-500 cursor-pointer transition-all duration-150 hover:bg-violet-500/20 hover:text-violet-600"
|
|
224
|
+
onclick={() => (showFolderBrowser = true)}
|
|
225
|
+
aria-label="Add project"
|
|
226
|
+
title="Add project"
|
|
227
|
+
>
|
|
228
|
+
<Icon name="lucide:plus" class="w-4 h-4" />
|
|
229
|
+
</button>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="flex-1 overflow-y-auto flex flex-col">
|
|
233
|
+
{#each filteredProjects() as project (project.id)}
|
|
234
|
+
<div
|
|
235
|
+
class="flex items-center gap-2.5 py-2.5 px-3 bg-transparent border-none rounded-lg text-slate-600 dark:text-slate-400 text-sm text-left cursor-pointer transition-all duration-150 relative group
|
|
236
|
+
hover:bg-violet-500/10
|
|
237
|
+
{currentProjectId === project.id
|
|
238
|
+
? 'bg-violet-500/10 dark:bg-violet-500/20 text-slate-900 dark:text-slate-100'
|
|
239
|
+
: ''}"
|
|
240
|
+
role="button"
|
|
241
|
+
title={project.path}
|
|
242
|
+
tabindex="0"
|
|
243
|
+
onclick={() => selectProject(project)}
|
|
244
|
+
onkeydown={(e) => e.key === 'Enter' && selectProject(project)}
|
|
245
|
+
>
|
|
246
|
+
<div class="relative shrink-0">
|
|
247
|
+
<Icon name="lucide:folder" class="w-4 h-4" />
|
|
248
|
+
<span
|
|
249
|
+
class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-slate-50 dark:border-slate-900/95 {getStatusColor(project.id ?? '')}"
|
|
250
|
+
></span>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<div class="flex-1 flex items-center justify-between gap-2 min-w-0">
|
|
254
|
+
<div class="flex-1 min-w-0">
|
|
255
|
+
<span class="block overflow-hidden text-ellipsis whitespace-nowrap">{project.name}</span>
|
|
256
|
+
<span class="block text-3xs text-slate-400 dark:text-slate-500 overflow-hidden text-ellipsis whitespace-nowrap font-mono leading-tight">{project.path}</span>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="flex items-center gap-1 shrink-0">
|
|
259
|
+
<ProjectUserAvatars projectStatus={presenceState.statuses.get(project.id ?? '')} maxVisible={2} />
|
|
260
|
+
<button
|
|
261
|
+
type="button"
|
|
262
|
+
class="flex items-center justify-center w-6 h-6 bg-transparent border-none rounded-md text-slate-400 dark:text-slate-600 cursor-pointer transition-all duration-150 hover:bg-red-500/20 hover:text-red-500 shrink-0"
|
|
263
|
+
onclick={(e) => handleDeleteClick(project, e)}
|
|
264
|
+
aria-label="Delete project"
|
|
265
|
+
title="Delete"
|
|
266
|
+
>
|
|
267
|
+
<Icon name="lucide:trash-2" class="w-3.5 h-3.5" />
|
|
268
|
+
</button>
|
|
269
|
+
</div>
|
|
270
|
+
</div>
|
|
271
|
+
</div>
|
|
272
|
+
{:else}
|
|
273
|
+
<div
|
|
274
|
+
class="flex flex-col items-center gap-3 py-8 px-4 text-slate-600 dark:text-slate-500 text-sm text-center"
|
|
275
|
+
>
|
|
276
|
+
<Icon name="lucide:folder-plus" class="w-8 h-8 opacity-40" />
|
|
277
|
+
<span>No projects yet</span>
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
class="py-2 px-4 bg-violet-500/10 dark:bg-violet-500/15 border border-violet-500/20 dark:border-violet-500/30 rounded-lg text-violet-600 text-xs font-medium cursor-pointer transition-all duration-150 hover:bg-violet-500/20 dark:hover:bg-violet-500/25"
|
|
281
|
+
onclick={() => (showFolderBrowser = true)}
|
|
282
|
+
>
|
|
283
|
+
Add your first project
|
|
284
|
+
</button>
|
|
285
|
+
</div>
|
|
286
|
+
{/each}
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<!-- Footer Actions -->
|
|
291
|
+
<footer class="flex flex-col p-3 border-t border-slate-200 dark:border-slate-800" in:fade={{ duration: 150 }}>
|
|
292
|
+
<ViewMenu />
|
|
293
|
+
<TunnelButton onClick={() => (showTunnelModal = true)} />
|
|
294
|
+
|
|
295
|
+
<button
|
|
296
|
+
type="button"
|
|
297
|
+
class="flex items-center gap-2.5 w-full py-2.5 px-3 bg-transparent border-none rounded-lg text-slate-500 text-sm cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
298
|
+
onclick={() => openSettingsModal()}
|
|
299
|
+
>
|
|
300
|
+
<Icon name="lucide:settings" class="w-4 h-4" />
|
|
301
|
+
<span>Settings</span>
|
|
302
|
+
</button>
|
|
303
|
+
</footer>
|
|
304
|
+
{:else}
|
|
305
|
+
<!-- Collapsed State: Icon Buttons -->
|
|
306
|
+
<div class="flex-1 flex flex-col items-center gap-2 py-4 px-2">
|
|
307
|
+
<button
|
|
308
|
+
type="button"
|
|
309
|
+
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 relative hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
310
|
+
onclick={() => (showFolderBrowser = true)}
|
|
311
|
+
title="Add Project"
|
|
312
|
+
>
|
|
313
|
+
<Icon name="lucide:folder-plus" class="w-5 h-5" />
|
|
314
|
+
</button>
|
|
315
|
+
|
|
316
|
+
<div class="w-6 h-px bg-violet-500/10 my-1"></div>
|
|
317
|
+
|
|
318
|
+
{#each existingProjects.slice(0, 5) as project (project.id)}
|
|
319
|
+
{@const projectStatus = presenceState.statuses.get(project.id ?? '')}
|
|
320
|
+
{@const activeUserCount = (projectStatus?.activeUsers || []).length}
|
|
321
|
+
<button
|
|
322
|
+
type="button"
|
|
323
|
+
class="flex items-center justify-center w-9 h-9 border-none rounded-lg cursor-pointer transition-all duration-150 relative font-semibold text-sm
|
|
324
|
+
{currentProjectId === project.id
|
|
325
|
+
? 'bg-violet-500/10 dark:bg-violet-500/20 text-violet-700 dark:text-violet-300'
|
|
326
|
+
: 'bg-slate-200/50 dark:bg-slate-800/50 text-slate-600 dark:text-slate-400 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100'}"
|
|
327
|
+
onclick={() => selectProject(project)}
|
|
328
|
+
title={project.name}
|
|
329
|
+
>
|
|
330
|
+
<span>{getProjectInitials(project.name)}</span>
|
|
331
|
+
<span
|
|
332
|
+
class="absolute bottom-1 right-1 w-2.5 h-2.5 rounded-full border-2 border-slate-50 dark:border-slate-900/95 {getStatusColor(project.id ?? '')}"
|
|
333
|
+
></span>
|
|
334
|
+
{#if activeUserCount > 0}
|
|
335
|
+
<span
|
|
336
|
+
class="absolute -top-1 -right-1 min-w-4 h-4 px-0.5 rounded-full bg-violet-500 text-white text-3xs font-bold flex items-center justify-center border-2 border-slate-50 dark:border-slate-900/95"
|
|
337
|
+
>
|
|
338
|
+
{activeUserCount}
|
|
339
|
+
</span>
|
|
340
|
+
{/if}
|
|
341
|
+
</button>
|
|
342
|
+
{/each}
|
|
343
|
+
</div>
|
|
344
|
+
|
|
345
|
+
<footer class="flex flex-col gap-2 py-3 px-2 border-t border-slate-200 dark:border-slate-800">
|
|
346
|
+
<ViewMenu collapsed={true} />
|
|
347
|
+
<TunnelButton collapsed={true} onClick={() => (showTunnelModal = true)} />
|
|
348
|
+
|
|
349
|
+
<button
|
|
350
|
+
type="button"
|
|
351
|
+
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 relative hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
|
|
352
|
+
onclick={() => openSettingsModal()}
|
|
353
|
+
title="Settings"
|
|
354
|
+
>
|
|
355
|
+
<Icon name="lucide:settings" class="w-5 h-5" />
|
|
356
|
+
</button>
|
|
357
|
+
</footer>
|
|
358
|
+
{/if}
|
|
359
|
+
</nav>
|
|
360
|
+
</aside>
|
|
361
|
+
|
|
362
|
+
<!-- Folder Browser (includes its own Modal) -->
|
|
363
|
+
<FolderBrowser
|
|
364
|
+
bind:isOpen={showFolderBrowser}
|
|
365
|
+
onClose={closeFolderBrowser}
|
|
366
|
+
onSelect={createProjectFromFolder}
|
|
367
|
+
/>
|
|
368
|
+
|
|
369
|
+
<!-- Delete Confirmation Dialog -->
|
|
370
|
+
<Dialog
|
|
371
|
+
bind:isOpen={showDeleteDialog}
|
|
372
|
+
onClose={closeDeleteDialog}
|
|
373
|
+
type="error"
|
|
374
|
+
title="Delete Project"
|
|
375
|
+
message='This will remove "{projectToDelete?.name}" from your project list. The actual project files on disk will not be deleted.'
|
|
376
|
+
confirmText="Delete"
|
|
377
|
+
cancelText="Cancel"
|
|
378
|
+
onConfirm={confirmDeleteProject}
|
|
379
|
+
/>
|
|
380
|
+
|
|
381
|
+
<!-- Tunnel Modal -->
|
|
382
|
+
<TunnelModal bind:isOpen={showTunnelModal} onClose={() => (showTunnelModal = false)} />
|