@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,757 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Icon from '$frontend/lib/components/common/Icon.svelte';
|
|
3
|
+
import { fly } from 'svelte/transition';
|
|
4
|
+
import { cubicOut } from 'svelte/easing';
|
|
5
|
+
|
|
6
|
+
interface ConsoleMessage {
|
|
7
|
+
id: string;
|
|
8
|
+
type: 'log' | 'info' | 'warn' | 'error' | 'debug' | 'trace' | 'clear';
|
|
9
|
+
text: string;
|
|
10
|
+
args?: any[];
|
|
11
|
+
location?: {
|
|
12
|
+
url: string;
|
|
13
|
+
lineNumber: number;
|
|
14
|
+
columnNumber: number;
|
|
15
|
+
};
|
|
16
|
+
stackTrace?: string;
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let {
|
|
21
|
+
isOpen = $bindable(false),
|
|
22
|
+
messages = $bindable<ConsoleMessage[]>([]),
|
|
23
|
+
onClear = () => {},
|
|
24
|
+
onExecuteCommand = (command: string) => {},
|
|
25
|
+
onToggleLogging = (enabled: boolean) => {},
|
|
26
|
+
isLoggingEnabled = $bindable(true)
|
|
27
|
+
}: {
|
|
28
|
+
isOpen: boolean;
|
|
29
|
+
messages: ConsoleMessage[];
|
|
30
|
+
onClear: () => void;
|
|
31
|
+
onExecuteCommand: (command: string) => void;
|
|
32
|
+
onToggleLogging: (enabled: boolean) => void;
|
|
33
|
+
isLoggingEnabled: boolean;
|
|
34
|
+
} = $props();
|
|
35
|
+
|
|
36
|
+
let consoleContainer = $state<HTMLDivElement | undefined>();
|
|
37
|
+
let commandInput = $state('');
|
|
38
|
+
let filterLevel = $state<'all' | 'log' | 'info' | 'warn' | 'error'>('all');
|
|
39
|
+
let searchQuery = $state('');
|
|
40
|
+
let isAutoScroll = $state(true);
|
|
41
|
+
let commandHistory = $state<string[]>([]);
|
|
42
|
+
let historyIndex = $state(-1);
|
|
43
|
+
|
|
44
|
+
// Filter messages based on level and search query
|
|
45
|
+
const filteredMessages = $derived.by(() => {
|
|
46
|
+
let filtered = messages;
|
|
47
|
+
|
|
48
|
+
// Filter by level
|
|
49
|
+
if (filterLevel !== 'all') {
|
|
50
|
+
filtered = filtered.filter(msg => msg.type === filterLevel);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Filter by search query
|
|
54
|
+
if (searchQuery.trim()) {
|
|
55
|
+
const query = searchQuery.toLowerCase();
|
|
56
|
+
filtered = filtered.filter(msg =>
|
|
57
|
+
msg.text.toLowerCase().includes(query) ||
|
|
58
|
+
msg.args?.some((arg: any) => String(arg).toLowerCase().includes(query)) ||
|
|
59
|
+
msg.location?.url.toLowerCase().includes(query)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return filtered;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Count messages by type for badges
|
|
67
|
+
const messageCounts = $derived.by(() => {
|
|
68
|
+
const counts = { log: 0, info: 0, warn: 0, error: 0, debug: 0, trace: 0 };
|
|
69
|
+
messages.forEach(msg => {
|
|
70
|
+
if (msg.type in counts) {
|
|
71
|
+
counts[msg.type as keyof typeof counts]++;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return counts;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
function formatTimestamp(timestamp: number): string {
|
|
78
|
+
const date = new Date(timestamp);
|
|
79
|
+
return date.toLocaleTimeString([], { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit', fractionalSecondDigits: 3 });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function formatArgs(args?: any[]): any[] {
|
|
83
|
+
if (!args || args.length === 0) return [];
|
|
84
|
+
return args;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function formatValue(value: any, depth: number = 0, maxDepth: number = 10): { display: string, type: string, expandable: boolean, fullContent?: string, preview?: string, structure?: any } {
|
|
88
|
+
// Prevent infinite recursion
|
|
89
|
+
if (depth > maxDepth) {
|
|
90
|
+
return { display: '[Max depth reached]', type: 'unknown', expandable: false };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Handle null and undefined
|
|
94
|
+
if (value === null) {
|
|
95
|
+
return { display: 'null', type: 'null', expandable: false };
|
|
96
|
+
}
|
|
97
|
+
if (value === undefined) {
|
|
98
|
+
return { display: 'undefined', type: 'undefined', expandable: false };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Handle primitives
|
|
102
|
+
if (typeof value === 'string') {
|
|
103
|
+
return { display: `"${value}"`, type: 'string', expandable: false };
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === 'number') {
|
|
106
|
+
return { display: String(value), type: 'number', expandable: false };
|
|
107
|
+
}
|
|
108
|
+
if (typeof value === 'boolean') {
|
|
109
|
+
return { display: String(value), type: 'boolean', expandable: false };
|
|
110
|
+
}
|
|
111
|
+
if (typeof value === 'bigint') {
|
|
112
|
+
return { display: String(value) + 'n', type: 'bigint', expandable: false };
|
|
113
|
+
}
|
|
114
|
+
if (typeof value === 'symbol') {
|
|
115
|
+
return { display: String(value), type: 'symbol', expandable: false };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Handle functions
|
|
119
|
+
if (typeof value === 'function') {
|
|
120
|
+
const funcStr = value.toString();
|
|
121
|
+
const funcName = value.name || 'anonymous';
|
|
122
|
+
const params = funcStr.match(/\(([^)]*)\)/) || ['', ''];
|
|
123
|
+
const preview = `ƒ ${funcName}(${params[1]})`;
|
|
124
|
+
return {
|
|
125
|
+
display: preview,
|
|
126
|
+
type: 'function',
|
|
127
|
+
expandable: true,
|
|
128
|
+
fullContent: funcStr,
|
|
129
|
+
preview: preview
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Handle arrays - DevTools style
|
|
134
|
+
if (Array.isArray(value)) {
|
|
135
|
+
if (value.length === 0) {
|
|
136
|
+
return { display: '[]', type: 'array', expandable: false };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const preview = `(${value.length}) [${value.map((item, i) => {
|
|
140
|
+
if (i >= 3) return null;
|
|
141
|
+
if (typeof item === 'string') return `"${item}"`;
|
|
142
|
+
if (typeof item === 'object' && item !== null) {
|
|
143
|
+
if (Array.isArray(item)) return `Array(${item.length})`;
|
|
144
|
+
return '{...}';
|
|
145
|
+
}
|
|
146
|
+
return String(item);
|
|
147
|
+
}).filter(Boolean).join(', ')}${value.length > 3 ? ', ...' : ''}]`;
|
|
148
|
+
|
|
149
|
+
// Create structure for nested expansion
|
|
150
|
+
const structure = value.map((item, index) => ({
|
|
151
|
+
key: String(index),
|
|
152
|
+
value: item,
|
|
153
|
+
formatted: formatValue(item, depth + 1, maxDepth),
|
|
154
|
+
type: Array.isArray(item) ? 'array' : typeof item
|
|
155
|
+
}));
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
display: `Array${preview}`,
|
|
159
|
+
type: 'array',
|
|
160
|
+
expandable: true,
|
|
161
|
+
preview: `Array${preview}`,
|
|
162
|
+
structure: structure
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Handle objects - DevTools style
|
|
167
|
+
if (typeof value === 'object') {
|
|
168
|
+
// Handle Error objects
|
|
169
|
+
if (value instanceof Error) {
|
|
170
|
+
const errorStructure = [
|
|
171
|
+
{ key: 'name', value: value.name, formatted: formatValue(value.name, depth + 1, maxDepth), type: 'string' },
|
|
172
|
+
{ key: 'message', value: value.message, formatted: formatValue(value.message, depth + 1, maxDepth), type: 'string' }
|
|
173
|
+
];
|
|
174
|
+
if (value.stack) {
|
|
175
|
+
errorStructure.push({ key: 'stack', value: value.stack, formatted: formatValue(value.stack, depth + 1, maxDepth), type: 'string' });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
display: `${value.name}: ${value.message}`,
|
|
180
|
+
type: 'error',
|
|
181
|
+
expandable: true,
|
|
182
|
+
preview: `${value.name}: ${value.message}`,
|
|
183
|
+
structure: errorStructure
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Handle Date objects
|
|
188
|
+
if (value instanceof Date) {
|
|
189
|
+
const dateStr = value.toString();
|
|
190
|
+
const structure = [
|
|
191
|
+
{ key: 'toString()', value: dateStr, formatted: formatValue(dateStr, depth + 1, maxDepth), type: 'string' },
|
|
192
|
+
{ key: 'toISOString()', value: value.toISOString(), formatted: formatValue(value.toISOString(), depth + 1, maxDepth), type: 'string' },
|
|
193
|
+
{ key: 'getTime()', value: value.getTime(), formatted: formatValue(value.getTime(), depth + 1, maxDepth), type: 'number' }
|
|
194
|
+
];
|
|
195
|
+
return {
|
|
196
|
+
display: dateStr,
|
|
197
|
+
type: 'date',
|
|
198
|
+
expandable: true,
|
|
199
|
+
preview: dateStr,
|
|
200
|
+
structure: structure
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Handle RegExp objects
|
|
205
|
+
if (value instanceof RegExp) {
|
|
206
|
+
const regexStr = value.toString();
|
|
207
|
+
const structure = [
|
|
208
|
+
{ key: 'source', value: value.source, formatted: formatValue(value.source, depth + 1, maxDepth), type: 'string' },
|
|
209
|
+
{ key: 'flags', value: value.flags, formatted: formatValue(value.flags, depth + 1, maxDepth), type: 'string' },
|
|
210
|
+
{ key: 'global', value: value.global, formatted: formatValue(value.global, depth + 1, maxDepth), type: 'boolean' },
|
|
211
|
+
{ key: 'ignoreCase', value: value.ignoreCase, formatted: formatValue(value.ignoreCase, depth + 1, maxDepth), type: 'boolean' },
|
|
212
|
+
{ key: 'multiline', value: value.multiline, formatted: formatValue(value.multiline, depth + 1, maxDepth), type: 'boolean' }
|
|
213
|
+
];
|
|
214
|
+
return {
|
|
215
|
+
display: regexStr,
|
|
216
|
+
type: 'regexp',
|
|
217
|
+
expandable: true,
|
|
218
|
+
preview: regexStr,
|
|
219
|
+
structure: structure
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Handle regular objects
|
|
224
|
+
const keys = Object.keys(value);
|
|
225
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
226
|
+
|
|
227
|
+
if (keys.length === 0) {
|
|
228
|
+
return { display: '{}', type: 'object', expandable: false };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Create preview like DevTools - show first few properties
|
|
232
|
+
const previewProps = keys.slice(0, 5).map(key => {
|
|
233
|
+
const val = value[key];
|
|
234
|
+
if (typeof val === 'string') return `${key}: "${val}"`;
|
|
235
|
+
if (typeof val === 'object' && val !== null) {
|
|
236
|
+
if (Array.isArray(val)) return `${key}: Array(${val.length})`;
|
|
237
|
+
return `${key}: {...}`;
|
|
238
|
+
}
|
|
239
|
+
return `${key}: ${String(val)}`;
|
|
240
|
+
});
|
|
241
|
+
const preview = `{${previewProps.join(', ')}${keys.length > 5 ? `, ...${keys.length - 5} more` : ''}}`;
|
|
242
|
+
|
|
243
|
+
// Create complete structure for expansion
|
|
244
|
+
const structure = keys.map(key => {
|
|
245
|
+
const descriptor = descriptors[key];
|
|
246
|
+
const val = value[key];
|
|
247
|
+
return {
|
|
248
|
+
key: key,
|
|
249
|
+
value: val,
|
|
250
|
+
formatted: formatValue(val, depth + 1, maxDepth),
|
|
251
|
+
type: Array.isArray(val) ? 'array' : typeof val,
|
|
252
|
+
writable: descriptor.writable !== false,
|
|
253
|
+
enumerable: descriptor.enumerable !== false,
|
|
254
|
+
configurable: descriptor.configurable !== false
|
|
255
|
+
};
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Get constructor name for better object identification
|
|
259
|
+
const constructorName = value.constructor ? value.constructor.name : 'Object';
|
|
260
|
+
const objectDisplay = constructorName === 'Object' ? preview : `${constructorName} ${preview}`;
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
display: objectDisplay,
|
|
264
|
+
type: 'object',
|
|
265
|
+
expandable: true,
|
|
266
|
+
preview: objectDisplay,
|
|
267
|
+
structure: structure
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return { display: String(value), type: 'unknown', expandable: false };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// DevTools-style color scheme
|
|
275
|
+
function getDevToolsColors(type: string, theme: 'light' | 'dark' = 'dark'): string {
|
|
276
|
+
if (theme === 'light') {
|
|
277
|
+
switch (type) {
|
|
278
|
+
case 'string': return 'color: #c41230;'; // DevTools red for strings
|
|
279
|
+
case 'number': return 'color: #1976d2;'; // DevTools blue for numbers
|
|
280
|
+
case 'boolean': return 'color: #9c27b0;'; // DevTools purple for booleans
|
|
281
|
+
case 'null': return 'color: #808080;'; // DevTools gray for null
|
|
282
|
+
case 'undefined': return 'color: #808080;'; // DevTools gray for undefined
|
|
283
|
+
case 'function': return 'color: #795548;'; // DevTools brown for functions
|
|
284
|
+
case 'object': return 'color: #333;'; // DevTools dark for objects
|
|
285
|
+
case 'array': return 'color: #333;'; // DevTools dark for arrays
|
|
286
|
+
case 'error': return 'color: #d32f2f;'; // DevTools red for errors
|
|
287
|
+
case 'key': return 'color: #881391;'; // DevTools purple for keys
|
|
288
|
+
default: return 'color: #333;';
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
switch (type) {
|
|
292
|
+
case 'string': return 'color: #f28b54;'; // DevTools light orange for strings
|
|
293
|
+
case 'number': return 'color: #9c27b0;'; // DevTools blue for numbers
|
|
294
|
+
case 'boolean': return 'color: #9c27b0;'; // DevTools purple for booleans
|
|
295
|
+
case 'null': return 'color: #808080;'; // DevTools gray for null
|
|
296
|
+
case 'undefined': return 'color: #808080;'; // DevTools gray for undefined
|
|
297
|
+
case 'function': return 'color: #5db0d7;'; // DevTools cyan for functions
|
|
298
|
+
case 'object': return 'color: #e8eaed;'; // DevTools light for objects
|
|
299
|
+
case 'array': return 'color: #e8eaed;'; // DevTools light for arrays
|
|
300
|
+
case 'error': return 'color: #f28b54;'; // DevTools orange for errors
|
|
301
|
+
case 'key': return 'color: #9c27b0;'; // DevTools purple for keys
|
|
302
|
+
default: return 'color: #e8eaed;';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Store for expanded state of tree nodes
|
|
308
|
+
let expandedNodes = $state(new Set<string>());
|
|
309
|
+
|
|
310
|
+
function toggleNode(nodeId: string) {
|
|
311
|
+
if (expandedNodes.has(nodeId)) {
|
|
312
|
+
expandedNodes.delete(nodeId);
|
|
313
|
+
} else {
|
|
314
|
+
expandedNodes.add(nodeId);
|
|
315
|
+
}
|
|
316
|
+
expandedNodes = new Set(expandedNodes); // Trigger reactivity
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function generateNodeId(): string {
|
|
320
|
+
return `node-${Math.random().toString(36).substr(2, 9)}`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function getMessageColors(type: ConsoleMessage['type']): { bg: string, border: string, text: string, icon: string } {
|
|
324
|
+
switch (type) {
|
|
325
|
+
case 'error':
|
|
326
|
+
return {
|
|
327
|
+
bg: 'bg-red-50/80 dark:bg-red-950/30',
|
|
328
|
+
border: 'border-l-red-500',
|
|
329
|
+
text: 'text-red-800 dark:text-red-200',
|
|
330
|
+
icon: 'text-red-600 dark:text-red-400'
|
|
331
|
+
};
|
|
332
|
+
case 'warn':
|
|
333
|
+
return {
|
|
334
|
+
bg: 'bg-yellow-50/80 dark:bg-yellow-950/30',
|
|
335
|
+
border: 'border-l-yellow-500',
|
|
336
|
+
text: 'text-yellow-800 dark:text-yellow-200',
|
|
337
|
+
icon: 'text-yellow-600 dark:text-yellow-400'
|
|
338
|
+
};
|
|
339
|
+
case 'info':
|
|
340
|
+
return {
|
|
341
|
+
bg: 'bg-violet-50/80 dark:bg-violet-950/30',
|
|
342
|
+
border: 'border-l-blue-500',
|
|
343
|
+
text: 'text-violet-800 dark:text-violet-200',
|
|
344
|
+
icon: 'text-violet-600 dark:text-violet-400'
|
|
345
|
+
};
|
|
346
|
+
case 'debug':
|
|
347
|
+
return {
|
|
348
|
+
bg: 'bg-purple-50/80 dark:bg-purple-950/30',
|
|
349
|
+
border: 'border-l-purple-500',
|
|
350
|
+
text: 'text-purple-800 dark:text-purple-200',
|
|
351
|
+
icon: 'text-purple-600 dark:text-purple-400'
|
|
352
|
+
};
|
|
353
|
+
case 'trace':
|
|
354
|
+
return {
|
|
355
|
+
bg: 'bg-green-50/80 dark:bg-green-950/30',
|
|
356
|
+
border: 'border-l-green-500',
|
|
357
|
+
text: 'text-green-800 dark:text-green-200',
|
|
358
|
+
icon: 'text-green-600 dark:text-green-400'
|
|
359
|
+
};
|
|
360
|
+
case 'clear':
|
|
361
|
+
return {
|
|
362
|
+
bg: 'bg-slate-50/80 dark:bg-slate-900/30',
|
|
363
|
+
border: 'border-l-slate-400',
|
|
364
|
+
text: 'text-slate-600 dark:text-slate-400',
|
|
365
|
+
icon: 'text-slate-500 dark:text-slate-400'
|
|
366
|
+
};
|
|
367
|
+
default:
|
|
368
|
+
return {
|
|
369
|
+
bg: 'bg-transparent hover:bg-slate-50/50 dark:hover:bg-slate-800/50',
|
|
370
|
+
border: 'border-l-transparent',
|
|
371
|
+
text: 'text-slate-900 dark:text-slate-100',
|
|
372
|
+
icon: 'text-slate-600 dark:text-slate-400'
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function getValueTypeColor(type: string): string {
|
|
378
|
+
switch (type) {
|
|
379
|
+
case 'string': return 'text-green-600 dark:text-green-400';
|
|
380
|
+
case 'number': return 'text-violet-600 dark:text-violet-400';
|
|
381
|
+
case 'boolean': return 'text-purple-600 dark:text-purple-400';
|
|
382
|
+
case 'null': return 'text-gray-500 dark:text-gray-400';
|
|
383
|
+
case 'undefined': return 'text-gray-500 dark:text-gray-400';
|
|
384
|
+
case 'function': return 'text-cyan-600 dark:text-cyan-400';
|
|
385
|
+
case 'object': return 'text-orange-600 dark:text-orange-400';
|
|
386
|
+
case 'array': return 'text-violet-600 dark:text-violet-400';
|
|
387
|
+
case 'error': return 'text-red-600 dark:text-red-400';
|
|
388
|
+
case 'date': return 'text-pink-600 dark:text-pink-400';
|
|
389
|
+
case 'regexp': return 'text-yellow-600 dark:text-yellow-400';
|
|
390
|
+
case 'symbol': return 'text-violet-600 dark:text-violet-400';
|
|
391
|
+
default: return 'text-slate-700 dark:text-slate-300';
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function handleCommandSubmit(e: Event) {
|
|
396
|
+
e.preventDefault();
|
|
397
|
+
const command = commandInput.trim();
|
|
398
|
+
if (!command) return;
|
|
399
|
+
|
|
400
|
+
// Add to history
|
|
401
|
+
commandHistory.unshift(command);
|
|
402
|
+
if (commandHistory.length > 50) {
|
|
403
|
+
commandHistory = commandHistory.slice(0, 50);
|
|
404
|
+
}
|
|
405
|
+
historyIndex = -1;
|
|
406
|
+
|
|
407
|
+
// Execute command
|
|
408
|
+
onExecuteCommand(command);
|
|
409
|
+
commandInput = '';
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function handleKeyDown(e: KeyboardEvent) {
|
|
413
|
+
if (e.key === 'ArrowUp') {
|
|
414
|
+
e.preventDefault();
|
|
415
|
+
if (historyIndex < commandHistory.length - 1) {
|
|
416
|
+
historyIndex++;
|
|
417
|
+
commandInput = commandHistory[historyIndex] || '';
|
|
418
|
+
}
|
|
419
|
+
} else if (e.key === 'ArrowDown') {
|
|
420
|
+
e.preventDefault();
|
|
421
|
+
if (historyIndex > 0) {
|
|
422
|
+
historyIndex--;
|
|
423
|
+
commandInput = commandHistory[historyIndex] || '';
|
|
424
|
+
} else if (historyIndex === 0) {
|
|
425
|
+
historyIndex = -1;
|
|
426
|
+
commandInput = '';
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function scrollToBottom() {
|
|
432
|
+
if (consoleContainer && isAutoScroll) {
|
|
433
|
+
consoleContainer.scrollTop = consoleContainer.scrollHeight;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function toggleAutoScroll() {
|
|
438
|
+
isAutoScroll = !isAutoScroll;
|
|
439
|
+
if (isAutoScroll) {
|
|
440
|
+
scrollToBottom();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Auto-scroll when new messages arrive
|
|
445
|
+
$effect(() => {
|
|
446
|
+
if (messages.length > 0) {
|
|
447
|
+
setTimeout(scrollToBottom, 50);
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// Handle scroll to detect if user scrolled up
|
|
452
|
+
function handleScroll() {
|
|
453
|
+
if (!consoleContainer) return;
|
|
454
|
+
|
|
455
|
+
const { scrollTop, scrollHeight, clientHeight } = consoleContainer;
|
|
456
|
+
const isAtBottom = scrollTop + clientHeight >= scrollHeight - 10;
|
|
457
|
+
|
|
458
|
+
if (!isAtBottom && isAutoScroll) {
|
|
459
|
+
isAutoScroll = false;
|
|
460
|
+
} else if (isAtBottom && !isAutoScroll) {
|
|
461
|
+
isAutoScroll = true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
</script>
|
|
466
|
+
|
|
467
|
+
{#if isOpen}
|
|
468
|
+
<div
|
|
469
|
+
class="h-full flex flex-col bg-white dark:bg-gray-900 border-l border-gray-200 dark:border-gray-700"
|
|
470
|
+
in:fly={{ x: 300, duration: 300, easing: cubicOut }}
|
|
471
|
+
out:fly={{ x: 300, duration: 250, easing: cubicOut }}
|
|
472
|
+
>
|
|
473
|
+
<!-- DevTools-style Header -->
|
|
474
|
+
<div class="flex-shrink-0 bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
|
|
475
|
+
<!-- Toolbar -->
|
|
476
|
+
<div class="flex items-center justify-between px-3 py-2">
|
|
477
|
+
<div class="flex items-center gap-3">
|
|
478
|
+
<!-- Clear button (DevTools style) -->
|
|
479
|
+
<button
|
|
480
|
+
onclick={onClear}
|
|
481
|
+
class="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
482
|
+
title="Clear console"
|
|
483
|
+
>
|
|
484
|
+
<Icon name="lucide:ban" class="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
|
485
|
+
</button>
|
|
486
|
+
|
|
487
|
+
<!-- Filter buttons (DevTools style) -->
|
|
488
|
+
<div class="flex items-center gap-1">
|
|
489
|
+
<button
|
|
490
|
+
onclick={() => filterLevel = 'all'}
|
|
491
|
+
class="px-2 py-1 text-xs font-medium rounded {filterLevel === 'all' ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'} transition-colors"
|
|
492
|
+
>
|
|
493
|
+
All
|
|
494
|
+
</button>
|
|
495
|
+
{#if messageCounts.error > 0}
|
|
496
|
+
<button
|
|
497
|
+
onclick={() => filterLevel = 'error'}
|
|
498
|
+
class="px-2 py-1 text-xs font-medium rounded flex items-center gap-1 {filterLevel === 'error' ? 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'} transition-colors"
|
|
499
|
+
>
|
|
500
|
+
<span class="text-red-500">✕</span>
|
|
501
|
+
{messageCounts.error}
|
|
502
|
+
</button>
|
|
503
|
+
{/if}
|
|
504
|
+
{#if messageCounts.warn > 0}
|
|
505
|
+
<button
|
|
506
|
+
onclick={() => filterLevel = 'warn'}
|
|
507
|
+
class="px-2 py-1 text-xs font-medium rounded flex items-center gap-1 {filterLevel === 'warn' ? 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'} transition-colors"
|
|
508
|
+
>
|
|
509
|
+
<span class="text-yellow-500">⚠</span>
|
|
510
|
+
{messageCounts.warn}
|
|
511
|
+
</button>
|
|
512
|
+
{/if}
|
|
513
|
+
{#if messageCounts.info > 0}
|
|
514
|
+
<button
|
|
515
|
+
onclick={() => filterLevel = 'info'}
|
|
516
|
+
class="px-2 py-1 text-xs font-medium rounded flex items-center gap-1 {filterLevel === 'info' ? 'bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'} transition-colors"
|
|
517
|
+
>
|
|
518
|
+
<span class="text-violet-500">ℹ</span>
|
|
519
|
+
{messageCounts.info}
|
|
520
|
+
</button>
|
|
521
|
+
{/if}
|
|
522
|
+
{#if messageCounts.log > 0}
|
|
523
|
+
<button
|
|
524
|
+
onclick={() => filterLevel = 'log'}
|
|
525
|
+
class="px-2 py-1 text-xs font-medium rounded flex items-center gap-1 {filterLevel === 'log' ? 'bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-300' : 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700'} transition-colors"
|
|
526
|
+
>
|
|
527
|
+
{messageCounts.log}
|
|
528
|
+
</button>
|
|
529
|
+
{/if}
|
|
530
|
+
</div>
|
|
531
|
+
</div>
|
|
532
|
+
|
|
533
|
+
<div class="flex items-center gap-2">
|
|
534
|
+
<!-- Search -->
|
|
535
|
+
<div class="relative">
|
|
536
|
+
<Icon name="lucide:search" class="absolute left-2.5 top-1/2 transform -translate-y-1/2 w-3.5 h-3.5 text-gray-400" />
|
|
537
|
+
<input
|
|
538
|
+
type="text"
|
|
539
|
+
bind:value={searchQuery}
|
|
540
|
+
placeholder="Filter"
|
|
541
|
+
class="pl-8 pr-3 py-1 text-xs bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded focus:outline-none focus:ring-1 focus:ring-violet-500 focus:border-violet-500 w-40"
|
|
542
|
+
/>
|
|
543
|
+
</div>
|
|
544
|
+
|
|
545
|
+
<!-- Settings toggle -->
|
|
546
|
+
<button
|
|
547
|
+
onclick={() => onToggleLogging(!isLoggingEnabled)}
|
|
548
|
+
class="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
549
|
+
title={isLoggingEnabled ? 'Disable logging' : 'Enable logging'}
|
|
550
|
+
>
|
|
551
|
+
<Icon
|
|
552
|
+
name={isLoggingEnabled ? 'lucide:pause' : 'lucide:play'}
|
|
553
|
+
class="w-4 h-4 {isLoggingEnabled ? 'text-gray-600 dark:text-gray-400' : 'text-gray-400'}"
|
|
554
|
+
/>
|
|
555
|
+
</button>
|
|
556
|
+
|
|
557
|
+
<!-- Close -->
|
|
558
|
+
<button
|
|
559
|
+
onclick={() => isOpen = false}
|
|
560
|
+
class="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors"
|
|
561
|
+
title="Close console"
|
|
562
|
+
>
|
|
563
|
+
<Icon name="lucide:x" class="w-4 h-4 text-gray-600 dark:text-gray-400" />
|
|
564
|
+
</button>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
|
|
569
|
+
<!-- Console Messages (DevTools style) -->
|
|
570
|
+
<div
|
|
571
|
+
bind:this={consoleContainer}
|
|
572
|
+
onscroll={handleScroll}
|
|
573
|
+
role="log"
|
|
574
|
+
class="flex-1 overflow-y-auto bg-white dark:bg-gray-900 font-mono text-sm"
|
|
575
|
+
>
|
|
576
|
+
{#if filteredMessages.length === 0}
|
|
577
|
+
<div class="flex items-center justify-center h-full text-gray-500 dark:text-gray-400">
|
|
578
|
+
{#if messages.length === 0}
|
|
579
|
+
<div class="text-center">
|
|
580
|
+
<div class="text-4xl mb-2 opacity-20">⚡</div>
|
|
581
|
+
<p class="text-sm">Console is empty</p>
|
|
582
|
+
</div>
|
|
583
|
+
{:else}
|
|
584
|
+
<div class="text-center">
|
|
585
|
+
<div class="text-4xl mb-2 opacity-20">🔍</div>
|
|
586
|
+
<p class="text-sm">No messages match your filter</p>
|
|
587
|
+
</div>
|
|
588
|
+
{/if}
|
|
589
|
+
</div>
|
|
590
|
+
{:else}
|
|
591
|
+
{#each filteredMessages as message (message.id)}
|
|
592
|
+
{@const colors = getMessageColors(message.type)}
|
|
593
|
+
<div class="flex items-start px-3 border-b border-gray-100 dark:border-gray-800 {colors.bg} {colors.border} border-l-2 hover:bg-gray-50 dark:hover:bg-gray-800/50 transition-colors group">
|
|
594
|
+
<!-- Message content -->
|
|
595
|
+
<div class="flex-1 min-w-0 py-2 pr-3">
|
|
596
|
+
<!-- Message text -->
|
|
597
|
+
<div class="break-all">
|
|
598
|
+
{message.text}
|
|
599
|
+
</div>
|
|
600
|
+
|
|
601
|
+
<!-- Arguments (DevTools style) -->
|
|
602
|
+
{#if message.args && message.args.length > 0}
|
|
603
|
+
{#each formatArgs(message.args) as arg, index}
|
|
604
|
+
{@const formatted = formatValue(arg)}
|
|
605
|
+
{@const nodeId = generateNodeId()}
|
|
606
|
+
<div class="ml-1 font-mono text-sm">
|
|
607
|
+
{#if !formatted.expandable}
|
|
608
|
+
<span style={getDevToolsColors(formatted.type, 'dark')} class="break-all">{formatted.display}</span>
|
|
609
|
+
{:else if formatted.type === 'function' && formatted.fullContent}
|
|
610
|
+
<div class="flex items-start cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors duration-150 px-1 py-0.5 rounded">
|
|
611
|
+
<button
|
|
612
|
+
type="button"
|
|
613
|
+
onclick={() => toggleNode(nodeId)}
|
|
614
|
+
class="inline-block w-3 h-3 text-xs text-slate-500 dark:text-slate-400 cursor-pointer select-none mr-1 mt-0.5 flex-shrink-0 hover:text-slate-700 dark:hover:text-slate-300 font-mono"
|
|
615
|
+
>
|
|
616
|
+
{expandedNodes.has(nodeId) ? '▼' : '▶'}
|
|
617
|
+
</button>
|
|
618
|
+
<span style={getDevToolsColors('function', 'dark')} class="italic break-all">{formatted.display}</span>
|
|
619
|
+
{#if expandedNodes.has(nodeId)}
|
|
620
|
+
<div class="pl-2 border-l border-dotted border-slate-300 dark:border-slate-600">
|
|
621
|
+
<pre class="text-xs p-2 rounded bg-slate-50 dark:bg-slate-900 border border-slate-200 dark:border-slate-700 overflow-x-auto text-slate-800 dark:text-slate-200" style="white-space: pre-wrap; word-break: break-all;">{formatted.fullContent}</pre>
|
|
622
|
+
</div>
|
|
623
|
+
{/if}
|
|
624
|
+
</div>
|
|
625
|
+
{:else if formatted.structure}
|
|
626
|
+
<div class="flex items-start cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors duration-150 px-1 py-0.5 rounded">
|
|
627
|
+
<button
|
|
628
|
+
type="button"
|
|
629
|
+
onclick={() => toggleNode(nodeId)}
|
|
630
|
+
class="inline-block w-3 h-3 text-xs text-slate-500 dark:text-slate-400 cursor-pointer select-none mr-1 mt-0.5 flex-shrink-0 hover:text-slate-700 dark:hover:text-slate-300 font-mono"
|
|
631
|
+
>
|
|
632
|
+
{expandedNodes.has(nodeId) ? '▼' : '▶'}
|
|
633
|
+
</button>
|
|
634
|
+
<span style={getDevToolsColors(formatted.type, 'dark')} class="break-all">{formatted.display}</span>
|
|
635
|
+
{#if expandedNodes.has(nodeId)}
|
|
636
|
+
<div class="pl-2 border-l border-dotted border-slate-300 dark:border-slate-600">
|
|
637
|
+
{#each formatted.structure as item}
|
|
638
|
+
{@const keyStyle = formatted.type === 'array' ? getDevToolsColors('number', 'dark') : getDevToolsColors('key', 'dark')}
|
|
639
|
+
{@const subNodeId = generateNodeId()}
|
|
640
|
+
<div class="flex items-start hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors duration-150 px-1 py-0.5 rounded ml-3">
|
|
641
|
+
{#if item.formatted.expandable && item.formatted.structure}
|
|
642
|
+
<button
|
|
643
|
+
type="button"
|
|
644
|
+
onclick={() => toggleNode(subNodeId)}
|
|
645
|
+
class="inline-block w-3 h-3 text-xs text-slate-500 dark:text-slate-400 cursor-pointer select-none mr-1 mt-0.5 flex-shrink-0 hover:text-slate-700 dark:hover:text-slate-300 font-mono"
|
|
646
|
+
>
|
|
647
|
+
{expandedNodes.has(subNodeId) ? '▼' : '▶'}
|
|
648
|
+
</button>
|
|
649
|
+
{/if}
|
|
650
|
+
<span style={keyStyle} class="font-medium mr-1">{item.key}:</span>
|
|
651
|
+
<span style={getDevToolsColors(item.formatted.type, 'dark')} class="break-all">{item.formatted.display}</span>
|
|
652
|
+
{#if item.formatted.expandable && item.formatted.structure && expandedNodes.has(subNodeId)}
|
|
653
|
+
<div class="pl-2 border-l border-dotted border-slate-300 dark:border-slate-600">
|
|
654
|
+
{#each item.formatted.structure as subItem}
|
|
655
|
+
<div class="ml-3">
|
|
656
|
+
<span style={getDevToolsColors('key', 'dark')} class="font-medium mr-1">{subItem.key}:</span>
|
|
657
|
+
<span style={getDevToolsColors(subItem.formatted.type, 'dark')} class="break-all">{subItem.formatted.display}</span>
|
|
658
|
+
</div>
|
|
659
|
+
{/each}
|
|
660
|
+
</div>
|
|
661
|
+
{/if}
|
|
662
|
+
</div>
|
|
663
|
+
{/each}
|
|
664
|
+
</div>
|
|
665
|
+
{/if}
|
|
666
|
+
</div>
|
|
667
|
+
{:else}
|
|
668
|
+
<span style={getDevToolsColors(formatted.type, 'dark')} class="break-all">{formatted.display}</span>
|
|
669
|
+
{/if}
|
|
670
|
+
</div>
|
|
671
|
+
{/each}
|
|
672
|
+
{/if}
|
|
673
|
+
|
|
674
|
+
<!-- Location info -->
|
|
675
|
+
{#if message.location}
|
|
676
|
+
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
|
677
|
+
<button
|
|
678
|
+
type="button"
|
|
679
|
+
class="hover:underline cursor-pointer text-left"
|
|
680
|
+
title="Go to source"
|
|
681
|
+
onclick={() => {}}
|
|
682
|
+
>
|
|
683
|
+
{message.location.url.split('/').pop()}:{message.location.lineNumber}:{message.location.columnNumber}
|
|
684
|
+
</button>
|
|
685
|
+
</div>
|
|
686
|
+
{/if}
|
|
687
|
+
|
|
688
|
+
<!-- Stack trace -->
|
|
689
|
+
{#if message.stackTrace}
|
|
690
|
+
<details class="mt-2">
|
|
691
|
+
<summary class="cursor-pointer text-gray-500 dark:text-gray-400 text-xs hover:text-gray-700 dark:hover:text-gray-300 select-none">
|
|
692
|
+
Show stack trace
|
|
693
|
+
</summary>
|
|
694
|
+
<pre class="mt-2 p-3 bg-gray-100 dark:bg-gray-800 rounded text-xs text-gray-700 dark:text-gray-300 overflow-x-auto border-l-2 border-gray-300 dark:border-gray-600">{message.stackTrace}</pre>
|
|
695
|
+
</details>
|
|
696
|
+
{/if}
|
|
697
|
+
</div>
|
|
698
|
+
|
|
699
|
+
<!-- Actions (shown on hover) -->
|
|
700
|
+
<div class="opacity-0 group-hover:opacity-100 transition-opacity p-2 flex-shrink-0">
|
|
701
|
+
<button
|
|
702
|
+
onclick={() => {
|
|
703
|
+
const text = message.text + (message.args && message.args.length > 0 ? ' ' + message.args.map(arg => String(arg)).join(' ') : '');
|
|
704
|
+
navigator.clipboard.writeText(text);
|
|
705
|
+
}}
|
|
706
|
+
class="p-1 rounded hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
|
707
|
+
title="Copy to clipboard"
|
|
708
|
+
>
|
|
709
|
+
<Icon name="lucide:copy" class="w-3 h-3" />
|
|
710
|
+
</button>
|
|
711
|
+
</div>
|
|
712
|
+
</div>
|
|
713
|
+
{/each}
|
|
714
|
+
{/if}
|
|
715
|
+
</div>
|
|
716
|
+
|
|
717
|
+
<!-- Command Input (DevTools style) -->
|
|
718
|
+
<div class="flex-shrink-0 border-t border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900">
|
|
719
|
+
<form onsubmit={handleCommandSubmit} class="flex items-start px-3 py-2">
|
|
720
|
+
<!-- Prompt indicator -->
|
|
721
|
+
<div class="flex items-center mt-1 mr-2 text-violet-600 dark:text-violet-400 font-mono font-bold">
|
|
722
|
+
>
|
|
723
|
+
</div>
|
|
724
|
+
|
|
725
|
+
<!-- Input area -->
|
|
726
|
+
<div class="flex-1 min-h-6">
|
|
727
|
+
<input
|
|
728
|
+
type="text"
|
|
729
|
+
bind:value={commandInput}
|
|
730
|
+
onkeydown={handleKeyDown}
|
|
731
|
+
placeholder="Type JavaScript expression..."
|
|
732
|
+
class="w-full bg-transparent text-sm font-mono text-gray-900 dark:text-gray-100 outline-none placeholder-gray-500 dark:placeholder-gray-400 resize-none"
|
|
733
|
+
style="font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Fira Mono', 'Droid Sans Mono', 'Source Code Pro', monospace;"
|
|
734
|
+
/>
|
|
735
|
+
</div>
|
|
736
|
+
|
|
737
|
+
<!-- Execute button (only shown when typing) -->
|
|
738
|
+
{#if commandInput.trim()}
|
|
739
|
+
<button
|
|
740
|
+
type="submit"
|
|
741
|
+
class="ml-2 px-2 py-1 text-xs bg-violet-600 text-white rounded hover:bg-violet-700 transition-colors"
|
|
742
|
+
title="Execute (Enter)"
|
|
743
|
+
>
|
|
744
|
+
Execute
|
|
745
|
+
</button>
|
|
746
|
+
{/if}
|
|
747
|
+
</form>
|
|
748
|
+
|
|
749
|
+
<!-- Help text -->
|
|
750
|
+
{#if !commandInput.trim()}
|
|
751
|
+
<div class="px-3 pb-2 text-xs text-gray-500 dark:text-gray-400">
|
|
752
|
+
Tip: Use ↑/↓ for command history
|
|
753
|
+
</div>
|
|
754
|
+
{/if}
|
|
755
|
+
</div>
|
|
756
|
+
</div>
|
|
757
|
+
{/if}
|