@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,403 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import Modal from '$frontend/lib/components/common/Modal.svelte';
4
+ import Dialog from '$frontend/lib/components/common/Dialog.svelte';
5
+ import {
6
+ workspaceState,
7
+ setActiveMobilePanel,
8
+ type PanelId
9
+ } from '$frontend/lib/stores/ui/workspace.svelte';
10
+ import { projectState, removeProject } from '$frontend/lib/stores/core/projects.svelte';
11
+ import { presenceState } from '$frontend/lib/stores/core/presence.svelte';
12
+ import { openSettingsModal } from '$frontend/lib/stores/ui/settings-modal.svelte';
13
+ import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
14
+ import type { IconName } from '$shared/types/ui/icons';
15
+ import TunnelButton from '$frontend/lib/components/tunnel/TunnelButton.svelte';
16
+ import TunnelModal from '$frontend/lib/components/tunnel/TunnelModal.svelte';
17
+ import type { Project } from '$shared/types/database/schema';
18
+ import FolderBrowser from '$frontend/lib/components/common/FolderBrowser.svelte';
19
+ import ProjectUserAvatars from '$frontend/lib/components/common/ProjectUserAvatars.svelte';
20
+ import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
21
+ import ws from '$frontend/lib/utils/ws';
22
+ import { debug } from '$shared/utils/logger';
23
+
24
+ // Modal states
25
+ let showTunnelModal = $state(false);
26
+
27
+ // Project dropdown state
28
+ let showProjectMenu = $state(false);
29
+ let showFolderBrowser = $state(false);
30
+ let showDeleteDialog = $state(false);
31
+ let projectToDelete = $state<Project | null>(null);
32
+ let searchQuery = $state('');
33
+
34
+ // Get current project status from shared store
35
+ const currentProjectStatus = $derived(
36
+ projectState.currentProject?.id
37
+ ? presenceState.statuses.get(projectState.currentProject.id)
38
+ : undefined
39
+ );
40
+
41
+ const panels: { id: PanelId; icon: IconName; label: string }[] = [
42
+ { id: 'chat', icon: 'lucide:bot', label: 'AI' },
43
+ { id: 'files', icon: 'lucide:folder', label: 'Files' },
44
+ { id: 'git', icon: 'lucide:git-branch', label: 'Source Control' },
45
+ { id: 'terminal', icon: 'lucide:terminal', label: 'Terminal' },
46
+ { id: 'preview', icon: 'lucide:globe', label: 'Preview' }
47
+ ];
48
+
49
+ // Filtered projects based on search query
50
+ const filteredProjects = $derived(() => {
51
+ if (!searchQuery.trim()) return projectState.projects;
52
+ const query = searchQuery.toLowerCase();
53
+ return projectState.projects.filter(
54
+ (p) => p.name.toLowerCase().includes(query) || p.path.toLowerCase().includes(query)
55
+ );
56
+ });
57
+
58
+ function selectPanel(panelId: PanelId) {
59
+ setActiveMobilePanel(panelId);
60
+ }
61
+
62
+ function toggleProjectMenu() {
63
+ showProjectMenu = !showProjectMenu;
64
+ if (!showProjectMenu) {
65
+ searchQuery = '';
66
+ }
67
+ }
68
+
69
+ // Get status color from presence data (single source of truth)
70
+ // Green only when the project is the CURRENT project AND the current chat session has an active stream
71
+ function getStatusColor(projectId: string): string {
72
+ const currentProjectId = projectState.currentProject?.id;
73
+ if (projectId !== currentProjectId) return 'bg-slate-500/30';
74
+ const status = presenceState.statuses.get(projectId);
75
+ const currentChatSessionId = sessionState.currentSession?.id;
76
+ if (!status?.streams || !currentChatSessionId) return 'bg-slate-500/30';
77
+ const hasActiveForSession = status.streams.some(
78
+ (s: any) => s.status === 'active' && s.chatSessionId === currentChatSessionId
79
+ );
80
+ if (hasActiveForSession) return 'bg-emerald-500';
81
+ return 'bg-slate-500/30';
82
+ }
83
+
84
+ function openAddProject() {
85
+ showProjectMenu = false;
86
+ showFolderBrowser = true;
87
+ }
88
+
89
+ function closeFolderBrowser() {
90
+ showFolderBrowser = false;
91
+ }
92
+
93
+ function closeProjectMenu() {
94
+ showProjectMenu = false;
95
+ searchQuery = '';
96
+ }
97
+
98
+ function handleDeleteClick(project: Project, event: MouseEvent) {
99
+ event.stopPropagation();
100
+ projectToDelete = project;
101
+ showDeleteDialog = true;
102
+ }
103
+
104
+ async function confirmDeleteProject() {
105
+ if (!projectToDelete) return;
106
+ const deleteId = projectToDelete.id!;
107
+
108
+ try {
109
+ await ws.http('projects:delete', { id: deleteId });
110
+ removeProject(deleteId);
111
+ showDeleteDialog = false;
112
+ projectToDelete = null;
113
+ } catch (error) {
114
+ debug.error('workspace', 'Failed to delete project:', error);
115
+ addNotification({
116
+ type: 'error',
117
+ title: 'Error',
118
+ message: 'Failed to delete project',
119
+ duration: 5000
120
+ });
121
+ }
122
+ }
123
+
124
+ function closeDeleteDialog() {
125
+ showDeleteDialog = false;
126
+ projectToDelete = null;
127
+ }
128
+
129
+ async function createProjectFromFolder(folderPath: string, folderName: string) {
130
+ try {
131
+ showFolderBrowser = false;
132
+
133
+ const projects = await ws.http('projects:list', {});
134
+
135
+ const existingProject = projects
136
+ ? projects.find((p: any) => p.path === folderPath)
137
+ : null;
138
+
139
+ if (existingProject) {
140
+ const { setCurrentProject } = await import('$frontend/lib/stores/core/projects.svelte');
141
+ setCurrentProject(existingProject);
142
+ return;
143
+ }
144
+
145
+ const newProject = await ws.http('projects:create', { name: folderName, path: folderPath });
146
+
147
+ if (newProject) {
148
+ const { setCurrentProject } = await import('$frontend/lib/stores/core/projects.svelte');
149
+ setCurrentProject(newProject);
150
+ }
151
+ } catch (error) {
152
+ console.error('Failed to create project:', error);
153
+ }
154
+ }
155
+ </script>
156
+
157
+ <header
158
+ class="flex items-center bg-white/90 dark:bg-slate-900/98 py-2 px-3 gap-2 relative z-30"
159
+ >
160
+ <!-- Project Selector -->
161
+ <button
162
+ type="button"
163
+ class="flex items-center gap-2 px-3 py-2.5 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-lg text-slate-900 dark:text-slate-100 text-sm font-medium cursor-pointer transition-all duration-150 flex-1 min-w-0 active:bg-violet-500/10"
164
+ onclick={toggleProjectMenu}
165
+ aria-expanded={showProjectMenu}
166
+ aria-haspopup="menu"
167
+ >
168
+ <div class="relative shrink-0"><Icon name="lucide:folder-open" class="w-4 h-4" /><span class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-slate-100 dark:border-slate-800 {getStatusColor(projectState.currentProject?.id ?? '')}"></span></div>
169
+ <span class="flex-1 text-left overflow-hidden text-ellipsis whitespace-nowrap">
170
+ {projectState.currentProject?.name ?? 'No Project'}
171
+ </span>
172
+ <div class="shrink-0" onclick={(e) => e.stopPropagation()}>
173
+ <ProjectUserAvatars projectStatus={currentProjectStatus} maxVisible={2} />
174
+ </div>
175
+ <Icon name="lucide:chevron-down" class="w-3 h-3 opacity-60 shrink-0" />
176
+ </button>
177
+
178
+ <!-- Panel Tabs (Icon Only) -->
179
+ <div
180
+ class="flex gap-1 bg-slate-100/80 dark:bg-slate-800/50 p-1 border border-slate-200 dark:border-slate-800 rounded-lg"
181
+ role="tablist"
182
+ aria-label="Panel Tabs"
183
+ >
184
+ {#each panels as panel}
185
+ <button
186
+ type="button"
187
+ class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150
188
+ {workspaceState.activeMobilePanel === panel.id
189
+ ? 'bg-violet-500/10 dark:bg-violet-500/20 text-slate-900 dark:text-slate-100 shadow-violet-500/20'
190
+ : 'active:bg-violet-500/10'}"
191
+ role="tab"
192
+ aria-selected={workspaceState.activeMobilePanel === panel.id}
193
+ aria-controls={`panel-${panel.id}`}
194
+ aria-label={panel.label}
195
+ title={panel.label}
196
+ onclick={() => selectPanel(panel.id)}
197
+ >
198
+ <Icon name={panel.icon} class="w-5 h-5" />
199
+ </button>
200
+ {/each}
201
+ </div>
202
+
203
+ <!-- Action Buttons -->
204
+ <div
205
+ class="flex gap-1 bg-slate-100/80 dark:bg-slate-800/50 p-1 border border-slate-200 dark:border-slate-800 rounded-lg"
206
+ role="tablist"
207
+ aria-label="Action Buttons"
208
+ >
209
+ <!-- Tunnel Button -->
210
+ <button
211
+ type="button"
212
+ class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150 active:bg-violet-500/10"
213
+ role="tab"
214
+ onclick={() => (showTunnelModal = true)}
215
+ aria-label="Public Tunnel"
216
+ title="Public Tunnel"
217
+ >
218
+ <Icon name="lucide:cloud-upload" class="w-5 h-5" />
219
+ </button>
220
+
221
+ <!-- Settings Button -->
222
+ <button
223
+ type="button"
224
+ class="flex items-center justify-center w-9 h-8 bg-transparent border-none rounded-md text-slate-500 cursor-pointer transition-all duration-150 active:bg-violet-500/10"
225
+ role="tab"
226
+ onclick={() => openSettingsModal()}
227
+ aria-label="Settings"
228
+ title="Settings"
229
+ >
230
+ <Icon name="lucide:settings" class="w-5 h-5" />
231
+ </button>
232
+ </div>
233
+ </header>
234
+
235
+ <!-- Project Selection Modal -->
236
+ <Modal bind:isOpen={showProjectMenu} onClose={closeProjectMenu} size="md">
237
+ {#snippet header()}
238
+ <div class="flex items-center justify-between px-4 py-3 md:px-6 md:py-4">
239
+ <h2 class="text-base md:text-lg font-bold text-slate-900 dark:text-slate-100">Projects</h2>
240
+ <div class="flex items-center gap-2">
241
+ <button
242
+ type="button"
243
+ class="flex items-center justify-center w-8 h-8 bg-violet-500/10 dark:bg-violet-500/15 border border-violet-500/20 rounded-lg text-violet-600 dark:text-violet-400 cursor-pointer transition-all duration-150 hover:bg-violet-500/20"
244
+ onclick={openAddProject}
245
+ aria-label="Add project"
246
+ title="Add project"
247
+ >
248
+ <Icon name="lucide:plus" class="w-4 h-4" />
249
+ </button>
250
+ <button
251
+ type="button"
252
+ class="p-1.5 md:p-2 rounded-lg text-slate-500 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-violet-500/10 transition-colors"
253
+ onclick={closeProjectMenu}
254
+ aria-label="Close modal"
255
+ >
256
+ <svg class="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
257
+ <path
258
+ stroke-linecap="round"
259
+ stroke-linejoin="round"
260
+ stroke-width="2"
261
+ d="M6 18L18 6M6 6l12 12"
262
+ />
263
+ </svg>
264
+ </button>
265
+ </div>
266
+ </div>
267
+ {/snippet}
268
+
269
+ {#snippet children()}
270
+ <!-- Search Box -->
271
+ {#if projectState.projects.length > 0}
272
+ <div class="mb-4">
273
+ <div
274
+ class="flex items-center gap-2 py-2.5 px-3.5 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-lg"
275
+ >
276
+ <Icon name="lucide:search" class="w-4 h-4 text-slate-500 dark:text-slate-400 shrink-0" />
277
+ <input
278
+ type="text"
279
+ bind:value={searchQuery}
280
+ placeholder="Search projects..."
281
+ class="flex-1 bg-transparent border-none outline-none text-slate-900 dark:text-slate-100 text-sm placeholder:text-slate-500 dark:placeholder:text-slate-400"
282
+ />
283
+ {#if searchQuery}
284
+ <button
285
+ type="button"
286
+ class="flex items-center justify-center w-5 h-5 bg-transparent border-none rounded text-slate-400 cursor-pointer transition-all duration-150 hover:text-slate-600 dark:hover:text-slate-300"
287
+ onclick={() => (searchQuery = '')}
288
+ aria-label="Clear search"
289
+ >
290
+ <Icon name="lucide:x" class="w-3.5 h-3.5" />
291
+ </button>
292
+ {/if}
293
+ </div>
294
+ </div>
295
+ {/if}
296
+
297
+ {#if projectState.projects.length === 0}
298
+ <div class="flex flex-col items-center gap-3 py-8 text-slate-600 dark:text-slate-500 text-sm">
299
+ <Icon name="lucide:folder-x" class="w-12 h-12 text-slate-400 opacity-40" />
300
+ <p class="font-medium">No projects yet</p>
301
+ <p class="text-xs text-slate-500 dark:text-slate-500">Create your first project below</p>
302
+ </div>
303
+ {:else}
304
+ <div class="space-y-2">
305
+ {#each filteredProjects() as project (project.id)}
306
+ {@const isActive = projectState.currentProject?.id === project.id}
307
+ <div
308
+ class="flex items-center gap-2 w-full p-3 bg-transparent border border-slate-200 dark:border-slate-700 rounded-lg text-slate-900 dark:text-slate-100 text-sm text-left transition-all duration-150
309
+ {isActive
310
+ ? 'border-violet-300 dark:border-violet-600 bg-violet-50 dark:bg-violet-900/10'
311
+ : ''}"
312
+ >
313
+ <button
314
+ type="button"
315
+ class="flex items-center gap-3 flex-1 min-w-0 bg-transparent border-none cursor-pointer text-left"
316
+ onclick={() => {
317
+ import('$frontend/lib/stores/core/projects.svelte').then((m) =>
318
+ m.setCurrentProject(project)
319
+ );
320
+ closeProjectMenu();
321
+ }}
322
+ >
323
+ <div
324
+ class="relative w-8 h-8 {isActive
325
+ ? 'bg-violet-200 dark:bg-violet-800/30'
326
+ : 'bg-violet-100 dark:bg-violet-900/20'} rounded-lg flex items-center justify-center flex-shrink-0"
327
+ >
328
+ <Icon name="lucide:folder" class="text-violet-600 dark:text-violet-400 w-4 h-4" />
329
+ <span
330
+ class="absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-white dark:border-slate-900 {getStatusColor(project.id ?? '')}"
331
+ ></span>
332
+ </div>
333
+ <div class="flex-1 min-w-0">
334
+ <div class="flex items-center gap-2">
335
+ <p class="font-semibold text-slate-900 dark:text-slate-100 truncate">
336
+ {project.name}
337
+ </p>
338
+ {#if isActive}
339
+ <span
340
+ class="inline-flex items-center gap-1 px-2 py-0.5 bg-green-100 dark:bg-green-900/20 text-green-700 dark:text-green-300 text-xs font-medium rounded-full"
341
+ >
342
+ <Icon name="lucide:circle-check" class="w-3 h-3" />
343
+ Active
344
+ </span>
345
+ {/if}
346
+ </div>
347
+ <p class="text-xs text-slate-500 dark:text-slate-400 truncate font-mono">
348
+ {project.path}
349
+ </p>
350
+ </div>
351
+ </button>
352
+ <ProjectUserAvatars projectStatus={presenceState.statuses.get(project.id ?? '')} maxVisible={2} />
353
+ <button
354
+ type="button"
355
+ class="flex items-center justify-center w-8 h-8 bg-transparent border-none rounded-lg text-slate-400 dark:text-slate-500 cursor-pointer transition-all duration-150 hover:bg-red-500/15 hover:text-red-500 shrink-0"
356
+ onclick={(e) => handleDeleteClick(project, e)}
357
+ aria-label="Delete project"
358
+ title="Delete"
359
+ >
360
+ <Icon name="lucide:trash-2" class="w-4 h-4" />
361
+ </button>
362
+ </div>
363
+ {:else}
364
+ <div
365
+ class="flex flex-col items-center gap-2 py-8 text-slate-500 dark:text-slate-400 text-sm"
366
+ >
367
+ <Icon name="lucide:search-x" class="w-10 h-10 opacity-40" />
368
+ <p class="font-medium">No projects found</p>
369
+ <button
370
+ type="button"
371
+ class="text-xs text-violet-600 dark:text-violet-400 underline cursor-pointer hover:text-violet-700 dark:hover:text-violet-300"
372
+ onclick={() => (searchQuery = '')}
373
+ >
374
+ Clear search
375
+ </button>
376
+ </div>
377
+ {/each}
378
+ </div>
379
+ {/if}
380
+ {/snippet}
381
+ </Modal>
382
+
383
+ <!-- Folder Browser -->
384
+ <FolderBrowser
385
+ bind:isOpen={showFolderBrowser}
386
+ onClose={closeFolderBrowser}
387
+ onSelect={createProjectFromFolder}
388
+ />
389
+
390
+ <!-- Delete Confirmation Dialog -->
391
+ <Dialog
392
+ bind:isOpen={showDeleteDialog}
393
+ onClose={closeDeleteDialog}
394
+ type="error"
395
+ title="Delete Project"
396
+ message='This will remove "{projectToDelete?.name}" from your project list. The actual project files on disk will not be deleted.'
397
+ confirmText="Delete"
398
+ cancelText="Cancel"
399
+ onConfirm={confirmDeleteProject}
400
+ />
401
+
402
+ <!-- Tunnel Modal -->
403
+ <TunnelModal bind:isOpen={showTunnelModal} onClose={() => (showTunnelModal = false)} />
@@ -0,0 +1,100 @@
1
+ <script lang="ts">
2
+ import { browser } from '$frontend/lib/app-environment';
3
+ import { onMount, onDestroy } from 'svelte';
4
+ import PanelHeader from './PanelHeader.svelte';
5
+ import ChatPanel from './panels/ChatPanel.svelte';
6
+ import PreviewPanel from './panels/PreviewPanel.svelte';
7
+ import FilesPanel from './panels/FilesPanel.svelte';
8
+ import TerminalPanel from './panels/TerminalPanel.svelte';
9
+ import GitPanel from './panels/GitPanel.svelte';
10
+ import HistoryModal from '$frontend/lib/components/history/HistoryModal.svelte';
11
+ import { workspaceState, type PanelId } from '$frontend/lib/stores/ui/workspace.svelte';
12
+
13
+ interface Props {
14
+ panelId: PanelId;
15
+ noPadding?: boolean;
16
+ }
17
+
18
+ const { panelId, noPadding = false }: Props = $props();
19
+
20
+ const panel = $derived(workspaceState.panels[panelId]);
21
+ const isMinimized = $derived(panel?.minimized ?? false);
22
+
23
+ // Panel refs for actions
24
+ let chatPanelRef: any = $state();
25
+ let filesPanelRef: any = $state();
26
+ let terminalPanelRef: any = $state();
27
+ let previewPanelRef: any = $state();
28
+ let gitPanelRef: any = $state();
29
+ // History modal state
30
+ let showHistoryModal = $state(false);
31
+
32
+ // Mobile detection
33
+ let isMobile = $state(false);
34
+
35
+ function handleResize() {
36
+ if (browser) {
37
+ isMobile = window.innerWidth < 1024;
38
+ }
39
+ }
40
+
41
+ function openHistoryModal() {
42
+ showHistoryModal = true;
43
+ }
44
+
45
+ function closeHistoryModal() {
46
+ showHistoryModal = false;
47
+ }
48
+
49
+ onMount(() => {
50
+ handleResize();
51
+ if (browser) {
52
+ window.addEventListener('resize', handleResize);
53
+ }
54
+ });
55
+
56
+ onDestroy(() => {
57
+ if (browser) {
58
+ window.removeEventListener('resize', handleResize);
59
+ }
60
+ });
61
+ </script>
62
+
63
+ <div
64
+ class="flex flex-col h-full {isMobile
65
+ ? 'bg-transparent'
66
+ : 'bg-white/90 dark:bg-slate-900/60 backdrop-blur-3 border border-slate-200 dark:border-slate-800 rounded-xl'} overflow-hidden"
67
+ >
68
+ <!-- Panel Header -->
69
+ <PanelHeader
70
+ {panelId}
71
+ {chatPanelRef}
72
+ {filesPanelRef}
73
+ {terminalPanelRef}
74
+ {previewPanelRef}
75
+ {gitPanelRef}
76
+ onHistoryOpen={openHistoryModal}
77
+ />
78
+
79
+ <!-- Panel Content -->
80
+ {#if !isMinimized}
81
+ <div class="flex-1 overflow-hidden {noPadding ? '' : panelId === 'chat' ? 'p-3' : ''}">
82
+ {#if panelId === 'chat'}
83
+ <ChatPanel bind:this={chatPanelRef} />
84
+ {:else if panelId === 'preview'}
85
+ <PreviewPanel bind:this={previewPanelRef} />
86
+ {:else if panelId === 'files'}
87
+ <FilesPanel bind:this={filesPanelRef} />
88
+ {:else if panelId === 'terminal'}
89
+ <TerminalPanel bind:this={terminalPanelRef} />
90
+ {:else if panelId === 'git'}
91
+ <GitPanel bind:this={gitPanelRef} />
92
+ {/if}
93
+ </div>
94
+ {/if}
95
+ </div>
96
+
97
+ <!-- History Modal (only for chat panel) -->
98
+ {#if panelId === 'chat'}
99
+ <HistoryModal bind:isOpen={showHistoryModal} onClose={closeHistoryModal} />
100
+ {/if}