@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,369 @@
1
+ /**
2
+ * Core Terminal Service (WebSocket Version)
3
+ * Handles terminal operations using WebSocket bi-directional communication
4
+ * Replaces SSE-based streaming with WebSocket events
5
+ */
6
+
7
+ import type { TerminalLine, TerminalSession, TerminalCommand } from '$shared/types/terminal';
8
+ import { terminalSessionManager, type TerminalSessionState } from './session.service';
9
+ import { backgroundTerminalService } from './background';
10
+ import ws from '$frontend/lib/utils/ws';
11
+ import { debug } from '$shared/utils/logger';
12
+
13
+ export interface TerminalConnectOptions {
14
+ sessionId: string;
15
+ workingDirectory?: string;
16
+ projectPath?: string;
17
+ projectId?: string;
18
+ terminalSize?: { cols: number; rows: number };
19
+ }
20
+
21
+ export interface StreamingResponse {
22
+ type: 'output' | 'error' | 'directory' | 'exit' | 'complete' | 'clear-screen';
23
+ content?: string;
24
+ newDirectory?: string;
25
+ sessionId?: string;
26
+ projectId?: string;
27
+ timestamp?: string;
28
+ }
29
+
30
+ export class TerminalService {
31
+ private resizeEndpointAvailable: boolean | null = true; // WebSocket always available
32
+ private activeListeners = new Map<string, Array<() => void>>();
33
+ private lastOutputSeq = new Map<string, number>();
34
+
35
+ /**
36
+ * Connect to persistent PTY session with streaming output via WebSocket
37
+ */
38
+ async connectToSession(
39
+ options: TerminalConnectOptions,
40
+ onData: (data: StreamingResponse) => void
41
+ ): Promise<void> {
42
+ const { sessionId, workingDirectory, projectPath, projectId, terminalSize } = options;
43
+
44
+ debug.log('terminal', `🔌 Connecting to PTY session via WebSocket: ${sessionId}`);
45
+
46
+ // CRITICAL: Cleanup existing listeners BEFORE creating new ones
47
+ // This prevents duplicate event handlers when reconnecting to the same session
48
+ // (e.g., when switching between projects and coming back)
49
+ this.cleanupListeners(sessionId);
50
+
51
+ // Reset sequence tracking for fresh deduplication
52
+ this.lastOutputSeq.delete(sessionId);
53
+
54
+ // Get or create session state
55
+ const session = terminalSessionManager.getOrCreateSession(
56
+ sessionId,
57
+ projectId,
58
+ projectPath,
59
+ workingDirectory
60
+ );
61
+
62
+ // Create unique stream ID for this connection
63
+ const streamId = `${sessionId}-${Date.now()}`;
64
+
65
+ // Get current output count to mark where new output starts
66
+ let outputStartIndex = 0;
67
+ if (typeof window !== 'undefined') {
68
+ try {
69
+ const terminalStoreModule = await import('$frontend/lib/stores/features/terminal.svelte');
70
+ const termSession = terminalStoreModule.terminalStore.getSession(sessionId);
71
+ if (termSession && termSession.lines) {
72
+ outputStartIndex = termSession.lines.length;
73
+ }
74
+ } catch {
75
+ // Ignore error, use default 0
76
+ }
77
+ }
78
+
79
+ // Setup WebSocket listeners for this session
80
+ const listeners: Array<() => void> = [];
81
+
82
+ // Listen for ready event
83
+ const unsubReady = ws.on('terminal:ready', (data) => {
84
+ if (data.sessionId === sessionId) {
85
+ debug.log('terminal', `✅ PTY session ready: ${sessionId} (PID: ${data.pid})`);
86
+ // Update session state with stream ID
87
+ terminalSessionManager.updateSession(sessionId, {
88
+ streamId: data.streamId,
89
+ processId: data.pid,
90
+ isExecuting: true
91
+ });
92
+ }
93
+ });
94
+ listeners.push(unsubReady);
95
+
96
+ // Listen for output (with sequence-based deduplication)
97
+ const unsubOutput = ws.on('terminal:output', (data) => {
98
+ if (data.sessionId === sessionId) {
99
+ // Deduplicate: skip if we've already seen this sequence number
100
+ // Multiple WS connections in the same project room can deliver
101
+ // the same terminal:output event multiple times
102
+ if (data.seq !== undefined && data.seq !== null) {
103
+ const lastSeq = this.lastOutputSeq.get(sessionId) || 0;
104
+ if (data.seq <= lastSeq) return;
105
+ this.lastOutputSeq.set(sessionId, data.seq);
106
+ }
107
+
108
+ onData({
109
+ type: 'output',
110
+ content: data.content,
111
+ sessionId: data.sessionId,
112
+ projectId: data.projectId,
113
+ timestamp: data.timestamp
114
+ });
115
+ }
116
+ });
117
+ listeners.push(unsubOutput);
118
+
119
+ // Listen for directory changes
120
+ const unsubDirectory = ws.on('terminal:directory', (data) => {
121
+ if (data.sessionId === sessionId) {
122
+ terminalSessionManager.updateWorkingDirectory(sessionId, data.newDirectory);
123
+ onData({
124
+ type: 'directory',
125
+ newDirectory: data.newDirectory,
126
+ sessionId: data.sessionId
127
+ });
128
+ }
129
+ });
130
+ listeners.push(unsubDirectory);
131
+
132
+ // Listen for exit
133
+ const unsubExit = ws.on('terminal:exit', (data) => {
134
+ if (data.sessionId === sessionId) {
135
+ debug.log('terminal', `🏁 PTY session exited: ${sessionId} (code: ${data.exitCode})`);
136
+
137
+ // Clear stream info
138
+ backgroundTerminalService.endStream(sessionId, true);
139
+
140
+ onData({
141
+ type: 'exit',
142
+ content: data.exitCode === 0 ? 'success' : 'error',
143
+ sessionId: data.sessionId
144
+ });
145
+
146
+ // Cleanup listeners
147
+ this.cleanupListeners(sessionId);
148
+ }
149
+ });
150
+ listeners.push(unsubExit);
151
+
152
+ // Listen for errors
153
+ const unsubError = ws.on('terminal:error', (data) => {
154
+ if (data.sessionId === sessionId) {
155
+ debug.error('terminal', `❌ PTY error for ${sessionId}:`, data.error);
156
+ onData({
157
+ type: 'error',
158
+ content: data.error,
159
+ sessionId: data.sessionId
160
+ });
161
+ }
162
+ });
163
+ listeners.push(unsubError);
164
+
165
+ // Store listeners for cleanup
166
+ this.activeListeners.set(sessionId, listeners);
167
+
168
+ // Create terminal session (now using HTTP pattern)
169
+ try {
170
+ const response = await ws.http('terminal:create-session', {
171
+ sessionId,
172
+ streamId,
173
+ workingDirectory: session.workingDirectory,
174
+ projectPath,
175
+ cols: terminalSize?.cols || 80,
176
+ rows: terminalSize?.rows || 24,
177
+ outputStartIndex
178
+ });
179
+
180
+ debug.log('terminal', `✅ Terminal session created:`, response);
181
+
182
+ // Update session with response data
183
+ terminalSessionManager.updateSession(sessionId, {
184
+ streamId: response.streamId,
185
+ processId: response.pid,
186
+ isExecuting: true
187
+ });
188
+ } catch (error) {
189
+ debug.error('terminal', `❌ Failed to create terminal session:`, error);
190
+ // Cleanup listeners on error
191
+ this.cleanupListeners(sessionId);
192
+ throw error;
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Send Ctrl+C interrupt signal to a specific session
198
+ * Always sends the signal regardless of execution state for utility/accessibility
199
+ */
200
+ async cancelCommand(sessionId: string): Promise<boolean> {
201
+ const session = terminalSessionManager.getSession(sessionId);
202
+ if (!session) {
203
+ return false;
204
+ }
205
+
206
+ try {
207
+ // Step 1: Cancel client-side state (if executing)
208
+ if (session.isExecuting) {
209
+ terminalSessionManager.cancelExecution(sessionId);
210
+ }
211
+
212
+ // Step 2: Always send Ctrl+C signal via WebSocket
213
+ // This is useful as a utility shortcut even when not executing
214
+ try {
215
+ const data = await ws.http('terminal:cancel', { sessionId }, 10000);
216
+ if (data.sessionId === sessionId) {
217
+ // Clear stream info only if was executing
218
+ if (session.isExecuting) {
219
+ backgroundTerminalService.endStream(sessionId, true);
220
+ }
221
+ return true;
222
+ }
223
+ return false;
224
+ } catch {
225
+ return false;
226
+ }
227
+ } catch (error) {
228
+ debug.error('terminal', 'Error sending Ctrl+C signal:', error);
229
+ return false;
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Resize terminal for a specific session
235
+ */
236
+ async resizeTerminal(sessionId: string, cols: number, rows: number): Promise<boolean> {
237
+ try {
238
+ debug.log('terminal', `🔧 Resizing terminal ${sessionId} to ${cols}x${rows}`);
239
+
240
+ // Send resize request via WebSocket HTTP
241
+ await ws.http('terminal:resize', { sessionId, cols, rows });
242
+ return true;
243
+ } catch (error) {
244
+ debug.error('terminal', 'Error resizing terminal:', error);
245
+ return false;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Send input to terminal session
251
+ */
252
+ sendInput(sessionId: string, data: string): void {
253
+ debug.log('terminal', `⌨️ Sending input to ${sessionId}:`, data);
254
+ ws.emit('terminal:input', { sessionId, data });
255
+ }
256
+
257
+ /**
258
+ * Kill terminal session completely
259
+ */
260
+ async killSession(sessionId: string): Promise<boolean> {
261
+ try {
262
+ debug.log('terminal', `💀 Killing terminal session: ${sessionId}`);
263
+
264
+ // Cleanup listeners first
265
+ this.cleanupListeners(sessionId);
266
+
267
+ // Send kill request and wait for confirmation
268
+ try {
269
+ const data = await ws.http('terminal:kill-session', { sessionId }, 5000);
270
+ return data.sessionId === sessionId;
271
+ } catch {
272
+ return false;
273
+ }
274
+ } catch (error) {
275
+ debug.error('terminal', 'Error killing session:', error);
276
+ return false;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Check shell availability
282
+ */
283
+ async checkShellAvailability(): Promise<{
284
+ available: boolean;
285
+ path: string | null;
286
+ platform: string;
287
+ isWindows: boolean;
288
+ shellType: string;
289
+ }> {
290
+ try {
291
+ return await ws.http('terminal:check-shell', {}, 5000);
292
+ } catch {
293
+ return {
294
+ available: false,
295
+ path: null,
296
+ platform: 'unknown',
297
+ isWindows: false,
298
+ shellType: 'Unknown'
299
+ };
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Get missed output for a session
305
+ */
306
+ async getMissedOutput(
307
+ sessionId: string,
308
+ streamId?: string,
309
+ fromIndex: number = 0
310
+ ): Promise<{
311
+ success: boolean;
312
+ output: string[];
313
+ outputCount: number;
314
+ status: string;
315
+ }> {
316
+ try {
317
+ const data = await ws.http('terminal:missed-output', { sessionId, streamId, fromIndex }, 5000);
318
+ if (data.sessionId === sessionId) {
319
+ return {
320
+ success: true,
321
+ output: data.output,
322
+ outputCount: data.outputCount,
323
+ status: data.status
324
+ };
325
+ }
326
+ return {
327
+ success: false,
328
+ output: [],
329
+ outputCount: 0,
330
+ status: 'invalid_session'
331
+ };
332
+ } catch {
333
+ return {
334
+ success: false,
335
+ output: [],
336
+ outputCount: 0,
337
+ status: 'timeout'
338
+ };
339
+ }
340
+ }
341
+
342
+ /**
343
+ * Cleanup listeners for a session
344
+ */
345
+ cleanupListeners(sessionId: string): void {
346
+ const listeners = this.activeListeners.get(sessionId);
347
+ if (listeners) {
348
+ listeners.forEach(unsub => unsub());
349
+ this.activeListeners.delete(sessionId);
350
+ this.lastOutputSeq.delete(sessionId);
351
+ debug.log('terminal', `🧹 Cleaned up listeners for ${sessionId}`);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Cleanup all listeners (call on component unmount)
357
+ */
358
+ cleanup(): void {
359
+ this.activeListeners.forEach((listeners, sessionId) => {
360
+ listeners.forEach(unsub => unsub());
361
+ });
362
+ this.activeListeners.clear();
363
+ this.lastOutputSeq.clear();
364
+ debug.log('terminal', '🧹 Cleaned up all terminal listeners');
365
+ }
366
+ }
367
+
368
+ // Export singleton instance
369
+ export const terminalService = new TerminalService();
@@ -0,0 +1,117 @@
1
+ import ws from '$frontend/lib/utils/ws';
2
+ import { debug } from '$shared/utils/logger';
3
+
4
+ /**
5
+ * Core Application Store
6
+ * Main app state: UI, navigation, loading, errors
7
+ *
8
+ * State persistence: lastView saved to server via user:save-state
9
+ * No localStorage usage for view state
10
+ */
11
+
12
+ interface PageInfo {
13
+ title: string;
14
+ description: string;
15
+ actions?: import('svelte').Snippet;
16
+ }
17
+
18
+ interface AppState {
19
+ // UI Navigation
20
+ currentView: string;
21
+ isLoading: boolean;
22
+ isRestoring: boolean;
23
+ isCancelling: boolean;
24
+ error: string | null;
25
+
26
+ // Page Information
27
+ pageInfo: PageInfo;
28
+
29
+ // App Loading State
30
+ isAppLoading: boolean;
31
+ isAppInitialized: boolean;
32
+ }
33
+
34
+ // Core app state using Svelte 5 runes
35
+ export const appState = $state<AppState>({
36
+ // UI Navigation
37
+ currentView: 'chat',
38
+ isLoading: false,
39
+ isRestoring: false,
40
+ isCancelling: false,
41
+ error: null,
42
+
43
+ // Page Information
44
+ pageInfo: {
45
+ title: 'Claude Code',
46
+ description: '',
47
+ actions: undefined
48
+ },
49
+
50
+ // App Loading State
51
+ isAppLoading: true,
52
+ isAppInitialized: false
53
+ });
54
+
55
+ // ========================================
56
+ // UI STATE MANAGEMENT
57
+ // ========================================
58
+
59
+ export function setLoading(loading: boolean) {
60
+ appState.isLoading = loading;
61
+ }
62
+
63
+ export function setCurrentView(view: string) {
64
+ appState.currentView = view;
65
+ // Save current view to server (fire-and-forget)
66
+ ws.http('user:save-state', { key: 'lastView', value: view }).catch(err => {
67
+ debug.error('workspace', 'Error saving lastView to server:', err);
68
+ });
69
+ }
70
+
71
+ export function setPageInfo(title: string, description?: string, actions?: import('svelte').Snippet) {
72
+ appState.pageInfo.title = title;
73
+ appState.pageInfo.description = description || '';
74
+ appState.pageInfo.actions = actions;
75
+ }
76
+
77
+ export function setError(error: string | null) {
78
+ appState.error = error;
79
+ }
80
+
81
+ export function clearError() {
82
+ appState.error = null;
83
+ }
84
+
85
+ // App loading state management
86
+ export function setAppLoading(loading: boolean) {
87
+ appState.isAppLoading = loading;
88
+ }
89
+
90
+ export function setAppInitialized() {
91
+ appState.isAppInitialized = true;
92
+ appState.isAppLoading = false;
93
+ }
94
+
95
+ /**
96
+ * Restore last view from server-provided state.
97
+ * Called during initialization with state from user:restore-state.
98
+ */
99
+ export function restoreLastView(lastView?: string | null) {
100
+ if (lastView) {
101
+ const validViews = ['chat', 'files', 'terminal', 'history', 'settings'];
102
+ if (validViews.includes(lastView)) {
103
+ appState.currentView = lastView;
104
+ return lastView;
105
+ }
106
+ }
107
+ return 'chat'; // Default fallback
108
+ }
109
+
110
+ // ========================================
111
+ // INITIALIZATION
112
+ // ========================================
113
+
114
+ export function initializeStore() {
115
+ // Initialize core app store
116
+ // Any initialization logic can be added here
117
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Files Store
3
+ * File explorer state management
4
+ */
5
+
6
+ import type { FileNode } from '$shared/types/filesystem';
7
+
8
+ interface FileState {
9
+ files: FileNode[];
10
+ selectedFile: FileNode | null;
11
+ expandedFolders: Set<string>;
12
+ isLoading: boolean;
13
+ error: string | null;
14
+ }
15
+
16
+ // File state using Svelte 5 runes
17
+ export const fileState = $state<FileState>({
18
+ files: [],
19
+ selectedFile: null,
20
+ expandedFolders: new Set<string>(),
21
+ isLoading: false,
22
+ error: null
23
+ });
24
+
25
+ // ========================================
26
+ // FILE MANAGEMENT
27
+ // ========================================
28
+
29
+ export function setFiles(files: FileNode[]) {
30
+ fileState.files = files;
31
+ }
32
+
33
+ export function setSelectedFile(file: FileNode | null) {
34
+ fileState.selectedFile = file;
35
+ }
36
+
37
+ export function toggleFolderExpansion(folderPath: string) {
38
+ if (fileState.expandedFolders.has(folderPath)) {
39
+ fileState.expandedFolders.delete(folderPath);
40
+ } else {
41
+ fileState.expandedFolders.add(folderPath);
42
+ }
43
+ }
44
+
45
+ export function expandFolder(folderPath: string) {
46
+ fileState.expandedFolders.add(folderPath);
47
+ }
48
+
49
+ export function collapseFolder(folderPath: string) {
50
+ fileState.expandedFolders.delete(folderPath);
51
+ }
52
+
53
+ export function clearFiles() {
54
+ fileState.files = [];
55
+ fileState.selectedFile = null;
56
+ fileState.expandedFolders.clear();
57
+ }
58
+
59
+ // ========================================
60
+ // STATE MANAGEMENT
61
+ // ========================================
62
+
63
+ export function setLoading(loading: boolean) {
64
+ fileState.isLoading = loading;
65
+ }
66
+
67
+ export function setError(error: string | null) {
68
+ fileState.error = error;
69
+ }
70
+
71
+ export function clearError() {
72
+ fileState.error = null;
73
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Presence Store
3
+ * Shared reactive state for project presence (active users)
4
+ * Subscribes once to projectStatusService, shared across all components
5
+ */
6
+
7
+ import { onMount } from 'svelte';
8
+ import { projectStatusService, type ProjectStatus } from '$frontend/lib/services/project/status.service';
9
+ import { userStore } from '$frontend/lib/stores/features/user.svelte';
10
+
11
+ // Shared reactive state
12
+ export const presenceState = $state<{
13
+ statuses: Map<string, ProjectStatus>;
14
+ }>({
15
+ statuses: new Map()
16
+ });
17
+
18
+ let subscribed = false;
19
+
20
+ /**
21
+ * Initialize presence subscription (call once at app level)
22
+ * Automatically excludes the current user from activeUsers
23
+ */
24
+ export function initPresence() {
25
+ if (subscribed) return;
26
+ subscribed = true;
27
+
28
+ projectStatusService.onStatusUpdate((statuses) => {
29
+ const currentUserId = userStore.currentUser?.id;
30
+ const statusMap = new Map<string, ProjectStatus>();
31
+ statuses.forEach((status) => {
32
+ statusMap.set(status.projectId, {
33
+ ...status,
34
+ activeUsers: currentUserId
35
+ ? status.activeUsers.filter((u) => u.userId !== currentUserId)
36
+ : status.activeUsers
37
+ });
38
+ });
39
+ presenceState.statuses = statusMap;
40
+ });
41
+ }
42
+
43
+ /**
44
+ * Get presence status for a specific project
45
+ */
46
+ export function getProjectPresence(projectId: string): ProjectStatus | undefined {
47
+ return presenceState.statuses.get(projectId);
48
+ }