@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,615 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { sessionState, removeSession, setCurrentSession } from '$frontend/lib/stores/core/sessions.svelte';
4
+ import { projectState, setCurrentProject } from '$frontend/lib/stores/core/projects.svelte';
5
+ import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
6
+ import { setCurrentView } from '$frontend/lib/stores/core/app.svelte';
7
+ import ws from '$frontend/lib/utils/ws';
8
+ import type { ChatSession, Project } from '$shared/types/database/schema';
9
+ import type { SDKMessage } from '$shared/types/messaging';
10
+ import Input from '../common/Input.svelte';
11
+ import Select from '../common/Select.svelte';
12
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
13
+ import PageTemplate from '../common/PageTemplate.svelte';
14
+ import Button from '../common/Button.svelte';
15
+ import { showConfirm } from '$frontend/lib/stores/ui/dialog.svelte';
16
+ import Modal from '$frontend/lib/components/common/Modal.svelte';
17
+ import TimelineModal from '../checkpoint/TimelineModal.svelte';
18
+ import { debug } from '$shared/utils/logger';
19
+
20
+ // Use real session data from session store
21
+ const sessions = $derived(sessionState.sessions);
22
+
23
+ // Helper to get project name from project ID
24
+ function getProjectName(projectId: string): string {
25
+ const project = projectState.projects.find(p => p.id === projectId);
26
+ return project?.name || 'Unknown Project';
27
+ }
28
+
29
+ // Helper to get relative time (last active)
30
+ function getRelativeTime(dateString: string): string {
31
+ const now = Date.now();
32
+ const date = new Date(dateString).getTime();
33
+ const diffMs = now - date;
34
+ const diffMins = Math.floor(diffMs / 1000 / 60);
35
+ const diffHours = Math.floor(diffMins / 60);
36
+ const diffDays = Math.floor(diffHours / 24);
37
+
38
+ if (diffMins < 1) return 'Just now';
39
+ if (diffMins < 60) return `${diffMins}m ago`;
40
+ if (diffHours < 24) return `${diffHours}h ago`;
41
+ if (diffDays < 7) return `${diffDays}d ago`;
42
+ if (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`;
43
+ if (diffDays < 365) return `${Math.floor(diffDays / 30)}mo ago`;
44
+ return `${Math.floor(diffDays / 365)}y ago`;
45
+ }
46
+
47
+ // Helper to get last activity timestamp
48
+ function getLastActive(session: ChatSession): string {
49
+ // Use ended_at if available, otherwise use started_at
50
+ const timestamp = session.ended_at && session.ended_at !== ''
51
+ ? session.ended_at
52
+ : session.started_at;
53
+ return getRelativeTime(timestamp);
54
+ }
55
+
56
+ // Helper to calculate session duration in minutes
57
+ function getSessionDuration(session: ChatSession): number {
58
+ const start = new Date(session.started_at).getTime();
59
+ const end = session.ended_at ? new Date(session.ended_at).getTime() : Date.now();
60
+ return Math.round((end - start) / 1000 / 60);
61
+ }
62
+
63
+ // Cache for session data to avoid multiple API calls
64
+ const sessionDataCache = $state<Record<string, {
65
+ messages: SDKMessage[];
66
+ title: string;
67
+ summary: string;
68
+ count: number;
69
+ userCount: number;
70
+ assistantCount: number;
71
+ }>>({});
72
+ let loadingSessionData = $state(true);
73
+
74
+ // Helper to get session data from cache or API
75
+ async function getSessionData(sessionId: string) {
76
+ if (sessionDataCache[sessionId]) {
77
+ return sessionDataCache[sessionId];
78
+ }
79
+
80
+ try {
81
+ // Get messages from current HEAD checkpoint (active branch only)
82
+ const messages = await ws.http('messages:list', { session_id: sessionId });
83
+
84
+ // Get title from first user message in current HEAD
85
+ const firstUserMessage = messages.find((m: SDKMessage) => m.type === 'user');
86
+ let title = 'New Conversation';
87
+ if (firstUserMessage) {
88
+ // Handle content properly - it can be string or array of content blocks
89
+ let textContent = '';
90
+ if (typeof firstUserMessage.message.content === 'string') {
91
+ textContent = firstUserMessage.message.content;
92
+ } else if (Array.isArray(firstUserMessage.message.content)) {
93
+ // Extract text from content blocks
94
+ const textBlocks = firstUserMessage.message.content.filter((c: any) => c.type === 'text');
95
+ textContent = textBlocks.map((b: any) => 'text' in b ? b.text : '').join(' ');
96
+ }
97
+
98
+ if (textContent) {
99
+ title = textContent.slice(0, 60) + (textContent.length > 60 ? '...' : '');
100
+ }
101
+ }
102
+
103
+ // Get summary from last assistant message in current HEAD checkpoint
104
+ const assistantMessages = messages.filter((m: SDKMessage) => m.type === 'assistant');
105
+ let summary = 'No messages yet';
106
+ if (assistantMessages.length > 0) {
107
+ const lastMessage = assistantMessages[assistantMessages.length - 1];
108
+ const textBlocks = lastMessage.message.content.filter((c: any) => c.type === 'text');
109
+ if (textBlocks.length > 0) {
110
+ const fullText = textBlocks.map((b: any) => 'text' in b ? b.text : '').join(' ');
111
+ const cleanText = fullText.replace(/```[\s\S]*?```/g, '').trim();
112
+ summary = cleanText.slice(0, 150) + (cleanText.length > 150 ? '...' : '');
113
+ }
114
+ }
115
+
116
+ // Count user and assistant messages in current HEAD checkpoint
117
+ // Filter out empty user messages (same as ChatInterface.svelte timeline logic)
118
+ const userMessages = messages.filter((m: SDKMessage) => {
119
+ if (m.type !== 'user') return false;
120
+ // Extract text content
121
+ let textContent = '';
122
+ if (typeof m.message.content === 'string') {
123
+ textContent = m.message.content;
124
+ } else if (Array.isArray(m.message.content)) {
125
+ const textBlocks = m.message.content.filter(c => c.type === 'text');
126
+ textContent = textBlocks.map(b => 'text' in b ? b.text : '').join(' ');
127
+ }
128
+ return textContent.trim().length > 0;
129
+ });
130
+
131
+ const totalBubbles = userMessages.length + assistantMessages.length; // Total message bubbles in chat
132
+
133
+ const data = {
134
+ messages,
135
+ title,
136
+ summary,
137
+ count: totalBubbles, // Total bubbles (user + assistant with non-empty content)
138
+ userCount: userMessages.length, // Number of chat sessions/exchanges (non-empty user messages)
139
+ assistantCount: assistantMessages.length
140
+ };
141
+
142
+ sessionDataCache[sessionId] = data;
143
+ debug.log('session', `Loaded session ${sessionId}:`, {
144
+ title,
145
+ totalMessages: messages.length,
146
+ userCount: userMessages.length,
147
+ assistantCount: assistantMessages.length,
148
+ totalBubbles: totalBubbles,
149
+ summary: summary.substring(0, 50)
150
+ });
151
+ return data;
152
+ } catch (error) {
153
+ debug.error('session', 'Error fetching session data:', error);
154
+ return {
155
+ messages: [],
156
+ title: 'New Conversation',
157
+ summary: 'No messages yet',
158
+ count: 0,
159
+ userCount: 0,
160
+ assistantCount: 0
161
+ };
162
+ }
163
+ }
164
+
165
+ // Helper functions that use cached data
166
+ function getMessageCount(sessionId: string): number {
167
+ return sessionDataCache[sessionId]?.count || 0;
168
+ }
169
+
170
+ function getUserMessageCount(sessionId: string): number {
171
+ return sessionDataCache[sessionId]?.userCount || 0;
172
+ }
173
+
174
+ function getSessionTitle(sessionId: string): string {
175
+ return sessionDataCache[sessionId]?.title || 'New Conversation';
176
+ }
177
+
178
+ function getSessionSummary(sessionId: string): string {
179
+ return sessionDataCache[sessionId]?.summary || 'No messages yet';
180
+ }
181
+
182
+ // Preload session data for visible sessions
183
+ async function preloadSessionData() {
184
+ loadingSessionData = true;
185
+ try {
186
+ // Load all sessions in parallel for better performance
187
+ await Promise.all(
188
+ sessions.slice(0, 20).map(session => getSessionData(session.id))
189
+ );
190
+ } catch (error) {
191
+ debug.error('session', 'Error preloading session data:', error);
192
+ } finally {
193
+ loadingSessionData = false;
194
+ }
195
+ }
196
+
197
+ // Initialize session data on mount
198
+ onMount(() => {
199
+ preloadSessionData();
200
+ });
201
+
202
+ // Reload session data when sessions change
203
+ $effect(() => {
204
+ if (sessions.length > 0 && !loadingSessionData) {
205
+ // Check if there are sessions without cached data
206
+ const uncachedSessions = sessions.filter(s => !sessionDataCache[s.id]);
207
+ if (uncachedSessions.length > 0) {
208
+ debug.log('session', `Found ${uncachedSessions.length} uncached sessions, loading...`);
209
+ preloadSessionData();
210
+ }
211
+ }
212
+ });
213
+
214
+ let searchQuery = $state('');
215
+ let projectFilter = $state('all');
216
+ let showTimelineModal = $state(false);
217
+ let timelineSession = $state<ChatSession | null>(null);
218
+
219
+ function openTimelineModal(session: ChatSession) {
220
+ timelineSession = session;
221
+ showTimelineModal = true;
222
+ }
223
+
224
+ function closeTimelineModal() {
225
+ timelineSession = null;
226
+ showTimelineModal = false;
227
+ }
228
+
229
+ // Get unique projects
230
+ const projects = $derived(
231
+ Array.from(new Set(sessions.map(s => getProjectName(s.project_id))))
232
+ );
233
+
234
+ // Filtered sessions (sorted newest first)
235
+ const filteredSessions = $derived(
236
+ sessions
237
+ .filter(session => {
238
+ const title = getSessionTitle(session.id);
239
+ const summary = getSessionSummary(session.id);
240
+ const projectName = getProjectName(session.project_id);
241
+ const messageCount = getMessageCount(session.id);
242
+
243
+ // Filter out sessions with 0 messages
244
+ if (messageCount === 0) {
245
+ return false;
246
+ }
247
+
248
+ const matchesSearch = searchQuery === '' ||
249
+ title.toLowerCase().includes(searchQuery.toLowerCase()) ||
250
+ summary.toLowerCase().includes(searchQuery.toLowerCase()) ||
251
+ projectName.toLowerCase().includes(searchQuery.toLowerCase());
252
+
253
+ const matchesProject = projectFilter === 'all' || projectName === projectFilter;
254
+
255
+ return matchesSearch && matchesProject;
256
+ })
257
+ .sort((a, b) => new Date(b.started_at).getTime() - new Date(a.started_at).getTime())
258
+ );
259
+
260
+ // Session stats (only count sessions with messages)
261
+ const sessionsWithMessages = $derived(sessions.filter(s => getMessageCount(s.id) > 0));
262
+
263
+ const stats = $derived({
264
+ totalSessions: sessionsWithMessages.length,
265
+ totalChats: sessionsWithMessages.reduce((sum, s) => sum + getUserMessageCount(s.id), 0),
266
+ totalMessages: sessionsWithMessages.reduce((sum, s) => sum + getMessageCount(s.id), 0),
267
+ totalDuration: sessionsWithMessages.reduce((sum, s) => sum + getSessionDuration(s), 0)
268
+ });
269
+
270
+ function formatDuration(minutes: number): string {
271
+ const hours = Math.floor(minutes / 60);
272
+ const mins = minutes % 60;
273
+ if (hours > 0) {
274
+ return `${hours}h ${mins}m`;
275
+ }
276
+ return `${mins}m`;
277
+ }
278
+
279
+ function formatDate(dateString: string): string {
280
+ return new Date(dateString).toLocaleDateString('en-US', {
281
+ month: 'short',
282
+ day: 'numeric',
283
+ year: 'numeric'
284
+ });
285
+ }
286
+
287
+ // Helper to check if session is currently active
288
+ function isActiveSession(session: ChatSession): boolean {
289
+ return sessionState.currentSession?.id === session.id;
290
+ }
291
+
292
+ async function resumeSession(session: ChatSession | null) {
293
+ if (!session) return;
294
+
295
+ const sessionData = await getSessionData(session.id);
296
+ const title = sessionData.title;
297
+ const projectName = getProjectName(session.project_id);
298
+
299
+ const confirmed = await showConfirm({
300
+ title: 'Resume Session',
301
+ message: `Resume session "${title}" from project "${projectName}"?\n\nThis will load the conversation and navigate to the chat view.`,
302
+ type: 'info',
303
+ confirmText: 'Resume',
304
+ cancelText: 'Cancel'
305
+ });
306
+
307
+ if (!confirmed) return;
308
+
309
+ try {
310
+ // Reactivate if ended (does NOT end other sessions — multi-session parallel)
311
+ let targetSession = session;
312
+ if (session.ended_at) {
313
+ const reactivatedSession = await ws.http('sessions:update', { id: session.id, reactivate: true });
314
+ if (reactivatedSession) {
315
+ const sessionIndex = sessionState.sessions.findIndex(s => s.id === session.id);
316
+ if (sessionIndex !== -1) {
317
+ sessionState.sessions[sessionIndex] = reactivatedSession;
318
+ }
319
+ targetSession = reactivatedSession;
320
+ }
321
+ }
322
+
323
+ // Find the project
324
+ const project = projectState.projects.find(p => p.id === session.project_id);
325
+ if (project) {
326
+ // Set current project and session
327
+ setCurrentProject(project);
328
+ await setCurrentSession(targetSession);
329
+ // Navigate to chat view
330
+ setCurrentView('chat');
331
+ } else {
332
+ addNotification({
333
+ type: 'error',
334
+ title: 'Project Not Found',
335
+ message: 'The project for this session could not be found',
336
+ duration: 4000
337
+ });
338
+ }
339
+ } catch (error) {
340
+ debug.error('session', 'Error resuming session:', error);
341
+ addNotification({
342
+ type: 'error',
343
+ title: 'Resume Failed',
344
+ message: 'Failed to resume session',
345
+ duration: 5000
346
+ });
347
+ }
348
+ }
349
+
350
+ async function deleteSession(session: ChatSession) {
351
+ const sessionData = await getSessionData(session.id);
352
+ const title = sessionData.title;
353
+
354
+ const confirmed = await showConfirm({
355
+ title: 'Delete Session',
356
+ message: `Are you sure you want to delete session "${title}"? This action cannot be undone.`,
357
+ type: 'error',
358
+ confirmText: 'Delete',
359
+ cancelText: 'Cancel'
360
+ });
361
+
362
+ if (confirmed) {
363
+ try {
364
+ // Delete from database via WebSocket
365
+ await ws.http('sessions:delete', { id: session.id });
366
+ // Remove from local state
367
+ removeSession(session.id);
368
+ // Clear cache
369
+ delete sessionDataCache[session.id];
370
+ // User already knows session was deleted from UI update
371
+ } catch (error) {
372
+ addNotification({
373
+
374
+ type: 'error',
375
+ title: 'Error',
376
+ message: 'Failed to delete session',
377
+ duration: 5000
378
+ });
379
+ }
380
+ }
381
+ }
382
+
383
+ async function clearHistory() {
384
+ const confirmed = await showConfirm({
385
+ title: 'Clear All Session History',
386
+ message: 'Are you sure you want to clear all session history? This will delete all sessions. This action cannot be undone.',
387
+ type: 'error',
388
+ confirmText: 'Clear All',
389
+ cancelText: 'Cancel'
390
+ });
391
+
392
+ if (!confirmed) return;
393
+
394
+ if (sessions.length === 0) {
395
+ return;
396
+ }
397
+
398
+ try {
399
+ // Delete all sessions from database
400
+ const deletePromises = sessions.map(async (session) => {
401
+ try {
402
+ await ws.http('sessions:delete', { id: session.id });
403
+ // Remove from local state
404
+ removeSession(session.id);
405
+ // Clear cache
406
+ delete sessionDataCache[session.id];
407
+ return { success: true };
408
+ } catch (error) {
409
+ debug.error('session', `Error deleting session ${session.id}:`, error);
410
+ return { success: false, error: 'Failed to delete session' };
411
+ }
412
+ });
413
+
414
+ // Wait for all deletions to complete
415
+ const results = await Promise.all(deletePromises);
416
+
417
+ // Check if any deletions failed
418
+ const failed = results.filter(r => !r.success);
419
+ if (failed.length > 0) {
420
+ addNotification({
421
+ type: 'warning',
422
+ title: 'Partial Deletion',
423
+ message: `Failed to delete ${failed.length} session(s)`,
424
+ duration: 5000
425
+ });
426
+ }
427
+ } catch (error) {
428
+ debug.error('session', 'Error clearing history:', error);
429
+ addNotification({
430
+ type: 'error',
431
+ title: 'Clear Failed',
432
+ message: 'Failed to clear session history',
433
+ duration: 5000
434
+ });
435
+ }
436
+ }
437
+ </script>
438
+
439
+ <PageTemplate
440
+ title="Session History"
441
+ description="Previous conversation sessions"
442
+ >
443
+ {#snippet actions()}
444
+ <Button variant="outline" onclick={clearHistory} class="rounded-lg px-2 sm:px-4 py-1.5 sm:py-2">
445
+ <Icon name="lucide:trash-2" class="w-3.5 h-3.5 sm:w-4 sm:h-4 sm:mr-2" />
446
+ <span class="hidden sm:inline">Clear History</span>
447
+ </Button>
448
+ {/snippet}
449
+
450
+ <div class="flex flex-col h-full gap-6">
451
+ <!-- Modern Stats -->
452
+ <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
453
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
454
+ <div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalSessions}</div>
455
+ <div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Sessions</div>
456
+ </div>
457
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
458
+ <div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalChats}</div>
459
+ <div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Chats</div>
460
+ </div>
461
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
462
+ <div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{stats.totalMessages}</div>
463
+ <div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Messages</div>
464
+ </div>
465
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4 transition-all duration-200">
466
+ <div class="text-2xl font-bold text-slate-600 dark:text-slate-400">{formatDuration(stats.totalDuration)}</div>
467
+ <div class="text-sm text-slate-600 dark:text-slate-400 mt-1">Total Duration</div>
468
+ </div>
469
+ </div>
470
+
471
+ <!-- Modern Filters -->
472
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-4">
473
+ <div class="flex flex-col lg:flex-row gap-4">
474
+ <!-- Search -->
475
+ <div class="flex-1">
476
+ <Input
477
+ bind:value={searchQuery}
478
+ placeholder="Search sessions by title, summary, or project..."
479
+ type="search"
480
+ />
481
+ </div>
482
+
483
+ <!-- Modern Project Filter -->
484
+ <Select
485
+ bind:value={projectFilter}
486
+ placeholder="All Projects"
487
+ options={[
488
+ { value: "all", label: "All Projects" },
489
+ ...projects.map(project => ({ value: project, label: project }))
490
+ ]}
491
+ class="bg-slate-50 dark:bg-slate-800 border-slate-200 dark:border-slate-700 rounded-lg"
492
+ />
493
+ </div>
494
+ </div>
495
+
496
+ <!-- Modern Sessions List -->
497
+ <div class="flex-1 overflow-auto">
498
+ {#if loadingSessionData}
499
+ <div class="flex items-center justify-center py-12">
500
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-violet-600"></div>
501
+ <span class="ml-3 text-slate-600 dark:text-slate-400">Loading session data...</span>
502
+ </div>
503
+ {:else if filteredSessions.length === 0}
504
+ <div class="flex items-center justify-center h-full">
505
+ <div class="text-center">
506
+ <div class="bg-slate-100 dark:bg-slate-800 rounded-full w-24 h-24 flex items-center justify-center mx-auto mb-6">
507
+ <Icon name="lucide:clock" class="w-12 h-12 text-slate-400" />
508
+ </div>
509
+ <h3 class="text-lg font-bold bg-gradient-to-r from-violet-600 to-violet-600 bg-clip-text text-transparent mb-2">No sessions found</h3>
510
+ <p class="text-slate-600 dark:text-slate-400">
511
+ {searchQuery ? 'Try adjusting your search criteria' : 'Start a new chat session to see it here'}
512
+ </p>
513
+ </div>
514
+ </div>
515
+ {:else}
516
+ <div class="space-y-4">
517
+ {#each filteredSessions as session (session.id)}
518
+ <div class="bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-lg p-6 transition-all duration-200">
519
+ <div class="flex flex-col lg:flex-row lg:items-center gap-6">
520
+ <!-- Session Info -->
521
+ <div class="flex-1 space-y-2">
522
+ <!-- Title and Badges -->
523
+ <div class="flex flex-wrap items-center gap-2">
524
+ <h3 class="font-bold text-violet-700 dark:text-violet-500">
525
+ {getSessionTitle(session.id)}
526
+ </h3>
527
+ {#if isActiveSession(session)}
528
+ <span class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 border border-green-200 dark:border-green-700">
529
+ <Icon name="lucide:circle-check" class="w-3 h-3" />
530
+ Active
531
+ </span>
532
+ {/if}
533
+ <span class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-full bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300">
534
+ <Icon name="lucide:clock" class="w-3 h-3" />
535
+ {getLastActive(session)}
536
+ </span>
537
+ </div>
538
+
539
+ <!-- Metadata - Responsive Flex Wrap -->
540
+ <div class="flex flex-wrap gap-x-3 sm:gap-x-4 gap-y-1 text-xs sm:text-sm text-slate-600 dark:text-slate-400">
541
+ <span class="font-semibold text-slate-600 dark:text-slate-400">
542
+ {getProjectName(session.project_id)}
543
+ </span>
544
+
545
+ <span class="flex items-center gap-1">
546
+ <Icon name="lucide:calendar" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
547
+ {formatDate(session.started_at)}
548
+ </span>
549
+ <span class="flex items-center gap-1" title="Chat exchanges">
550
+ <Icon name="lucide:messages-square" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
551
+ {getUserMessageCount(session.id)} chats
552
+ </span>
553
+ <span class="flex items-center gap-1" title="Total message bubbles">
554
+ <Icon name="lucide:message-circle" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
555
+ {getMessageCount(session.id)} msgs
556
+ </span>
557
+ <span class="flex items-center gap-1">
558
+ <Icon name="lucide:timer" class="w-3 h-3 sm:w-3.5 sm:h-3.5" />
559
+ {formatDuration(getSessionDuration(session))}
560
+ </span>
561
+ </div>
562
+
563
+ <!-- Summary -->
564
+ <p class="text-sm text-slate-600 dark:text-slate-400 line-clamp-2">
565
+ {getSessionSummary(session.id)}
566
+ </p>
567
+
568
+ </div>
569
+
570
+ <!-- Modern Actions -->
571
+ <div class="flex flex-wrap sm:flex-nowrap items-center gap-2 sm:gap-3 w-full lg:w-auto">
572
+ <button
573
+ onclick={() => openTimelineModal(session)}
574
+ class="flex-1 sm:flex-initial px-3 sm:px-4 py-2 bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-700 text-purple-700 dark:text-purple-300 rounded-lg hover:bg-purple-100 dark:hover:bg-purple-900/30 transition-all duration-200 flex items-center justify-center gap-1.5 sm:gap-2 text-xs sm:text-sm"
575
+ title="View checkpoint timeline"
576
+ >
577
+ <Icon name="lucide:git-branch" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
578
+ <span>Timeline</span>
579
+ </button>
580
+
581
+ <button
582
+ onclick={() => resumeSession(session)}
583
+ class="flex-1 sm:flex-initial px-3 sm:px-4 py-2 bg-violet-600 dark:bg-violet-600 border border-violet-600 dark:border-violet-600 text-white rounded-lg hover:bg-violet-700 dark:hover:bg-violet-700 hover:border-violet-700 dark:hover:border-violet-700 transition-all duration-200 flex items-center justify-center gap-1.5 sm:gap-2 text-xs sm:text-sm"
584
+ >
585
+ <Icon name="lucide:message-circle-code" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
586
+ <span>Resume</span>
587
+ </button>
588
+
589
+ <button
590
+ onclick={() => deleteSession(session)}
591
+ class="flex p-2.5 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-600 dark:text-slate-400 hover:text-red-600 dark:hover:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/30 hover:border-red-200 dark:hover:border-red-700 rounded-lg transition-all duration-200"
592
+ title="Delete session"
593
+ aria-label="Delete session"
594
+ >
595
+ <Icon name="lucide:trash-2" class="w-3.5 h-3.5 sm:w-4 sm:h-4" />
596
+ </button>
597
+ </div>
598
+ </div>
599
+ </div>
600
+ {/each}
601
+ </div>
602
+ {/if}
603
+ </div>
604
+ </div>
605
+ </PageTemplate>
606
+
607
+ <!-- Timeline Modal (Readonly) -->
608
+ {#if timelineSession}
609
+ <TimelineModal
610
+ bind:isOpen={showTimelineModal}
611
+ onClose={closeTimelineModal}
612
+ sessionIdOverride={timelineSession.id}
613
+ readonly={true}
614
+ />
615
+ {/if}
@@ -0,0 +1,34 @@
1
+ // Component exports
2
+ export { default as ThemeToggle } from './common/ThemeToggle.svelte';
3
+ export { default as LoadingSpinner } from './common/LoadingSpinner.svelte';
4
+ export { default as Button } from './common/Button.svelte';
5
+ export { default as Input } from './common/Input.svelte';
6
+ export { default as Card } from './common/Card.svelte';
7
+ export { default as ModelSelector } from './common/ModelSelector.svelte';
8
+ export { default as MonacoEditor } from './common/MonacoEditor.svelte';
9
+ export { default as PageTemplate } from './common/PageTemplate.svelte';
10
+ export { default as Modal } from './common/Modal.svelte';
11
+
12
+ // Chat components
13
+ export { default as ChatMessages } from './chat/message/ChatMessages.svelte';
14
+ export { default as ChatInterface } from './chat/ChatInterface.svelte';
15
+ export { default as ChatMessage } from './chat/message/ChatMessage.svelte';
16
+ export { default as ChatInput } from './chat/input/ChatInput.svelte';
17
+ export { default as DateSeparator } from './chat/message/DateSeparator.svelte';
18
+
19
+ // Tool Display components - exported for potential external use
20
+ export * from './chat/tools';
21
+
22
+ // File components
23
+ export { default as FileTree } from './files/FileTree.svelte';
24
+ export { default as FileNode } from './files/FileNode.svelte';
25
+ export { default as FileViewer } from './files/FileViewer.svelte';
26
+
27
+ // Terminal components
28
+ export { default as TerminalView } from './terminal/TerminalView.svelte';
29
+ export { default as Terminal } from './terminal/Terminal.svelte';
30
+ export { default as TerminalTabs } from './terminal/TerminalTabs.svelte';
31
+
32
+ // View components
33
+ export { default as HistoryView } from './history/HistoryView.svelte';
34
+ export { default as SettingsView } from './settings/SettingsView.svelte';