@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,458 @@
|
|
|
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 type { GitBranchInfo, GitRemote } from '$shared/types/git';
|
|
6
|
+
import { projectState } from '$frontend/lib/stores/core/projects.svelte';
|
|
7
|
+
import { showError, showInfo } from '$frontend/lib/stores/ui/notification.svelte';
|
|
8
|
+
import { debug } from '$shared/utils/logger';
|
|
9
|
+
import ws from '$frontend/lib/utils/ws';
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
branchInfo: GitBranchInfo | null;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
onSwitch: (name: string) => void;
|
|
16
|
+
onCreate: (name: string) => void;
|
|
17
|
+
onDelete: (name: string, force?: boolean) => void;
|
|
18
|
+
onRename: (oldName: string, newName: string) => void;
|
|
19
|
+
onMerge: (name: string) => void;
|
|
20
|
+
onRemotesChanged?: () => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { isOpen, branchInfo, onClose, onSwitch, onCreate, onDelete, onRename, onMerge, onRemotesChanged }: Props = $props();
|
|
24
|
+
|
|
25
|
+
const projectId = $derived(projectState.currentProject?.id || '');
|
|
26
|
+
|
|
27
|
+
let searchQuery = $state('');
|
|
28
|
+
let showCreateForm = $state(false);
|
|
29
|
+
let newBranchName = $state('');
|
|
30
|
+
let activeTab = $state<'local' | 'remote'>('local');
|
|
31
|
+
|
|
32
|
+
// Remote management
|
|
33
|
+
let remotes = $state<GitRemote[]>([]);
|
|
34
|
+
let isLoadingRemotes = $state(false);
|
|
35
|
+
let showAddRemoteForm = $state(false);
|
|
36
|
+
let newRemoteName = $state('origin');
|
|
37
|
+
let newRemoteUrl = $state('');
|
|
38
|
+
let showRemoteSection = $state(false);
|
|
39
|
+
|
|
40
|
+
// Confirm dialog
|
|
41
|
+
let showConfirmDialog = $state(false);
|
|
42
|
+
let confirmConfig = $state({
|
|
43
|
+
title: '',
|
|
44
|
+
message: '',
|
|
45
|
+
type: 'warning' as 'info' | 'warning' | 'error' | 'success',
|
|
46
|
+
confirmText: 'Confirm',
|
|
47
|
+
onConfirm: () => {}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const filteredLocal = $derived(
|
|
51
|
+
branchInfo?.local.filter(b =>
|
|
52
|
+
b.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
53
|
+
) ?? []
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const filteredRemote = $derived(
|
|
57
|
+
branchInfo?.remote.filter(b =>
|
|
58
|
+
b.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
59
|
+
) ?? []
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
function handleCreate() {
|
|
63
|
+
if (!newBranchName.trim()) return;
|
|
64
|
+
onCreate(newBranchName.trim());
|
|
65
|
+
newBranchName = '';
|
|
66
|
+
showCreateForm = false;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function handleSwitchRemote(remoteBranch: string) {
|
|
70
|
+
const parts = remoteBranch.split('/');
|
|
71
|
+
const localName = parts.slice(1).join('/');
|
|
72
|
+
onSwitch(localName);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handleClose() {
|
|
76
|
+
searchQuery = '';
|
|
77
|
+
newBranchName = '';
|
|
78
|
+
showCreateForm = false;
|
|
79
|
+
showAddRemoteForm = false;
|
|
80
|
+
newRemoteName = 'origin';
|
|
81
|
+
newRemoteUrl = '';
|
|
82
|
+
onClose();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function loadRemotes() {
|
|
86
|
+
if (!projectId) return;
|
|
87
|
+
isLoadingRemotes = true;
|
|
88
|
+
try {
|
|
89
|
+
remotes = await ws.http('git:remotes', { projectId });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
debug.error('git', 'Failed to load remotes:', err);
|
|
92
|
+
} finally {
|
|
93
|
+
isLoadingRemotes = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async function handleAddRemote() {
|
|
98
|
+
if (!newRemoteName.trim() || !newRemoteUrl.trim() || !projectId) return;
|
|
99
|
+
try {
|
|
100
|
+
await ws.http('git:add-remote', { projectId, name: newRemoteName.trim(), url: newRemoteUrl.trim() });
|
|
101
|
+
showInfo('Remote Added', `Remote "${newRemoteName.trim()}" connected.`);
|
|
102
|
+
newRemoteName = 'origin';
|
|
103
|
+
newRemoteUrl = '';
|
|
104
|
+
showAddRemoteForm = false;
|
|
105
|
+
await loadRemotes();
|
|
106
|
+
onRemotesChanged?.();
|
|
107
|
+
} catch (err) {
|
|
108
|
+
debug.error('git', 'Failed to add remote:', err);
|
|
109
|
+
showError('Failed to Add Remote', err instanceof Error ? err.message : 'Unknown error');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleRemoveRemote(name: string) {
|
|
114
|
+
confirmConfig = {
|
|
115
|
+
title: 'Remove Remote',
|
|
116
|
+
message: `Disconnect remote "${name}"? This will not delete the remote repository itself.`,
|
|
117
|
+
type: 'warning',
|
|
118
|
+
confirmText: 'Remove',
|
|
119
|
+
onConfirm: async () => {
|
|
120
|
+
if (!projectId) return;
|
|
121
|
+
try {
|
|
122
|
+
await ws.http('git:remove-remote', { projectId, name });
|
|
123
|
+
showInfo('Remote Removed', `Remote "${name}" disconnected.`);
|
|
124
|
+
await loadRemotes();
|
|
125
|
+
onRemotesChanged?.();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
debug.error('git', 'Failed to remove remote:', err);
|
|
128
|
+
showError('Failed to Remove Remote', err instanceof Error ? err.message : 'Unknown error');
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
showConfirmDialog = true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Load remotes when modal opens
|
|
136
|
+
$effect(() => {
|
|
137
|
+
if (isOpen) {
|
|
138
|
+
loadRemotes();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
</script>
|
|
142
|
+
|
|
143
|
+
<Modal isOpen={isOpen} onClose={handleClose} size="md">
|
|
144
|
+
{#snippet header()}
|
|
145
|
+
<div class="flex items-center justify-between px-4 py-3 md:px-6 md:py-4">
|
|
146
|
+
<div class="flex items-center gap-2.5">
|
|
147
|
+
<Icon name="lucide:git-branch" class="w-5 h-5 text-violet-600" />
|
|
148
|
+
<h2 class="text-base md:text-lg font-bold text-slate-900 dark:text-slate-100">Branches</h2>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="flex items-center gap-2">
|
|
151
|
+
<button
|
|
152
|
+
type="button"
|
|
153
|
+
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"
|
|
154
|
+
onclick={handleClose}
|
|
155
|
+
aria-label="Close modal"
|
|
156
|
+
>
|
|
157
|
+
<svg class="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
158
|
+
<path
|
|
159
|
+
stroke-linecap="round"
|
|
160
|
+
stroke-linejoin="round"
|
|
161
|
+
stroke-width="2"
|
|
162
|
+
d="M6 18L18 6M6 6l12 12"
|
|
163
|
+
/>
|
|
164
|
+
</svg>
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
{/snippet}
|
|
169
|
+
|
|
170
|
+
{#snippet children()}
|
|
171
|
+
<!-- Create branch form -->
|
|
172
|
+
{#if showCreateForm}
|
|
173
|
+
<div class="mb-4 p-3 bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 rounded-lg space-y-2">
|
|
174
|
+
<input
|
|
175
|
+
type="text"
|
|
176
|
+
bind:value={newBranchName}
|
|
177
|
+
placeholder="New branch name..."
|
|
178
|
+
class="w-full px-3 py-2 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg text-slate-900 dark:text-slate-100 outline-none focus:border-violet-500/40"
|
|
179
|
+
onkeydown={(e) => e.key === 'Enter' && handleCreate()}
|
|
180
|
+
autofocus
|
|
181
|
+
/>
|
|
182
|
+
<div class="flex gap-2">
|
|
183
|
+
<button
|
|
184
|
+
type="button"
|
|
185
|
+
class="flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors cursor-pointer border-none
|
|
186
|
+
{newBranchName.trim()
|
|
187
|
+
? 'bg-violet-600 text-white hover:bg-violet-700'
|
|
188
|
+
: 'bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-500 cursor-not-allowed'}"
|
|
189
|
+
onclick={handleCreate}
|
|
190
|
+
disabled={!newBranchName.trim()}
|
|
191
|
+
>
|
|
192
|
+
Create Branch
|
|
193
|
+
</button>
|
|
194
|
+
<button
|
|
195
|
+
type="button"
|
|
196
|
+
class="px-3 py-2 text-sm font-medium bg-transparent border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer"
|
|
197
|
+
onclick={() => { showCreateForm = false; newBranchName = ''; }}
|
|
198
|
+
>
|
|
199
|
+
Cancel
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
{:else}
|
|
204
|
+
<button
|
|
205
|
+
type="button"
|
|
206
|
+
class="flex items-center justify-center gap-2 w-full mb-4 py-2.5 px-3 border border-dashed border-slate-300 dark:border-slate-600 rounded-lg text-sm text-slate-500 hover:text-violet-600 hover:border-violet-400 transition-colors cursor-pointer bg-transparent"
|
|
207
|
+
onclick={() => showCreateForm = true}
|
|
208
|
+
>
|
|
209
|
+
<Icon name="lucide:plus" class="w-4 h-4" />
|
|
210
|
+
<span>Create New Branch</span>
|
|
211
|
+
</button>
|
|
212
|
+
{/if}
|
|
213
|
+
|
|
214
|
+
<!-- Search -->
|
|
215
|
+
<div class="mb-4">
|
|
216
|
+
<div 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">
|
|
217
|
+
<Icon name="lucide:search" class="w-4 h-4 text-slate-500 dark:text-slate-400 shrink-0" />
|
|
218
|
+
<input
|
|
219
|
+
type="text"
|
|
220
|
+
bind:value={searchQuery}
|
|
221
|
+
placeholder="Search branches..."
|
|
222
|
+
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"
|
|
223
|
+
/>
|
|
224
|
+
{#if searchQuery}
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
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"
|
|
228
|
+
onclick={() => (searchQuery = '')}
|
|
229
|
+
aria-label="Clear search"
|
|
230
|
+
>
|
|
231
|
+
<Icon name="lucide:x" class="w-3.5 h-3.5" />
|
|
232
|
+
</button>
|
|
233
|
+
{/if}
|
|
234
|
+
</div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<!-- Tabs -->
|
|
238
|
+
<div class="flex gap-1 mb-3">
|
|
239
|
+
<button
|
|
240
|
+
type="button"
|
|
241
|
+
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors cursor-pointer border-none
|
|
242
|
+
{activeTab === 'local'
|
|
243
|
+
? 'bg-violet-500/10 text-violet-600'
|
|
244
|
+
: 'bg-transparent text-slate-500 hover:text-slate-700 dark:hover:text-slate-300'}"
|
|
245
|
+
onclick={() => activeTab = 'local'}
|
|
246
|
+
>
|
|
247
|
+
Local ({filteredLocal.length})
|
|
248
|
+
</button>
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
class="px-3 py-1.5 text-sm font-medium rounded-lg transition-colors cursor-pointer border-none
|
|
252
|
+
{activeTab === 'remote'
|
|
253
|
+
? 'bg-violet-500/10 text-violet-600'
|
|
254
|
+
: 'bg-transparent text-slate-500 hover:text-slate-700 dark:hover:text-slate-300'}"
|
|
255
|
+
onclick={() => activeTab = 'remote'}
|
|
256
|
+
>
|
|
257
|
+
Remote ({filteredRemote.length})
|
|
258
|
+
</button>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- Branch list -->
|
|
262
|
+
<div class="space-y-1.5 max-h-80 overflow-y-auto">
|
|
263
|
+
{#if activeTab === 'local'}
|
|
264
|
+
{#each filteredLocal as branch (branch.name)}
|
|
265
|
+
<div
|
|
266
|
+
class="group flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors
|
|
267
|
+
{branch.isCurrent
|
|
268
|
+
? 'bg-violet-50 dark:bg-violet-900/10 border border-violet-200 dark:border-violet-800'
|
|
269
|
+
: 'hover:bg-slate-100 dark:hover:bg-slate-800/50 border border-slate-200 dark:border-slate-700'}"
|
|
270
|
+
>
|
|
271
|
+
<div class="flex items-center gap-2.5 flex-1 min-w-0">
|
|
272
|
+
{#if branch.isCurrent}
|
|
273
|
+
<Icon name="lucide:check" class="w-4 h-4 text-violet-600 shrink-0" />
|
|
274
|
+
{:else}
|
|
275
|
+
<div class="w-4 h-4 shrink-0"></div>
|
|
276
|
+
{/if}
|
|
277
|
+
<span class="text-sm text-slate-900 dark:text-slate-100 truncate {branch.isCurrent ? 'font-semibold' : ''}">
|
|
278
|
+
{branch.name}
|
|
279
|
+
</span>
|
|
280
|
+
{#if branch.isCurrent}
|
|
281
|
+
<span class="inline-flex items-center px-2 py-0.5 bg-violet-100 dark:bg-violet-900/20 text-violet-600 dark:text-violet-400 text-xs font-medium rounded-full">
|
|
282
|
+
Current
|
|
283
|
+
</span>
|
|
284
|
+
{/if}
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
{#if !branch.isCurrent}
|
|
288
|
+
<div class="flex items-center gap-1 -my-2">
|
|
289
|
+
<button
|
|
290
|
+
type="button"
|
|
291
|
+
class="flex items-center justify-center w-8 h-8 rounded-lg text-slate-500 hover:bg-violet-500/10 hover:text-violet-600 transition-colors cursor-pointer bg-transparent border-none"
|
|
292
|
+
onclick={() => onSwitch(branch.name)}
|
|
293
|
+
title="Switch to this branch"
|
|
294
|
+
>
|
|
295
|
+
<Icon name="lucide:arrow-right" class="w-4 h-4" />
|
|
296
|
+
</button>
|
|
297
|
+
<button
|
|
298
|
+
type="button"
|
|
299
|
+
class="flex items-center justify-center w-8 h-8 rounded-lg text-slate-500 hover:bg-blue-500/10 hover:text-blue-500 transition-colors cursor-pointer bg-transparent border-none"
|
|
300
|
+
onclick={() => onMerge(branch.name)}
|
|
301
|
+
title="Merge into current branch"
|
|
302
|
+
>
|
|
303
|
+
<Icon name="lucide:git-merge" class="w-4 h-4" />
|
|
304
|
+
</button>
|
|
305
|
+
<button
|
|
306
|
+
type="button"
|
|
307
|
+
class="flex items-center justify-center w-8 h-8 rounded-lg text-slate-500 hover:bg-red-500/10 hover:text-red-500 transition-colors cursor-pointer bg-transparent border-none"
|
|
308
|
+
onclick={() => onDelete(branch.name)}
|
|
309
|
+
title="Delete branch"
|
|
310
|
+
>
|
|
311
|
+
<Icon name="lucide:trash-2" class="w-4 h-4" />
|
|
312
|
+
</button>
|
|
313
|
+
</div>
|
|
314
|
+
{/if}
|
|
315
|
+
</div>
|
|
316
|
+
{:else}
|
|
317
|
+
<div class="flex flex-col items-center gap-2 py-8 text-slate-500 dark:text-slate-400 text-sm">
|
|
318
|
+
<Icon name="lucide:search-x" class="w-10 h-10 opacity-40" />
|
|
319
|
+
<p class="font-medium">No local branches found</p>
|
|
320
|
+
</div>
|
|
321
|
+
{/each}
|
|
322
|
+
{:else if activeTab === 'remote'}
|
|
323
|
+
{#each filteredRemote as branch (branch.name)}
|
|
324
|
+
<div
|
|
325
|
+
class="group flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800/50 transition-colors border border-slate-200 dark:border-slate-700"
|
|
326
|
+
>
|
|
327
|
+
<div class="flex items-center gap-2.5 flex-1 min-w-0">
|
|
328
|
+
<Icon name="lucide:cloud" class="w-4 h-4 text-slate-400 shrink-0" />
|
|
329
|
+
<span class="text-sm text-slate-900 dark:text-slate-100 truncate">{branch.name}</span>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div class="items-center gap-1 shrink-0 hidden group-hover:flex">
|
|
333
|
+
<button
|
|
334
|
+
type="button"
|
|
335
|
+
class="flex items-center justify-center w-8 h-8 rounded-lg text-slate-500 hover:bg-violet-500/10 hover:text-violet-600 transition-colors cursor-pointer bg-transparent border-none"
|
|
336
|
+
onclick={() => handleSwitchRemote(branch.name)}
|
|
337
|
+
title="Checkout remote branch"
|
|
338
|
+
>
|
|
339
|
+
<Icon name="lucide:arrow-right" class="w-4 h-4" />
|
|
340
|
+
</button>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
{:else}
|
|
344
|
+
<div class="flex flex-col items-center gap-2 py-8 text-slate-500 dark:text-slate-400 text-sm">
|
|
345
|
+
<Icon name="lucide:search-x" class="w-10 h-10 opacity-40" />
|
|
346
|
+
<p class="font-medium">No remote branches found</p>
|
|
347
|
+
</div>
|
|
348
|
+
{/each}
|
|
349
|
+
{/if}
|
|
350
|
+
</div>
|
|
351
|
+
|
|
352
|
+
<!-- Remote Connections Section -->
|
|
353
|
+
<div class="mt-4 pt-4 border-t border-slate-200 dark:border-slate-700">
|
|
354
|
+
<button
|
|
355
|
+
type="button"
|
|
356
|
+
class="flex items-center gap-2 w-full text-left bg-transparent border-none cursor-pointer px-0 py-1"
|
|
357
|
+
onclick={() => showRemoteSection = !showRemoteSection}
|
|
358
|
+
>
|
|
359
|
+
<Icon name={showRemoteSection ? 'lucide:chevron-down' : 'lucide:chevron-right'} class="w-3.5 h-3.5 text-slate-400" />
|
|
360
|
+
<Icon name="lucide:server" class="w-3.5 h-3.5 text-slate-500" />
|
|
361
|
+
<span class="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide">
|
|
362
|
+
Remote Servers ({remotes.length})
|
|
363
|
+
</span>
|
|
364
|
+
</button>
|
|
365
|
+
|
|
366
|
+
{#if showRemoteSection}
|
|
367
|
+
<div class="mt-2 space-y-2">
|
|
368
|
+
{#if isLoadingRemotes}
|
|
369
|
+
<div class="flex items-center justify-center py-4">
|
|
370
|
+
<div class="w-4 h-4 border-2 border-slate-200 dark:border-slate-700 border-t-violet-600 rounded-full animate-spin"></div>
|
|
371
|
+
</div>
|
|
372
|
+
{:else}
|
|
373
|
+
<!-- Existing remotes -->
|
|
374
|
+
{#each remotes as remote (remote.name)}
|
|
375
|
+
<div class="group flex items-center gap-2.5 px-3 py-2 rounded-lg border border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-800/30">
|
|
376
|
+
<div class="flex items-center justify-center w-6 h-6 rounded-md bg-slate-100 dark:bg-slate-800">
|
|
377
|
+
<Icon name="lucide:globe" class="w-3.5 h-3.5 text-slate-500" />
|
|
378
|
+
</div>
|
|
379
|
+
<div class="flex-1 min-w-0">
|
|
380
|
+
<div class="text-sm font-medium text-slate-900 dark:text-slate-100">{remote.name}</div>
|
|
381
|
+
<div class="text-xs text-slate-500 dark:text-slate-400 truncate font-mono">{remote.fetchUrl}</div>
|
|
382
|
+
</div>
|
|
383
|
+
<button
|
|
384
|
+
type="button"
|
|
385
|
+
class="flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-red-500/10 hover:text-red-500 transition-colors cursor-pointer bg-transparent border-none shrink-0 opacity-0 group-hover:opacity-100"
|
|
386
|
+
onclick={() => handleRemoveRemote(remote.name)}
|
|
387
|
+
title="Disconnect remote"
|
|
388
|
+
>
|
|
389
|
+
<Icon name="lucide:unlink" class="w-3.5 h-3.5" />
|
|
390
|
+
</button>
|
|
391
|
+
</div>
|
|
392
|
+
{/each}
|
|
393
|
+
|
|
394
|
+
<!-- Add remote form -->
|
|
395
|
+
{#if showAddRemoteForm}
|
|
396
|
+
<div class="p-3 bg-slate-50 dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 rounded-lg space-y-2">
|
|
397
|
+
<input
|
|
398
|
+
type="text"
|
|
399
|
+
bind:value={newRemoteName}
|
|
400
|
+
placeholder="Name (e.g. origin)"
|
|
401
|
+
class="w-full px-3 py-2 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg text-slate-900 dark:text-slate-100 outline-none focus:border-violet-500/40"
|
|
402
|
+
/>
|
|
403
|
+
<input
|
|
404
|
+
type="text"
|
|
405
|
+
bind:value={newRemoteUrl}
|
|
406
|
+
placeholder="URL (e.g. https://github.com/user/repo.git)"
|
|
407
|
+
class="w-full px-3 py-2 text-sm bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg text-slate-900 dark:text-slate-100 outline-none focus:border-violet-500/40 font-mono"
|
|
408
|
+
onkeydown={(e) => e.key === 'Enter' && handleAddRemote()}
|
|
409
|
+
autofocus
|
|
410
|
+
/>
|
|
411
|
+
<div class="flex gap-2">
|
|
412
|
+
<button
|
|
413
|
+
type="button"
|
|
414
|
+
class="flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors cursor-pointer border-none
|
|
415
|
+
{newRemoteName.trim() && newRemoteUrl.trim()
|
|
416
|
+
? 'bg-violet-600 text-white hover:bg-violet-700'
|
|
417
|
+
: 'bg-slate-200 dark:bg-slate-700 text-slate-400 dark:text-slate-500 cursor-not-allowed'}"
|
|
418
|
+
onclick={handleAddRemote}
|
|
419
|
+
disabled={!newRemoteName.trim() || !newRemoteUrl.trim()}
|
|
420
|
+
>
|
|
421
|
+
Connect
|
|
422
|
+
</button>
|
|
423
|
+
<button
|
|
424
|
+
type="button"
|
|
425
|
+
class="px-3 py-2 text-sm font-medium bg-transparent border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer"
|
|
426
|
+
onclick={() => { showAddRemoteForm = false; newRemoteName = 'origin'; newRemoteUrl = ''; }}
|
|
427
|
+
>
|
|
428
|
+
Cancel
|
|
429
|
+
</button>
|
|
430
|
+
</div>
|
|
431
|
+
</div>
|
|
432
|
+
{:else}
|
|
433
|
+
<button
|
|
434
|
+
type="button"
|
|
435
|
+
class="flex items-center gap-2 w-full px-3 py-2 text-xs text-slate-500 hover:text-violet-600 hover:bg-violet-500/5 rounded-lg transition-colors cursor-pointer bg-transparent border-none"
|
|
436
|
+
onclick={() => { showAddRemoteForm = true; showRemoteSection = true; }}
|
|
437
|
+
>
|
|
438
|
+
<Icon name="lucide:plus" class="w-3.5 h-3.5" />
|
|
439
|
+
<span>Add Remote Server</span>
|
|
440
|
+
</button>
|
|
441
|
+
{/if}
|
|
442
|
+
{/if}
|
|
443
|
+
</div>
|
|
444
|
+
{/if}
|
|
445
|
+
</div>
|
|
446
|
+
{/snippet}
|
|
447
|
+
</Modal>
|
|
448
|
+
|
|
449
|
+
<!-- Confirm Dialog -->
|
|
450
|
+
<Dialog
|
|
451
|
+
bind:isOpen={showConfirmDialog}
|
|
452
|
+
onClose={() => showConfirmDialog = false}
|
|
453
|
+
type={confirmConfig.type}
|
|
454
|
+
title={confirmConfig.title}
|
|
455
|
+
message={confirmConfig.message}
|
|
456
|
+
confirmText={confirmConfig.confirmText}
|
|
457
|
+
onConfirm={confirmConfig.onConfirm}
|
|
458
|
+
/>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import FileChangeItem from './FileChangeItem.svelte';
|
|
4
|
+
import type { GitFileChange } from '$shared/types/git';
|
|
5
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
title: string;
|
|
9
|
+
icon: IconName;
|
|
10
|
+
files: GitFileChange[];
|
|
11
|
+
section: 'staged' | 'unstaged' | 'untracked' | 'conflicted';
|
|
12
|
+
collapsed?: boolean;
|
|
13
|
+
onStage?: (path: string) => void;
|
|
14
|
+
onUnstage?: (path: string) => void;
|
|
15
|
+
onDiscard?: (path: string) => void;
|
|
16
|
+
onStageAll?: () => void;
|
|
17
|
+
onUnstageAll?: () => void;
|
|
18
|
+
onDiscardAll?: () => void;
|
|
19
|
+
onViewDiff?: (file: GitFileChange, section: string) => void;
|
|
20
|
+
onResolve?: (path: string) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
title, icon, files, section,
|
|
25
|
+
collapsed = false,
|
|
26
|
+
onStage, onUnstage, onDiscard,
|
|
27
|
+
onStageAll, onUnstageAll, onDiscardAll,
|
|
28
|
+
onViewDiff, onResolve
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
let isCollapsed = $state(collapsed);
|
|
32
|
+
</script>
|
|
33
|
+
|
|
34
|
+
{#if files.length > 0}
|
|
35
|
+
<div class="mb-1">
|
|
36
|
+
<!-- Section header -->
|
|
37
|
+
<div
|
|
38
|
+
onclick={() => isCollapsed = !isCollapsed}
|
|
39
|
+
class="group flex items-center gap-2 py-3 px-2 cursor-pointer select-none hover:bg-slate-100 dark:hover:bg-slate-800/40 rounded-md transition-colors">
|
|
40
|
+
<div
|
|
41
|
+
class="flex items-center gap-2 flex-1 min-w-0 bg-transparent border-none text-left cursor-pointer p-0"
|
|
42
|
+
>
|
|
43
|
+
<Icon
|
|
44
|
+
name={isCollapsed ? 'lucide:chevron-right' : 'lucide:chevron-down'}
|
|
45
|
+
class="w-4 h-4 text-slate-500 shrink-0"
|
|
46
|
+
/>
|
|
47
|
+
<!-- <Icon name={icon} class="w-4 h-4 text-slate-500 shrink-0" /> -->
|
|
48
|
+
<span class="text-xs font-semibold text-slate-700 dark:text-slate-300 uppercase tracking-wide">
|
|
49
|
+
{title}
|
|
50
|
+
</span>
|
|
51
|
+
<span class="text-xs font-medium text-slate-400 dark:text-slate-600 ml-0.5">
|
|
52
|
+
{files.length}
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- Bulk actions (hidden until hover) -->
|
|
57
|
+
<div class="flex items-center gap-0.5 shrink-0 -my-2">
|
|
58
|
+
{#if section === 'staged' && onUnstageAll}
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
class="flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-red-500/10 hover:text-red-500 transition-colors bg-transparent border-none cursor-pointer"
|
|
62
|
+
onclick={(e) => { e.stopPropagation(); onUnstageAll?.(); }}
|
|
63
|
+
title="Unstage All"
|
|
64
|
+
>
|
|
65
|
+
<Icon name="lucide:minus" class="w-4 h-4" />
|
|
66
|
+
</button>
|
|
67
|
+
{:else if (section === 'unstaged' || section === 'untracked') && onStageAll}
|
|
68
|
+
{#if onDiscardAll}
|
|
69
|
+
<button
|
|
70
|
+
type="button"
|
|
71
|
+
class="flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-red-500/10 hover:text-red-500 transition-colors bg-transparent border-none cursor-pointer"
|
|
72
|
+
onclick={(e) => { e.stopPropagation(); onDiscardAll?.(); }}
|
|
73
|
+
title="Discard All"
|
|
74
|
+
>
|
|
75
|
+
<Icon name="lucide:undo-2" class="w-4 h-4" />
|
|
76
|
+
</button>
|
|
77
|
+
{/if}
|
|
78
|
+
<button
|
|
79
|
+
type="button"
|
|
80
|
+
class="flex items-center justify-center w-7 h-7 rounded-md text-slate-400 hover:bg-emerald-500/10 hover:text-emerald-500 transition-colors bg-transparent border-none cursor-pointer"
|
|
81
|
+
onclick={(e) => { e.stopPropagation(); onStageAll?.(); }}
|
|
82
|
+
title="Stage All"
|
|
83
|
+
>
|
|
84
|
+
<Icon name="lucide:plus" class="w-4 h-4" />
|
|
85
|
+
</button>
|
|
86
|
+
{/if}
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Files list -->
|
|
91
|
+
{#if !isCollapsed}
|
|
92
|
+
<div class="ml-2">
|
|
93
|
+
{#each files as file (file.path)}
|
|
94
|
+
<FileChangeItem
|
|
95
|
+
{file}
|
|
96
|
+
{section}
|
|
97
|
+
{onStage}
|
|
98
|
+
{onUnstage}
|
|
99
|
+
{onDiscard}
|
|
100
|
+
{onViewDiff}
|
|
101
|
+
{onResolve}
|
|
102
|
+
/>
|
|
103
|
+
{/each}
|
|
104
|
+
</div>
|
|
105
|
+
{/if}
|
|
106
|
+
</div>
|
|
107
|
+
{/if}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
stagedCount: number;
|
|
6
|
+
isCommitting: boolean;
|
|
7
|
+
onCommit: (message: string) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { stagedCount, isCommitting, onCommit }: Props = $props();
|
|
11
|
+
|
|
12
|
+
let commitMessage = $state('');
|
|
13
|
+
let textareaEl = $state<HTMLTextAreaElement | null>(null);
|
|
14
|
+
|
|
15
|
+
function handleCommit() {
|
|
16
|
+
if (!commitMessage.trim() || stagedCount === 0) return;
|
|
17
|
+
onCommit(commitMessage.trim());
|
|
18
|
+
commitMessage = '';
|
|
19
|
+
autoResize();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
23
|
+
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
handleCommit();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function autoResize() {
|
|
30
|
+
if (!textareaEl) return;
|
|
31
|
+
// Reset to single line to measure content
|
|
32
|
+
textareaEl.style.height = 'auto';
|
|
33
|
+
// Line height is ~20px for text-xs, so 5 lines max = 100px
|
|
34
|
+
const lineHeight = 20;
|
|
35
|
+
const maxHeight = lineHeight * 5;
|
|
36
|
+
const scrollHeight = textareaEl.scrollHeight;
|
|
37
|
+
textareaEl.style.height = Math.min(scrollHeight, maxHeight) + 'px';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function handleInput() {
|
|
41
|
+
autoResize();
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div class="px-2 pb-2">
|
|
46
|
+
<div class="flex flex-col gap-1.5">
|
|
47
|
+
<textarea
|
|
48
|
+
bind:this={textareaEl}
|
|
49
|
+
bind:value={commitMessage}
|
|
50
|
+
placeholder="Commit message..."
|
|
51
|
+
class="w-full px-2.5 py-2 text-sm bg-white dark:bg-slate-800/80 border border-slate-200 dark:border-slate-700 rounded-md text-slate-900 dark:text-slate-100 placeholder:text-slate-400 dark:placeholder:text-slate-600 resize-none outline-none focus:border-violet-500/40 focus:ring-1 focus:ring-violet-500/20 transition-colors overflow-hidden"
|
|
52
|
+
rows="1"
|
|
53
|
+
style="height: 27px"
|
|
54
|
+
onkeydown={handleKeydown}
|
|
55
|
+
oninput={handleInput}
|
|
56
|
+
disabled={isCommitting}
|
|
57
|
+
></textarea>
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
class="flex items-center justify-center gap-1.5 w-full py-1.5 px-3 rounded-md text-xs font-medium transition-all duration-150
|
|
61
|
+
{stagedCount > 0 && commitMessage.trim() && !isCommitting
|
|
62
|
+
? 'bg-violet-600 text-white hover:bg-violet-700 cursor-pointer'
|
|
63
|
+
: 'bg-slate-100 dark:bg-slate-800 text-slate-400 dark:text-slate-600 cursor-not-allowed'}"
|
|
64
|
+
onclick={handleCommit}
|
|
65
|
+
disabled={stagedCount === 0 || !commitMessage.trim() || isCommitting}
|
|
66
|
+
>
|
|
67
|
+
{#if isCommitting}
|
|
68
|
+
<div class="w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin"></div>
|
|
69
|
+
<span>Committing...</span>
|
|
70
|
+
{:else}
|
|
71
|
+
<Icon name="lucide:check" class="w-3.5 h-3.5" />
|
|
72
|
+
<span>Commit{stagedCount > 0 ? ` (${stagedCount})` : ''}</span>
|
|
73
|
+
{/if}
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|