@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,391 @@
1
+ /**
2
+ * Browser Native UI Handlers
3
+ * Handles native UI events (dialogs, select, context menu, etc.) for BrowserPreview
4
+ */
5
+
6
+ import { debug } from '$shared/utils/logger';
7
+ import ws from '$frontend/lib/utils/ws';
8
+ import type { BrowserDialogEvent, BrowserPrintEvent, BrowserSelectInfo, BrowserContextMenuInfo } from '$frontend/lib/types/native-ui';
9
+ import type { TabManager } from './tab-manager.svelte';
10
+
11
+ export interface NativeUIHandlerConfig {
12
+ tabManager: TabManager;
13
+ transformBrowserToDisplayCoordinates?: (x: number, y: number) => { x: number, y: number } | null;
14
+ onSelectOpen?: (selectInfo: BrowserSelectInfo) => void;
15
+ onContextMenuOpen?: (menuInfo: BrowserContextMenuInfo) => void;
16
+ onCopyToClipboard?: (text: string) => void;
17
+ onOpenUrlNewTab?: (url: string) => void;
18
+ onDownloadImage?: (base64: string, type: string, filename: string) => void;
19
+ onCopyImageToClipboard?: (base64: string, type: string) => void;
20
+ }
21
+
22
+ /**
23
+ * Create native UI event handler
24
+ */
25
+ export function createNativeUIHandler(config: NativeUIHandlerConfig) {
26
+ const {
27
+ tabManager,
28
+ transformBrowserToDisplayCoordinates,
29
+ onSelectOpen,
30
+ onContextMenuOpen,
31
+ onCopyToClipboard,
32
+ onOpenUrlNewTab,
33
+ onDownloadImage,
34
+ onCopyImageToClipboard
35
+ } = config;
36
+
37
+ /**
38
+ * Setup WebSocket event listeners for native UI events
39
+ */
40
+ function setupEventListeners() {
41
+ // Listen to dialog events
42
+ ws.on('preview:browser-dialog', handleDialogEvent);
43
+
44
+ // Listen to print events
45
+ ws.on('preview:browser-print', handlePrintEvent);
46
+
47
+ // Listen to select events
48
+ ws.on('preview:browser-select', handleSelectEvent);
49
+
50
+ // Listen to context menu events
51
+ ws.on('preview:browser-context-menu', handleContextMenuEvent);
52
+
53
+ // Listen to clipboard copy events
54
+ ws.on('preview:browser-copy-to-clipboard', handleCopyToClipboard);
55
+
56
+ // Listen to open URL events
57
+ ws.on('preview:browser-open-url-new-tab', handleOpenUrlNewTab);
58
+
59
+ // Listen to download image events
60
+ ws.on('preview:browser-download-image', handleDownloadImage);
61
+
62
+ // Listen to copy image to clipboard events
63
+ ws.on('preview:browser-copy-image-to-clipboard', handleCopyImageToClipboard);
64
+ }
65
+
66
+ /**
67
+ * Handle dialog events (alert, confirm, prompt)
68
+ */
69
+ async function handleDialogEvent(data: BrowserDialogEvent) {
70
+ debug.log('preview', `🎭 Dialog event received: ${data.type} - ${data.message} (dialogId: ${data.dialogId})`);
71
+
72
+ let response: boolean | null = null;
73
+ let promptText: string | undefined = undefined;
74
+
75
+ // Show native browser dialog based on type
76
+ switch (data.type) {
77
+ case 'alert':
78
+ window.alert(data.message);
79
+ response = true; // Alert always accepts
80
+ break;
81
+
82
+ case 'confirm':
83
+ response = window.confirm(data.message);
84
+ break;
85
+
86
+ case 'prompt':
87
+ const result = window.prompt(data.message, data.defaultValue || '');
88
+ if (result !== null) {
89
+ response = true;
90
+ promptText = result;
91
+ } else {
92
+ response = false;
93
+ }
94
+ break;
95
+
96
+ case 'beforeunload':
97
+ response = window.confirm(data.message);
98
+ break;
99
+ }
100
+
101
+ // Send response back to backend
102
+ if (response !== null) {
103
+ debug.log('preview', `📤 Sending dialog response - dialogId: ${data.dialogId}, accept: ${response}${promptText ? `, promptText: "${promptText}"` : ''}`);
104
+
105
+ ws.emit('preview:browser-dialog-input', {
106
+ dialogId: data.dialogId,
107
+ accept: response,
108
+ promptText
109
+ });
110
+
111
+ debug.log('preview', `✅ Dialog response sent successfully`);
112
+ } else {
113
+ debug.warn('preview', `⚠️ No response to send for dialog: ${data.dialogId}`);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Handle print events
119
+ */
120
+ async function handlePrintEvent(data: BrowserPrintEvent) {
121
+ debug.log('preview', `🖨️ Print event received for session: ${data.sessionId}`);
122
+ window.print();
123
+ }
124
+
125
+ /**
126
+ * Handle select dropdown events
127
+ */
128
+ function handleSelectEvent(data: BrowserSelectInfo) {
129
+ debug.log('preview', `📋 Select event received at (${data.x}, ${data.y}) with ${data.options.length} options for session ${data.sessionId}`);
130
+
131
+ // Check if this is for the active tab
132
+ const activeTab = tabManager.activeTab;
133
+ if (!activeTab) {
134
+ debug.warn('preview', `Select event ignored - no active tab`);
135
+ return;
136
+ }
137
+
138
+ if (activeTab.sessionId !== data.sessionId) {
139
+ debug.warn('preview', `Select event ignored - session mismatch (active: ${activeTab.sessionId}, event: ${data.sessionId})`);
140
+ return;
141
+ }
142
+
143
+ if (!transformBrowserToDisplayCoordinates) {
144
+ debug.error('preview', 'transformBrowserToDisplayCoordinates not available');
145
+ return;
146
+ }
147
+
148
+ // Transform coordinates from browser (Puppeteer) to display coordinates
149
+ // The transformation function will handle cases where canvas is not ready (returns null)
150
+ const topLeft = transformBrowserToDisplayCoordinates(data.boundingBox.x, data.boundingBox.y);
151
+ const bottomRight = transformBrowserToDisplayCoordinates(
152
+ data.boundingBox.x + data.boundingBox.width,
153
+ data.boundingBox.y + data.boundingBox.height
154
+ );
155
+
156
+ if (!topLeft || !bottomRight) {
157
+ debug.warn('preview', `Select dropdown skipped - coordinate transformation failed (${data.boundingBox.x}, ${data.boundingBox.y})`);
158
+ return;
159
+ }
160
+
161
+ // Create transformed select info with display coordinates
162
+ const transformedSelectInfo: BrowserSelectInfo = {
163
+ ...data,
164
+ boundingBox: {
165
+ x: topLeft.x,
166
+ y: topLeft.y,
167
+ width: bottomRight.x - topLeft.x,
168
+ height: bottomRight.y - topLeft.y
169
+ }
170
+ };
171
+
172
+ debug.log('preview', `📋 Transformed select position: (${transformedSelectInfo.boundingBox.x}, ${transformedSelectInfo.boundingBox.y})`);
173
+
174
+ // Show select dropdown overlay
175
+ if (onSelectOpen) {
176
+ onSelectOpen(transformedSelectInfo);
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Handle select option selection
182
+ */
183
+ function respondSelectOption(selectInfo: BrowserSelectInfo, selectedIndex: number) {
184
+ debug.log('preview', `📋 Select option selected: ${selectedIndex}`);
185
+
186
+ ws.emit('preview:browser-select-input', {
187
+ sessionId: selectInfo.sessionId,
188
+ selectId: selectInfo.selectId,
189
+ selectedIndex
190
+ });
191
+ }
192
+
193
+ /**
194
+ * Handle context menu events
195
+ */
196
+ function handleContextMenuEvent(data: BrowserContextMenuInfo) {
197
+ debug.log('preview', `📜 Context menu event received at (${data.x}, ${data.y}) for session ${data.sessionId}`);
198
+
199
+ // Check if this is for the active tab
200
+ const activeTab = tabManager.activeTab;
201
+ if (!activeTab) {
202
+ debug.warn('preview', `Context menu event ignored - no active tab`);
203
+ return;
204
+ }
205
+
206
+ if (activeTab.sessionId !== data.sessionId) {
207
+ debug.warn('preview', `Context menu event ignored - session mismatch (active: ${activeTab.sessionId}, event: ${data.sessionId})`);
208
+ return;
209
+ }
210
+
211
+ if (!transformBrowserToDisplayCoordinates) {
212
+ debug.error('preview', 'transformBrowserToDisplayCoordinates not available');
213
+ return;
214
+ }
215
+
216
+ // Transform coordinates from browser (Puppeteer) to display coordinates
217
+ // The transformation function will handle cases where canvas is not ready (returns null)
218
+ const position = transformBrowserToDisplayCoordinates(data.x, data.y);
219
+
220
+ if (!position) {
221
+ debug.warn('preview', `Context menu skipped - coordinate transformation failed (${data.x}, ${data.y})`);
222
+ return;
223
+ }
224
+
225
+ // Create transformed context menu info with display coordinates
226
+ const transformedMenuInfo: BrowserContextMenuInfo = {
227
+ ...data,
228
+ x: position.x,
229
+ y: position.y
230
+ };
231
+
232
+ debug.log('preview', `📜 Transformed context menu position: (${transformedMenuInfo.x}, ${transformedMenuInfo.y})`);
233
+
234
+ // Show context menu overlay
235
+ if (onContextMenuOpen) {
236
+ onContextMenuOpen(transformedMenuInfo);
237
+ }
238
+ }
239
+
240
+ /**
241
+ * Handle context menu item selection
242
+ */
243
+ async function respondContextMenuItem(menuInfo: BrowserContextMenuInfo, itemId: string) {
244
+ debug.log('preview', `📜 Context menu item selected: ${itemId}`);
245
+
246
+ // For paste action, read clipboard first
247
+ let clipboardText: string | undefined = undefined;
248
+ if (itemId === 'paste') {
249
+ try {
250
+ if (navigator.clipboard && navigator.clipboard.readText) {
251
+ clipboardText = await navigator.clipboard.readText();
252
+ debug.log('preview', `📋 Clipboard text read: ${clipboardText.length} characters`);
253
+ } else {
254
+ debug.warn('preview', '⚠️ Clipboard API not available');
255
+ }
256
+ } catch (error) {
257
+ debug.error('preview', '❌ Failed to read clipboard:', error);
258
+ // Continue without clipboard text - backend will show warning
259
+ }
260
+ }
261
+
262
+ // Send selection back to backend
263
+ ws.emit('preview:browser-context-menu-input', {
264
+ sessionId: menuInfo.sessionId,
265
+ menuId: menuInfo.menuId,
266
+ itemId,
267
+ clipboardText
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Handle copy to clipboard
273
+ */
274
+ function handleCopyToClipboard(data: { text: string }) {
275
+ debug.log('preview', `📋 Copy to clipboard: ${data.text}`);
276
+
277
+ // Copy to clipboard
278
+ if (navigator.clipboard && navigator.clipboard.writeText) {
279
+ navigator.clipboard.writeText(data.text).then(() => {
280
+ debug.log('preview', '✅ Copied to clipboard');
281
+ }).catch((error) => {
282
+ debug.error('preview', '❌ Failed to copy to clipboard:', error);
283
+ });
284
+ }
285
+
286
+ if (onCopyToClipboard) {
287
+ onCopyToClipboard(data.text);
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Handle open URL in new tab
293
+ */
294
+ function handleOpenUrlNewTab(data: { url: string }) {
295
+ debug.log('preview', `🔗 Open URL in new tab: ${data.url}`);
296
+
297
+ if (onOpenUrlNewTab) {
298
+ onOpenUrlNewTab(data.url);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Handle download image
304
+ */
305
+ async function handleDownloadImage(data: { base64: string; type: string; filename: string }) {
306
+ debug.log('preview', `💾 Download image: ${data.filename}`);
307
+
308
+ try {
309
+ // Convert base64 to blob using Data URL
310
+ const res = await fetch(`data:${data.type};base64,${data.base64}`);
311
+ const blob = await res.blob();
312
+
313
+ // Ask user for filename (works in all browsers)
314
+ const userFilename = window.prompt('Save image as:', data.filename);
315
+
316
+ // User cancelled
317
+ if (userFilename === null) {
318
+ debug.log('preview', '⚠️ Save cancelled by user');
319
+ return;
320
+ }
321
+
322
+ // Use provided filename or original if empty
323
+ const finalFilename = userFilename.trim() || data.filename;
324
+
325
+ // Trigger download
326
+ const objectUrl = URL.createObjectURL(blob);
327
+ const link = document.createElement('a');
328
+ link.href = objectUrl;
329
+ link.download = finalFilename;
330
+ document.body.appendChild(link);
331
+ link.click();
332
+ document.body.removeChild(link);
333
+ URL.revokeObjectURL(objectUrl);
334
+
335
+ debug.log('preview', `✅ Image downloaded: ${finalFilename}`);
336
+ } catch (error) {
337
+ debug.error('preview', '❌ Failed to download image:', error);
338
+ }
339
+
340
+ if (onDownloadImage) {
341
+ onDownloadImage(data.base64, data.type, data.filename);
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Handle copy image to clipboard
347
+ */
348
+ async function handleCopyImageToClipboard(data: { base64: string; type: string }) {
349
+ debug.log('preview', `📋 Copy image to clipboard`);
350
+
351
+ try {
352
+ // Convert base64 to blob using Data URL
353
+ const res = await fetch(`data:${data.type};base64,${data.base64}`);
354
+ const blob = await res.blob();
355
+
356
+ // Copy to clipboard using Clipboard API
357
+ if (navigator.clipboard && navigator.clipboard.write) {
358
+ await navigator.clipboard.write([
359
+ new ClipboardItem({
360
+ [data.type]: blob
361
+ })
362
+ ]);
363
+ debug.log('preview', '✅ Image copied to clipboard');
364
+ } else {
365
+ debug.error('preview', '❌ Clipboard API not supported');
366
+ }
367
+ } catch (error) {
368
+ debug.error('preview', '❌ Failed to copy image to clipboard:', error);
369
+ }
370
+
371
+ if (onCopyImageToClipboard) {
372
+ onCopyImageToClipboard(data.base64, data.type);
373
+ }
374
+ }
375
+
376
+ return {
377
+ setupEventListeners,
378
+ handleDialogEvent,
379
+ handlePrintEvent,
380
+ handleSelectEvent,
381
+ respondSelectOption,
382
+ handleContextMenuEvent,
383
+ respondContextMenuItem,
384
+ handleCopyToClipboard,
385
+ handleOpenUrlNewTab,
386
+ handleDownloadImage,
387
+ handleCopyImageToClipboard
388
+ };
389
+ }
390
+
391
+ export type NativeUIHandler = ReturnType<typeof createNativeUIHandler>;
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Browser Stream Message Handler
3
+ * Handles stream messages from backend for BrowserPreview
4
+ */
5
+
6
+ import { debug } from '$shared/utils/logger';
7
+ import type { PreviewTab, TabManager } from './tab-manager.svelte';
8
+ import { getTabTitle } from './tab-manager.svelte';
9
+
10
+ export interface StreamMessageHandlerConfig {
11
+ tabManager: TabManager;
12
+ onNavigationUpdate?: (tabId: string, url: string) => void;
13
+ onCursorUpdate?: (x: number, y: number, clicking?: boolean) => void;
14
+ onTestCompleted?: () => void;
15
+ transformBrowserToDisplayCoordinates?: (x: number, y: number) => { x: number, y: number } | null;
16
+ }
17
+
18
+ /**
19
+ * Create stream message handler
20
+ */
21
+ export function createStreamMessageHandler(config: StreamMessageHandlerConfig) {
22
+ const { tabManager, onNavigationUpdate, onCursorUpdate, onTestCompleted, transformBrowserToDisplayCoordinates } = config;
23
+
24
+ /**
25
+ * Handle stream messages for a specific tab
26
+ */
27
+ function handleStreamMessage(message: any, tabId?: string) {
28
+ const targetTabId = tabId || tabManager.activeTabId;
29
+ if (!targetTabId) return;
30
+
31
+ const tab = tabManager.getTab(targetTabId);
32
+ if (!tab) return;
33
+
34
+ // Debug logging for navigation events
35
+ if (message.type === 'navigation' || message.type === 'navigation-loading') {
36
+ debug.log('preview', `📢 [${message.type}] Tab: ${targetTabId}, URL: ${message.data?.url}, Active: ${targetTabId === tabManager.activeTabId}`);
37
+ }
38
+
39
+ switch (message.type) {
40
+ case 'session-info':
41
+ handleSessionInfo(targetTabId, message.data, tab);
42
+ break;
43
+
44
+ case 'screencast-frame':
45
+ handleScreencastFrame(targetTabId, message.data);
46
+ break;
47
+
48
+ case 'cursor-position':
49
+ handleCursorPosition(targetTabId, message.data);
50
+ break;
51
+
52
+ case 'cursor-click':
53
+ handleCursorClick(targetTabId, message.data);
54
+ break;
55
+
56
+ case 'test-completed':
57
+ handleTestCompleted(targetTabId);
58
+ break;
59
+
60
+ case 'console-message':
61
+ handleConsoleMessage(targetTabId, message.data, tab);
62
+ break;
63
+
64
+ case 'console-clear':
65
+ handleConsoleClear(targetTabId);
66
+ break;
67
+
68
+ case 'navigation-loading':
69
+ handleNavigationLoading(targetTabId, message.data);
70
+ break;
71
+
72
+ case 'navigation':
73
+ handleNavigation(targetTabId, message.data, tab);
74
+ break;
75
+
76
+ case 'new-window':
77
+ handleNewWindow(message.data);
78
+ break;
79
+
80
+ case 'ping':
81
+ // Ignore ping messages
82
+ break;
83
+
84
+ case 'error':
85
+ debug.error('preview', 'Stream error:', message.message);
86
+ break;
87
+
88
+ default:
89
+ debug.warn('preview', 'Unknown stream message type:', message.type);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Handle connection change
95
+ */
96
+ function handleConnectionChange(connected: boolean, tabId?: string) {
97
+ const targetTabId = tabId || tabManager.activeTabId;
98
+ if (!targetTabId) return;
99
+
100
+ tabManager.updateTab(targetTabId, { isConnected: connected });
101
+ }
102
+
103
+ // Private handlers
104
+
105
+ function handleSessionInfo(tabId: string, data: any, tab: PreviewTab) {
106
+ tabManager.updateTab(tabId, {
107
+ sessionInfo: data,
108
+ deviceSize: data.deviceSize || tab.deviceSize,
109
+ rotation: data.rotation || tab.rotation
110
+ });
111
+
112
+ // Setup canvas if this is the active tab
113
+ if (tabId === tabManager.activeTabId && tab.canvasAPI) {
114
+ tab.canvasAPI.setupCanvas();
115
+ }
116
+ }
117
+
118
+ function handleScreencastFrame(tabId: string, data: any) {
119
+ const tab = tabManager.getTab(tabId);
120
+ const updates: any = { lastFrameData: data };
121
+
122
+ // If tab is navigating, complete navigation when new frame is received
123
+ if (tab?.isNavigating) {
124
+ updates.isNavigating = false;
125
+ debug.log('preview', `✅ Navigation frame received for tab: ${tabId}, completing progress`);
126
+ }
127
+
128
+ tabManager.updateTab(tabId, updates);
129
+
130
+ if (tabId === tabManager.activeTabId) {
131
+ debug.log('preview', `🎬 Screencast frame received for active tab: ${tabId}`);
132
+ }
133
+ }
134
+
135
+ function handleCursorPosition(tabId: string, data: any) {
136
+ if (data && tabId === tabManager.activeTabId && transformBrowserToDisplayCoordinates) {
137
+ const transformedPosition = transformBrowserToDisplayCoordinates(data.x, data.y);
138
+ if (transformedPosition && onCursorUpdate) {
139
+ onCursorUpdate(transformedPosition.x, transformedPosition.y, false);
140
+ }
141
+ }
142
+ }
143
+
144
+ function handleCursorClick(tabId: string, data: any) {
145
+ if (data && tabId === tabManager.activeTabId && transformBrowserToDisplayCoordinates) {
146
+ const transformedPosition = transformBrowserToDisplayCoordinates(data.x, data.y);
147
+ if (transformedPosition && onCursorUpdate) {
148
+ onCursorUpdate(transformedPosition.x, transformedPosition.y, true);
149
+ }
150
+ }
151
+ }
152
+
153
+ function handleTestCompleted(tabId: string) {
154
+ if (tabId === tabManager.activeTabId && onTestCompleted) {
155
+ onTestCompleted();
156
+ }
157
+ }
158
+
159
+ function handleConsoleMessage(tabId: string, data: any, tab: PreviewTab) {
160
+ if (data && data.message) {
161
+ const tabLogs = [...(tab.consoleLogs || []), data.message];
162
+ // Keep only last 1000 messages for performance
163
+ const trimmedLogs = tabLogs.length > 1000 ? tabLogs.slice(-500) : tabLogs;
164
+ tabManager.updateTab(tabId, { consoleLogs: trimmedLogs });
165
+ }
166
+ }
167
+
168
+ function handleConsoleClear(tabId: string) {
169
+ tabManager.updateTab(tabId, { consoleLogs: [] });
170
+ }
171
+
172
+ function handleNavigationLoading(tabId: string, data: any) {
173
+ if (data && data.url) {
174
+ const tab = tabManager.getTab(tabId);
175
+ // isNavigating: true if session already exists (navigating within same session)
176
+ // isNavigating: false if no session yet (initial load)
177
+ const isNavigating = tab?.sessionId ? true : false;
178
+
179
+ tabManager.updateTab(tabId, {
180
+ isLoading: true,
181
+ isNavigating,
182
+ url: data.url,
183
+ title: getTabTitle(data.url)
184
+ });
185
+
186
+ // Only update parent if this is the active tab AND not already navigating via HTTP
187
+ // When navigating via HTTP (Go button), the HTTP response will handle URL updates
188
+ // to avoid race conditions with stream events overwriting the final redirected URL
189
+ if (tabId === tabManager.activeTabId && onNavigationUpdate && !tab?.isNavigating) {
190
+ onNavigationUpdate(tabId, data.url);
191
+ }
192
+ }
193
+ }
194
+
195
+ function handleNavigation(tabId: string, data: any, tab: PreviewTab) {
196
+ if (data && data.url && data.url !== tab.url) {
197
+ debug.log('preview', `🧭 Navigation completed for tab ${tabId}: ${tab.url} → ${data.url}`);
198
+ tabManager.updateTab(tabId, {
199
+ isLoading: false,
200
+ isNavigating: false,
201
+ url: data.url,
202
+ title: getTabTitle(data.url)
203
+ });
204
+
205
+ // Only update parent if this is the active tab
206
+ if (tabId === tabManager.activeTabId && onNavigationUpdate) {
207
+ onNavigationUpdate(tabId, data.url);
208
+ }
209
+ } else if (data && data.url === tab.url) {
210
+ // Same URL but navigation completed (e.g., page refresh)
211
+ debug.log('preview', `🔄 Same URL navigation completed for tab ${tabId}: ${data.url}`);
212
+ tabManager.updateTab(tabId, {
213
+ isLoading: false,
214
+ isNavigating: false
215
+ });
216
+ }
217
+ }
218
+
219
+ function handleNewWindow(data: any) {
220
+ if (data && data.url) {
221
+ tabManager.createTab(data.url);
222
+ }
223
+ }
224
+
225
+ return {
226
+ handleStreamMessage,
227
+ handleConnectionChange
228
+ };
229
+ }
230
+
231
+ export type StreamMessageHandler = ReturnType<typeof createStreamMessageHandler>;