@myrialabs/clopen 0.0.5 → 0.0.6
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 +5 -5
- package/.github/workflows/ci.yml +86 -86
- package/CONTRIBUTING.md +499 -499
- package/LICENSE +21 -21
- package/README.md +209 -209
- package/backend/index.ts +168 -165
- package/backend/lib/chat/helpers.ts +42 -42
- package/backend/lib/chat/index.ts +1 -1
- package/backend/lib/chat/stream-manager.ts +1126 -1126
- package/backend/lib/database/README.md +76 -76
- package/backend/lib/database/index.ts +118 -118
- package/backend/lib/database/migrations/001_create_projects_table.ts +30 -30
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +32 -32
- package/backend/lib/database/migrations/003_create_messages_table.ts +31 -31
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -34
- package/backend/lib/database/migrations/005_create_settings_table.ts +23 -23
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +57 -57
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +40 -40
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +61 -61
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -41
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -70
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -156
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -41
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -118
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -18
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -18
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -34
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -32
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -24
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -18
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -32
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -33
- package/backend/lib/database/migrations/index.ts +153 -153
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -87
- package/backend/lib/database/queries/engine-queries.ts +75 -75
- package/backend/lib/database/queries/index.ts +8 -8
- package/backend/lib/database/queries/message-queries.ts +471 -471
- package/backend/lib/database/queries/project-queries.ts +117 -117
- package/backend/lib/database/queries/session-queries.ts +270 -270
- package/backend/lib/database/queries/settings-queries.ts +33 -33
- package/backend/lib/database/queries/snapshot-queries.ts +325 -325
- package/backend/lib/database/queries/utils-queries.ts +58 -58
- package/backend/lib/database/seeders/index.ts +12 -12
- package/backend/lib/database/seeders/settings_seeder.ts +83 -83
- package/backend/lib/database/utils/connection.ts +173 -173
- package/backend/lib/database/utils/index.ts +3 -3
- package/backend/lib/database/utils/migration-runner.ts +117 -117
- package/backend/lib/database/utils/seeder-runner.ts +120 -120
- package/backend/lib/engine/adapters/claude/environment.ts +160 -164
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -60
- package/backend/lib/engine/adapters/claude/index.ts +1 -1
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -38
- package/backend/lib/engine/adapters/claude/stream.ts +177 -177
- package/backend/lib/engine/adapters/opencode/index.ts +2 -2
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -862
- package/backend/lib/engine/adapters/opencode/server.ts +104 -104
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -755
- package/backend/lib/engine/index.ts +196 -196
- package/backend/lib/engine/types.ts +58 -58
- package/backend/lib/files/file-operations.ts +478 -478
- package/backend/lib/files/file-reading.ts +308 -308
- package/backend/lib/files/file-watcher.ts +383 -383
- package/backend/lib/files/path-browsing.ts +382 -382
- package/backend/lib/git/git-executor.ts +89 -88
- package/backend/lib/git/git-parser.ts +411 -411
- package/backend/lib/git/git-service.ts +505 -505
- package/backend/lib/mcp/README.md +1144 -1144
- package/backend/lib/mcp/config.ts +317 -316
- package/backend/lib/mcp/index.ts +35 -35
- package/backend/lib/mcp/project-context.ts +236 -236
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -156
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -419
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -791
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -501
- package/backend/lib/mcp/servers/helper.ts +143 -143
- package/backend/lib/mcp/servers/index.ts +44 -44
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -56
- package/backend/lib/mcp/servers/weather/index.ts +31 -31
- package/backend/lib/mcp/stdio-server.ts +103 -103
- package/backend/lib/mcp/types.ts +65 -65
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -86
- package/backend/lib/preview/browser/browser-console-manager.ts +262 -262
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -222
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -421
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -415
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -512
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +103 -103
- package/backend/lib/preview/browser/browser-pool.ts +357 -357
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -882
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -935
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -695
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -292
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -85
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -438
- package/backend/lib/preview/browser/types.ts +359 -359
- package/backend/lib/preview/index.ts +23 -23
- package/backend/lib/project/index.ts +1 -1
- package/backend/lib/project/status-manager.ts +181 -181
- package/backend/lib/shared/env.ts +117 -0
- package/backend/lib/shared/index.ts +5 -2
- package/backend/lib/shared/port-utils.ts +25 -25
- package/backend/lib/shared/process-manager.ts +280 -280
- package/backend/lib/snapshot/blob-store.ts +227 -227
- package/backend/lib/snapshot/gitignore.ts +307 -307
- package/backend/lib/snapshot/helpers.ts +397 -397
- package/backend/lib/snapshot/snapshot-service.ts +483 -483
- package/backend/lib/terminal/helpers.ts +14 -14
- package/backend/lib/terminal/index.ts +7 -7
- package/backend/lib/terminal/pty-manager.ts +3 -3
- package/backend/lib/terminal/pty-session-manager.ts +370 -387
- package/backend/lib/terminal/shell-utils.ts +315 -312
- package/backend/lib/terminal/stream-manager.ts +292 -292
- package/backend/lib/tunnel/global-tunnel-manager.ts +266 -243
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -311
- package/backend/lib/user/helpers.ts +87 -87
- package/backend/lib/utils/ws.ts +944 -944
- package/backend/lib/vite-dev.ts +295 -295
- package/backend/middleware/cors.ts +16 -15
- package/backend/middleware/error-handler.ts +50 -49
- package/backend/middleware/logger.ts +9 -9
- package/backend/types/api.ts +24 -24
- package/backend/ws/README.md +1505 -1505
- package/backend/ws/chat/background.ts +198 -198
- package/backend/ws/chat/index.ts +21 -21
- package/backend/ws/chat/stream.ts +707 -707
- package/backend/ws/engine/claude/accounts.ts +399 -401
- package/backend/ws/engine/claude/index.ts +13 -13
- package/backend/ws/engine/claude/status.ts +43 -43
- package/backend/ws/engine/index.ts +14 -14
- package/backend/ws/engine/opencode/index.ts +11 -11
- package/backend/ws/engine/opencode/status.ts +30 -30
- package/backend/ws/engine/utils.ts +36 -36
- package/backend/ws/files/index.ts +30 -30
- package/backend/ws/files/read.ts +189 -189
- package/backend/ws/files/search.ts +453 -453
- package/backend/ws/files/watch.ts +124 -124
- package/backend/ws/files/write.ts +143 -143
- package/backend/ws/git/branch.ts +106 -106
- package/backend/ws/git/commit.ts +39 -39
- package/backend/ws/git/conflict.ts +68 -68
- package/backend/ws/git/diff.ts +69 -69
- package/backend/ws/git/index.ts +24 -24
- package/backend/ws/git/log.ts +41 -41
- package/backend/ws/git/remote.ts +214 -214
- package/backend/ws/git/staging.ts +84 -84
- package/backend/ws/git/status.ts +90 -90
- package/backend/ws/index.ts +69 -69
- package/backend/ws/mcp/index.ts +61 -61
- package/backend/ws/messages/crud.ts +74 -74
- package/backend/ws/messages/index.ts +14 -14
- package/backend/ws/preview/browser/cleanup.ts +129 -129
- package/backend/ws/preview/browser/console.ts +114 -114
- package/backend/ws/preview/browser/interact.ts +513 -513
- package/backend/ws/preview/browser/mcp.ts +129 -129
- package/backend/ws/preview/browser/native-ui.ts +235 -235
- package/backend/ws/preview/browser/stats.ts +55 -55
- package/backend/ws/preview/browser/tab-info.ts +126 -126
- package/backend/ws/preview/browser/tab.ts +166 -166
- package/backend/ws/preview/browser/webcodecs.ts +293 -293
- package/backend/ws/preview/index.ts +146 -146
- package/backend/ws/projects/crud.ts +113 -113
- package/backend/ws/projects/index.ts +25 -25
- package/backend/ws/projects/presence.ts +46 -46
- package/backend/ws/projects/status.ts +116 -116
- package/backend/ws/sessions/crud.ts +327 -327
- package/backend/ws/sessions/index.ts +33 -33
- package/backend/ws/settings/crud.ts +112 -112
- package/backend/ws/settings/index.ts +14 -14
- package/backend/ws/snapshot/index.ts +17 -17
- package/backend/ws/snapshot/restore.ts +173 -173
- package/backend/ws/snapshot/timeline.ts +141 -141
- package/backend/ws/system/index.ts +14 -14
- package/backend/ws/system/operations.ts +49 -49
- package/backend/ws/terminal/index.ts +40 -40
- package/backend/ws/terminal/persistence.ts +153 -153
- package/backend/ws/terminal/session.ts +382 -382
- package/backend/ws/terminal/stream.ts +79 -79
- package/backend/ws/tunnel/index.ts +14 -14
- package/backend/ws/tunnel/operations.ts +91 -91
- package/backend/ws/types.ts +20 -20
- package/backend/ws/user/crud.ts +156 -156
- package/backend/ws/user/index.ts +14 -14
- package/bin/clopen.ts +307 -307
- package/bun.lock +1353 -1352
- package/frontend/App.svelte +38 -34
- package/frontend/app.css +313 -313
- package/frontend/lib/app-environment.ts +10 -10
- package/frontend/lib/components/chat/ChatInterface.svelte +406 -406
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +56 -56
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +223 -223
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +394 -394
- package/frontend/lib/components/chat/formatters/Tools.svelte +69 -69
- package/frontend/lib/components/chat/formatters/index.ts +2 -2
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -421
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -78
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -30
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -33
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -619
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -48
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -31
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -201
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -148
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -216
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -357
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -57
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -478
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -541
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -86
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -86
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -157
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -59
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -124
- package/frontend/lib/components/chat/shared/index.ts +1 -1
- package/frontend/lib/components/chat/shared/utils.ts +115 -115
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -35
- package/frontend/lib/components/chat/tools/BashTool.svelte +45 -45
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -139
- package/frontend/lib/components/chat/tools/EditTool.svelte +47 -47
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +31 -31
- package/frontend/lib/components/chat/tools/GlobTool.svelte +50 -50
- package/frontend/lib/components/chat/tools/GrepTool.svelte +89 -89
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +25 -25
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +30 -30
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +37 -37
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +33 -33
- package/frontend/lib/components/chat/tools/ReadTool.svelte +40 -40
- package/frontend/lib/components/chat/tools/TaskTool.svelte +63 -63
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +74 -74
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +34 -34
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +83 -83
- package/frontend/lib/components/chat/tools/WriteTool.svelte +32 -32
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +78 -78
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +407 -407
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -45
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +18 -18
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +26 -26
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +53 -53
- package/frontend/lib/components/chat/tools/components/index.ts +7 -7
- package/frontend/lib/components/chat/tools/index.ts +25 -25
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +248 -248
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -78
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -391
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -26
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +86 -86
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -108
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -59
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -168
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -44
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -304
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -65
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -53
- package/frontend/lib/components/common/Alert.svelte +138 -138
- package/frontend/lib/components/common/AvatarBubble.svelte +55 -55
- package/frontend/lib/components/common/Button.svelte +71 -71
- package/frontend/lib/components/common/Card.svelte +102 -102
- package/frontend/lib/components/common/Checkbox.svelte +48 -48
- package/frontend/lib/components/common/Dialog.svelte +248 -248
- package/frontend/lib/components/common/FolderBrowser.svelte +842 -842
- package/frontend/lib/components/common/Icon.svelte +57 -57
- package/frontend/lib/components/common/Input.svelte +72 -72
- package/frontend/lib/components/common/Lightbox.svelte +232 -232
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -52
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -48
- package/frontend/lib/components/common/Modal.svelte +177 -177
- package/frontend/lib/components/common/ModalProvider.svelte +27 -27
- package/frontend/lib/components/common/ModelSelector.svelte +110 -110
- package/frontend/lib/components/common/MonacoEditor.svelte +568 -568
- package/frontend/lib/components/common/NotificationToast.svelte +113 -113
- package/frontend/lib/components/common/PageTemplate.svelte +75 -75
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -79
- package/frontend/lib/components/common/Select.svelte +97 -97
- package/frontend/lib/components/common/Textarea.svelte +79 -79
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -44
- package/frontend/lib/components/common/lucide-icons.ts +1642 -1642
- package/frontend/lib/components/common/material-icons.ts +1082 -1082
- package/frontend/lib/components/common/xterm/XTerm.svelte +809 -795
- package/frontend/lib/components/common/xterm/index.ts +15 -15
- package/frontend/lib/components/common/xterm/terminal-config.ts +67 -67
- package/frontend/lib/components/common/xterm/types.ts +30 -30
- package/frontend/lib/components/common/xterm/xterm-service.ts +379 -353
- package/frontend/lib/components/files/FileNode.svelte +383 -383
- package/frontend/lib/components/files/FileTree.svelte +681 -681
- package/frontend/lib/components/files/FileViewer.svelte +728 -728
- package/frontend/lib/components/files/SearchResults.svelte +303 -303
- package/frontend/lib/components/git/BranchManager.svelte +458 -458
- package/frontend/lib/components/git/ChangesSection.svelte +107 -107
- package/frontend/lib/components/git/CommitForm.svelte +76 -76
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -158
- package/frontend/lib/components/git/DiffViewer.svelte +364 -364
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -97
- package/frontend/lib/components/git/GitButton.svelte +33 -33
- package/frontend/lib/components/git/GitLog.svelte +361 -361
- package/frontend/lib/components/git/GitModal.svelte +80 -80
- package/frontend/lib/components/history/HistoryModal.svelte +563 -563
- package/frontend/lib/components/history/HistoryView.svelte +614 -614
- package/frontend/lib/components/index.ts +34 -34
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -549
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -1058
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +756 -756
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -450
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -236
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -224
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +338 -338
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +35 -35
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -155
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -837
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -113
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -296
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -391
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -231
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -210
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -239
- package/frontend/lib/components/preview/index.ts +1 -1
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -235
- package/frontend/lib/components/settings/SettingsView.svelte +36 -36
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -51
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -160
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -76
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -917
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -187
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -203
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -10
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -357
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -205
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -197
- package/frontend/lib/components/terminal/Terminal.svelte +367 -367
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -87
- package/frontend/lib/components/terminal/TerminalView.svelte +54 -54
- package/frontend/lib/components/tunnel/TunnelActive.svelte +157 -142
- package/frontend/lib/components/tunnel/TunnelButton.svelte +60 -54
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +285 -284
- package/frontend/lib/components/tunnel/TunnelModal.svelte +48 -47
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -49
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -382
- package/frontend/lib/components/workspace/MobileNavigator.svelte +394 -403
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -100
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -505
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -162
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -169
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -15
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -17
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -42
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +84 -84
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -37
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -274
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -1261
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -1560
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -150
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -73
- package/frontend/lib/constants/preview.ts +44 -44
- package/frontend/lib/services/chat/chat.service.ts +704 -704
- package/frontend/lib/services/chat/index.ts +6 -6
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -86
- package/frontend/lib/services/notification/index.ts +7 -7
- package/frontend/lib/services/notification/push.service.ts +143 -143
- package/frontend/lib/services/notification/sound.service.ts +126 -126
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -61
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -1499
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -67
- package/frontend/lib/services/preview/index.ts +22 -22
- package/frontend/lib/services/project/index.ts +7 -7
- package/frontend/lib/services/project/status.service.ts +159 -159
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -47
- package/frontend/lib/services/terminal/background/index.ts +129 -129
- package/frontend/lib/services/terminal/background/session-restore.ts +273 -273
- package/frontend/lib/services/terminal/background/stream-manager.ts +285 -285
- package/frontend/lib/services/terminal/index.ts +13 -13
- package/frontend/lib/services/terminal/persistence.service.ts +260 -260
- package/frontend/lib/services/terminal/project.service.ts +952 -952
- package/frontend/lib/services/terminal/session.service.ts +363 -363
- package/frontend/lib/services/terminal/terminal.service.ts +369 -369
- package/frontend/lib/stores/core/app.svelte.ts +117 -117
- package/frontend/lib/stores/core/files.svelte.ts +72 -72
- package/frontend/lib/stores/core/presence.svelte.ts +48 -48
- package/frontend/lib/stores/core/projects.svelte.ts +317 -317
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -383
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -58
- package/frontend/lib/stores/features/models.svelte.ts +89 -89
- package/frontend/lib/stores/features/settings.svelte.ts +87 -87
- package/frontend/lib/stores/features/terminal.svelte.ts +700 -700
- package/frontend/lib/stores/features/tunnel.svelte.ts +163 -161
- package/frontend/lib/stores/features/user.svelte.ts +95 -95
- package/frontend/lib/stores/ui/chat-input.svelte.ts +56 -56
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -61
- package/frontend/lib/stores/ui/dialog.svelte.ts +58 -58
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -214
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -166
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -88
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -179
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -754
- package/frontend/lib/types/native-ui.ts +73 -73
- package/frontend/lib/utils/chat/date-separator.ts +38 -38
- package/frontend/lib/utils/chat/message-grouper.ts +218 -218
- package/frontend/lib/utils/chat/message-processor.ts +134 -134
- package/frontend/lib/utils/chat/tool-handler.ts +160 -160
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -142
- package/frontend/lib/utils/click-outside.ts +20 -20
- package/frontend/lib/utils/context-manager.ts +256 -256
- package/frontend/lib/utils/file-icon-mappings.ts +768 -768
- package/frontend/lib/utils/folder-icon-mappings.ts +1029 -1029
- package/frontend/lib/utils/git-status.ts +68 -68
- package/frontend/lib/utils/platform.ts +112 -112
- package/frontend/lib/utils/port-check.ts +64 -64
- package/frontend/lib/utils/terminalFormatter.ts +206 -206
- package/frontend/lib/utils/theme.ts +6 -6
- package/frontend/lib/utils/tree-visualizer.ts +320 -320
- package/frontend/lib/utils/ws.ts +44 -44
- package/frontend/main.ts +13 -13
- package/index.html +70 -70
- package/package.json +111 -111
- package/scripts/generate-icons.ts +86 -86
- package/scripts/pre-publish-check.sh +142 -142
- package/scripts/setup-hooks.sh +134 -134
- package/scripts/validate-branch-name.sh +47 -47
- package/scripts/validate-commit-msg.sh +42 -42
- package/shared/constants/engines.ts +134 -134
- package/shared/types/database/connection.ts +15 -15
- package/shared/types/database/index.ts +5 -5
- package/shared/types/database/schema.ts +140 -140
- package/shared/types/engine/index.ts +45 -45
- package/shared/types/filesystem/index.ts +21 -21
- package/shared/types/git.ts +171 -171
- package/shared/types/messaging/index.ts +238 -238
- package/shared/types/messaging/tool.ts +525 -525
- package/shared/types/network/api.ts +17 -17
- package/shared/types/network/index.ts +4 -4
- package/shared/types/stores/app.ts +22 -22
- package/shared/types/stores/dialog.ts +20 -20
- package/shared/types/stores/index.ts +2 -2
- package/shared/types/stores/settings.ts +15 -15
- package/shared/types/terminal/index.ts +43 -43
- package/shared/types/ui/components.ts +60 -60
- package/shared/types/ui/icons.ts +22 -22
- package/shared/types/ui/index.ts +21 -21
- package/shared/types/ui/notifications.ts +13 -13
- package/shared/types/ui/theme.ts +11 -11
- package/shared/types/websocket/index.ts +43 -43
- package/shared/types/window.d.ts +12 -12
- package/shared/utils/anonymous-user.ts +167 -167
- package/shared/utils/async.ts +10 -10
- package/shared/utils/diff-calculator.ts +184 -184
- package/shared/utils/file-type-detection.ts +165 -165
- package/shared/utils/logger.ts +144 -144
- package/shared/utils/message-formatter.ts +79 -79
- package/shared/utils/path.ts +47 -47
- package/shared/utils/ws-client.ts +768 -768
- package/shared/utils/ws-server.ts +660 -660
- package/static/favicon.svg +7 -7
- package/static/fonts/dm-sans.css +96 -96
- package/svelte.config.js +20 -20
- package/tsconfig.json +41 -41
- package/vite.config.ts +33 -33
|
@@ -1,308 +1,308 @@
|
|
|
1
|
-
import { join, extname } from 'path';
|
|
2
|
-
import { readFileWithEncoding, isTextFile } from '$shared/utils/file-type-detection';
|
|
3
|
-
|
|
4
|
-
import { debug } from '$shared/utils/logger';
|
|
5
|
-
|
|
6
|
-
// Bun-compatible readdir implementation (cross-platform)
|
|
7
|
-
async function readdir(path: string): Promise<string[]> {
|
|
8
|
-
let proc;
|
|
9
|
-
if (process.platform === 'win32') {
|
|
10
|
-
proc = Bun.spawn(['cmd', '/c', 'dir', '/b', path], { stdout: 'pipe', stderr: 'ignore' });
|
|
11
|
-
} else {
|
|
12
|
-
proc = Bun.spawn(['ls', '-1', path], { stdout: 'pipe', stderr: 'ignore' });
|
|
13
|
-
}
|
|
14
|
-
const result = await new Response(proc.stdout).text();
|
|
15
|
-
// Split and clean up, removing \r characters for Windows compatibility
|
|
16
|
-
return result.trim().split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Return types
|
|
20
|
-
export interface FileTreeNode {
|
|
21
|
-
name: string;
|
|
22
|
-
type: 'file' | 'directory';
|
|
23
|
-
path: string;
|
|
24
|
-
size?: number;
|
|
25
|
-
modified: string;
|
|
26
|
-
extension?: string;
|
|
27
|
-
children?: FileTreeNode[];
|
|
28
|
-
error?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Helper function to build file tree
|
|
32
|
-
export async function buildFileTree(
|
|
33
|
-
rootPath: string,
|
|
34
|
-
maxDepth: number = 3,
|
|
35
|
-
currentDepth: number = 0,
|
|
36
|
-
expandedPaths?: Set<string>
|
|
37
|
-
): Promise<FileTreeNode | null> {
|
|
38
|
-
try {
|
|
39
|
-
const file = Bun.file(rootPath);
|
|
40
|
-
const stats = await file.stat();
|
|
41
|
-
const name = rootPath.split(/[/\\]/).pop() || 'root';
|
|
42
|
-
|
|
43
|
-
if (stats.isFile()) {
|
|
44
|
-
return {
|
|
45
|
-
name,
|
|
46
|
-
type: 'file',
|
|
47
|
-
path: rootPath,
|
|
48
|
-
size: stats.size,
|
|
49
|
-
modified: stats.mtime.toISOString(),
|
|
50
|
-
extension: extname(rootPath)
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (stats.isDirectory()) {
|
|
55
|
-
try {
|
|
56
|
-
const items = await readdir(rootPath);
|
|
57
|
-
const children: FileTreeNode[] = [];
|
|
58
|
-
|
|
59
|
-
// Check if this folder should load children:
|
|
60
|
-
// 1. Initial load (depth 0) - always load
|
|
61
|
-
// 2. Folder is in expandedPaths - load recursively
|
|
62
|
-
const shouldLoadChildren = currentDepth < 1 || (expandedPaths && expandedPaths.has(rootPath));
|
|
63
|
-
|
|
64
|
-
if (shouldLoadChildren) {
|
|
65
|
-
for (const item of items.slice(0, 100)) { // Limit to 100 items per directory
|
|
66
|
-
const itemPath = join(rootPath, item);
|
|
67
|
-
|
|
68
|
-
// Show all files and directories
|
|
69
|
-
try {
|
|
70
|
-
const itemFile = Bun.file(itemPath);
|
|
71
|
-
const itemStats = await itemFile.stat();
|
|
72
|
-
|
|
73
|
-
if (itemStats.isFile()) {
|
|
74
|
-
children.push({
|
|
75
|
-
name: item,
|
|
76
|
-
type: 'file',
|
|
77
|
-
path: itemPath,
|
|
78
|
-
size: itemStats.size,
|
|
79
|
-
modified: itemStats.mtime.toISOString(),
|
|
80
|
-
extension: extname(item)
|
|
81
|
-
});
|
|
82
|
-
} else if (itemStats.isDirectory()) {
|
|
83
|
-
// Check if this directory should be expanded
|
|
84
|
-
const isExpanded = expandedPaths && expandedPaths.has(itemPath);
|
|
85
|
-
|
|
86
|
-
if (isExpanded) {
|
|
87
|
-
// Recursively load children for expanded folders
|
|
88
|
-
const subTree = await buildFileTree(itemPath, maxDepth, currentDepth + 1, expandedPaths);
|
|
89
|
-
if (subTree) {
|
|
90
|
-
children.push(subTree);
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
// For collapsed directories, just add basic info without loading children
|
|
94
|
-
children.push({
|
|
95
|
-
name: item,
|
|
96
|
-
type: 'directory',
|
|
97
|
-
path: itemPath,
|
|
98
|
-
modified: itemStats.mtime.toISOString(),
|
|
99
|
-
// Add empty children array to indicate it can be expanded
|
|
100
|
-
children: []
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
} catch (error) {
|
|
105
|
-
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Sort children: directories first, then files
|
|
111
|
-
children.sort((a, b) => {
|
|
112
|
-
if (a.type !== b.type) {
|
|
113
|
-
return a.type === 'directory' ? -1 : 1;
|
|
114
|
-
}
|
|
115
|
-
return a.name.localeCompare(b.name);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
return {
|
|
119
|
-
name,
|
|
120
|
-
type: 'directory',
|
|
121
|
-
path: rootPath,
|
|
122
|
-
children,
|
|
123
|
-
modified: stats.mtime.toISOString()
|
|
124
|
-
};
|
|
125
|
-
} catch {
|
|
126
|
-
// Permission denied or other error reading directory
|
|
127
|
-
return {
|
|
128
|
-
name,
|
|
129
|
-
type: 'directory',
|
|
130
|
-
path: rootPath,
|
|
131
|
-
error: 'Permission denied',
|
|
132
|
-
modified: stats.mtime.toISOString()
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return null;
|
|
138
|
-
} catch (error) {
|
|
139
|
-
debug.error('file', `Error building file tree for ${rootPath}:`, error);
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Helper function to list directory contents
|
|
145
|
-
export async function listDirectoryContents(dirPath: string): Promise<FileTreeNode[]> {
|
|
146
|
-
// Use stat directly instead of Bun.file for directory checking
|
|
147
|
-
const dirFile = Bun.file(dirPath);
|
|
148
|
-
const stats = await dirFile.stat();
|
|
149
|
-
|
|
150
|
-
if (!stats.isDirectory()) {
|
|
151
|
-
throw new Error('Path is not a directory');
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
const items = await readdir(dirPath);
|
|
155
|
-
const children: FileTreeNode[] = [];
|
|
156
|
-
|
|
157
|
-
// Show all files and directories
|
|
158
|
-
for (const item of items.slice(0, 100)) { // Limit to 100 items per directory
|
|
159
|
-
|
|
160
|
-
const itemPath = join(dirPath, item);
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
const itemFile = Bun.file(itemPath);
|
|
164
|
-
const itemStats = await itemFile.stat();
|
|
165
|
-
|
|
166
|
-
if (itemStats.isFile()) {
|
|
167
|
-
children.push({
|
|
168
|
-
name: item,
|
|
169
|
-
type: 'file',
|
|
170
|
-
path: itemPath,
|
|
171
|
-
size: itemStats.size,
|
|
172
|
-
modified: itemStats.mtime.toISOString(),
|
|
173
|
-
extension: extname(item)
|
|
174
|
-
});
|
|
175
|
-
} else if (itemStats.isDirectory()) {
|
|
176
|
-
children.push({
|
|
177
|
-
name: item,
|
|
178
|
-
type: 'directory',
|
|
179
|
-
path: itemPath,
|
|
180
|
-
modified: itemStats.mtime.toISOString(),
|
|
181
|
-
// Add empty children array to indicate it can be expanded
|
|
182
|
-
children: []
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
} catch (error) {
|
|
186
|
-
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Sort children: directories first, then files
|
|
191
|
-
children.sort((a, b) => {
|
|
192
|
-
if (a.type !== b.type) {
|
|
193
|
-
return a.type === 'directory' ? -1 : 1;
|
|
194
|
-
}
|
|
195
|
-
return a.name.localeCompare(b.name);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
return children;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// File content data
|
|
202
|
-
export interface FileContentData {
|
|
203
|
-
content: string;
|
|
204
|
-
size: number;
|
|
205
|
-
modified: string;
|
|
206
|
-
extension: string;
|
|
207
|
-
encoding?: string;
|
|
208
|
-
isBinary?: boolean;
|
|
209
|
-
error?: string;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Helper function to read file contents
|
|
213
|
-
export async function readFileContents(filePath: string): Promise<FileContentData> {
|
|
214
|
-
const file = Bun.file(filePath);
|
|
215
|
-
if (!(await file.exists())) {
|
|
216
|
-
throw new Error('File does not exist');
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
const stats = await file.stat();
|
|
220
|
-
|
|
221
|
-
// Check if it's a text file
|
|
222
|
-
const isText = await isTextFile(filePath);
|
|
223
|
-
|
|
224
|
-
if (!isText) {
|
|
225
|
-
// Binary file
|
|
226
|
-
const buffer = Buffer.from(await file.arrayBuffer());
|
|
227
|
-
return {
|
|
228
|
-
content: `[Binary file - ${buffer.length} bytes]`,
|
|
229
|
-
size: stats.size,
|
|
230
|
-
modified: stats.mtime.toISOString(),
|
|
231
|
-
extension: extname(filePath),
|
|
232
|
-
isBinary: true
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// Text file - detect encoding and read
|
|
237
|
-
const { content, detectedEncoding } = await readFileWithEncoding(filePath);
|
|
238
|
-
|
|
239
|
-
return {
|
|
240
|
-
content,
|
|
241
|
-
size: stats.size,
|
|
242
|
-
modified: stats.mtime.toISOString(),
|
|
243
|
-
extension: extname(filePath),
|
|
244
|
-
encoding: detectedEncoding
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Helper function to search files
|
|
249
|
-
export async function searchFiles(rootPath: string, query: string): Promise<FileTreeNode[]> {
|
|
250
|
-
const results: FileTreeNode[] = [];
|
|
251
|
-
|
|
252
|
-
async function searchRecursive(dirPath: string, depth: number = 0) {
|
|
253
|
-
// Limit search depth to prevent infinite recursion
|
|
254
|
-
if (depth > 5) return;
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const items = await readdir(dirPath);
|
|
258
|
-
|
|
259
|
-
for (const item of items) {
|
|
260
|
-
// Skip common directories that shouldn't be searched
|
|
261
|
-
const skipDirs = ['node_modules', '.git', '.svelte-kit', 'build', 'dist', 'coverage', '.next', '.nuxt', 'target'];
|
|
262
|
-
if (skipDirs.includes(item)) continue;
|
|
263
|
-
|
|
264
|
-
const itemPath = join(dirPath, item);
|
|
265
|
-
|
|
266
|
-
try {
|
|
267
|
-
const itemFile = Bun.file(itemPath);
|
|
268
|
-
const stats = await itemFile.stat();
|
|
269
|
-
|
|
270
|
-
// Check if item name matches query (case insensitive)
|
|
271
|
-
if (item.toLowerCase().includes(query.toLowerCase())) {
|
|
272
|
-
results.push({
|
|
273
|
-
name: item,
|
|
274
|
-
type: stats.isDirectory() ? 'directory' : 'file',
|
|
275
|
-
path: itemPath,
|
|
276
|
-
size: stats.isFile() ? stats.size : undefined,
|
|
277
|
-
modified: stats.mtime.toISOString(),
|
|
278
|
-
extension: stats.isFile() ? extname(item) : undefined
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Recursively search in directories
|
|
283
|
-
if (stats.isDirectory()) {
|
|
284
|
-
await searchRecursive(itemPath, depth + 1);
|
|
285
|
-
}
|
|
286
|
-
} catch (error) {
|
|
287
|
-
// Skip items we can't access
|
|
288
|
-
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
} catch (error) {
|
|
292
|
-
debug.debug('file', `Cannot read directory ${dirPath}:`, error);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
await searchRecursive(rootPath);
|
|
297
|
-
|
|
298
|
-
// Sort results: directories first, then files, both alphabetically
|
|
299
|
-
results.sort((a, b) => {
|
|
300
|
-
if (a.type !== b.type) {
|
|
301
|
-
return a.type === 'directory' ? -1 : 1;
|
|
302
|
-
}
|
|
303
|
-
return a.name.localeCompare(b.name);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
// Limit results to prevent overwhelming the UI
|
|
307
|
-
return results.slice(0, 100);
|
|
308
|
-
}
|
|
1
|
+
import { join, extname } from 'path';
|
|
2
|
+
import { readFileWithEncoding, isTextFile } from '$shared/utils/file-type-detection';
|
|
3
|
+
|
|
4
|
+
import { debug } from '$shared/utils/logger';
|
|
5
|
+
|
|
6
|
+
// Bun-compatible readdir implementation (cross-platform)
|
|
7
|
+
async function readdir(path: string): Promise<string[]> {
|
|
8
|
+
let proc;
|
|
9
|
+
if (process.platform === 'win32') {
|
|
10
|
+
proc = Bun.spawn(['cmd', '/c', 'dir', '/b', path], { stdout: 'pipe', stderr: 'ignore' });
|
|
11
|
+
} else {
|
|
12
|
+
proc = Bun.spawn(['ls', '-1', path], { stdout: 'pipe', stderr: 'ignore' });
|
|
13
|
+
}
|
|
14
|
+
const result = await new Response(proc.stdout).text();
|
|
15
|
+
// Split and clean up, removing \r characters for Windows compatibility
|
|
16
|
+
return result.trim().split(/\r?\n/).map(line => line.trim()).filter(Boolean);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Return types
|
|
20
|
+
export interface FileTreeNode {
|
|
21
|
+
name: string;
|
|
22
|
+
type: 'file' | 'directory';
|
|
23
|
+
path: string;
|
|
24
|
+
size?: number;
|
|
25
|
+
modified: string;
|
|
26
|
+
extension?: string;
|
|
27
|
+
children?: FileTreeNode[];
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Helper function to build file tree
|
|
32
|
+
export async function buildFileTree(
|
|
33
|
+
rootPath: string,
|
|
34
|
+
maxDepth: number = 3,
|
|
35
|
+
currentDepth: number = 0,
|
|
36
|
+
expandedPaths?: Set<string>
|
|
37
|
+
): Promise<FileTreeNode | null> {
|
|
38
|
+
try {
|
|
39
|
+
const file = Bun.file(rootPath);
|
|
40
|
+
const stats = await file.stat();
|
|
41
|
+
const name = rootPath.split(/[/\\]/).pop() || 'root';
|
|
42
|
+
|
|
43
|
+
if (stats.isFile()) {
|
|
44
|
+
return {
|
|
45
|
+
name,
|
|
46
|
+
type: 'file',
|
|
47
|
+
path: rootPath,
|
|
48
|
+
size: stats.size,
|
|
49
|
+
modified: stats.mtime.toISOString(),
|
|
50
|
+
extension: extname(rootPath)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (stats.isDirectory()) {
|
|
55
|
+
try {
|
|
56
|
+
const items = await readdir(rootPath);
|
|
57
|
+
const children: FileTreeNode[] = [];
|
|
58
|
+
|
|
59
|
+
// Check if this folder should load children:
|
|
60
|
+
// 1. Initial load (depth 0) - always load
|
|
61
|
+
// 2. Folder is in expandedPaths - load recursively
|
|
62
|
+
const shouldLoadChildren = currentDepth < 1 || (expandedPaths && expandedPaths.has(rootPath));
|
|
63
|
+
|
|
64
|
+
if (shouldLoadChildren) {
|
|
65
|
+
for (const item of items.slice(0, 100)) { // Limit to 100 items per directory
|
|
66
|
+
const itemPath = join(rootPath, item);
|
|
67
|
+
|
|
68
|
+
// Show all files and directories
|
|
69
|
+
try {
|
|
70
|
+
const itemFile = Bun.file(itemPath);
|
|
71
|
+
const itemStats = await itemFile.stat();
|
|
72
|
+
|
|
73
|
+
if (itemStats.isFile()) {
|
|
74
|
+
children.push({
|
|
75
|
+
name: item,
|
|
76
|
+
type: 'file',
|
|
77
|
+
path: itemPath,
|
|
78
|
+
size: itemStats.size,
|
|
79
|
+
modified: itemStats.mtime.toISOString(),
|
|
80
|
+
extension: extname(item)
|
|
81
|
+
});
|
|
82
|
+
} else if (itemStats.isDirectory()) {
|
|
83
|
+
// Check if this directory should be expanded
|
|
84
|
+
const isExpanded = expandedPaths && expandedPaths.has(itemPath);
|
|
85
|
+
|
|
86
|
+
if (isExpanded) {
|
|
87
|
+
// Recursively load children for expanded folders
|
|
88
|
+
const subTree = await buildFileTree(itemPath, maxDepth, currentDepth + 1, expandedPaths);
|
|
89
|
+
if (subTree) {
|
|
90
|
+
children.push(subTree);
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
// For collapsed directories, just add basic info without loading children
|
|
94
|
+
children.push({
|
|
95
|
+
name: item,
|
|
96
|
+
type: 'directory',
|
|
97
|
+
path: itemPath,
|
|
98
|
+
modified: itemStats.mtime.toISOString(),
|
|
99
|
+
// Add empty children array to indicate it can be expanded
|
|
100
|
+
children: []
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Sort children: directories first, then files
|
|
111
|
+
children.sort((a, b) => {
|
|
112
|
+
if (a.type !== b.type) {
|
|
113
|
+
return a.type === 'directory' ? -1 : 1;
|
|
114
|
+
}
|
|
115
|
+
return a.name.localeCompare(b.name);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
name,
|
|
120
|
+
type: 'directory',
|
|
121
|
+
path: rootPath,
|
|
122
|
+
children,
|
|
123
|
+
modified: stats.mtime.toISOString()
|
|
124
|
+
};
|
|
125
|
+
} catch {
|
|
126
|
+
// Permission denied or other error reading directory
|
|
127
|
+
return {
|
|
128
|
+
name,
|
|
129
|
+
type: 'directory',
|
|
130
|
+
path: rootPath,
|
|
131
|
+
error: 'Permission denied',
|
|
132
|
+
modified: stats.mtime.toISOString()
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
debug.error('file', `Error building file tree for ${rootPath}:`, error);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Helper function to list directory contents
|
|
145
|
+
export async function listDirectoryContents(dirPath: string): Promise<FileTreeNode[]> {
|
|
146
|
+
// Use stat directly instead of Bun.file for directory checking
|
|
147
|
+
const dirFile = Bun.file(dirPath);
|
|
148
|
+
const stats = await dirFile.stat();
|
|
149
|
+
|
|
150
|
+
if (!stats.isDirectory()) {
|
|
151
|
+
throw new Error('Path is not a directory');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const items = await readdir(dirPath);
|
|
155
|
+
const children: FileTreeNode[] = [];
|
|
156
|
+
|
|
157
|
+
// Show all files and directories
|
|
158
|
+
for (const item of items.slice(0, 100)) { // Limit to 100 items per directory
|
|
159
|
+
|
|
160
|
+
const itemPath = join(dirPath, item);
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const itemFile = Bun.file(itemPath);
|
|
164
|
+
const itemStats = await itemFile.stat();
|
|
165
|
+
|
|
166
|
+
if (itemStats.isFile()) {
|
|
167
|
+
children.push({
|
|
168
|
+
name: item,
|
|
169
|
+
type: 'file',
|
|
170
|
+
path: itemPath,
|
|
171
|
+
size: itemStats.size,
|
|
172
|
+
modified: itemStats.mtime.toISOString(),
|
|
173
|
+
extension: extname(item)
|
|
174
|
+
});
|
|
175
|
+
} else if (itemStats.isDirectory()) {
|
|
176
|
+
children.push({
|
|
177
|
+
name: item,
|
|
178
|
+
type: 'directory',
|
|
179
|
+
path: itemPath,
|
|
180
|
+
modified: itemStats.mtime.toISOString(),
|
|
181
|
+
// Add empty children array to indicate it can be expanded
|
|
182
|
+
children: []
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Sort children: directories first, then files
|
|
191
|
+
children.sort((a, b) => {
|
|
192
|
+
if (a.type !== b.type) {
|
|
193
|
+
return a.type === 'directory' ? -1 : 1;
|
|
194
|
+
}
|
|
195
|
+
return a.name.localeCompare(b.name);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
return children;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// File content data
|
|
202
|
+
export interface FileContentData {
|
|
203
|
+
content: string;
|
|
204
|
+
size: number;
|
|
205
|
+
modified: string;
|
|
206
|
+
extension: string;
|
|
207
|
+
encoding?: string;
|
|
208
|
+
isBinary?: boolean;
|
|
209
|
+
error?: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Helper function to read file contents
|
|
213
|
+
export async function readFileContents(filePath: string): Promise<FileContentData> {
|
|
214
|
+
const file = Bun.file(filePath);
|
|
215
|
+
if (!(await file.exists())) {
|
|
216
|
+
throw new Error('File does not exist');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const stats = await file.stat();
|
|
220
|
+
|
|
221
|
+
// Check if it's a text file
|
|
222
|
+
const isText = await isTextFile(filePath);
|
|
223
|
+
|
|
224
|
+
if (!isText) {
|
|
225
|
+
// Binary file
|
|
226
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
227
|
+
return {
|
|
228
|
+
content: `[Binary file - ${buffer.length} bytes]`,
|
|
229
|
+
size: stats.size,
|
|
230
|
+
modified: stats.mtime.toISOString(),
|
|
231
|
+
extension: extname(filePath),
|
|
232
|
+
isBinary: true
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Text file - detect encoding and read
|
|
237
|
+
const { content, detectedEncoding } = await readFileWithEncoding(filePath);
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
content,
|
|
241
|
+
size: stats.size,
|
|
242
|
+
modified: stats.mtime.toISOString(),
|
|
243
|
+
extension: extname(filePath),
|
|
244
|
+
encoding: detectedEncoding
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Helper function to search files
|
|
249
|
+
export async function searchFiles(rootPath: string, query: string): Promise<FileTreeNode[]> {
|
|
250
|
+
const results: FileTreeNode[] = [];
|
|
251
|
+
|
|
252
|
+
async function searchRecursive(dirPath: string, depth: number = 0) {
|
|
253
|
+
// Limit search depth to prevent infinite recursion
|
|
254
|
+
if (depth > 5) return;
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const items = await readdir(dirPath);
|
|
258
|
+
|
|
259
|
+
for (const item of items) {
|
|
260
|
+
// Skip common directories that shouldn't be searched
|
|
261
|
+
const skipDirs = ['node_modules', '.git', '.svelte-kit', 'build', 'dist', 'coverage', '.next', '.nuxt', 'target'];
|
|
262
|
+
if (skipDirs.includes(item)) continue;
|
|
263
|
+
|
|
264
|
+
const itemPath = join(dirPath, item);
|
|
265
|
+
|
|
266
|
+
try {
|
|
267
|
+
const itemFile = Bun.file(itemPath);
|
|
268
|
+
const stats = await itemFile.stat();
|
|
269
|
+
|
|
270
|
+
// Check if item name matches query (case insensitive)
|
|
271
|
+
if (item.toLowerCase().includes(query.toLowerCase())) {
|
|
272
|
+
results.push({
|
|
273
|
+
name: item,
|
|
274
|
+
type: stats.isDirectory() ? 'directory' : 'file',
|
|
275
|
+
path: itemPath,
|
|
276
|
+
size: stats.isFile() ? stats.size : undefined,
|
|
277
|
+
modified: stats.mtime.toISOString(),
|
|
278
|
+
extension: stats.isFile() ? extname(item) : undefined
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Recursively search in directories
|
|
283
|
+
if (stats.isDirectory()) {
|
|
284
|
+
await searchRecursive(itemPath, depth + 1);
|
|
285
|
+
}
|
|
286
|
+
} catch (error) {
|
|
287
|
+
// Skip items we can't access
|
|
288
|
+
debug.debug('file', `Cannot access ${itemPath}:`, error);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} catch (error) {
|
|
292
|
+
debug.debug('file', `Cannot read directory ${dirPath}:`, error);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
await searchRecursive(rootPath);
|
|
297
|
+
|
|
298
|
+
// Sort results: directories first, then files, both alphabetically
|
|
299
|
+
results.sort((a, b) => {
|
|
300
|
+
if (a.type !== b.type) {
|
|
301
|
+
return a.type === 'directory' ? -1 : 1;
|
|
302
|
+
}
|
|
303
|
+
return a.name.localeCompare(b.name);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Limit results to prevent overwhelming the UI
|
|
307
|
+
return results.slice(0, 100);
|
|
308
|
+
}
|