@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,33 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { WriteToolInput } from '$shared/types/messaging';
|
|
3
|
+
import { FileHeader, DiffBlock } from './components';
|
|
4
|
+
import TextMessage from '../formatters/TextMessage.svelte';
|
|
5
|
+
|
|
6
|
+
const { toolInput }: { toolInput: WriteToolInput } = $props();
|
|
7
|
+
|
|
8
|
+
const filePath = toolInput.input.file_path || '';
|
|
9
|
+
const fileName = filePath.split(/[/\\]/).pop() || filePath || 'unknown';
|
|
10
|
+
const content = toolInput.input.content || '';
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<FileHeader
|
|
14
|
+
{filePath}
|
|
15
|
+
{fileName}
|
|
16
|
+
iconColor="text-violet-600 dark:text-violet-400"
|
|
17
|
+
/>
|
|
18
|
+
|
|
19
|
+
<!-- Code Changes -->
|
|
20
|
+
<div class="mt-4">
|
|
21
|
+
<DiffBlock oldString="" newString={content} label="Write" />
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<!-- Tool Result -->
|
|
25
|
+
<!-- {#if toolInput.$result}
|
|
26
|
+
<div class="mt-4 bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3">
|
|
27
|
+
{#if typeof toolInput.$result.content === 'string'}
|
|
28
|
+
<TextMessage content={toolInput.$result.content} />
|
|
29
|
+
{:else}
|
|
30
|
+
<TextMessage content={JSON.stringify(toolInput.$result.content)} />
|
|
31
|
+
{/if}
|
|
32
|
+
</div>
|
|
33
|
+
{/if} -->
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
4
|
+
import { removeCommonIndentation } from '../../shared/utils';
|
|
5
|
+
import { isTerminalOutput, processAnsiCodes, escapeHtml } from '$frontend/lib/utils/terminalFormatter';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
code: string;
|
|
9
|
+
type: 'add' | 'remove' | 'neutral';
|
|
10
|
+
label?: string;
|
|
11
|
+
showTruncation?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const { code, type, label }: Props = $props();
|
|
15
|
+
|
|
16
|
+
function formatCode(code: string) {
|
|
17
|
+
// First remove common indentation
|
|
18
|
+
const cleanCode = removeCommonIndentation(code);
|
|
19
|
+
|
|
20
|
+
// Check if it's terminal output and process ANSI codes if needed
|
|
21
|
+
if (isTerminalOutput(cleanCode)) {
|
|
22
|
+
// For terminal output, process ANSI codes but don't escape HTML
|
|
23
|
+
// since we're displaying it in a <pre> tag
|
|
24
|
+
return processAnsiCodes(cleanCode);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// For non-terminal code, don't escape HTML either since it's in <pre>
|
|
28
|
+
// The <pre> tag already handles text display safely
|
|
29
|
+
return cleanCode;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const styles = {
|
|
33
|
+
add: {
|
|
34
|
+
icon: 'lucide:circle-plus' as IconName,
|
|
35
|
+
iconColor: 'text-green-500',
|
|
36
|
+
labelColor: 'text-green-700 dark:text-green-300',
|
|
37
|
+
bgColor: 'bg-green-50 dark:bg-green-950',
|
|
38
|
+
borderColor: 'border-green-200 dark:border-green-800',
|
|
39
|
+
textColor: 'text-green-800 dark:text-green-200'
|
|
40
|
+
},
|
|
41
|
+
remove: {
|
|
42
|
+
icon: 'lucide:circle-minus' as IconName,
|
|
43
|
+
iconColor: 'text-red-500',
|
|
44
|
+
labelColor: 'text-red-700 dark:text-red-300',
|
|
45
|
+
bgColor: 'bg-red-50 dark:bg-red-950',
|
|
46
|
+
borderColor: 'border-red-200 dark:border-red-800',
|
|
47
|
+
textColor: 'text-red-800 dark:text-red-200'
|
|
48
|
+
},
|
|
49
|
+
neutral: {
|
|
50
|
+
icon: 'lucide:code' as IconName,
|
|
51
|
+
iconColor: 'text-slate-500',
|
|
52
|
+
labelColor: 'text-slate-700 dark:text-slate-300',
|
|
53
|
+
bgColor: 'bg-slate-50 dark:bg-slate-950',
|
|
54
|
+
borderColor: 'border-slate-200 dark:border-slate-800',
|
|
55
|
+
textColor: 'text-slate-800 dark:text-slate-200'
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const style = styles[type];
|
|
60
|
+
const formattedCode = formatCode(code);
|
|
61
|
+
const isTerminal = isTerminalOutput(code);
|
|
62
|
+
</script>
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
{#if label}
|
|
66
|
+
<div class="flex items-center gap-2 mb-2">
|
|
67
|
+
<!-- <Icon name={style.icon} class="{style.iconColor} w-4 h-4" /> -->
|
|
68
|
+
<span class="text-xs font-medium {style.labelColor}">{label}:</span>
|
|
69
|
+
</div>
|
|
70
|
+
{/if}
|
|
71
|
+
<div class="max-h-72 {style.bgColor} border {style.borderColor} rounded-md py-2.5 px-3 whitespace-pre-wrap overflow-auto">
|
|
72
|
+
{#if isTerminal}
|
|
73
|
+
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
|
74
|
+
<pre class="text-xs {style.textColor} font-mono">{@html formattedCode}</pre>
|
|
75
|
+
{:else}
|
|
76
|
+
<pre class="text-xs {style.textColor} font-mono">{formattedCode}</pre>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import { removeCommonIndentationFromLines } from '../../shared/utils';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
oldString: string;
|
|
7
|
+
newString: string;
|
|
8
|
+
label?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface DiffLine {
|
|
12
|
+
type: 'unchanged' | 'removed' | 'added';
|
|
13
|
+
content: string;
|
|
14
|
+
highlights?: Array<{ start: number; end: number }>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface GroupedDiff {
|
|
18
|
+
type: 'unchanged' | 'change';
|
|
19
|
+
removed?: DiffLine[];
|
|
20
|
+
added?: DiffLine[];
|
|
21
|
+
lines?: DiffLine[];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { oldString, newString, label }: Props = $props();
|
|
25
|
+
|
|
26
|
+
// Myers diff algorithm for finding longest common subsequence
|
|
27
|
+
function findLCS(arr1: string[], arr2: string[]): number[][] {
|
|
28
|
+
const m = arr1.length;
|
|
29
|
+
const n = arr2.length;
|
|
30
|
+
const dp: number[][] = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
|
|
31
|
+
|
|
32
|
+
for (let i = 1; i <= m; i++) {
|
|
33
|
+
for (let j = 1; j <= n; j++) {
|
|
34
|
+
if (arr1[i - 1] === arr2[j - 1]) {
|
|
35
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
36
|
+
} else {
|
|
37
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return dp;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Character-level diff for highlighting specific changes within lines
|
|
46
|
+
function findCharacterDiff(oldLine: string, newLine: string): { oldHighlights: Array<{ start: number; end: number }>, newHighlights: Array<{ start: number; end: number }> } {
|
|
47
|
+
// Use patience diff algorithm for better results
|
|
48
|
+
const oldHighlights: Array<{ start: number; end: number }> = [];
|
|
49
|
+
const newHighlights: Array<{ start: number; end: number }> = [];
|
|
50
|
+
|
|
51
|
+
// Find common prefix
|
|
52
|
+
let prefixLen = 0;
|
|
53
|
+
const minLen = Math.min(oldLine.length, newLine.length);
|
|
54
|
+
while (prefixLen < minLen && oldLine[prefixLen] === newLine[prefixLen]) {
|
|
55
|
+
prefixLen++;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Find common suffix
|
|
59
|
+
let suffixLen = 0;
|
|
60
|
+
const maxSuffixLen = minLen - prefixLen;
|
|
61
|
+
while (suffixLen < maxSuffixLen &&
|
|
62
|
+
oldLine[oldLine.length - 1 - suffixLen] === newLine[newLine.length - 1 - suffixLen]) {
|
|
63
|
+
suffixLen++;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Extract the differing middle parts
|
|
67
|
+
const oldMiddleStart = prefixLen;
|
|
68
|
+
const oldMiddleEnd = oldLine.length - suffixLen;
|
|
69
|
+
const newMiddleStart = prefixLen;
|
|
70
|
+
const newMiddleEnd = newLine.length - suffixLen;
|
|
71
|
+
|
|
72
|
+
if (oldMiddleStart < oldMiddleEnd || newMiddleStart < newMiddleEnd) {
|
|
73
|
+
// There are differences
|
|
74
|
+
const oldMiddle = oldLine.substring(oldMiddleStart, oldMiddleEnd);
|
|
75
|
+
const newMiddle = newLine.substring(newMiddleStart, newMiddleEnd);
|
|
76
|
+
|
|
77
|
+
// If the middle parts are completely different or one is empty, highlight the whole middle
|
|
78
|
+
if (oldMiddle.length === 0 || newMiddle.length === 0 ||
|
|
79
|
+
!hasCommonSubstring(oldMiddle, newMiddle)) {
|
|
80
|
+
if (oldMiddle.length > 0) {
|
|
81
|
+
oldHighlights.push({ start: oldMiddleStart, end: oldMiddleEnd });
|
|
82
|
+
}
|
|
83
|
+
if (newMiddle.length > 0) {
|
|
84
|
+
newHighlights.push({ start: newMiddleStart, end: newMiddleEnd });
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
// Find fine-grained differences using recursive approach
|
|
88
|
+
const middleDiff = findFineDiff(oldMiddle, newMiddle);
|
|
89
|
+
|
|
90
|
+
// Adjust positions relative to full string
|
|
91
|
+
for (const range of middleDiff.oldRanges) {
|
|
92
|
+
oldHighlights.push({
|
|
93
|
+
start: oldMiddleStart + range.start,
|
|
94
|
+
end: oldMiddleStart + range.end
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
for (const range of middleDiff.newRanges) {
|
|
98
|
+
newHighlights.push({
|
|
99
|
+
start: newMiddleStart + range.start,
|
|
100
|
+
end: newMiddleStart + range.end
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { oldHighlights, newHighlights };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Helper function to check if strings have common substrings
|
|
110
|
+
function hasCommonSubstring(str1: string, str2: string, minLength: number = 3): boolean {
|
|
111
|
+
if (str1.length < minLength || str2.length < minLength) return false;
|
|
112
|
+
|
|
113
|
+
for (let i = 0; i <= str1.length - minLength; i++) {
|
|
114
|
+
const substr = str1.substring(i, i + minLength);
|
|
115
|
+
if (str2.includes(substr)) return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Fine-grained diff for the middle differing part
|
|
121
|
+
function findFineDiff(oldStr: string, newStr: string): {
|
|
122
|
+
oldRanges: Array<{ start: number; end: number }>,
|
|
123
|
+
newRanges: Array<{ start: number; end: number }>
|
|
124
|
+
} {
|
|
125
|
+
const oldRanges: Array<{ start: number; end: number }> = [];
|
|
126
|
+
const newRanges: Array<{ start: number; end: number }> = [];
|
|
127
|
+
|
|
128
|
+
// Use word-based chunking for better diff
|
|
129
|
+
const oldChunks = splitIntoChunks(oldStr);
|
|
130
|
+
const newChunks = splitIntoChunks(newStr);
|
|
131
|
+
|
|
132
|
+
// Find LCS of chunks
|
|
133
|
+
const dp = findLCS(oldChunks.map(c => c.text), newChunks.map(c => c.text));
|
|
134
|
+
|
|
135
|
+
let i = oldChunks.length;
|
|
136
|
+
let j = newChunks.length;
|
|
137
|
+
|
|
138
|
+
// Backtrack to find differences
|
|
139
|
+
while (i > 0 || j > 0) {
|
|
140
|
+
if (i > 0 && j > 0 && oldChunks[i - 1].text === newChunks[j - 1].text) {
|
|
141
|
+
// Chunks match
|
|
142
|
+
i--;
|
|
143
|
+
j--;
|
|
144
|
+
} else if (j === 0 || (i > 0 && dp[i][j] === dp[i - 1][j])) {
|
|
145
|
+
// Chunk deleted from old
|
|
146
|
+
const chunk = oldChunks[i - 1];
|
|
147
|
+
oldRanges.unshift({ start: chunk.start, end: chunk.end });
|
|
148
|
+
i--;
|
|
149
|
+
} else {
|
|
150
|
+
// Chunk added to new
|
|
151
|
+
const chunk = newChunks[j - 1];
|
|
152
|
+
newRanges.unshift({ start: chunk.start, end: chunk.end });
|
|
153
|
+
j--;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Merge adjacent ranges
|
|
158
|
+
return {
|
|
159
|
+
oldRanges: mergeRanges(oldRanges),
|
|
160
|
+
newRanges: mergeRanges(newRanges)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Split string into chunks (words and delimiters)
|
|
165
|
+
function splitIntoChunks(str: string): Array<{ text: string; start: number; end: number }> {
|
|
166
|
+
const chunks: Array<{ text: string; start: number; end: number }> = [];
|
|
167
|
+
const regex = /(\w+|[^\w]+)/g;
|
|
168
|
+
let match;
|
|
169
|
+
|
|
170
|
+
while ((match = regex.exec(str)) !== null) {
|
|
171
|
+
chunks.push({
|
|
172
|
+
text: match[0],
|
|
173
|
+
start: match.index,
|
|
174
|
+
end: match.index + match[0].length
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return chunks;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Merge adjacent or overlapping ranges
|
|
182
|
+
function mergeRanges(ranges: Array<{ start: number; end: number }>): Array<{ start: number; end: number }> {
|
|
183
|
+
if (ranges.length === 0) return [];
|
|
184
|
+
|
|
185
|
+
ranges.sort((a, b) => a.start - b.start);
|
|
186
|
+
const merged: Array<{ start: number; end: number }> = [];
|
|
187
|
+
let current = ranges[0];
|
|
188
|
+
|
|
189
|
+
for (let i = 1; i < ranges.length; i++) {
|
|
190
|
+
const next = ranges[i];
|
|
191
|
+
if (next.start <= current.end) {
|
|
192
|
+
// Merge overlapping or adjacent regions
|
|
193
|
+
current = { start: current.start, end: Math.max(current.end, next.end) };
|
|
194
|
+
} else {
|
|
195
|
+
merged.push(current);
|
|
196
|
+
current = next;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
merged.push(current);
|
|
200
|
+
|
|
201
|
+
return merged;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function computeDiff(oldStr: string, newStr: string): GroupedDiff[] {
|
|
205
|
+
const oldLines = oldStr.split('\n');
|
|
206
|
+
const newLines = newStr.split('\n');
|
|
207
|
+
|
|
208
|
+
// Remove common indentation from both old and new strings
|
|
209
|
+
const { lines: cleanOldLines } = removeCommonIndentationFromLines(oldLines);
|
|
210
|
+
const { lines: cleanNewLines } = removeCommonIndentationFromLines(newLines);
|
|
211
|
+
|
|
212
|
+
// Compute line-level diff using LCS
|
|
213
|
+
const dp = findLCS(cleanOldLines, cleanNewLines);
|
|
214
|
+
|
|
215
|
+
const diffLines: DiffLine[] = [];
|
|
216
|
+
let i = cleanOldLines.length;
|
|
217
|
+
let j = cleanNewLines.length;
|
|
218
|
+
|
|
219
|
+
// Backtrack to generate diff
|
|
220
|
+
while (i > 0 || j > 0) {
|
|
221
|
+
if (i > 0 && j > 0 && cleanOldLines[i - 1] === cleanNewLines[j - 1]) {
|
|
222
|
+
diffLines.unshift({ type: 'unchanged', content: cleanOldLines[i - 1] });
|
|
223
|
+
i--;
|
|
224
|
+
j--;
|
|
225
|
+
} else if (j === 0 || (i > 0 && dp[i][j] === dp[i - 1][j])) {
|
|
226
|
+
diffLines.unshift({ type: 'removed', content: cleanOldLines[i - 1] });
|
|
227
|
+
i--;
|
|
228
|
+
} else {
|
|
229
|
+
diffLines.unshift({ type: 'added', content: cleanNewLines[j - 1] });
|
|
230
|
+
j--;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Group consecutive changes and apply character-level diff
|
|
235
|
+
const grouped: GroupedDiff[] = [];
|
|
236
|
+
let current: GroupedDiff | null = null;
|
|
237
|
+
|
|
238
|
+
for (const line of diffLines) {
|
|
239
|
+
if (line.type === 'unchanged') {
|
|
240
|
+
if (current) {
|
|
241
|
+
grouped.push(current);
|
|
242
|
+
current = null;
|
|
243
|
+
}
|
|
244
|
+
grouped.push({ type: 'unchanged', lines: [line] });
|
|
245
|
+
} else {
|
|
246
|
+
if (!current) {
|
|
247
|
+
current = { type: 'change', removed: [], added: [] };
|
|
248
|
+
}
|
|
249
|
+
if (line.type === 'removed') {
|
|
250
|
+
current.removed!.push(line);
|
|
251
|
+
} else {
|
|
252
|
+
current.added!.push(line);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (current) {
|
|
258
|
+
grouped.push(current);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Apply character-level diff to changed line pairs
|
|
262
|
+
for (const group of grouped) {
|
|
263
|
+
if (group.type === 'change' && group.removed && group.added) {
|
|
264
|
+
const minLen = Math.min(group.removed.length, group.added.length);
|
|
265
|
+
|
|
266
|
+
// For paired lines, compute character-level diff
|
|
267
|
+
for (let idx = 0; idx < minLen; idx++) {
|
|
268
|
+
const oldLine = group.removed[idx];
|
|
269
|
+
const newLine = group.added[idx];
|
|
270
|
+
|
|
271
|
+
// Only compute character diff if lines are similar enough
|
|
272
|
+
if (oldLine.content.length > 0 && newLine.content.length > 0) {
|
|
273
|
+
const similarity = computeSimilarity(oldLine.content, newLine.content);
|
|
274
|
+
if (similarity > 0.3) { // Lines are at least 30% similar
|
|
275
|
+
const { oldHighlights, newHighlights } = findCharacterDiff(oldLine.content, newLine.content);
|
|
276
|
+
if (oldHighlights.length > 0) {
|
|
277
|
+
oldLine.highlights = oldHighlights;
|
|
278
|
+
}
|
|
279
|
+
if (newHighlights.length > 0) {
|
|
280
|
+
newLine.highlights = newHighlights;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return grouped;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Compute similarity ratio between two strings
|
|
292
|
+
function computeSimilarity(str1: string, str2: string): number {
|
|
293
|
+
const chars1 = str1.split('');
|
|
294
|
+
const chars2 = str2.split('');
|
|
295
|
+
const dp = findLCS(chars1, chars2);
|
|
296
|
+
const lcsLength = dp[chars1.length][chars2.length];
|
|
297
|
+
const maxLen = Math.max(chars1.length, chars2.length);
|
|
298
|
+
return maxLen === 0 ? 0 : lcsLength / maxLen;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Render text with highlights
|
|
302
|
+
function renderLineWithHighlights(line: DiffLine): string {
|
|
303
|
+
if (!line.highlights || line.highlights.length === 0) {
|
|
304
|
+
return line.content;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
let result = '';
|
|
308
|
+
let lastEnd = 0;
|
|
309
|
+
|
|
310
|
+
for (const highlight of line.highlights) {
|
|
311
|
+
// Add non-highlighted part
|
|
312
|
+
if (highlight.start > lastEnd) {
|
|
313
|
+
result += line.content.substring(lastEnd, highlight.start);
|
|
314
|
+
}
|
|
315
|
+
// Add highlighted part with special marker
|
|
316
|
+
result += '{{HIGHLIGHT_START}}' + line.content.substring(highlight.start, highlight.end) + '{{HIGHLIGHT_END}}';
|
|
317
|
+
lastEnd = highlight.end;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Add any remaining non-highlighted part
|
|
321
|
+
if (lastEnd < line.content.length) {
|
|
322
|
+
result += line.content.substring(lastEnd);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return result;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const diffGroups = computeDiff(oldString, newString);
|
|
329
|
+
const hasChanges = diffGroups.some(group => group.type === 'change');
|
|
330
|
+
</script>
|
|
331
|
+
|
|
332
|
+
<div>
|
|
333
|
+
{#if label}
|
|
334
|
+
<div class="flex items-center gap-2 mb-2">
|
|
335
|
+
<Icon name="lucide:git-compare" class="text-violet-500 w-4 h-4" />
|
|
336
|
+
<span class="text-sm font-medium text-slate-700 dark:text-slate-300">{label}</span>
|
|
337
|
+
</div>
|
|
338
|
+
{/if}
|
|
339
|
+
|
|
340
|
+
<div class="relative max-h-96 bg-slate-50 dark:bg-slate-950 border border-slate-200/60 dark:border-slate-800/60 rounded-md overflow-auto">
|
|
341
|
+
{#if hasChanges}
|
|
342
|
+
<div class="text-xs font-mono leading-5 min-w-fit"><!--
|
|
343
|
+
-->{#each diffGroups as group}<!--
|
|
344
|
+
-->{#if group.type === 'unchanged'}<!--
|
|
345
|
+
-->{#each group.lines || [] as line}<!--
|
|
346
|
+
--><div class="relative flex"><!--
|
|
347
|
+
--><div class="sticky left-0 w-1 bg-slate-50 dark:bg-slate-950"></div><!--
|
|
348
|
+
--><pre class="flex-1 px-3 text-slate-600 dark:text-slate-400 whitespace-pre">{line.content}</pre><!--
|
|
349
|
+
--></div><!--
|
|
350
|
+
-->{/each}<!--
|
|
351
|
+
-->{:else if group.type === 'change'}<!--
|
|
352
|
+
-->{#each group.removed || [] as line}<!--
|
|
353
|
+
--><div class="relative flex bg-red-100 dark:bg-red-500/10"><!--
|
|
354
|
+
--><div class="sticky left-0 w-1 bg-red-500 dark:bg-red-500/80"></div><!--
|
|
355
|
+
--><pre class="flex-1 px-3 whitespace-pre"><!--
|
|
356
|
+
-->{#if line.highlights && line.highlights.length > 0}<!--
|
|
357
|
+
-->{@const rendered = line.content}<!--
|
|
358
|
+
-->{#each line.highlights as highlight, idx}<!--
|
|
359
|
+
-->{#if idx === 0 && highlight.start > 0}<!--
|
|
360
|
+
--><span class="text-red-700 dark:text-red-300">{rendered.substring(0, highlight.start)}</span><!--
|
|
361
|
+
-->{/if}<!--
|
|
362
|
+
--><span class="bg-red-300 dark:bg-red-400/30 text-red-900 dark:text-red-100 px-0.5">{rendered.substring(highlight.start, highlight.end)}</span><!--
|
|
363
|
+
-->{#if idx < line.highlights.length - 1}<!--
|
|
364
|
+
--><span class="text-red-700 dark:text-red-300">{rendered.substring(highlight.end, line.highlights[idx + 1].start)}</span><!--
|
|
365
|
+
-->{:else if highlight.end < rendered.length}<!--
|
|
366
|
+
--><span class="text-red-700 dark:text-red-300">{rendered.substring(highlight.end)}</span><!--
|
|
367
|
+
-->{/if}<!--
|
|
368
|
+
-->{/each}<!--
|
|
369
|
+
-->{:else}<!--
|
|
370
|
+
--><span class="text-red-700 dark:text-red-300">{line.content}</span><!--
|
|
371
|
+
-->{/if}<!--
|
|
372
|
+
--></pre><!--
|
|
373
|
+
--></div><!--
|
|
374
|
+
-->{/each}<!--
|
|
375
|
+
-->{#each group.added || [] as line}<!--
|
|
376
|
+
--><div class="relative flex bg-green-100 dark:bg-green-500/10"><!--
|
|
377
|
+
--><div class="sticky left-0 w-1 bg-green-500 dark:bg-green-500/80"></div><!--
|
|
378
|
+
--><pre class="flex-1 px-3 whitespace-pre"><!--
|
|
379
|
+
-->{#if line.highlights && line.highlights.length > 0}<!--
|
|
380
|
+
-->{@const rendered = line.content}<!--
|
|
381
|
+
-->{#each line.highlights as highlight, idx}<!--
|
|
382
|
+
-->{#if idx === 0 && highlight.start > 0}<!--
|
|
383
|
+
--><span class="text-green-700 dark:text-green-300">{rendered.substring(0, highlight.start)}</span><!--
|
|
384
|
+
-->{/if}<!--
|
|
385
|
+
--><span class="bg-green-300 dark:bg-green-400/30 text-green-900 dark:text-green-100 px-0.5">{rendered.substring(highlight.start, highlight.end)}</span><!--
|
|
386
|
+
-->{#if idx < line.highlights.length - 1}<!--
|
|
387
|
+
--><span class="text-green-700 dark:text-green-300">{rendered.substring(highlight.end, line.highlights[idx + 1].start)}</span><!--
|
|
388
|
+
-->{:else if highlight.end < rendered.length}<!--
|
|
389
|
+
--><span class="text-green-700 dark:text-green-300">{rendered.substring(highlight.end)}</span><!--
|
|
390
|
+
-->{/if}<!--
|
|
391
|
+
-->{/each}<!--
|
|
392
|
+
-->{:else}<!--
|
|
393
|
+
--><span class="text-green-700 dark:text-green-300">{line.content}</span><!--
|
|
394
|
+
-->{/if}<!--
|
|
395
|
+
--></pre><!--
|
|
396
|
+
--></div>
|
|
397
|
+
<!--
|
|
398
|
+
-->{/each}<!--
|
|
399
|
+
-->{/if}<!--
|
|
400
|
+
-->{/each}<!--
|
|
401
|
+
--></div>
|
|
402
|
+
{:else}
|
|
403
|
+
<div class="text-sm text-slate-600 dark:text-slate-400">
|
|
404
|
+
No changes detected
|
|
405
|
+
</div>
|
|
406
|
+
{/if}
|
|
407
|
+
</div>
|
|
408
|
+
</div>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
4
|
+
import { getFileIcon } from '$frontend/lib/utils/file-icon-mappings';
|
|
5
|
+
import { formatPath } from '../../shared/utils';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
filePath: string;
|
|
9
|
+
fileName?: string;
|
|
10
|
+
iconColor?: string;
|
|
11
|
+
badges?: Array<{ text: string; color: string }>;
|
|
12
|
+
box?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { filePath, fileName, iconColor, badges = [], box = true }: Props = $props();
|
|
16
|
+
|
|
17
|
+
const displayFileName = fileName || filePath.split(/[/\\]/).pop() || filePath;
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<div class={box ? "bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3" : ""}>
|
|
21
|
+
<div class="flex items-center gap-3 mb-1">
|
|
22
|
+
<Icon
|
|
23
|
+
name={getFileIcon(displayFileName)}
|
|
24
|
+
class="w-6 h-6 {iconColor || ''}"
|
|
25
|
+
/>
|
|
26
|
+
<div class="flex-1 min-w-0">
|
|
27
|
+
<h3 class="font-medium text-slate-900 dark:text-slate-100 truncate">
|
|
28
|
+
{displayFileName}
|
|
29
|
+
</h3>
|
|
30
|
+
<p class="text-xs text-slate-600 dark:text-slate-400 truncate" title={filePath}>
|
|
31
|
+
{formatPath(filePath)}
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
{#if badges.length > 0}
|
|
37
|
+
<div class="flex gap-2 mt-3">
|
|
38
|
+
{#each badges as badge}
|
|
39
|
+
<div class="text-xs px-2 py-1 rounded {badge.color}">
|
|
40
|
+
{badge.text}
|
|
41
|
+
</div>
|
|
42
|
+
{/each}
|
|
43
|
+
</div>
|
|
44
|
+
{/if}
|
|
45
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import type { IconName } from '$shared/types/ui/icons';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
icon: IconName;
|
|
7
|
+
text: string;
|
|
8
|
+
iconColor?: string;
|
|
9
|
+
textColor?: string;
|
|
10
|
+
title?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { icon, text, iconColor = 'text-slate-500', textColor = 'text-slate-600 dark:text-slate-400', title="" }: Props = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div class="flex items-center gap-1.5" {title}>
|
|
17
|
+
<Icon name={icon} class="{iconColor} w-3.5 h-3.5" />
|
|
18
|
+
<span class="text-xs {textColor}">{text}</span>
|
|
19
|
+
</div>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface StatItem {
|
|
3
|
+
label: string;
|
|
4
|
+
value: string | number;
|
|
5
|
+
color: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
stats: StatItem[];
|
|
10
|
+
columns?: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { stats, columns = 3 }: Props = $props();
|
|
14
|
+
|
|
15
|
+
const gridClass = columns === 2 ? 'grid-cols-2' : columns === 4 ? 'grid-cols-4' : 'grid-cols-3';
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<div class="grid {gridClass} gap-4 text-center text-sm">
|
|
19
|
+
{#each stats as stat}
|
|
20
|
+
<div>
|
|
21
|
+
<div class="text-lg font-bold {stat.color}">
|
|
22
|
+
{stat.value}
|
|
23
|
+
</div>
|
|
24
|
+
<div class="text-slate-500 dark:text-slate-400 text-xs">{stat.label}</div>
|
|
25
|
+
</div>
|
|
26
|
+
{/each}
|
|
27
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
command: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
timeout?: number;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { command, description, timeout }: Props = $props();
|
|
9
|
+
|
|
10
|
+
// Parse command parts for better display
|
|
11
|
+
function parseCommandParts(cmd: string) {
|
|
12
|
+
const parts = cmd.split(' ');
|
|
13
|
+
const mainCommand = parts[0];
|
|
14
|
+
const args = parts.slice(1);
|
|
15
|
+
return { mainCommand, args };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const { mainCommand, args } = parseCommandParts(command);
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<!-- Description (if provided) -->
|
|
22
|
+
{#if description}
|
|
23
|
+
<p class="mb-2 text-slate-700 dark:text-slate-300">
|
|
24
|
+
{description}
|
|
25
|
+
</p>
|
|
26
|
+
{/if}
|
|
27
|
+
|
|
28
|
+
<!-- Command Display -->
|
|
29
|
+
<div class="bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3">
|
|
30
|
+
<div class="flex items-center justify-between gap-2 mb-2">
|
|
31
|
+
<div class="flex items-center gap-2">
|
|
32
|
+
<div class="w-2 h-2 bg-green-500 rounded-full"></div>
|
|
33
|
+
<span class="text-xs font-medium text-slate-700 dark:text-slate-300">Command:</span>
|
|
34
|
+
</div>
|
|
35
|
+
{#if timeout}
|
|
36
|
+
<div class="inline-block ml-auto text-xs bg-orange-100 dark:bg-orange-900 text-orange-700 dark:text-orange-300 px-2 py-0.5 rounded">
|
|
37
|
+
Timeout: {timeout}ms
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Terminal-style command display -->
|
|
43
|
+
<div class="bg-slate-50 dark:bg-slate-950 border border-slate-200/60 dark:border-slate-800/60 rounded-md p-2.5 font-mono text-sm">
|
|
44
|
+
<div class="flex items-start gap-2">
|
|
45
|
+
<span class="text-green-600 dark:text-green-400 select-none">$</span>
|
|
46
|
+
<div class="flex-1 text-slate-900 dark:text-slate-200 break-all">
|
|
47
|
+
<span class="text-violet-600 dark:text-violet-300 font-medium">{mainCommand}</span>
|
|
48
|
+
{#if args.length > 0}
|
|
49
|
+
<span class="text-slate-700 dark:text-slate-300"> {args.join(' ')}</span>
|
|
50
|
+
{/if}
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Shared UI components for tool displays
|
|
2
|
+
export { default as FileHeader } from './FileHeader.svelte';
|
|
3
|
+
export { default as CodeBlock } from './CodeBlock.svelte';
|
|
4
|
+
export { default as DiffBlock } from './DiffBlock.svelte';
|
|
5
|
+
export { default as StatsBadges } from './StatsBadges.svelte';
|
|
6
|
+
export { default as InfoLine } from './InfoLine.svelte';
|
|
7
|
+
export { default as TerminalCommand } from './TerminalCommand.svelte';
|