@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,917 @@
1
+ <script lang="ts">
2
+ import { onMount, onDestroy } from 'svelte';
3
+ import { browser } from '$frontend/lib/app-environment';
4
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
5
+ import Dialog from '$frontend/lib/components/common/Dialog.svelte';
6
+ import ws from '$frontend/lib/utils/ws';
7
+ import { isDarkMode } from '$frontend/lib/utils/theme';
8
+ import { ENGINES } from '$shared/constants/engines';
9
+ import { claudeAccountsStore, type ClaudeAccountItem as ClaudeCodeAccountItem } from '$frontend/lib/stores/features/claude-accounts.svelte';
10
+ import type { Terminal } from 'xterm';
11
+ import type { FitAddon } from '@xterm/addon-fit';
12
+
13
+ const claudeCodeEngine = ENGINES.find(e => e.type === 'claude-code')!;
14
+ const openCodeEngine = ENGINES.find(e => e.type === 'opencode')!;
15
+
16
+ interface ClaudeCodeStatus {
17
+ installed: boolean;
18
+ version: string | null;
19
+ activeAccount: { id: number; name: string } | null;
20
+ accountsCount: number;
21
+ backendOS: 'windows' | 'macos' | 'linux';
22
+ }
23
+
24
+ interface OpenCodeStatus {
25
+ installed: boolean;
26
+ version: string | null;
27
+ backendOS: 'windows' | 'macos' | 'linux';
28
+ }
29
+
30
+ let claudeCodeStatus = $state<ClaudeCodeStatus | null>(null);
31
+ let isLoadingClaudeCodeStatus = $state(true);
32
+ const claudeCodeAccounts = $derived(claudeAccountsStore.accounts);
33
+
34
+ // OpenCode state
35
+ let openCodeStatus = $state<OpenCodeStatus | null>(null);
36
+ let isLoadingOpenCodeStatus = $state(true);
37
+
38
+ // Add account flow
39
+ type ClaudeCodeSetupStep = 'idle' | 'loading-url' | 'waiting-code' | 'submitting' | 'success' | 'error';
40
+ let claudeCodeSetupStep = $state<ClaudeCodeSetupStep>('idle');
41
+ let claudeCodeSetupId = $state<string | null>(null);
42
+ let claudeCodeAuthUrl = $state<string | null>(null);
43
+ let claudeCodeAuthCode = $state('');
44
+ let claudeCodeAccountName = $state('');
45
+ let claudeCodeSetupError = $state('');
46
+
47
+ // Install guide - Claude Code
48
+ type ClaudeCodeInstallTab = 'unix' | 'powershell';
49
+ let activeClaudeCodeInstallTab = $state<ClaudeCodeInstallTab>('unix');
50
+
51
+ // Install guide - OpenCode
52
+ type OpenCodeInstallTab = 'unix' | 'bun';
53
+ let activeOpenCodeInstallTab = $state<OpenCodeInstallTab>('unix');
54
+
55
+ // Rename
56
+ let claudeCodeRenamingId = $state<number | null>(null);
57
+ let claudeCodeRenameValue = $state('');
58
+
59
+ // Delete confirmation dialog
60
+ let claudeCodeDeleteDialogOpen = $state(false);
61
+ let claudeCodeDeleteTargetId = $state<number | null>(null);
62
+
63
+ // Copy URL feedback
64
+ let claudeCodeUrlCopied = $state(false);
65
+ let claudeCodeUrlCopiedTimer: ReturnType<typeof setTimeout> | null = null;
66
+
67
+ // Copy command feedback
68
+ let claudeCodeCommandCopied = $state(false);
69
+ let claudeCodeCommandCopiedTimer: ReturnType<typeof setTimeout> | null = null;
70
+
71
+ // Copy command feedback - OpenCode
72
+ let openCodeCommandCopied = $state(false);
73
+ let openCodeCommandCopiedTimer: ReturnType<typeof setTimeout> | null = null;
74
+
75
+ // Debug PTY (xterm.js)
76
+ const showDebug = $state(false);
77
+ let debugTermContainer = $state<HTMLDivElement>();
78
+ let debugTerminal: Terminal | null = null;
79
+ let debugFitAddon: FitAddon | null = null;
80
+ let debugTermReady = $state(false);
81
+ let ptyPhase = $state('');
82
+ let ptyBufferLen = $state(0);
83
+ let hasPtyData = $state(false);
84
+
85
+ // Event listener cleanup functions
86
+ const cleanups: Array<() => void> = [];
87
+
88
+ const claudeCodeInstallCommands: Record<ClaudeCodeInstallTab, { label: string; command: string }> = {
89
+ unix: { label: 'macOS / Linux / WSL', command: 'curl -fsSL https://claude.ai/install.sh | bash' },
90
+ powershell: { label: 'Windows PowerShell', command: 'irm https://claude.ai/install.ps1 | iex' },
91
+ };
92
+
93
+ const openCodeInstallCommands: Record<OpenCodeInstallTab, { label: string; command: string }> = {
94
+ unix: { label: 'macOS / Linux / WSL', command: 'curl -fsSL https://opencode.ai/install | bash' },
95
+ bun: { label: 'Bun', command: 'bun add -g opencode-ai' },
96
+ };
97
+
98
+ async function initDebugTerminal() {
99
+ if (!browser || !debugTermContainer || debugTerminal) return;
100
+
101
+ const [{ Terminal }, { FitAddon }] = await Promise.all([
102
+ import('xterm'),
103
+ import('@xterm/addon-fit')
104
+ ]);
105
+
106
+ await import('xterm/css/xterm.css');
107
+
108
+ debugTerminal = new Terminal({
109
+ theme: {
110
+ background: '#0f172a',
111
+ foreground: '#e2e8f0',
112
+ cursor: '#22c55e',
113
+ black: '#18181b',
114
+ red: '#ef4444',
115
+ green: '#22c55e',
116
+ yellow: '#eab308',
117
+ blue: '#60a5fa',
118
+ magenta: '#a855f7',
119
+ cyan: '#06b6d4',
120
+ white: '#f4f4f5',
121
+ brightBlack: '#52525b',
122
+ brightRed: '#f87171',
123
+ brightGreen: '#4ade80',
124
+ brightYellow: '#facc15',
125
+ brightBlue: '#60a5fa',
126
+ brightMagenta: '#c084fc',
127
+ brightCyan: '#22d3ee',
128
+ brightWhite: '#ffffff'
129
+ },
130
+ fontSize: 11,
131
+ fontFamily: 'JetBrains Mono, Monaco, "Cascadia Code", Consolas, monospace',
132
+ lineHeight: 1.1,
133
+ cursorBlink: false,
134
+ cursorStyle: 'underline' as const,
135
+ convertEol: true,
136
+ scrollback: 5000,
137
+ disableStdin: true,
138
+ allowTransparency: false,
139
+ cols: 120,
140
+ rows: 20
141
+ });
142
+
143
+ debugFitAddon = new FitAddon();
144
+ debugTerminal.loadAddon(debugFitAddon);
145
+ debugTerminal.open(debugTermContainer);
146
+ debugFitAddon.fit();
147
+ debugTermReady = true;
148
+ }
149
+
150
+ function disposeDebugTerminal() {
151
+ if (debugTerminal) {
152
+ debugTerminal.dispose();
153
+ debugTerminal = null;
154
+ debugFitAddon = null;
155
+ debugTermReady = false;
156
+ }
157
+ }
158
+
159
+ onMount(async () => {
160
+ // Listen for setup events from backend
161
+ cleanups.push(
162
+ ws.on('engine:claude-account-setup-url', (data: { setupId: string; authUrl: string }) => {
163
+ claudeCodeSetupId = data.setupId;
164
+ claudeCodeAuthUrl = data.authUrl;
165
+ claudeCodeSetupStep = 'waiting-code';
166
+ }),
167
+ ws.on('engine:claude-account-setup-complete', async (_data: { setupId: string; accountId: number }) => {
168
+ claudeCodeSetupStep = 'success';
169
+ await refreshClaudeCodeAccounts();
170
+ }),
171
+ ws.on('engine:claude-account-setup-error', (data: { setupId: string; message: string }) => {
172
+ claudeCodeSetupError = data.message;
173
+ claudeCodeSetupStep = 'error';
174
+ }),
175
+ ws.on('engine:claude-account-setup-pty-data', (data: { setupId: string; data: string; phase: string; bufferLength: number }) => {
176
+ hasPtyData = true;
177
+ ptyPhase = data.phase;
178
+ ptyBufferLen = data.bufferLength;
179
+ // Write raw data to xterm.js — it handles ANSI natively
180
+ if (debugTerminal) {
181
+ debugTerminal.write(data.data);
182
+ }
183
+ })
184
+ );
185
+
186
+ await Promise.all([refreshClaudeCodeStatus(), refreshOpenCodeStatus()]);
187
+ });
188
+
189
+ onDestroy(() => {
190
+ // Cleanup all event listeners
191
+ for (const cleanup of cleanups) cleanup();
192
+ cleanups.length = 0;
193
+
194
+ // Dispose debug terminal
195
+ disposeDebugTerminal();
196
+
197
+ // Cancel any running setup
198
+ if (claudeCodeSetupId && claudeCodeSetupStep !== 'idle' && claudeCodeSetupStep !== 'success' && claudeCodeSetupStep !== 'error') {
199
+ ws.emit('engine:claude-account-setup-cancel', { setupId: claudeCodeSetupId });
200
+ }
201
+ });
202
+
203
+ // Init debug terminal when container is available
204
+ $effect(() => {
205
+ if (debugTermContainer && !debugTerminal && showDebug) {
206
+ initDebugTerminal();
207
+ }
208
+ });
209
+
210
+ // Fit debug terminal on resize
211
+ $effect(() => {
212
+ if (debugTermReady && debugFitAddon && debugTermContainer) {
213
+ const observer = new ResizeObserver(() => {
214
+ debugFitAddon?.fit();
215
+ });
216
+ observer.observe(debugTermContainer);
217
+ return () => observer.disconnect();
218
+ }
219
+ });
220
+
221
+ async function refreshClaudeCodeStatus() {
222
+ isLoadingClaudeCodeStatus = true;
223
+ try {
224
+ claudeCodeStatus = await ws.http('engine:claude-status', {});
225
+
226
+ if (claudeCodeStatus) {
227
+ activeClaudeCodeInstallTab = claudeCodeStatus.backendOS === 'windows' ? 'powershell' : 'unix';
228
+ }
229
+
230
+ if (claudeCodeStatus?.installed) {
231
+ await refreshClaudeCodeAccounts();
232
+ }
233
+ } catch {
234
+ claudeCodeStatus = null;
235
+ }
236
+ isLoadingClaudeCodeStatus = false;
237
+ }
238
+
239
+ async function refreshClaudeCodeAccounts() {
240
+ await claudeAccountsStore.refresh();
241
+ }
242
+
243
+ function startClaudeCodeSetup() {
244
+ claudeCodeSetupStep = 'loading-url';
245
+ claudeCodeSetupError = '';
246
+ claudeCodeAuthCode = '';
247
+ claudeCodeAccountName = '';
248
+ ptyPhase = '';
249
+ ptyBufferLen = 0;
250
+ hasPtyData = false;
251
+ // Clear debug terminal
252
+ if (debugTerminal) {
253
+ debugTerminal.clear();
254
+ debugTerminal.reset();
255
+ }
256
+
257
+ // Fire-and-forget: server will emit 'setup-url' or 'setup-error' back
258
+ ws.emit('engine:claude-account-setup-start', {});
259
+ }
260
+
261
+ function submitClaudeCodeAuth() {
262
+ if (!claudeCodeSetupId || !claudeCodeAuthCode.trim() || !claudeCodeAccountName.trim()) return;
263
+
264
+ claudeCodeSetupStep = 'submitting';
265
+ claudeCodeSetupError = '';
266
+
267
+ // Fire-and-forget: server will emit 'setup-complete' or 'setup-error' back
268
+ ws.emit('engine:claude-account-setup-submit', {
269
+ setupId: claudeCodeSetupId,
270
+ code: claudeCodeAuthCode.trim(),
271
+ name: claudeCodeAccountName.trim()
272
+ });
273
+ }
274
+
275
+ function cancelClaudeCodeSetup() {
276
+ if (claudeCodeSetupId) {
277
+ ws.emit('engine:claude-account-setup-cancel', { setupId: claudeCodeSetupId });
278
+ }
279
+ resetClaudeCodeSetup();
280
+ }
281
+
282
+ function resetClaudeCodeSetup() {
283
+ claudeCodeSetupStep = 'idle';
284
+ claudeCodeSetupId = null;
285
+ claudeCodeAuthUrl = null;
286
+ claudeCodeAuthCode = '';
287
+ claudeCodeAccountName = '';
288
+ claudeCodeSetupError = '';
289
+ }
290
+
291
+ async function switchClaudeCodeAccount(id: number) {
292
+ try {
293
+ await ws.http('engine:claude-accounts-switch', { id });
294
+ await refreshClaudeCodeAccounts();
295
+ } catch {
296
+ // Ignore
297
+ }
298
+ }
299
+
300
+ function confirmDeleteClaudeCodeAccount(id: number) {
301
+ claudeCodeDeleteTargetId = id;
302
+ claudeCodeDeleteDialogOpen = true;
303
+ }
304
+
305
+ async function deleteClaudeCodeAccount() {
306
+ if (claudeCodeDeleteTargetId === null) return;
307
+ try {
308
+ await ws.http('engine:claude-accounts-delete', { id: claudeCodeDeleteTargetId });
309
+ await refreshClaudeCodeAccounts();
310
+ } catch {
311
+ // Ignore
312
+ }
313
+ }
314
+
315
+ function startClaudeCodeRename(account: ClaudeCodeAccountItem) {
316
+ claudeCodeRenamingId = account.id;
317
+ claudeCodeRenameValue = account.name;
318
+ }
319
+
320
+ async function submitClaudeCodeRename() {
321
+ if (claudeCodeRenamingId === null || !claudeCodeRenameValue.trim()) return;
322
+
323
+ try {
324
+ await ws.http('engine:claude-accounts-rename', { id: claudeCodeRenamingId, name: claudeCodeRenameValue.trim() });
325
+ claudeCodeRenamingId = null;
326
+ claudeCodeRenameValue = '';
327
+ await refreshClaudeCodeAccounts();
328
+ } catch {
329
+ // Ignore
330
+ }
331
+ }
332
+
333
+ function cancelClaudeCodeRename() {
334
+ claudeCodeRenamingId = null;
335
+ claudeCodeRenameValue = '';
336
+ }
337
+
338
+ async function refreshOpenCodeStatus() {
339
+ isLoadingOpenCodeStatus = true;
340
+ try {
341
+ openCodeStatus = await ws.http('engine:opencode-status', {});
342
+
343
+ if (openCodeStatus) {
344
+ // On Windows, default to bun since quick install requires WSL
345
+ activeOpenCodeInstallTab = openCodeStatus.backendOS === 'windows' ? 'bun' : 'unix';
346
+ }
347
+ } catch {
348
+ openCodeStatus = null;
349
+ }
350
+ isLoadingOpenCodeStatus = false;
351
+ }
352
+
353
+ async function copyToClipboard(text: string) {
354
+ try {
355
+ await navigator.clipboard.writeText(text);
356
+ } catch {
357
+ // Fallback
358
+ }
359
+ }
360
+
361
+ async function copyClaudeCodeCommand() {
362
+ await copyToClipboard(claudeCodeInstallCommands[activeClaudeCodeInstallTab].command);
363
+ claudeCodeCommandCopied = true;
364
+ if (claudeCodeCommandCopiedTimer) clearTimeout(claudeCodeCommandCopiedTimer);
365
+ claudeCodeCommandCopiedTimer = setTimeout(() => { claudeCodeCommandCopied = false; }, 2000);
366
+ }
367
+
368
+ async function copyOpenCodeCommand() {
369
+ await copyToClipboard(openCodeInstallCommands[activeOpenCodeInstallTab].command);
370
+ openCodeCommandCopied = true;
371
+ if (openCodeCommandCopiedTimer) clearTimeout(openCodeCommandCopiedTimer);
372
+ openCodeCommandCopiedTimer = setTimeout(() => { openCodeCommandCopied = false; }, 2000);
373
+ }
374
+
375
+ async function copyClaudeCodeAuthUrl() {
376
+ if (!claudeCodeAuthUrl) return;
377
+ await copyToClipboard(claudeCodeAuthUrl);
378
+ claudeCodeUrlCopied = true;
379
+ if (claudeCodeUrlCopiedTimer) clearTimeout(claudeCodeUrlCopiedTimer);
380
+ claudeCodeUrlCopiedTimer = setTimeout(() => { claudeCodeUrlCopied = false; }, 2000);
381
+ }
382
+ </script>
383
+
384
+ <div class="space-y-6">
385
+ <!-- Header -->
386
+ <h3 class="text-base font-bold text-slate-900 dark:text-slate-100 mb-1.5">AI Engine</h3>
387
+ <p class="text-sm text-slate-600 dark:text-slate-500 mb-4">
388
+ Manage AI engine installations and accounts
389
+ </p>
390
+
391
+ <!-- Claude Code Card -->
392
+ <div class="rounded-xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 overflow-hidden">
393
+ <!-- Card Header -->
394
+ <div class="flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-slate-700/50">
395
+ <div class="flex items-center gap-3">
396
+ <div class="flex items-center justify-center [&>svg]:w-6 [&>svg]:h-6">
397
+ {@html isDarkMode() ? claudeCodeEngine.icon.dark : claudeCodeEngine.icon.light}
398
+ </div>
399
+ <div>
400
+ <h3 class="font-semibold text-slate-900 dark:text-slate-100">{claudeCodeEngine.name}</h3>
401
+ <p class="text-xs text-slate-500 dark:text-slate-400">{claudeCodeEngine.description}</p>
402
+ </div>
403
+ </div>
404
+
405
+ {#if isLoadingClaudeCodeStatus}
406
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400">
407
+ <Icon name="lucide:loader" class="w-3 h-3 animate-spin" />
408
+ Checking...
409
+ </span>
410
+ {:else if claudeCodeStatus?.installed}
411
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400">
412
+ <span class="w-1.5 h-1.5 rounded-full bg-green-500"></span>
413
+ Installed
414
+ </span>
415
+ {:else}
416
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400">
417
+ <span class="w-1.5 h-1.5 rounded-full bg-red-500"></span>
418
+ Not Installed
419
+ </span>
420
+ {/if}
421
+ </div>
422
+
423
+ <!-- Card Body -->
424
+ <div class="px-5 py-4">
425
+ {#if isLoadingClaudeCodeStatus}
426
+ <div class="flex items-center justify-center py-8">
427
+ <Icon name="lucide:loader" class="w-6 h-6 animate-spin text-slate-400" />
428
+ </div>
429
+ {:else if claudeCodeStatus && !claudeCodeStatus.installed}
430
+ <!-- Install Guide -->
431
+ <div class="space-y-4">
432
+ <p class="text-sm text-slate-600 dark:text-slate-300">
433
+ Claude Code is not installed on this system. Install it using one of the methods below:
434
+ </p>
435
+
436
+ <!-- Tab Buttons -->
437
+ <div class="flex flex-wrap gap-1.5">
438
+ {#each Object.entries(claudeCodeInstallCommands) as [key, { label }]}
439
+ <button
440
+ type="button"
441
+ class="px-3 py-1.5 rounded-lg text-xs font-medium transition-colors
442
+ {activeClaudeCodeInstallTab === key
443
+ ? 'bg-violet-500/15 text-violet-700 dark:text-violet-300'
444
+ : 'bg-slate-100 dark:bg-slate-700/50 text-slate-600 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-700'}"
445
+ onclick={() => (activeClaudeCodeInstallTab = key as ClaudeCodeInstallTab)}
446
+ >
447
+ {label}
448
+ </button>
449
+ {/each}
450
+ </div>
451
+
452
+ <!-- Command Block -->
453
+ <div class="relative group">
454
+ <pre class="bg-slate-100 dark:bg-slate-950 text-slate-800 dark:text-slate-200 rounded-lg px-4 py-3 text-sm font-mono overflow-x-auto">{claudeCodeInstallCommands[activeClaudeCodeInstallTab].command}</pre>
455
+ <button
456
+ type="button"
457
+ class="flex absolute top-2 right-2 p-1.5 rounded-md transition-colors {claudeCodeCommandCopied ? 'bg-violet-600/80 text-white' : 'bg-slate-300/80 dark:bg-slate-700/80 text-slate-600 dark:text-slate-300 hover:bg-slate-400/80 dark:hover:bg-slate-600'}"
458
+ onclick={copyClaudeCodeCommand}
459
+ aria-label="Copy command"
460
+ >
461
+ <Icon name={claudeCodeCommandCopied ? 'lucide:check' : 'lucide:copy'} class="w-3.5 h-3.5" />
462
+ </button>
463
+ </div>
464
+
465
+ <!-- Windows Git Bash note -->
466
+ {#if claudeCodeStatus.backendOS === 'windows'}
467
+ <div class="flex gap-2.5 p-3 rounded-lg bg-amber-50 dark:bg-amber-900/15 border border-amber-200 dark:border-amber-700/50">
468
+ <Icon name="lucide:info" class="w-4 h-4 shrink-0 mt-0.5 text-amber-600 dark:text-amber-400" />
469
+ <div class="text-sm text-amber-800 dark:text-amber-300 space-y-1">
470
+ <p class="font-medium">Git Bash is required</p>
471
+ <p class="text-xs text-amber-700 dark:text-amber-400">
472
+ Claude Code requires Git Bash to run on Windows. If you haven't installed it yet, download and install it first:
473
+ </p>
474
+ <a
475
+ href="https://git-scm.com/install/windows"
476
+ target="_blank"
477
+ rel="noopener noreferrer"
478
+ class="inline-flex items-center gap-1 text-xs font-medium text-amber-700 dark:text-amber-300 hover:text-amber-900 dark:hover:text-amber-100 underline underline-offset-2"
479
+ >
480
+ https://git-scm.com/install/windows
481
+ </a>
482
+ </div>
483
+ </div>
484
+ {/if}
485
+
486
+ <!-- More info link -->
487
+ <div class="flex items-center gap-2 text-xs text-slate-500 dark:text-slate-400">
488
+ <Icon name="lucide:book-open" class="w-3.5 h-3.5 shrink-0" />
489
+ <div>
490
+ <span>For complete installation instructions, visit the</span>
491
+ <a
492
+ href="https://code.claude.com/docs/en/quickstart"
493
+ target="_blank"
494
+ rel="noopener noreferrer"
495
+ class="inline-flex items-center gap-1 font-medium text-violet-600 dark:text-violet-400 hover:text-violet-800 dark:hover:text-violet-200 underline underline-offset-2"
496
+ >
497
+ official setup guide
498
+ </a>
499
+ </div>
500
+ </div>
501
+
502
+ <button
503
+ type="button"
504
+ class="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-violet-600 text-white hover:bg-violet-700 transition-colors"
505
+ onclick={refreshClaudeCodeStatus}
506
+ >
507
+ <Icon name="lucide:refresh-cw" class="w-4 h-4" />
508
+ Recheck Installation
509
+ </button>
510
+ </div>
511
+ {:else if claudeCodeStatus}
512
+ <!-- Installed View -->
513
+ <div class="space-y-5">
514
+ <!-- Version -->
515
+ <div class="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300">
516
+ <Icon name="lucide:tag" class="w-4 h-4 text-slate-400" />
517
+ <span>Version: <span class="font-mono font-medium text-slate-900 dark:text-slate-100">{claudeCodeStatus.version || 'Unknown'}</span></span>
518
+ </div>
519
+
520
+ <!-- Accounts Section -->
521
+ <div class="space-y-3">
522
+ <div class="flex items-center justify-between">
523
+ <h4 class="text-sm font-semibold text-slate-700 dark:text-slate-300">Accounts</h4>
524
+ <span class="text-xs text-slate-500">{claudeCodeAccounts.length} account{claudeCodeAccounts.length !== 1 ? 's' : ''}</span>
525
+ </div>
526
+
527
+ {#if claudeCodeAccounts.length === 0}
528
+ <p class="text-sm text-slate-500 dark:text-slate-400 italic">No accounts configured</p>
529
+ {:else}
530
+ <div class="space-y-2">
531
+ {#each claudeCodeAccounts as account (account.id)}
532
+ <div class="flex items-center justify-between px-3.5 py-2.5 rounded-lg border border-slate-200 dark:border-slate-700/50 bg-slate-50 dark:bg-slate-800/80 {account.isActive ? 'ring-1 ring-violet-500/40' : ''}">
533
+ <div class="w-full flex items-center gap-2.5 min-w-0">
534
+ <Icon name="lucide:user" class="w-4 h-4 shrink-0 text-slate-400" />
535
+ {#if claudeCodeRenamingId === account.id}
536
+ <div class="w-full flex items-center gap-2.5">
537
+ <input
538
+ type="text"
539
+ bind:value={claudeCodeRenameValue}
540
+ class="w-full px-2 py-0.5 text-sm rounded border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-700 text-slate-900 dark:text-slate-100 focus:outline-none focus:ring-1 focus:ring-violet-500"
541
+ />
542
+ <div class="flex items-center gap-1">
543
+ <button
544
+ type="button"
545
+ class="flex p-1.5 rounded-md text-slate-400 hover:text-green-600 hover:bg-green-50 dark:hover:bg-green-900/20 transition-colors"
546
+ onclick={submitClaudeCodeRename}
547
+ aria-label="Save"
548
+ >
549
+ <Icon name="lucide:check" class="w-3.5 h-3.5" />
550
+ </button>
551
+ <button
552
+ type="button"
553
+ class="flex p-1.5 rounded-md text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
554
+ onclick={cancelClaudeCodeRename}
555
+ aria-label="Cancel"
556
+ >
557
+ <Icon name="lucide:x" class="w-3.5 h-3.5" />
558
+ </button>
559
+ </div>
560
+ </div>
561
+ {:else}
562
+ <span class="text-sm font-medium text-slate-900 dark:text-slate-100 truncate">{account.name}</span>
563
+ {#if account.isActive}
564
+ <span class="inline-flex items-center px-2 py-0.5 rounded-full text-3xs font-semibold bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300">
565
+ Active
566
+ </span>
567
+ {/if}
568
+ {/if}
569
+ </div>
570
+
571
+ {#if claudeCodeRenamingId !== account.id}
572
+ <div class="flex items-center gap-1">
573
+ {#if !account.isActive}
574
+ <button
575
+ type="button"
576
+ class="flex p-1.5 rounded-md text-slate-400 hover:text-violet-600 hover:bg-violet-50 dark:hover:bg-violet-900/20 transition-colors"
577
+ onclick={() => switchClaudeCodeAccount(account.id)}
578
+ title="Switch to this account"
579
+ >
580
+ <Icon name="lucide:arrow-right-left" class="w-3.5 h-3.5" />
581
+ </button>
582
+ {/if}
583
+ <button
584
+ type="button"
585
+ class="flex p-1.5 rounded-md text-slate-400 hover:text-blue-600 hover:bg-blue-50 dark:hover:bg-blue-900/20 transition-colors"
586
+ onclick={() => startClaudeCodeRename(account)}
587
+ title="Rename"
588
+ >
589
+ <Icon name="lucide:pencil" class="w-3.5 h-3.5" />
590
+ </button>
591
+ <button
592
+ type="button"
593
+ class="flex p-1.5 rounded-md text-slate-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors"
594
+ onclick={() => confirmDeleteClaudeCodeAccount(account.id)}
595
+ title="Delete account"
596
+ >
597
+ <Icon name="lucide:trash-2" class="w-3.5 h-3.5" />
598
+ </button>
599
+ </div>
600
+ {/if}
601
+ </div>
602
+ {/each}
603
+ </div>
604
+ {/if}
605
+
606
+ <!-- Add Account Flow -->
607
+ <div class="mt-3">
608
+ {#if claudeCodeSetupStep === 'idle'}
609
+ <button
610
+ type="button"
611
+ class="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg border border-dashed border-slate-300 dark:border-slate-600 text-slate-600 dark:text-slate-400 hover:border-violet-400 hover:text-violet-600 dark:hover:text-violet-400 transition-colors w-full justify-center"
612
+ onclick={startClaudeCodeSetup}
613
+ >
614
+ <Icon name="lucide:plus" class="w-4 h-4" />
615
+ Add Account
616
+ </button>
617
+ {:else if claudeCodeSetupStep === 'loading-url'}
618
+ <div class="p-4 rounded-lg border border-violet-200 dark:border-violet-800/50 bg-violet-50/50 dark:bg-violet-900/10">
619
+ <div class="flex items-center justify-center gap-2 text-sm text-slate-500">
620
+ <Icon name="lucide:loader" class="w-4 h-4 animate-spin" />
621
+ <span>Starting authentication process...</span>
622
+ </div>
623
+ </div>
624
+ {:else if claudeCodeSetupStep === 'waiting-code'}
625
+ <div class="space-y-3 p-4 rounded-lg border border-violet-200 dark:border-violet-800/50 bg-violet-50/50 dark:bg-violet-900/10">
626
+ <!-- Step indicator -->
627
+ <div class="flex items-center gap-2 text-xs font-medium text-violet-600 dark:text-violet-400">
628
+ <Icon name="lucide:key" class="w-3.5 h-3.5" />
629
+ Step 1: Authenticate via browser
630
+ </div>
631
+
632
+ <p class="text-sm text-slate-600 dark:text-slate-400">
633
+ Open the URL below in your browser, complete the sign-in, then copy the authentication code back here.
634
+ </p>
635
+
636
+ <!-- Auth URL -->
637
+ <div>
638
+ <div class="bg-white dark:bg-slate-800 rounded-lg px-3 py-2 text-xs font-mono text-slate-700 dark:text-slate-300 break-all border border-slate-200 dark:border-slate-700">
639
+ {claudeCodeAuthUrl}
640
+ </div>
641
+ <div class="flex gap-2 mt-2">
642
+ <button
643
+ type="button"
644
+ class="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md transition-colors
645
+ {claudeCodeUrlCopied
646
+ ? 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300'
647
+ : 'bg-slate-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-600'}"
648
+ onclick={copyClaudeCodeAuthUrl}
649
+ >
650
+ {#if claudeCodeUrlCopied}
651
+ <Icon name="lucide:check" class="w-3 h-3" />
652
+ Copied
653
+ {:else}
654
+ <Icon name="lucide:copy" class="w-3 h-3" />
655
+ Copy URL
656
+ {/if}
657
+ </button>
658
+ <a
659
+ href={claudeCodeAuthUrl}
660
+ target="_blank"
661
+ rel="noopener noreferrer"
662
+ class="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium rounded-md bg-violet-100 dark:bg-violet-900/30 text-violet-700 dark:text-violet-300 hover:bg-violet-200 dark:hover:bg-violet-800/40 transition-colors"
663
+ >
664
+ <Icon name="lucide:external-link" class="w-3 h-3" />
665
+ Open in Browser
666
+ </a>
667
+ </div>
668
+ </div>
669
+
670
+ <!-- Divider -->
671
+ <div class="border-t border-slate-200 dark:border-slate-700/50"></div>
672
+
673
+ <!-- Step 2 -->
674
+ <div class="flex items-center gap-2 text-xs font-medium text-violet-600 dark:text-violet-400">
675
+ <Icon name="lucide:clipboard-paste" class="w-3.5 h-3.5" />
676
+ Step 2: Paste the code and name your account
677
+ </div>
678
+
679
+ <div class="space-y-2">
680
+ <input
681
+ type="text"
682
+ bind:value={claudeCodeAuthCode}
683
+ placeholder="Paste authentication code here"
684
+ class="w-full px-3 py-2 text-sm rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-500/40 focus:border-violet-500"
685
+ />
686
+ <input
687
+ type="text"
688
+ bind:value={claudeCodeAccountName}
689
+ placeholder="Account name (e.g. Personal, Work)"
690
+ class="w-full px-3 py-2 text-sm rounded-lg border border-slate-300 dark:border-slate-600 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100 placeholder:text-slate-400 focus:outline-none focus:ring-2 focus:ring-violet-500/40 focus:border-violet-500"
691
+ />
692
+ </div>
693
+
694
+ <!-- Actions -->
695
+ <div class="flex gap-2">
696
+ <button
697
+ type="button"
698
+ class="flex-1 flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-violet-600 text-white hover:bg-violet-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
699
+ onclick={submitClaudeCodeAuth}
700
+ disabled={!claudeCodeAuthCode.trim() || !claudeCodeAccountName.trim()}
701
+ >
702
+ <Icon name="lucide:send" class="w-4 h-4" />
703
+ Submit
704
+ </button>
705
+ <button
706
+ type="button"
707
+ class="px-4 py-2 text-sm font-medium rounded-lg border border-slate-300 dark:border-slate-600 text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-700 transition-colors"
708
+ onclick={cancelClaudeCodeSetup}
709
+ >
710
+ Cancel
711
+ </button>
712
+ </div>
713
+
714
+ <p class="text-2xs text-slate-400 dark:text-slate-500">
715
+ A background process is running. It will auto-close in 5 minutes if not completed.
716
+ </p>
717
+ </div>
718
+ {:else if claudeCodeSetupStep === 'submitting'}
719
+ <div class="p-4 rounded-lg border border-violet-200 dark:border-violet-800/50 bg-violet-50/50 dark:bg-violet-900/10">
720
+ <div class="flex items-center justify-center gap-2 text-sm text-slate-500">
721
+ <Icon name="lucide:loader" class="w-4 h-4 animate-spin" />
722
+ <span>Verifying code and retrieving token...</span>
723
+ </div>
724
+ </div>
725
+ {:else if claudeCodeSetupStep === 'success'}
726
+ <div class="flex items-center gap-2 p-3 rounded-lg bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800/50">
727
+ <Icon name="lucide:circle-check" class="w-5 h-5 text-green-600 dark:text-green-400" />
728
+ <span class="text-sm text-green-700 dark:text-green-300">Account added successfully!</span>
729
+ <button
730
+ type="button"
731
+ class="ml-auto text-xs text-green-600 dark:text-green-400 hover:underline"
732
+ onclick={resetClaudeCodeSetup}
733
+ >
734
+ Dismiss
735
+ </button>
736
+ </div>
737
+ {:else if claudeCodeSetupStep === 'error'}
738
+ <div class="space-y-3">
739
+ <div class="flex items-center gap-2 p-3 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50">
740
+ <Icon name="lucide:circle-alert" class="w-5 h-5 shrink-0 text-red-600 dark:text-red-400" />
741
+ <span class="text-sm text-red-700 dark:text-red-300">{claudeCodeSetupError}</span>
742
+ </div>
743
+ <button
744
+ type="button"
745
+ class="flex items-center justify-center gap-2 w-full px-4 py-2 text-sm font-medium rounded-lg border border-slate-300 dark:border-slate-700 text-slate-700 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
746
+ onclick={resetClaudeCodeSetup}
747
+ >
748
+ <Icon name="lucide:rotate-ccw" class="w-4 h-4" />
749
+ Try Again
750
+ </button>
751
+ </div>
752
+ {/if}
753
+ </div>
754
+ </div>
755
+ </div>
756
+ {/if}
757
+ </div>
758
+ </div>
759
+
760
+ <!-- Debug PTY Output (xterm.js) -->
761
+ {#if showDebug && (hasPtyData || claudeCodeSetupStep !== 'idle')}
762
+ <div class="rounded-xl border border-amber-300 dark:border-amber-700/50 bg-amber-50 dark:bg-amber-900/10 overflow-hidden">
763
+ <div class="flex items-center justify-between px-4 py-2 border-b border-amber-200 dark:border-amber-700/50">
764
+ <div class="flex items-center gap-2">
765
+ <Icon name="lucide:bug" class="w-4 h-4 text-amber-600" />
766
+ <span class="text-xs font-semibold text-amber-700 dark:text-amber-300">Debug: PTY Output</span>
767
+ <span class="text-3xs font-mono px-1.5 py-0.5 rounded bg-amber-200 dark:bg-amber-800 text-amber-800 dark:text-amber-200">
768
+ phase={ptyPhase} | buf={ptyBufferLen} | step={claudeCodeSetupStep}
769
+ </span>
770
+ </div>
771
+ <button
772
+ type="button"
773
+ class="text-xs text-amber-600 hover:text-amber-800 dark:text-amber-400 dark:hover:text-amber-200"
774
+ onclick={() => { if (debugTerminal) { debugTerminal.clear(); debugTerminal.reset(); } hasPtyData = false; }}
775
+ >
776
+ Clear
777
+ </button>
778
+ </div>
779
+ <div
780
+ bind:this={debugTermContainer}
781
+ class="h-80 bg-[#0f172a]"
782
+ ></div>
783
+ </div>
784
+ {/if}
785
+
786
+ <!-- OpenCode Card -->
787
+ <div class="rounded-xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 overflow-hidden">
788
+ <!-- Card Header -->
789
+ <div class="flex items-center justify-between px-5 py-4 border-b border-slate-200 dark:border-slate-700/50">
790
+ <div class="flex items-center gap-3">
791
+ <div class="flex items-center justify-center [&>svg]:w-6 [&>svg]:h-6">
792
+ {@html isDarkMode() ? openCodeEngine.icon.dark : openCodeEngine.icon.light}
793
+ </div>
794
+ <div>
795
+ <h3 class="font-semibold text-slate-900 dark:text-slate-100">{openCodeEngine.name}</h3>
796
+ <p class="text-xs text-slate-500 dark:text-slate-400">{openCodeEngine.description}</p>
797
+ </div>
798
+ </div>
799
+
800
+ {#if isLoadingOpenCodeStatus}
801
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-slate-100 dark:bg-slate-700 text-slate-500 dark:text-slate-400">
802
+ <Icon name="lucide:loader" class="w-3 h-3 animate-spin" />
803
+ Checking...
804
+ </span>
805
+ {:else if openCodeStatus?.installed}
806
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400">
807
+ <span class="w-1.5 h-1.5 rounded-full bg-green-500"></span>
808
+ Installed
809
+ </span>
810
+ {:else}
811
+ <span class="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400">
812
+ <span class="w-1.5 h-1.5 rounded-full bg-red-500"></span>
813
+ Not Installed
814
+ </span>
815
+ {/if}
816
+ </div>
817
+
818
+ <!-- Card Body -->
819
+ <div class="px-5 py-4">
820
+ {#if isLoadingOpenCodeStatus}
821
+ <div class="flex items-center justify-center py-8">
822
+ <Icon name="lucide:loader" class="w-6 h-6 animate-spin text-slate-400" />
823
+ </div>
824
+ {:else if openCodeStatus && !openCodeStatus.installed}
825
+ <!-- Install Guide -->
826
+ <div class="space-y-4">
827
+ <p class="text-sm text-slate-600 dark:text-slate-300">
828
+ Open Code is not installed on this system. Install it using one of the methods below:
829
+ </p>
830
+
831
+ <!-- Tab Buttons -->
832
+ <div class="flex flex-wrap gap-1.5">
833
+ {#each Object.entries(openCodeInstallCommands) as [key, { label }]}
834
+ <button
835
+ type="button"
836
+ class="px-3 py-1.5 rounded-lg text-xs font-medium transition-colors
837
+ {activeOpenCodeInstallTab === key
838
+ ? 'bg-violet-500/15 text-violet-700 dark:text-violet-300'
839
+ : 'bg-slate-100 dark:bg-slate-700/50 text-slate-600 dark:text-slate-400 hover:bg-slate-200 dark:hover:bg-slate-700'}"
840
+ onclick={() => (activeOpenCodeInstallTab = key as OpenCodeInstallTab)}
841
+ >
842
+ {label}
843
+ </button>
844
+ {/each}
845
+ </div>
846
+
847
+ <!-- Command Block -->
848
+ <div class="relative group">
849
+ <pre class="bg-slate-100 dark:bg-slate-950 text-slate-800 dark:text-slate-200 rounded-lg px-4 py-3 text-sm font-mono overflow-x-auto">{openCodeInstallCommands[activeOpenCodeInstallTab].command}</pre>
850
+ <button
851
+ type="button"
852
+ class="flex absolute top-2 right-2 p-1.5 rounded-md transition-colors {openCodeCommandCopied ? 'bg-violet-600/80 text-white' : 'bg-slate-300/80 dark:bg-slate-700/80 text-slate-600 dark:text-slate-300 hover:bg-slate-400/80 dark:hover:bg-slate-600'}"
853
+ onclick={copyOpenCodeCommand}
854
+ aria-label="Copy command"
855
+ >
856
+ <Icon name={openCodeCommandCopied ? 'lucide:check' : 'lucide:copy'} class="w-3.5 h-3.5" />
857
+ </button>
858
+ </div>
859
+
860
+ <!-- More info link -->
861
+ <div class="flex items-center gap-2 text-xs text-slate-500 dark:text-slate-400">
862
+ <Icon name="lucide:book-open" class="w-3.5 h-3.5 shrink-0" />
863
+ <div>
864
+ <span>For complete installation instructions, visit the</span>
865
+ <a
866
+ href="https://opencode.ai/docs"
867
+ target="_blank"
868
+ rel="noopener noreferrer"
869
+ class="inline-flex items-center gap-1 font-medium text-violet-600 dark:text-violet-400 hover:text-violet-800 dark:hover:text-violet-200 underline underline-offset-2"
870
+ >
871
+ official documentation
872
+ </a>
873
+ </div>
874
+ </div>
875
+
876
+ <button
877
+ type="button"
878
+ class="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-violet-600 text-white hover:bg-violet-700 transition-colors"
879
+ onclick={refreshOpenCodeStatus}
880
+ >
881
+ <Icon name="lucide:refresh-cw" class="w-4 h-4" />
882
+ Recheck Installation
883
+ </button>
884
+ </div>
885
+ {:else if openCodeStatus}
886
+ <!-- Installed View -->
887
+ <div class="space-y-4">
888
+ <!-- Version -->
889
+ <div class="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-300">
890
+ <Icon name="lucide:tag" class="w-4 h-4 text-slate-400" />
891
+ <span>Version: <span class="font-mono font-medium text-slate-900 dark:text-slate-100">{openCodeStatus.version || 'Unknown'}</span></span>
892
+ </div>
893
+
894
+ <!-- Info note -->
895
+ <div class="flex gap-2.5 p-3 rounded-lg bg-slate-50 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-700/50">
896
+ <Icon name="lucide:info" class="w-4 h-4 shrink-0 mt-0.5 text-slate-500 dark:text-slate-400" />
897
+ <p class="text-xs text-slate-600 dark:text-slate-400">
898
+ Open Code is installed and ready to use. API keys and provider configuration are managed through Open Code's own settings via the <span class="font-mono">/connect</span> command.
899
+ </p>
900
+ </div>
901
+
902
+ </div>
903
+ {/if}
904
+ </div>
905
+ </div>
906
+ </div>
907
+
908
+ <Dialog
909
+ bind:isOpen={claudeCodeDeleteDialogOpen}
910
+ onClose={() => { claudeCodeDeleteDialogOpen = false; claudeCodeDeleteTargetId = null; }}
911
+ type="error"
912
+ title="Delete Account"
913
+ message="Are you sure you want to delete this account? This action cannot be undone."
914
+ confirmText="Delete"
915
+ cancelText="Cancel"
916
+ onConfirm={deleteClaudeCodeAccount}
917
+ />