@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,33 @@
1
+ <script lang="ts">
2
+ import type { WriteToolInput } from '$shared/types/messaging';
3
+ import { FileHeader, DiffBlock } from './components';
4
+ import TextMessage from '../formatters/TextMessage.svelte';
5
+
6
+ const { toolInput }: { toolInput: WriteToolInput } = $props();
7
+
8
+ const filePath = toolInput.input.file_path || '';
9
+ const fileName = filePath.split(/[/\\]/).pop() || filePath || 'unknown';
10
+ const content = toolInput.input.content || '';
11
+ </script>
12
+
13
+ <FileHeader
14
+ {filePath}
15
+ {fileName}
16
+ iconColor="text-violet-600 dark:text-violet-400"
17
+ />
18
+
19
+ <!-- Code Changes -->
20
+ <div class="mt-4">
21
+ <DiffBlock oldString="" newString={content} label="Write" />
22
+ </div>
23
+
24
+ <!-- Tool Result -->
25
+ <!-- {#if toolInput.$result}
26
+ <div class="mt-4 bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3">
27
+ {#if typeof toolInput.$result.content === 'string'}
28
+ <TextMessage content={toolInput.$result.content} />
29
+ {:else}
30
+ <TextMessage content={JSON.stringify(toolInput.$result.content)} />
31
+ {/if}
32
+ </div>
33
+ {/if} -->
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import type { IconName } from '$shared/types/ui/icons';
4
+ import { removeCommonIndentation } from '../../shared/utils';
5
+ import { isTerminalOutput, processAnsiCodes, escapeHtml } from '$frontend/lib/utils/terminalFormatter';
6
+
7
+ interface Props {
8
+ code: string;
9
+ type: 'add' | 'remove' | 'neutral';
10
+ label?: string;
11
+ showTruncation?: boolean;
12
+ }
13
+
14
+ const { code, type, label }: Props = $props();
15
+
16
+ function formatCode(code: string) {
17
+ // First remove common indentation
18
+ const cleanCode = removeCommonIndentation(code);
19
+
20
+ // Check if it's terminal output and process ANSI codes if needed
21
+ if (isTerminalOutput(cleanCode)) {
22
+ // For terminal output, process ANSI codes but don't escape HTML
23
+ // since we're displaying it in a <pre> tag
24
+ return processAnsiCodes(cleanCode);
25
+ }
26
+
27
+ // For non-terminal code, don't escape HTML either since it's in <pre>
28
+ // The <pre> tag already handles text display safely
29
+ return cleanCode;
30
+ }
31
+
32
+ const styles = {
33
+ add: {
34
+ icon: 'lucide:circle-plus' as IconName,
35
+ iconColor: 'text-green-500',
36
+ labelColor: 'text-green-700 dark:text-green-300',
37
+ bgColor: 'bg-green-50 dark:bg-green-950',
38
+ borderColor: 'border-green-200 dark:border-green-800',
39
+ textColor: 'text-green-800 dark:text-green-200'
40
+ },
41
+ remove: {
42
+ icon: 'lucide:circle-minus' as IconName,
43
+ iconColor: 'text-red-500',
44
+ labelColor: 'text-red-700 dark:text-red-300',
45
+ bgColor: 'bg-red-50 dark:bg-red-950',
46
+ borderColor: 'border-red-200 dark:border-red-800',
47
+ textColor: 'text-red-800 dark:text-red-200'
48
+ },
49
+ neutral: {
50
+ icon: 'lucide:code' as IconName,
51
+ iconColor: 'text-slate-500',
52
+ labelColor: 'text-slate-700 dark:text-slate-300',
53
+ bgColor: 'bg-slate-50 dark:bg-slate-950',
54
+ borderColor: 'border-slate-200 dark:border-slate-800',
55
+ textColor: 'text-slate-800 dark:text-slate-200'
56
+ }
57
+ };
58
+
59
+ const style = styles[type];
60
+ const formattedCode = formatCode(code);
61
+ const isTerminal = isTerminalOutput(code);
62
+ </script>
63
+
64
+ <div>
65
+ {#if label}
66
+ <div class="flex items-center gap-2 mb-2">
67
+ <!-- <Icon name={style.icon} class="{style.iconColor} w-4 h-4" /> -->
68
+ <span class="text-xs font-medium {style.labelColor}">{label}:</span>
69
+ </div>
70
+ {/if}
71
+ <div class="max-h-72 {style.bgColor} border {style.borderColor} rounded-md py-2.5 px-3 whitespace-pre-wrap overflow-auto">
72
+ {#if isTerminal}
73
+ <!-- eslint-disable-next-line svelte/no-at-html-tags -->
74
+ <pre class="text-xs {style.textColor} font-mono">{@html formattedCode}</pre>
75
+ {:else}
76
+ <pre class="text-xs {style.textColor} font-mono">{formattedCode}</pre>
77
+ {/if}
78
+ </div>
79
+ </div>
@@ -0,0 +1,408 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import { removeCommonIndentationFromLines } from '../../shared/utils';
4
+
5
+ interface Props {
6
+ oldString: string;
7
+ newString: string;
8
+ label?: string;
9
+ }
10
+
11
+ interface DiffLine {
12
+ type: 'unchanged' | 'removed' | 'added';
13
+ content: string;
14
+ highlights?: Array<{ start: number; end: number }>;
15
+ }
16
+
17
+ interface GroupedDiff {
18
+ type: 'unchanged' | 'change';
19
+ removed?: DiffLine[];
20
+ added?: DiffLine[];
21
+ lines?: DiffLine[];
22
+ }
23
+
24
+ const { oldString, newString, label }: Props = $props();
25
+
26
+ // Myers diff algorithm for finding longest common subsequence
27
+ function findLCS(arr1: string[], arr2: string[]): number[][] {
28
+ const m = arr1.length;
29
+ const n = arr2.length;
30
+ const dp: number[][] = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
31
+
32
+ for (let i = 1; i <= m; i++) {
33
+ for (let j = 1; j <= n; j++) {
34
+ if (arr1[i - 1] === arr2[j - 1]) {
35
+ dp[i][j] = dp[i - 1][j - 1] + 1;
36
+ } else {
37
+ dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
38
+ }
39
+ }
40
+ }
41
+
42
+ return dp;
43
+ }
44
+
45
+ // Character-level diff for highlighting specific changes within lines
46
+ function findCharacterDiff(oldLine: string, newLine: string): { oldHighlights: Array<{ start: number; end: number }>, newHighlights: Array<{ start: number; end: number }> } {
47
+ // Use patience diff algorithm for better results
48
+ const oldHighlights: Array<{ start: number; end: number }> = [];
49
+ const newHighlights: Array<{ start: number; end: number }> = [];
50
+
51
+ // Find common prefix
52
+ let prefixLen = 0;
53
+ const minLen = Math.min(oldLine.length, newLine.length);
54
+ while (prefixLen < minLen && oldLine[prefixLen] === newLine[prefixLen]) {
55
+ prefixLen++;
56
+ }
57
+
58
+ // Find common suffix
59
+ let suffixLen = 0;
60
+ const maxSuffixLen = minLen - prefixLen;
61
+ while (suffixLen < maxSuffixLen &&
62
+ oldLine[oldLine.length - 1 - suffixLen] === newLine[newLine.length - 1 - suffixLen]) {
63
+ suffixLen++;
64
+ }
65
+
66
+ // Extract the differing middle parts
67
+ const oldMiddleStart = prefixLen;
68
+ const oldMiddleEnd = oldLine.length - suffixLen;
69
+ const newMiddleStart = prefixLen;
70
+ const newMiddleEnd = newLine.length - suffixLen;
71
+
72
+ if (oldMiddleStart < oldMiddleEnd || newMiddleStart < newMiddleEnd) {
73
+ // There are differences
74
+ const oldMiddle = oldLine.substring(oldMiddleStart, oldMiddleEnd);
75
+ const newMiddle = newLine.substring(newMiddleStart, newMiddleEnd);
76
+
77
+ // If the middle parts are completely different or one is empty, highlight the whole middle
78
+ if (oldMiddle.length === 0 || newMiddle.length === 0 ||
79
+ !hasCommonSubstring(oldMiddle, newMiddle)) {
80
+ if (oldMiddle.length > 0) {
81
+ oldHighlights.push({ start: oldMiddleStart, end: oldMiddleEnd });
82
+ }
83
+ if (newMiddle.length > 0) {
84
+ newHighlights.push({ start: newMiddleStart, end: newMiddleEnd });
85
+ }
86
+ } else {
87
+ // Find fine-grained differences using recursive approach
88
+ const middleDiff = findFineDiff(oldMiddle, newMiddle);
89
+
90
+ // Adjust positions relative to full string
91
+ for (const range of middleDiff.oldRanges) {
92
+ oldHighlights.push({
93
+ start: oldMiddleStart + range.start,
94
+ end: oldMiddleStart + range.end
95
+ });
96
+ }
97
+ for (const range of middleDiff.newRanges) {
98
+ newHighlights.push({
99
+ start: newMiddleStart + range.start,
100
+ end: newMiddleStart + range.end
101
+ });
102
+ }
103
+ }
104
+ }
105
+
106
+ return { oldHighlights, newHighlights };
107
+ }
108
+
109
+ // Helper function to check if strings have common substrings
110
+ function hasCommonSubstring(str1: string, str2: string, minLength: number = 3): boolean {
111
+ if (str1.length < minLength || str2.length < minLength) return false;
112
+
113
+ for (let i = 0; i <= str1.length - minLength; i++) {
114
+ const substr = str1.substring(i, i + minLength);
115
+ if (str2.includes(substr)) return true;
116
+ }
117
+ return false;
118
+ }
119
+
120
+ // Fine-grained diff for the middle differing part
121
+ function findFineDiff(oldStr: string, newStr: string): {
122
+ oldRanges: Array<{ start: number; end: number }>,
123
+ newRanges: Array<{ start: number; end: number }>
124
+ } {
125
+ const oldRanges: Array<{ start: number; end: number }> = [];
126
+ const newRanges: Array<{ start: number; end: number }> = [];
127
+
128
+ // Use word-based chunking for better diff
129
+ const oldChunks = splitIntoChunks(oldStr);
130
+ const newChunks = splitIntoChunks(newStr);
131
+
132
+ // Find LCS of chunks
133
+ const dp = findLCS(oldChunks.map(c => c.text), newChunks.map(c => c.text));
134
+
135
+ let i = oldChunks.length;
136
+ let j = newChunks.length;
137
+
138
+ // Backtrack to find differences
139
+ while (i > 0 || j > 0) {
140
+ if (i > 0 && j > 0 && oldChunks[i - 1].text === newChunks[j - 1].text) {
141
+ // Chunks match
142
+ i--;
143
+ j--;
144
+ } else if (j === 0 || (i > 0 && dp[i][j] === dp[i - 1][j])) {
145
+ // Chunk deleted from old
146
+ const chunk = oldChunks[i - 1];
147
+ oldRanges.unshift({ start: chunk.start, end: chunk.end });
148
+ i--;
149
+ } else {
150
+ // Chunk added to new
151
+ const chunk = newChunks[j - 1];
152
+ newRanges.unshift({ start: chunk.start, end: chunk.end });
153
+ j--;
154
+ }
155
+ }
156
+
157
+ // Merge adjacent ranges
158
+ return {
159
+ oldRanges: mergeRanges(oldRanges),
160
+ newRanges: mergeRanges(newRanges)
161
+ };
162
+ }
163
+
164
+ // Split string into chunks (words and delimiters)
165
+ function splitIntoChunks(str: string): Array<{ text: string; start: number; end: number }> {
166
+ const chunks: Array<{ text: string; start: number; end: number }> = [];
167
+ const regex = /(\w+|[^\w]+)/g;
168
+ let match;
169
+
170
+ while ((match = regex.exec(str)) !== null) {
171
+ chunks.push({
172
+ text: match[0],
173
+ start: match.index,
174
+ end: match.index + match[0].length
175
+ });
176
+ }
177
+
178
+ return chunks;
179
+ }
180
+
181
+ // Merge adjacent or overlapping ranges
182
+ function mergeRanges(ranges: Array<{ start: number; end: number }>): Array<{ start: number; end: number }> {
183
+ if (ranges.length === 0) return [];
184
+
185
+ ranges.sort((a, b) => a.start - b.start);
186
+ const merged: Array<{ start: number; end: number }> = [];
187
+ let current = ranges[0];
188
+
189
+ for (let i = 1; i < ranges.length; i++) {
190
+ const next = ranges[i];
191
+ if (next.start <= current.end) {
192
+ // Merge overlapping or adjacent regions
193
+ current = { start: current.start, end: Math.max(current.end, next.end) };
194
+ } else {
195
+ merged.push(current);
196
+ current = next;
197
+ }
198
+ }
199
+ merged.push(current);
200
+
201
+ return merged;
202
+ }
203
+
204
+ function computeDiff(oldStr: string, newStr: string): GroupedDiff[] {
205
+ const oldLines = oldStr.split('\n');
206
+ const newLines = newStr.split('\n');
207
+
208
+ // Remove common indentation from both old and new strings
209
+ const { lines: cleanOldLines } = removeCommonIndentationFromLines(oldLines);
210
+ const { lines: cleanNewLines } = removeCommonIndentationFromLines(newLines);
211
+
212
+ // Compute line-level diff using LCS
213
+ const dp = findLCS(cleanOldLines, cleanNewLines);
214
+
215
+ const diffLines: DiffLine[] = [];
216
+ let i = cleanOldLines.length;
217
+ let j = cleanNewLines.length;
218
+
219
+ // Backtrack to generate diff
220
+ while (i > 0 || j > 0) {
221
+ if (i > 0 && j > 0 && cleanOldLines[i - 1] === cleanNewLines[j - 1]) {
222
+ diffLines.unshift({ type: 'unchanged', content: cleanOldLines[i - 1] });
223
+ i--;
224
+ j--;
225
+ } else if (j === 0 || (i > 0 && dp[i][j] === dp[i - 1][j])) {
226
+ diffLines.unshift({ type: 'removed', content: cleanOldLines[i - 1] });
227
+ i--;
228
+ } else {
229
+ diffLines.unshift({ type: 'added', content: cleanNewLines[j - 1] });
230
+ j--;
231
+ }
232
+ }
233
+
234
+ // Group consecutive changes and apply character-level diff
235
+ const grouped: GroupedDiff[] = [];
236
+ let current: GroupedDiff | null = null;
237
+
238
+ for (const line of diffLines) {
239
+ if (line.type === 'unchanged') {
240
+ if (current) {
241
+ grouped.push(current);
242
+ current = null;
243
+ }
244
+ grouped.push({ type: 'unchanged', lines: [line] });
245
+ } else {
246
+ if (!current) {
247
+ current = { type: 'change', removed: [], added: [] };
248
+ }
249
+ if (line.type === 'removed') {
250
+ current.removed!.push(line);
251
+ } else {
252
+ current.added!.push(line);
253
+ }
254
+ }
255
+ }
256
+
257
+ if (current) {
258
+ grouped.push(current);
259
+ }
260
+
261
+ // Apply character-level diff to changed line pairs
262
+ for (const group of grouped) {
263
+ if (group.type === 'change' && group.removed && group.added) {
264
+ const minLen = Math.min(group.removed.length, group.added.length);
265
+
266
+ // For paired lines, compute character-level diff
267
+ for (let idx = 0; idx < minLen; idx++) {
268
+ const oldLine = group.removed[idx];
269
+ const newLine = group.added[idx];
270
+
271
+ // Only compute character diff if lines are similar enough
272
+ if (oldLine.content.length > 0 && newLine.content.length > 0) {
273
+ const similarity = computeSimilarity(oldLine.content, newLine.content);
274
+ if (similarity > 0.3) { // Lines are at least 30% similar
275
+ const { oldHighlights, newHighlights } = findCharacterDiff(oldLine.content, newLine.content);
276
+ if (oldHighlights.length > 0) {
277
+ oldLine.highlights = oldHighlights;
278
+ }
279
+ if (newHighlights.length > 0) {
280
+ newLine.highlights = newHighlights;
281
+ }
282
+ }
283
+ }
284
+ }
285
+ }
286
+ }
287
+
288
+ return grouped;
289
+ }
290
+
291
+ // Compute similarity ratio between two strings
292
+ function computeSimilarity(str1: string, str2: string): number {
293
+ const chars1 = str1.split('');
294
+ const chars2 = str2.split('');
295
+ const dp = findLCS(chars1, chars2);
296
+ const lcsLength = dp[chars1.length][chars2.length];
297
+ const maxLen = Math.max(chars1.length, chars2.length);
298
+ return maxLen === 0 ? 0 : lcsLength / maxLen;
299
+ }
300
+
301
+ // Render text with highlights
302
+ function renderLineWithHighlights(line: DiffLine): string {
303
+ if (!line.highlights || line.highlights.length === 0) {
304
+ return line.content;
305
+ }
306
+
307
+ let result = '';
308
+ let lastEnd = 0;
309
+
310
+ for (const highlight of line.highlights) {
311
+ // Add non-highlighted part
312
+ if (highlight.start > lastEnd) {
313
+ result += line.content.substring(lastEnd, highlight.start);
314
+ }
315
+ // Add highlighted part with special marker
316
+ result += '{{HIGHLIGHT_START}}' + line.content.substring(highlight.start, highlight.end) + '{{HIGHLIGHT_END}}';
317
+ lastEnd = highlight.end;
318
+ }
319
+
320
+ // Add any remaining non-highlighted part
321
+ if (lastEnd < line.content.length) {
322
+ result += line.content.substring(lastEnd);
323
+ }
324
+
325
+ return result;
326
+ }
327
+
328
+ const diffGroups = computeDiff(oldString, newString);
329
+ const hasChanges = diffGroups.some(group => group.type === 'change');
330
+ </script>
331
+
332
+ <div>
333
+ {#if label}
334
+ <div class="flex items-center gap-2 mb-2">
335
+ <Icon name="lucide:git-compare" class="text-violet-500 w-4 h-4" />
336
+ <span class="text-sm font-medium text-slate-700 dark:text-slate-300">{label}</span>
337
+ </div>
338
+ {/if}
339
+
340
+ <div class="relative max-h-96 bg-slate-50 dark:bg-slate-950 border border-slate-200/60 dark:border-slate-800/60 rounded-md overflow-auto">
341
+ {#if hasChanges}
342
+ <div class="text-xs font-mono leading-5 min-w-fit"><!--
343
+ -->{#each diffGroups as group}<!--
344
+ -->{#if group.type === 'unchanged'}<!--
345
+ -->{#each group.lines || [] as line}<!--
346
+ --><div class="relative flex"><!--
347
+ --><div class="sticky left-0 w-1 bg-slate-50 dark:bg-slate-950"></div><!--
348
+ --><pre class="flex-1 px-3 text-slate-600 dark:text-slate-400 whitespace-pre">{line.content}</pre><!--
349
+ --></div><!--
350
+ -->{/each}<!--
351
+ -->{:else if group.type === 'change'}<!--
352
+ -->{#each group.removed || [] as line}<!--
353
+ --><div class="relative flex bg-red-100 dark:bg-red-500/10"><!--
354
+ --><div class="sticky left-0 w-1 bg-red-500 dark:bg-red-500/80"></div><!--
355
+ --><pre class="flex-1 px-3 whitespace-pre"><!--
356
+ -->{#if line.highlights && line.highlights.length > 0}<!--
357
+ -->{@const rendered = line.content}<!--
358
+ -->{#each line.highlights as highlight, idx}<!--
359
+ -->{#if idx === 0 && highlight.start > 0}<!--
360
+ --><span class="text-red-700 dark:text-red-300">{rendered.substring(0, highlight.start)}</span><!--
361
+ -->{/if}<!--
362
+ --><span class="bg-red-300 dark:bg-red-400/30 text-red-900 dark:text-red-100 px-0.5">{rendered.substring(highlight.start, highlight.end)}</span><!--
363
+ -->{#if idx < line.highlights.length - 1}<!--
364
+ --><span class="text-red-700 dark:text-red-300">{rendered.substring(highlight.end, line.highlights[idx + 1].start)}</span><!--
365
+ -->{:else if highlight.end < rendered.length}<!--
366
+ --><span class="text-red-700 dark:text-red-300">{rendered.substring(highlight.end)}</span><!--
367
+ -->{/if}<!--
368
+ -->{/each}<!--
369
+ -->{:else}<!--
370
+ --><span class="text-red-700 dark:text-red-300">{line.content}</span><!--
371
+ -->{/if}<!--
372
+ --></pre><!--
373
+ --></div><!--
374
+ -->{/each}<!--
375
+ -->{#each group.added || [] as line}<!--
376
+ --><div class="relative flex bg-green-100 dark:bg-green-500/10"><!--
377
+ --><div class="sticky left-0 w-1 bg-green-500 dark:bg-green-500/80"></div><!--
378
+ --><pre class="flex-1 px-3 whitespace-pre"><!--
379
+ -->{#if line.highlights && line.highlights.length > 0}<!--
380
+ -->{@const rendered = line.content}<!--
381
+ -->{#each line.highlights as highlight, idx}<!--
382
+ -->{#if idx === 0 && highlight.start > 0}<!--
383
+ --><span class="text-green-700 dark:text-green-300">{rendered.substring(0, highlight.start)}</span><!--
384
+ -->{/if}<!--
385
+ --><span class="bg-green-300 dark:bg-green-400/30 text-green-900 dark:text-green-100 px-0.5">{rendered.substring(highlight.start, highlight.end)}</span><!--
386
+ -->{#if idx < line.highlights.length - 1}<!--
387
+ --><span class="text-green-700 dark:text-green-300">{rendered.substring(highlight.end, line.highlights[idx + 1].start)}</span><!--
388
+ -->{:else if highlight.end < rendered.length}<!--
389
+ --><span class="text-green-700 dark:text-green-300">{rendered.substring(highlight.end)}</span><!--
390
+ -->{/if}<!--
391
+ -->{/each}<!--
392
+ -->{:else}<!--
393
+ --><span class="text-green-700 dark:text-green-300">{line.content}</span><!--
394
+ -->{/if}<!--
395
+ --></pre><!--
396
+ --></div>
397
+ <!--
398
+ -->{/each}<!--
399
+ -->{/if}<!--
400
+ -->{/each}<!--
401
+ --></div>
402
+ {:else}
403
+ <div class="text-sm text-slate-600 dark:text-slate-400">
404
+ No changes detected
405
+ </div>
406
+ {/if}
407
+ </div>
408
+ </div>
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import type { IconName } from '$shared/types/ui/icons';
4
+ import { getFileIcon } from '$frontend/lib/utils/file-icon-mappings';
5
+ import { formatPath } from '../../shared/utils';
6
+
7
+ interface Props {
8
+ filePath: string;
9
+ fileName?: string;
10
+ iconColor?: string;
11
+ badges?: Array<{ text: string; color: string }>;
12
+ box?: boolean;
13
+ }
14
+
15
+ const { filePath, fileName, iconColor, badges = [], box = true }: Props = $props();
16
+
17
+ const displayFileName = fileName || filePath.split(/[/\\]/).pop() || filePath;
18
+ </script>
19
+
20
+ <div class={box ? "bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3" : ""}>
21
+ <div class="flex items-center gap-3 mb-1">
22
+ <Icon
23
+ name={getFileIcon(displayFileName)}
24
+ class="w-6 h-6 {iconColor || ''}"
25
+ />
26
+ <div class="flex-1 min-w-0">
27
+ <h3 class="font-medium text-slate-900 dark:text-slate-100 truncate">
28
+ {displayFileName}
29
+ </h3>
30
+ <p class="text-xs text-slate-600 dark:text-slate-400 truncate" title={filePath}>
31
+ {formatPath(filePath)}
32
+ </p>
33
+ </div>
34
+ </div>
35
+
36
+ {#if badges.length > 0}
37
+ <div class="flex gap-2 mt-3">
38
+ {#each badges as badge}
39
+ <div class="text-xs px-2 py-1 rounded {badge.color}">
40
+ {badge.text}
41
+ </div>
42
+ {/each}
43
+ </div>
44
+ {/if}
45
+ </div>
@@ -0,0 +1,19 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import type { IconName } from '$shared/types/ui/icons';
4
+
5
+ interface Props {
6
+ icon: IconName;
7
+ text: string;
8
+ iconColor?: string;
9
+ textColor?: string;
10
+ title?: string;
11
+ }
12
+
13
+ const { icon, text, iconColor = 'text-slate-500', textColor = 'text-slate-600 dark:text-slate-400', title="" }: Props = $props();
14
+ </script>
15
+
16
+ <div class="flex items-center gap-1.5" {title}>
17
+ <Icon name={icon} class="{iconColor} w-3.5 h-3.5" />
18
+ <span class="text-xs {textColor}">{text}</span>
19
+ </div>
@@ -0,0 +1,27 @@
1
+ <script lang="ts">
2
+ interface StatItem {
3
+ label: string;
4
+ value: string | number;
5
+ color: string;
6
+ }
7
+
8
+ interface Props {
9
+ stats: StatItem[];
10
+ columns?: number;
11
+ }
12
+
13
+ const { stats, columns = 3 }: Props = $props();
14
+
15
+ const gridClass = columns === 2 ? 'grid-cols-2' : columns === 4 ? 'grid-cols-4' : 'grid-cols-3';
16
+ </script>
17
+
18
+ <div class="grid {gridClass} gap-4 text-center text-sm">
19
+ {#each stats as stat}
20
+ <div>
21
+ <div class="text-lg font-bold {stat.color}">
22
+ {stat.value}
23
+ </div>
24
+ <div class="text-slate-500 dark:text-slate-400 text-xs">{stat.label}</div>
25
+ </div>
26
+ {/each}
27
+ </div>
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ command: string;
4
+ description?: string;
5
+ timeout?: number;
6
+ }
7
+
8
+ const { command, description, timeout }: Props = $props();
9
+
10
+ // Parse command parts for better display
11
+ function parseCommandParts(cmd: string) {
12
+ const parts = cmd.split(' ');
13
+ const mainCommand = parts[0];
14
+ const args = parts.slice(1);
15
+ return { mainCommand, args };
16
+ }
17
+
18
+ const { mainCommand, args } = parseCommandParts(command);
19
+ </script>
20
+
21
+ <!-- Description (if provided) -->
22
+ {#if description}
23
+ <p class="mb-2 text-slate-700 dark:text-slate-300">
24
+ {description}
25
+ </p>
26
+ {/if}
27
+
28
+ <!-- Command Display -->
29
+ <div class="bg-white dark:bg-slate-800 rounded-md border border-slate-200/60 dark:border-slate-700/60 p-3">
30
+ <div class="flex items-center justify-between gap-2 mb-2">
31
+ <div class="flex items-center gap-2">
32
+ <div class="w-2 h-2 bg-green-500 rounded-full"></div>
33
+ <span class="text-xs font-medium text-slate-700 dark:text-slate-300">Command:</span>
34
+ </div>
35
+ {#if timeout}
36
+ <div class="inline-block ml-auto text-xs bg-orange-100 dark:bg-orange-900 text-orange-700 dark:text-orange-300 px-2 py-0.5 rounded">
37
+ Timeout: {timeout}ms
38
+ </div>
39
+ {/if}
40
+ </div>
41
+
42
+ <!-- Terminal-style command display -->
43
+ <div class="bg-slate-50 dark:bg-slate-950 border border-slate-200/60 dark:border-slate-800/60 rounded-md p-2.5 font-mono text-sm">
44
+ <div class="flex items-start gap-2">
45
+ <span class="text-green-600 dark:text-green-400 select-none">$</span>
46
+ <div class="flex-1 text-slate-900 dark:text-slate-200 break-all">
47
+ <span class="text-violet-600 dark:text-violet-300 font-medium">{mainCommand}</span>
48
+ {#if args.length > 0}
49
+ <span class="text-slate-700 dark:text-slate-300"> {args.join(' ')}</span>
50
+ {/if}
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </div>
@@ -0,0 +1,7 @@
1
+ // Shared UI components for tool displays
2
+ export { default as FileHeader } from './FileHeader.svelte';
3
+ export { default as CodeBlock } from './CodeBlock.svelte';
4
+ export { default as DiffBlock } from './DiffBlock.svelte';
5
+ export { default as StatsBadges } from './StatsBadges.svelte';
6
+ export { default as InfoLine } from './InfoLine.svelte';
7
+ export { default as TerminalCommand } from './TerminalCommand.svelte';