@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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared git status utilities for label and color mapping.
|
|
3
|
+
* Used by FileChangeItem, DiffViewer, and GitPanel tab bar.
|
|
4
|
+
*
|
|
5
|
+
* Status codes (from git status --porcelain=v1):
|
|
6
|
+
* M = Modified, A = Added, D = Deleted, R = Renamed,
|
|
7
|
+
* C = Copied, U = Unmerged, ? = Untracked, T = Type changed
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** Maps a raw git status code to a single-letter display label */
|
|
11
|
+
export function getGitStatusLabel(status: string): string {
|
|
12
|
+
switch (status) {
|
|
13
|
+
case '?': return 'U';
|
|
14
|
+
case 'A': return 'A';
|
|
15
|
+
case 'D': return 'D';
|
|
16
|
+
case 'M': return 'M';
|
|
17
|
+
case 'R': return 'R';
|
|
18
|
+
case 'C': return 'C';
|
|
19
|
+
case 'U': return 'U';
|
|
20
|
+
case 'T': return 'T';
|
|
21
|
+
default: return status || '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Maps a raw git status code to a Tailwind text color class */
|
|
26
|
+
export function getGitStatusColor(status: string): string {
|
|
27
|
+
switch (status) {
|
|
28
|
+
case '?': return 'text-emerald-500';
|
|
29
|
+
case 'A': return 'text-emerald-500';
|
|
30
|
+
case 'D': return 'text-red-500';
|
|
31
|
+
case 'M': return 'text-amber-500';
|
|
32
|
+
case 'R': return 'text-blue-500';
|
|
33
|
+
case 'C': return 'text-teal-500';
|
|
34
|
+
case 'U': return 'text-orange-500';
|
|
35
|
+
case 'T': return 'text-violet-500';
|
|
36
|
+
default: return 'text-slate-500';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Maps a raw git status code to a full-word label (for badges/headers) */
|
|
41
|
+
export function getGitStatusBadgeLabel(status: string): string {
|
|
42
|
+
switch (status) {
|
|
43
|
+
case '?': return 'Untracked';
|
|
44
|
+
case 'A': return 'Added';
|
|
45
|
+
case 'D': return 'Deleted';
|
|
46
|
+
case 'M': return 'Modified';
|
|
47
|
+
case 'R': return 'Renamed';
|
|
48
|
+
case 'C': return 'Copied';
|
|
49
|
+
case 'U': return 'Unmerged';
|
|
50
|
+
case 'T': return 'Type Changed';
|
|
51
|
+
default: return status || '';
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Maps a raw git status code to Tailwind badge bg+text color classes */
|
|
56
|
+
export function getGitStatusBadgeColor(status: string): string {
|
|
57
|
+
switch (status) {
|
|
58
|
+
case '?': return 'bg-emerald-500/15 text-emerald-600 dark:text-emerald-400';
|
|
59
|
+
case 'A': return 'bg-emerald-500/15 text-emerald-600 dark:text-emerald-400';
|
|
60
|
+
case 'D': return 'bg-red-500/15 text-red-600 dark:text-red-400';
|
|
61
|
+
case 'M': return 'bg-amber-500/15 text-amber-600 dark:text-amber-400';
|
|
62
|
+
case 'R': return 'bg-blue-500/15 text-blue-600 dark:text-blue-400';
|
|
63
|
+
case 'C': return 'bg-teal-500/15 text-teal-600 dark:text-teal-400';
|
|
64
|
+
case 'U': return 'bg-orange-500/15 text-orange-600 dark:text-orange-400';
|
|
65
|
+
case 'T': return 'bg-violet-500/15 text-violet-600 dark:text-violet-400';
|
|
66
|
+
default: return 'bg-slate-500/15 text-slate-600 dark:text-slate-400';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform keyboard shortcut and platform detection utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export type Platform = 'mac' | 'windows' | 'linux' | 'unknown';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Detect the current platform
|
|
9
|
+
*/
|
|
10
|
+
export function detectPlatform(): Platform {
|
|
11
|
+
if (typeof window === 'undefined') {
|
|
12
|
+
// Server-side fallback
|
|
13
|
+
return 'unknown';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const userAgent = window.navigator.userAgent.toLowerCase();
|
|
17
|
+
const platform = window.navigator.platform.toLowerCase();
|
|
18
|
+
|
|
19
|
+
if (platform.includes('mac') || userAgent.includes('mac')) {
|
|
20
|
+
return 'mac';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (platform.includes('win') || userAgent.includes('win')) {
|
|
24
|
+
return 'windows';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (platform.includes('linux') || userAgent.includes('linux')) {
|
|
28
|
+
return 'linux';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return 'unknown';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if the given keyboard event matches the cancel command (Ctrl+C or Cmd+C)
|
|
36
|
+
*/
|
|
37
|
+
export function isCancelKeyCombo(event: KeyboardEvent): boolean {
|
|
38
|
+
const platform = detectPlatform();
|
|
39
|
+
|
|
40
|
+
// Check for 'c' key
|
|
41
|
+
if (event.key.toLowerCase() !== 'c') {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Mac uses Cmd+C, others use Ctrl+C
|
|
46
|
+
if (platform === 'mac') {
|
|
47
|
+
return event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
|
|
48
|
+
} else {
|
|
49
|
+
return event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Check if the given keyboard event matches the clear command (Ctrl+L or Cmd+L)
|
|
55
|
+
*/
|
|
56
|
+
export function isClearKeyCombo(event: KeyboardEvent): boolean {
|
|
57
|
+
const platform = detectPlatform();
|
|
58
|
+
|
|
59
|
+
// Check for 'l' key
|
|
60
|
+
if (event.key.toLowerCase() !== 'l') {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Mac uses Cmd+L, others use Ctrl+L
|
|
65
|
+
if (platform === 'mac') {
|
|
66
|
+
return event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey;
|
|
67
|
+
} else {
|
|
68
|
+
return event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get the display name for keyboard shortcuts based on platform
|
|
74
|
+
*/
|
|
75
|
+
export function getShortcutLabels() {
|
|
76
|
+
const platform = detectPlatform();
|
|
77
|
+
const modifier = platform === 'mac' ? '⌘' : 'Ctrl';
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
cancel: `${modifier}+C`,
|
|
81
|
+
clear: `${modifier}+L`,
|
|
82
|
+
modifier: modifier
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get platform-specific modifier key display
|
|
88
|
+
*/
|
|
89
|
+
export function getModifierKey(): string {
|
|
90
|
+
const platform = detectPlatform();
|
|
91
|
+
return platform === 'mac' ? '⌘' : 'Ctrl';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if we're on macOS
|
|
96
|
+
*/
|
|
97
|
+
export function isMac(): boolean {
|
|
98
|
+
return detectPlatform() === 'mac';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Check if we're on Windows
|
|
103
|
+
*/
|
|
104
|
+
export function isWindows(): boolean {
|
|
105
|
+
return detectPlatform() === 'windows';
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Check if we're on Linux
|
|
110
|
+
*/
|
|
111
|
+
export function isLinux(): boolean {
|
|
112
|
+
return detectPlatform() === 'linux';
|
|
113
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createServer } from 'http';
|
|
2
|
+
|
|
3
|
+
import { debug } from '$shared/utils/logger';
|
|
4
|
+
/**
|
|
5
|
+
* Check if a specific port is available
|
|
6
|
+
*/
|
|
7
|
+
export function checkPortAvailability(port: number): Promise<boolean> {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
const server = createServer();
|
|
10
|
+
|
|
11
|
+
server.listen(port, () => {
|
|
12
|
+
server.close(() => {
|
|
13
|
+
resolve(true); // Port is available
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
server.on('error', (err: NodeJS.ErrnoException) => {
|
|
18
|
+
if (err.code === 'EADDRINUSE') {
|
|
19
|
+
resolve(false); // Port is in use
|
|
20
|
+
} else {
|
|
21
|
+
resolve(false); // Other error, consider as unavailable
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Strictly check if port 5678 is available and fail if not
|
|
29
|
+
*/
|
|
30
|
+
export async function ensurePort5678Available(): Promise<void> {
|
|
31
|
+
const isAvailable = await checkPortAvailability(5678);
|
|
32
|
+
|
|
33
|
+
if (!isAvailable) {
|
|
34
|
+
debug.error('server', '❌ FATAL ERROR: Port 5678 is required but not available!');
|
|
35
|
+
debug.error('server', '❌ Please stop any service using port 5678 and try again.');
|
|
36
|
+
debug.error('server', '❌ This application strictly requires port 5678 to be available.');
|
|
37
|
+
debug.error('server', '❌ Use the following command to find what is using port 5678:');
|
|
38
|
+
debug.error('server', '❌ Windows: netstat -ano | findstr :5678');
|
|
39
|
+
debug.error('server', '❌ Linux/Mac: lsof -i :5678');
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
debug.log('server', '✅ Port 5678 is available');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get process info using a specific port (Windows only)
|
|
48
|
+
*/
|
|
49
|
+
export async function getPortProcessInfo(port: number): Promise<void> {
|
|
50
|
+
try {
|
|
51
|
+
const proc = Bun.spawn(['netstat', '-ano'], {
|
|
52
|
+
stdout: 'pipe',
|
|
53
|
+
stderr: 'ignore'
|
|
54
|
+
});
|
|
55
|
+
const output = await new Response(proc.stdout).text();
|
|
56
|
+
const filteredOutput = output.split('\n').filter((line: string) => line.includes(`:${port}`)).join('\n');
|
|
57
|
+
|
|
58
|
+
if (filteredOutput.trim()) {
|
|
59
|
+
debug.error('server', `❌ Port ${port} is being used by:`);
|
|
60
|
+
debug.error('server', filteredOutput);
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
debug.error('server', '❌ Could not check port usage:', error instanceof Error ? error.message : 'Unknown error');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Terminal output detection and ANSI code processing utilities
|
|
2
|
+
|
|
3
|
+
export interface TerminalSegment {
|
|
4
|
+
type: 'terminal' | 'text';
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Check if content looks like terminal output
|
|
9
|
+
export function isTerminalOutput(text: string): boolean {
|
|
10
|
+
// Exclude git diff output - should not be treated as terminal
|
|
11
|
+
if (/^diff --git /m.test(text) || /^@@\s+-\d+.*\+\d+.*@@/m.test(text)) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Strong indicators - any of these alone means it's terminal output
|
|
16
|
+
|
|
17
|
+
// 1. ANSI escape codes - definitive terminal indicator
|
|
18
|
+
if (/\u001b\[[\d;]*m/.test(text)) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// 2. Shell prompt at the beginning of a line
|
|
23
|
+
if (/^[\s]*[$>#%]\s+/m.test(text)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 3. Command execution pattern ($ command or > command)
|
|
28
|
+
if (/^[\s]*[$>]\s+\S+/m.test(text)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Weak indicators - need multiple to be considered terminal
|
|
33
|
+
let weakIndicators = 0;
|
|
34
|
+
|
|
35
|
+
// Terminal-specific formatting
|
|
36
|
+
if (/^[\s]*[│├└─┬┴┤┌┐]+/m.test(text)) weakIndicators++; // Box drawing
|
|
37
|
+
if (/^\s*\d+\s*│/m.test(text)) weakIndicators++; // Line numbers with pipe
|
|
38
|
+
if (/^[\s]*\[[\d:]+\]/m.test(text)) weakIndicators++; // [timestamp] format
|
|
39
|
+
if (/^[\s]*(?:✓|✔|✗|✖|⚠|➜|→|▶|•)\s+/m.test(text)) weakIndicators++; // Terminal symbols at line start
|
|
40
|
+
|
|
41
|
+
// Terminal-specific output patterns
|
|
42
|
+
if (/^(?:error|ERROR|warn|WARN|info|INFO|debug|DEBUG):/m.test(text)) weakIndicators++;
|
|
43
|
+
if (/^\s*at\s+\S+\s+\([^)]+:\d+:\d+\)/m.test(text)) weakIndicators++; // Stack traces
|
|
44
|
+
if (/^[\s]*\+\s+\d+ms/m.test(text)) weakIndicators++; // Timing info
|
|
45
|
+
|
|
46
|
+
// Need at least 3 weak indicators for terminal detection
|
|
47
|
+
// This prevents normal text about packages from being detected as terminal
|
|
48
|
+
return weakIndicators >= 3;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ANSI color mappings to CSS classes with improved visibility for both modes
|
|
52
|
+
const ansiToClass: Record<string, string> = {
|
|
53
|
+
'0': '', // reset
|
|
54
|
+
'1': 'font-bold', // bold
|
|
55
|
+
'2': 'opacity-60', // dim
|
|
56
|
+
'3': 'italic', // italic
|
|
57
|
+
'4': 'underline', // underline
|
|
58
|
+
'22': '', // normal intensity
|
|
59
|
+
'23': '', // not italic
|
|
60
|
+
'30': 'text-slate-800 dark:text-slate-200', // black - improved contrast
|
|
61
|
+
'31': 'text-red-600 dark:text-red-400', // red
|
|
62
|
+
'32': 'text-green-600 dark:text-green-400', // green
|
|
63
|
+
'33': 'text-yellow-700 dark:text-yellow-300', // yellow - better visibility
|
|
64
|
+
'34': 'text-blue-600 dark:text-blue-400', // blue
|
|
65
|
+
'35': 'text-purple-600 dark:text-purple-400', // magenta
|
|
66
|
+
'36': 'text-cyan-600 dark:text-cyan-400', // cyan
|
|
67
|
+
'37': 'text-slate-600 dark:text-slate-200', // white - improved contrast
|
|
68
|
+
'90': 'text-slate-500 dark:text-slate-500', // bright black (gray)
|
|
69
|
+
'91': 'text-red-500 dark:text-red-300', // bright red
|
|
70
|
+
'92': 'text-green-500 dark:text-green-300', // bright green
|
|
71
|
+
'93': 'text-yellow-600 dark:text-yellow-200', // bright yellow
|
|
72
|
+
'94': 'text-blue-500 dark:text-blue-300', // bright blue
|
|
73
|
+
'95': 'text-purple-500 dark:text-purple-300', // bright magenta
|
|
74
|
+
'96': 'text-cyan-500 dark:text-cyan-300', // bright cyan
|
|
75
|
+
'97': 'text-slate-900 dark:text-slate-100', // bright white
|
|
76
|
+
'39': 'text-slate-900 dark:text-slate-100' // default foreground
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// Process ANSI escape codes in terminal output
|
|
80
|
+
export function processAnsiCodes(text: string): string {
|
|
81
|
+
const ansiRegex = /\u001b\[([0-9;]*)m/g;
|
|
82
|
+
// Escape HTML first to prevent content (e.g. HTML tags in diffs) from being
|
|
83
|
+
// rendered as actual DOM elements when using {@html}
|
|
84
|
+
let processedText = escapeHtml(text);
|
|
85
|
+
let currentStyles: string[] = [];
|
|
86
|
+
|
|
87
|
+
// Replace ANSI codes with HTML spans (ANSI escape char \u001b is not affected by escapeHtml)
|
|
88
|
+
processedText = processedText.replace(ansiRegex, (match, codes) => {
|
|
89
|
+
const codeArray = codes.split(';');
|
|
90
|
+
|
|
91
|
+
for (const code of codeArray) {
|
|
92
|
+
if (code === '0' || code === '') {
|
|
93
|
+
// Reset all styles
|
|
94
|
+
currentStyles = [];
|
|
95
|
+
return '</span>';
|
|
96
|
+
} else if (ansiToClass[code]) {
|
|
97
|
+
const cssClass = ansiToClass[code];
|
|
98
|
+
if (cssClass) {
|
|
99
|
+
// Remove conflicting styles and add new one
|
|
100
|
+
if (code.startsWith('3') || code === '39' || code === '90') {
|
|
101
|
+
// Color codes - remove existing colors
|
|
102
|
+
currentStyles = currentStyles.filter(s => !s.includes('text-'));
|
|
103
|
+
}
|
|
104
|
+
if (!currentStyles.includes(cssClass)) {
|
|
105
|
+
currentStyles.push(cssClass);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return `<span class="${currentStyles.join(' ')}">`;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Clean up any unclosed spans
|
|
115
|
+
const openSpans = (processedText.match(/<span/g) || []).length;
|
|
116
|
+
const closeSpans = (processedText.match(/<\/span>/g) || []).length;
|
|
117
|
+
if (openSpans > closeSpans) {
|
|
118
|
+
processedText += '</span>'.repeat(openSpans - closeSpans);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return processedText;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Split content into segments (terminal vs regular text)
|
|
125
|
+
export function splitContentIntoSegments(text: string): TerminalSegment[] {
|
|
126
|
+
const segments: TerminalSegment[] = [];
|
|
127
|
+
|
|
128
|
+
// First check if the entire text has ANSI codes - if so, it's all terminal
|
|
129
|
+
if (/\u001b\[[\d;]*m/.test(text)) {
|
|
130
|
+
return [{ type: 'terminal', content: text }];
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// For text without ANSI codes, we need to be more careful
|
|
134
|
+
// Split by code blocks first to preserve them
|
|
135
|
+
const codeBlockRegex = /```[\s\S]*?```/g;
|
|
136
|
+
const codeBlocks: {start: number, end: number, content: string}[] = [];
|
|
137
|
+
let match;
|
|
138
|
+
|
|
139
|
+
// Find all code blocks
|
|
140
|
+
while ((match = codeBlockRegex.exec(text)) !== null) {
|
|
141
|
+
codeBlocks.push({
|
|
142
|
+
start: match.index,
|
|
143
|
+
end: match.index + match[0].length,
|
|
144
|
+
content: match[0]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Process text in chunks, avoiding code blocks
|
|
149
|
+
let lastIndex = 0;
|
|
150
|
+
for (let i = 0; i <= codeBlocks.length; i++) {
|
|
151
|
+
const startIndex = lastIndex;
|
|
152
|
+
const endIndex = i < codeBlocks.length ? codeBlocks[i].start : text.length;
|
|
153
|
+
|
|
154
|
+
if (startIndex < endIndex) {
|
|
155
|
+
const chunk = text.slice(startIndex, endIndex);
|
|
156
|
+
|
|
157
|
+
// Split chunk by double newlines
|
|
158
|
+
const blocks = chunk.split(/\n\n+/);
|
|
159
|
+
|
|
160
|
+
for (const block of blocks) {
|
|
161
|
+
if (!block.trim()) continue;
|
|
162
|
+
|
|
163
|
+
const blockType = isTerminalOutput(block) ? 'terminal' : 'text';
|
|
164
|
+
|
|
165
|
+
// Merge with previous segment if same type
|
|
166
|
+
if (segments.length > 0 && segments[segments.length - 1].type === blockType) {
|
|
167
|
+
segments[segments.length - 1].content += '\n\n' + block;
|
|
168
|
+
} else {
|
|
169
|
+
segments.push({ type: blockType, content: block });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Add code block as text (not terminal)
|
|
175
|
+
if (i < codeBlocks.length) {
|
|
176
|
+
if (segments.length > 0 && segments[segments.length - 1].type === 'text') {
|
|
177
|
+
segments[segments.length - 1].content += '\n\n' + codeBlocks[i].content;
|
|
178
|
+
} else {
|
|
179
|
+
segments.push({ type: 'text', content: codeBlocks[i].content });
|
|
180
|
+
}
|
|
181
|
+
lastIndex = codeBlocks[i].end;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// If no segments, treat as text
|
|
186
|
+
if (segments.length === 0 && text.trim()) {
|
|
187
|
+
segments.push({ type: 'text', content: text });
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return segments;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Format terminal output with proper styling
|
|
194
|
+
export function formatTerminalOutput(text: string): string {
|
|
195
|
+
const processedAnsi = processAnsiCodes(text);
|
|
196
|
+
return `<div class="font-mono text-sm whitespace-pre-wrap terminal-output bg-slate-100 dark:bg-slate-900 text-slate-900 dark:text-slate-100 p-4 rounded-lg overflow-x-auto border border-slate-300 dark:border-slate-700">${processedAnsi}</div>`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Escape HTML entities for security
|
|
200
|
+
export function escapeHtml(text: string): string {
|
|
201
|
+
return text
|
|
202
|
+
.replace(/&/g, '&')
|
|
203
|
+
.replace(/</g, '<')
|
|
204
|
+
.replace(/>/g, '>')
|
|
205
|
+
.replace(/"/g, '"')
|
|
206
|
+
.replace(/'/g, ''');
|
|
207
|
+
}
|