@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.
Files changed (456) hide show
  1. package/.env.example +6 -0
  2. package/.github/workflows/release.yml +60 -0
  3. package/.github/workflows/test.yml +40 -0
  4. package/CONTRIBUTING.md +499 -0
  5. package/LICENSE +21 -0
  6. package/README.md +209 -0
  7. package/backend/index.ts +156 -0
  8. package/backend/lib/chat/helpers.ts +42 -0
  9. package/backend/lib/chat/index.ts +2 -0
  10. package/backend/lib/chat/stream-manager.ts +1126 -0
  11. package/backend/lib/database/README.md +77 -0
  12. package/backend/lib/database/index.ts +119 -0
  13. package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
  14. package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
  15. package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
  16. package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
  17. package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
  18. package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
  19. package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
  20. package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
  21. package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
  22. package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
  23. package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
  24. package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
  25. package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
  26. package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
  27. package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
  28. package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
  29. package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
  30. package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
  31. package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
  32. package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
  33. package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
  34. package/backend/lib/database/migrations/index.ts +154 -0
  35. package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
  36. package/backend/lib/database/queries/engine-queries.ts +75 -0
  37. package/backend/lib/database/queries/index.ts +9 -0
  38. package/backend/lib/database/queries/message-queries.ts +472 -0
  39. package/backend/lib/database/queries/project-queries.ts +117 -0
  40. package/backend/lib/database/queries/session-queries.ts +271 -0
  41. package/backend/lib/database/queries/settings-queries.ts +34 -0
  42. package/backend/lib/database/queries/snapshot-queries.ts +326 -0
  43. package/backend/lib/database/queries/utils-queries.ts +59 -0
  44. package/backend/lib/database/seeders/index.ts +13 -0
  45. package/backend/lib/database/seeders/settings_seeder.ts +84 -0
  46. package/backend/lib/database/utils/connection.ts +174 -0
  47. package/backend/lib/database/utils/index.ts +4 -0
  48. package/backend/lib/database/utils/migration-runner.ts +118 -0
  49. package/backend/lib/database/utils/seeder-runner.ts +121 -0
  50. package/backend/lib/engine/adapters/claude/environment.ts +164 -0
  51. package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
  52. package/backend/lib/engine/adapters/claude/index.ts +1 -0
  53. package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
  54. package/backend/lib/engine/adapters/claude/stream.ts +177 -0
  55. package/backend/lib/engine/adapters/opencode/index.ts +2 -0
  56. package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
  57. package/backend/lib/engine/adapters/opencode/server.ts +104 -0
  58. package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
  59. package/backend/lib/engine/index.ts +196 -0
  60. package/backend/lib/engine/types.ts +58 -0
  61. package/backend/lib/files/file-operations.ts +478 -0
  62. package/backend/lib/files/file-reading.ts +308 -0
  63. package/backend/lib/files/file-watcher.ts +383 -0
  64. package/backend/lib/files/path-browsing.ts +382 -0
  65. package/backend/lib/git/git-executor.ts +88 -0
  66. package/backend/lib/git/git-parser.ts +411 -0
  67. package/backend/lib/git/git-service.ts +505 -0
  68. package/backend/lib/mcp/README.md +1144 -0
  69. package/backend/lib/mcp/config.ts +316 -0
  70. package/backend/lib/mcp/index.ts +35 -0
  71. package/backend/lib/mcp/project-context.ts +236 -0
  72. package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
  73. package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
  74. package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
  75. package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
  76. package/backend/lib/mcp/servers/helper.ts +143 -0
  77. package/backend/lib/mcp/servers/index.ts +45 -0
  78. package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
  79. package/backend/lib/mcp/servers/weather/index.ts +31 -0
  80. package/backend/lib/mcp/stdio-server.ts +103 -0
  81. package/backend/lib/mcp/types.ts +65 -0
  82. package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
  83. package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
  84. package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
  85. package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
  86. package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
  87. package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
  88. package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
  89. package/backend/lib/preview/browser/browser-pool.ts +357 -0
  90. package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
  91. package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
  92. package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
  93. package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
  94. package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
  95. package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
  96. package/backend/lib/preview/browser/types.ts +359 -0
  97. package/backend/lib/preview/index.ts +24 -0
  98. package/backend/lib/project/index.ts +2 -0
  99. package/backend/lib/project/status-manager.ts +182 -0
  100. package/backend/lib/shared/index.ts +2 -0
  101. package/backend/lib/shared/port-utils.ts +25 -0
  102. package/backend/lib/shared/process-manager.ts +281 -0
  103. package/backend/lib/snapshot/blob-store.ts +227 -0
  104. package/backend/lib/snapshot/gitignore.ts +307 -0
  105. package/backend/lib/snapshot/helpers.ts +397 -0
  106. package/backend/lib/snapshot/snapshot-service.ts +483 -0
  107. package/backend/lib/terminal/helpers.ts +14 -0
  108. package/backend/lib/terminal/index.ts +8 -0
  109. package/backend/lib/terminal/pty-manager.ts +4 -0
  110. package/backend/lib/terminal/pty-session-manager.ts +387 -0
  111. package/backend/lib/terminal/shell-utils.ts +313 -0
  112. package/backend/lib/terminal/stream-manager.ts +293 -0
  113. package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
  114. package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
  115. package/backend/lib/user/helpers.ts +87 -0
  116. package/backend/lib/utils/ws.ts +944 -0
  117. package/backend/lib/vite-dev.ts +353 -0
  118. package/backend/middleware/cors.ts +15 -0
  119. package/backend/middleware/error-handler.ts +49 -0
  120. package/backend/middleware/logger.ts +9 -0
  121. package/backend/types/api.ts +24 -0
  122. package/backend/ws/README.md +1505 -0
  123. package/backend/ws/chat/background.ts +198 -0
  124. package/backend/ws/chat/index.ts +21 -0
  125. package/backend/ws/chat/stream.ts +707 -0
  126. package/backend/ws/engine/claude/accounts.ts +401 -0
  127. package/backend/ws/engine/claude/index.ts +13 -0
  128. package/backend/ws/engine/claude/status.ts +43 -0
  129. package/backend/ws/engine/index.ts +14 -0
  130. package/backend/ws/engine/opencode/index.ts +11 -0
  131. package/backend/ws/engine/opencode/status.ts +30 -0
  132. package/backend/ws/engine/utils.ts +36 -0
  133. package/backend/ws/files/index.ts +30 -0
  134. package/backend/ws/files/read.ts +189 -0
  135. package/backend/ws/files/search.ts +453 -0
  136. package/backend/ws/files/watch.ts +124 -0
  137. package/backend/ws/files/write.ts +143 -0
  138. package/backend/ws/git/branch.ts +106 -0
  139. package/backend/ws/git/commit.ts +39 -0
  140. package/backend/ws/git/conflict.ts +68 -0
  141. package/backend/ws/git/diff.ts +69 -0
  142. package/backend/ws/git/index.ts +24 -0
  143. package/backend/ws/git/log.ts +41 -0
  144. package/backend/ws/git/remote.ts +214 -0
  145. package/backend/ws/git/staging.ts +84 -0
  146. package/backend/ws/git/status.ts +90 -0
  147. package/backend/ws/index.ts +69 -0
  148. package/backend/ws/mcp/index.ts +61 -0
  149. package/backend/ws/messages/crud.ts +74 -0
  150. package/backend/ws/messages/index.ts +14 -0
  151. package/backend/ws/preview/browser/cleanup.ts +129 -0
  152. package/backend/ws/preview/browser/console.ts +114 -0
  153. package/backend/ws/preview/browser/interact.ts +513 -0
  154. package/backend/ws/preview/browser/mcp.ts +129 -0
  155. package/backend/ws/preview/browser/native-ui.ts +235 -0
  156. package/backend/ws/preview/browser/stats.ts +55 -0
  157. package/backend/ws/preview/browser/tab-info.ts +126 -0
  158. package/backend/ws/preview/browser/tab.ts +166 -0
  159. package/backend/ws/preview/browser/webcodecs.ts +293 -0
  160. package/backend/ws/preview/index.ts +146 -0
  161. package/backend/ws/projects/crud.ts +113 -0
  162. package/backend/ws/projects/index.ts +25 -0
  163. package/backend/ws/projects/presence.ts +46 -0
  164. package/backend/ws/projects/status.ts +116 -0
  165. package/backend/ws/sessions/crud.ts +327 -0
  166. package/backend/ws/sessions/index.ts +33 -0
  167. package/backend/ws/settings/crud.ts +112 -0
  168. package/backend/ws/settings/index.ts +14 -0
  169. package/backend/ws/snapshot/index.ts +17 -0
  170. package/backend/ws/snapshot/restore.ts +173 -0
  171. package/backend/ws/snapshot/timeline.ts +141 -0
  172. package/backend/ws/system/index.ts +14 -0
  173. package/backend/ws/system/operations.ts +49 -0
  174. package/backend/ws/terminal/index.ts +40 -0
  175. package/backend/ws/terminal/persistence.ts +153 -0
  176. package/backend/ws/terminal/session.ts +382 -0
  177. package/backend/ws/terminal/stream.ts +79 -0
  178. package/backend/ws/tunnel/index.ts +14 -0
  179. package/backend/ws/tunnel/operations.ts +91 -0
  180. package/backend/ws/types.ts +20 -0
  181. package/backend/ws/user/crud.ts +156 -0
  182. package/backend/ws/user/index.ts +14 -0
  183. package/bin/clopen.ts +307 -0
  184. package/bun.lock +1352 -0
  185. package/frontend/App.svelte +34 -0
  186. package/frontend/app.css +313 -0
  187. package/frontend/lib/app-environment.ts +10 -0
  188. package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
  189. package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
  190. package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
  191. package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
  192. package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
  193. package/frontend/lib/components/chat/formatters/index.ts +3 -0
  194. package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
  195. package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
  196. package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
  197. package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
  198. package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
  199. package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
  200. package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
  201. package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
  202. package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
  203. package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
  204. package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
  205. package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
  206. package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
  207. package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
  208. package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
  209. package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
  210. package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
  211. package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
  212. package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
  213. package/frontend/lib/components/chat/shared/index.ts +2 -0
  214. package/frontend/lib/components/chat/shared/utils.ts +116 -0
  215. package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
  216. package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
  217. package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
  218. package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
  219. package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
  220. package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
  221. package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
  222. package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
  223. package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
  224. package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
  225. package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
  226. package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
  227. package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
  228. package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
  229. package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
  230. package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
  231. package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
  232. package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
  233. package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
  234. package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
  235. package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
  236. package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
  237. package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
  238. package/frontend/lib/components/chat/tools/components/index.ts +7 -0
  239. package/frontend/lib/components/chat/tools/index.ts +26 -0
  240. package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
  241. package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
  242. package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
  243. package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
  244. package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
  245. package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
  246. package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
  247. package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
  248. package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
  249. package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
  250. package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
  251. package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
  252. package/frontend/lib/components/common/Alert.svelte +139 -0
  253. package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
  254. package/frontend/lib/components/common/Button.svelte +71 -0
  255. package/frontend/lib/components/common/Card.svelte +102 -0
  256. package/frontend/lib/components/common/Checkbox.svelte +48 -0
  257. package/frontend/lib/components/common/Dialog.svelte +249 -0
  258. package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
  259. package/frontend/lib/components/common/Icon.svelte +58 -0
  260. package/frontend/lib/components/common/Input.svelte +72 -0
  261. package/frontend/lib/components/common/Lightbox.svelte +233 -0
  262. package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
  263. package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
  264. package/frontend/lib/components/common/Modal.svelte +177 -0
  265. package/frontend/lib/components/common/ModalProvider.svelte +28 -0
  266. package/frontend/lib/components/common/ModelSelector.svelte +110 -0
  267. package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
  268. package/frontend/lib/components/common/NotificationToast.svelte +113 -0
  269. package/frontend/lib/components/common/PageTemplate.svelte +76 -0
  270. package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
  271. package/frontend/lib/components/common/Select.svelte +98 -0
  272. package/frontend/lib/components/common/Textarea.svelte +80 -0
  273. package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
  274. package/frontend/lib/components/common/lucide-icons.ts +1642 -0
  275. package/frontend/lib/components/common/material-icons.ts +1082 -0
  276. package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
  277. package/frontend/lib/components/common/xterm/index.ts +16 -0
  278. package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
  279. package/frontend/lib/components/common/xterm/types.ts +31 -0
  280. package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
  281. package/frontend/lib/components/files/FileNode.svelte +384 -0
  282. package/frontend/lib/components/files/FileTree.svelte +681 -0
  283. package/frontend/lib/components/files/FileViewer.svelte +728 -0
  284. package/frontend/lib/components/files/SearchResults.svelte +303 -0
  285. package/frontend/lib/components/git/BranchManager.svelte +458 -0
  286. package/frontend/lib/components/git/ChangesSection.svelte +107 -0
  287. package/frontend/lib/components/git/CommitForm.svelte +76 -0
  288. package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
  289. package/frontend/lib/components/git/DiffViewer.svelte +364 -0
  290. package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
  291. package/frontend/lib/components/git/GitButton.svelte +33 -0
  292. package/frontend/lib/components/git/GitLog.svelte +361 -0
  293. package/frontend/lib/components/git/GitModal.svelte +80 -0
  294. package/frontend/lib/components/history/HistoryModal.svelte +563 -0
  295. package/frontend/lib/components/history/HistoryView.svelte +615 -0
  296. package/frontend/lib/components/index.ts +34 -0
  297. package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
  298. package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
  299. package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
  300. package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
  301. package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
  302. package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
  303. package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
  304. package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
  305. package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
  306. package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
  307. package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
  308. package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
  309. package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
  310. package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
  311. package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
  312. package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
  313. package/frontend/lib/components/preview/index.ts +2 -0
  314. package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
  315. package/frontend/lib/components/settings/SettingsView.svelte +36 -0
  316. package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
  317. package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
  318. package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
  319. package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
  320. package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
  321. package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
  322. package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
  323. package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
  324. package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
  325. package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
  326. package/frontend/lib/components/terminal/Terminal.svelte +368 -0
  327. package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
  328. package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
  329. package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
  330. package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
  331. package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
  332. package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
  333. package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
  334. package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
  335. package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
  336. package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
  337. package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
  338. package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
  339. package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
  340. package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
  341. package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
  342. package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
  343. package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
  344. package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
  345. package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
  346. package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
  347. package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
  348. package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
  349. package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
  350. package/frontend/lib/constants/preview.ts +45 -0
  351. package/frontend/lib/services/chat/chat.service.ts +704 -0
  352. package/frontend/lib/services/chat/index.ts +7 -0
  353. package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
  354. package/frontend/lib/services/notification/index.ts +8 -0
  355. package/frontend/lib/services/notification/push.service.ts +144 -0
  356. package/frontend/lib/services/notification/sound.service.ts +127 -0
  357. package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
  358. package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
  359. package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
  360. package/frontend/lib/services/preview/index.ts +23 -0
  361. package/frontend/lib/services/project/index.ts +8 -0
  362. package/frontend/lib/services/project/status.service.ts +159 -0
  363. package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
  364. package/frontend/lib/services/terminal/background/index.ts +130 -0
  365. package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
  366. package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
  367. package/frontend/lib/services/terminal/index.ts +14 -0
  368. package/frontend/lib/services/terminal/persistence.service.ts +260 -0
  369. package/frontend/lib/services/terminal/project.service.ts +953 -0
  370. package/frontend/lib/services/terminal/session.service.ts +364 -0
  371. package/frontend/lib/services/terminal/terminal.service.ts +369 -0
  372. package/frontend/lib/stores/core/app.svelte.ts +117 -0
  373. package/frontend/lib/stores/core/files.svelte.ts +73 -0
  374. package/frontend/lib/stores/core/presence.svelte.ts +48 -0
  375. package/frontend/lib/stores/core/projects.svelte.ts +317 -0
  376. package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
  377. package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
  378. package/frontend/lib/stores/features/models.svelte.ts +89 -0
  379. package/frontend/lib/stores/features/settings.svelte.ts +87 -0
  380. package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
  381. package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
  382. package/frontend/lib/stores/features/user.svelte.ts +96 -0
  383. package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
  384. package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
  385. package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
  386. package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
  387. package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
  388. package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
  389. package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
  390. package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
  391. package/frontend/lib/types/native-ui.ts +73 -0
  392. package/frontend/lib/utils/chat/date-separator.ts +39 -0
  393. package/frontend/lib/utils/chat/message-grouper.ts +219 -0
  394. package/frontend/lib/utils/chat/message-processor.ts +135 -0
  395. package/frontend/lib/utils/chat/tool-handler.ts +161 -0
  396. package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
  397. package/frontend/lib/utils/click-outside.ts +20 -0
  398. package/frontend/lib/utils/context-manager.ts +257 -0
  399. package/frontend/lib/utils/file-icon-mappings.ts +769 -0
  400. package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
  401. package/frontend/lib/utils/git-status.ts +68 -0
  402. package/frontend/lib/utils/platform.ts +113 -0
  403. package/frontend/lib/utils/port-check.ts +65 -0
  404. package/frontend/lib/utils/terminalFormatter.ts +207 -0
  405. package/frontend/lib/utils/theme.ts +6 -0
  406. package/frontend/lib/utils/tree-visualizer.ts +320 -0
  407. package/frontend/lib/utils/ws.ts +44 -0
  408. package/frontend/main.ts +13 -0
  409. package/index.html +70 -0
  410. package/package.json +111 -0
  411. package/scripts/generate-icons.ts +87 -0
  412. package/scripts/pre-publish-check.sh +142 -0
  413. package/scripts/setup-hooks.sh +134 -0
  414. package/scripts/validate-branch-name.sh +47 -0
  415. package/scripts/validate-commit-msg.sh +42 -0
  416. package/shared/constants/engines.ts +134 -0
  417. package/shared/types/database/connection.ts +16 -0
  418. package/shared/types/database/index.ts +6 -0
  419. package/shared/types/database/schema.ts +141 -0
  420. package/shared/types/engine/index.ts +45 -0
  421. package/shared/types/filesystem/index.ts +22 -0
  422. package/shared/types/git.ts +171 -0
  423. package/shared/types/messaging/index.ts +239 -0
  424. package/shared/types/messaging/tool.ts +526 -0
  425. package/shared/types/network/api.ts +18 -0
  426. package/shared/types/network/index.ts +5 -0
  427. package/shared/types/stores/app.ts +23 -0
  428. package/shared/types/stores/dialog.ts +21 -0
  429. package/shared/types/stores/index.ts +3 -0
  430. package/shared/types/stores/settings.ts +15 -0
  431. package/shared/types/terminal/index.ts +44 -0
  432. package/shared/types/ui/components.ts +61 -0
  433. package/shared/types/ui/icons.ts +23 -0
  434. package/shared/types/ui/index.ts +22 -0
  435. package/shared/types/ui/notifications.ts +14 -0
  436. package/shared/types/ui/theme.ts +12 -0
  437. package/shared/types/websocket/index.ts +43 -0
  438. package/shared/types/window.d.ts +13 -0
  439. package/shared/utils/anonymous-user.ts +168 -0
  440. package/shared/utils/async.ts +10 -0
  441. package/shared/utils/diff-calculator.ts +184 -0
  442. package/shared/utils/file-type-detection.ts +166 -0
  443. package/shared/utils/logger.ts +158 -0
  444. package/shared/utils/message-formatter.ts +79 -0
  445. package/shared/utils/path.ts +47 -0
  446. package/shared/utils/ws-client.ts +768 -0
  447. package/shared/utils/ws-server.ts +660 -0
  448. package/static/audio/notification.ogg +0 -0
  449. package/static/favicon.svg +8 -0
  450. package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
  451. package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
  452. package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
  453. package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
  454. package/static/fonts/dm-sans.css +96 -0
  455. package/svelte.config.js +20 -0
  456. 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
+ &gt;
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}