@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,391 @@
1
+ <script lang="ts">
2
+ import Modal from '$frontend/lib/components/common/Modal.svelte';
3
+ import Dialog from '$frontend/lib/components/common/Dialog.svelte';
4
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
5
+ import TimelineGraph from './timeline/TimelineGraph.svelte';
6
+ import { sessionState, loadMessagesForSession } from '$frontend/lib/stores/core/sessions.svelte';
7
+ import { appState } from '$frontend/lib/stores/core/app.svelte';
8
+ import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
9
+ import { debug } from '$shared/utils/logger';
10
+ import { buildGraph } from './timeline/graph-builder';
11
+ import { startAnimation } from './timeline/animation';
12
+ import { snapshotService } from '$frontend/lib/services/snapshot/snapshot.service';
13
+ import type { TimelineResponse, GraphNode, GraphEdge, VersionGroup, AnimationState } from './timeline/types';
14
+
15
+ let {
16
+ isOpen = $bindable(false),
17
+ onClose = () => {},
18
+ sessionIdOverride = undefined,
19
+ readonly = false
20
+ }: {
21
+ isOpen: boolean;
22
+ onClose: () => void;
23
+ sessionIdOverride?: string;
24
+ readonly?: boolean;
25
+ } = $props();
26
+
27
+ let timelineData = $state<TimelineResponse | null>(null);
28
+ let loading = $state(false);
29
+ let processingAction = $state(false);
30
+
31
+ // Confirmation dialog state
32
+ let showConfirmDialog = $state(false);
33
+ let pendingNode = $state<GraphNode | null>(null);
34
+
35
+ // Graph visualization state
36
+ let graphNodes = $state<GraphNode[]>([]);
37
+ let graphEdges = $state<GraphEdge[]>([]);
38
+ let versionGroups = $state<VersionGroup[]>([]);
39
+ let svgWidth = $state(800);
40
+ let svgHeight = $state(600);
41
+
42
+ // Animation state
43
+ const animationState = $state<AnimationState>({
44
+ isAnimating: false,
45
+ restoringNodeId: null,
46
+ progress: 0,
47
+ oldNodePositions: new Map(),
48
+ oldNodeStyles: new Map(),
49
+ oldVersionGroups: []
50
+ });
51
+
52
+ // Scroll container reference
53
+ let scrollContainer = $state<HTMLElement | undefined>();
54
+ let isContentReady = $state(false);
55
+ let hasScrolledToBottom = $state(false);
56
+
57
+ // Track message count for realtime updates
58
+ let previousMessageCount = $state(0);
59
+ let isInitialLoad = $state(true);
60
+
61
+ // Track if modal was opened (for detecting modal open event)
62
+ let wasOpen = $state(false);
63
+
64
+ // Get the session ID to use (override or current)
65
+ const sessionId = $derived(sessionIdOverride || sessionState.currentSession?.id);
66
+
67
+ // Load timeline data when modal opens
68
+ $effect(() => {
69
+ // Detect modal opening (transition from closed to open)
70
+ if (isOpen && !wasOpen && sessionId) {
71
+ isContentReady = false;
72
+ hasScrolledToBottom = false;
73
+ isInitialLoad = true;
74
+ previousMessageCount = sessionState.messages.length;
75
+ loadTimeline();
76
+ wasOpen = true;
77
+ } else if (!isOpen && wasOpen) {
78
+ hasScrolledToBottom = false;
79
+ isInitialLoad = true;
80
+ wasOpen = false;
81
+ }
82
+ });
83
+
84
+ // Realtime updates: reload timeline when messages change
85
+ $effect(() => {
86
+ const currentMessageCount = sessionState.messages.length;
87
+
88
+ if (
89
+ !readonly &&
90
+ isOpen &&
91
+ currentMessageCount !== previousMessageCount &&
92
+ previousMessageCount > 0 &&
93
+ !processingAction &&
94
+ !animationState.isAnimating &&
95
+ !loading
96
+ ) {
97
+ previousMessageCount = currentMessageCount;
98
+ loadTimeline();
99
+ }
100
+ });
101
+
102
+ // Scroll to bottom on initial load
103
+ $effect(() => {
104
+ if (timelineData && graphNodes.length > 0 && scrollContainer && !hasScrolledToBottom) {
105
+ setTimeout(() => {
106
+ if (scrollContainer && !hasScrolledToBottom) {
107
+ scrollContainer.scrollTop = scrollContainer.scrollHeight;
108
+ hasScrolledToBottom = true;
109
+ requestAnimationFrame(() => {
110
+ isContentReady = true;
111
+ });
112
+ }
113
+ }, 250);
114
+ } else if (timelineData && graphNodes.length > 0 && hasScrolledToBottom && !isContentReady) {
115
+ isContentReady = true;
116
+ }
117
+ });
118
+
119
+ async function loadTimeline() {
120
+ const showLoading = isInitialLoad;
121
+
122
+ if (showLoading) {
123
+ loading = true;
124
+ }
125
+
126
+ try {
127
+ if (!sessionId) {
128
+ throw new Error('No session ID available');
129
+ }
130
+
131
+ timelineData = await snapshotService.getTimeline(sessionId);
132
+ rebuildGraph();
133
+ } catch (error) {
134
+ debug.error('snapshot', 'Error loading timeline:', error);
135
+ addNotification({
136
+ type: 'error',
137
+ title: 'Timeline Error',
138
+ message: error instanceof Error ? error.message : 'Failed to load checkpoint timeline',
139
+ duration: 5000
140
+ });
141
+ } finally {
142
+ if (showLoading) {
143
+ loading = false;
144
+ isInitialLoad = false;
145
+ }
146
+ }
147
+ }
148
+
149
+ function rebuildGraph(captureOldState = false) {
150
+ if (!timelineData) return;
151
+
152
+ // Capture old state for animation (FLIP technique)
153
+ if (captureOldState && !animationState.isAnimating) {
154
+ animationState.oldNodePositions = new Map(
155
+ graphNodes.map(n => [n.id, { x: n.x, y: n.y }])
156
+ );
157
+ animationState.oldNodeStyles = new Map(
158
+ graphNodes.map(n => [n.id, { type: n.type }])
159
+ );
160
+ animationState.oldVersionGroups = [...versionGroups];
161
+ }
162
+
163
+ const graphData = buildGraph(timelineData);
164
+ graphNodes = graphData.nodes;
165
+ graphEdges = graphData.edges;
166
+ versionGroups = graphData.versionGroups;
167
+ svgWidth = graphData.svgWidth;
168
+ svgHeight = graphData.svgHeight;
169
+ }
170
+
171
+ // Handle node click - show confirmation dialog
172
+ function handleNodeClick(node: GraphNode) {
173
+ if (readonly) return;
174
+ if (processingAction || node.isCurrent || animationState.isAnimating || appState.isLoading) return;
175
+
176
+ pendingNode = node;
177
+ showConfirmDialog = true;
178
+ }
179
+
180
+ // Perform restore checkpoint after confirmation
181
+ async function confirmRestore() {
182
+ if (!pendingNode) return;
183
+
184
+ const node = pendingNode;
185
+ processingAction = true;
186
+
187
+ // Capture current state for animation
188
+ rebuildGraph(true);
189
+
190
+ // Update current node immediately to prevent flicker
191
+ const previousCurrentId = timelineData?.currentHeadId;
192
+ if (timelineData) {
193
+ timelineData.currentHeadId = node.id;
194
+ graphNodes = graphNodes.map(n => ({
195
+ ...n,
196
+ isCurrent: n.id === node.id
197
+ }));
198
+ }
199
+
200
+ try {
201
+ if (!sessionState.currentSession?.id) {
202
+ throw new Error('No active session');
203
+ }
204
+
205
+ // Unified restore - works for all nodes
206
+ await snapshotService.restore(
207
+ node.checkpoint.messageId,
208
+ sessionState.currentSession.id
209
+ );
210
+
211
+ // Reload messages
212
+ await loadMessagesForSession(sessionState.currentSession.id);
213
+
214
+ // Reload timeline
215
+ const newTimelineData = await snapshotService.getTimeline(sessionState.currentSession.id);
216
+
217
+ // Check if structure changed
218
+ if (JSON.stringify(newTimelineData.nodes) !== JSON.stringify(timelineData?.nodes)) {
219
+ timelineData = newTimelineData;
220
+ rebuildGraph();
221
+
222
+ // Start animation
223
+ animationState.isAnimating = true;
224
+ animationState.restoringNodeId = node.id;
225
+
226
+ startAnimation(
227
+ node.id,
228
+ (progress) => {
229
+ animationState.progress = progress;
230
+ },
231
+ () => {
232
+ animationState.isAnimating = false;
233
+ animationState.restoringNodeId = null;
234
+ animationState.progress = 0;
235
+ animationState.oldNodePositions.clear();
236
+ animationState.oldNodeStyles.clear();
237
+ animationState.oldVersionGroups = [];
238
+ }
239
+ );
240
+ } else {
241
+ if (timelineData) {
242
+ timelineData.currentHeadId = newTimelineData.currentHeadId;
243
+ }
244
+ rebuildGraph();
245
+ }
246
+ } catch (error) {
247
+ // Revert optimistic update on error
248
+ if (timelineData && previousCurrentId) {
249
+ timelineData.currentHeadId = previousCurrentId;
250
+ graphNodes = graphNodes.map(n => ({
251
+ ...n,
252
+ isCurrent: n.id === previousCurrentId
253
+ }));
254
+ }
255
+
256
+ debug.error('snapshot', 'Restore error:', error);
257
+ addNotification({
258
+ type: 'error',
259
+ title: 'Restore Failed',
260
+ message: error instanceof Error ? error.message : 'Unknown error',
261
+ duration: 5000
262
+ });
263
+ } finally {
264
+ processingAction = false;
265
+ pendingNode = null;
266
+ }
267
+ }
268
+
269
+ function getTruncatedMessage(text: string, maxLength: number = 85): string {
270
+ if (text.length <= maxLength) return text;
271
+ return text.substring(0, maxLength) + '...';
272
+ }
273
+ </script>
274
+
275
+ <Modal bind:isOpen={isOpen} bind:contentRef={scrollContainer} size="lg" onClose={onClose}>
276
+ {#snippet header()}
277
+ <div class="flex items-center justify-between px-4 py-3 md:px-6 md:py-4">
278
+ <div class="flex items-center gap-3">
279
+ <h2 id="modal-title" class="text-base md:text-lg font-bold text-slate-900 dark:text-slate-100">
280
+ Restore Checkpoint
281
+ </h2>
282
+ {#if readonly}
283
+ <div class="flex items-center gap-2 px-3 py-1.5 bg-purple-50 dark:bg-purple-900/20 rounded-lg">
284
+ <Icon name="lucide:eye" class="w-3 h-3 text-purple-600 dark:text-purple-400" />
285
+ <span class="text-xs text-purple-600 dark:text-purple-400">View Only</span>
286
+ </div>
287
+ {:else if processingAction}
288
+ <div class="flex items-center gap-2 px-3 py-1.5 bg-violet-50 dark:bg-violet-900/20 rounded-lg">
289
+ <div class="animate-spin rounded-full h-3 w-3 border-b-2 border-violet-600"></div>
290
+ <span class="text-xs text-violet-600 dark:text-violet-400">Restoring...</span>
291
+ </div>
292
+ {:else if appState.isLoading}
293
+ <div class="flex items-center gap-2 px-3 py-1.5 bg-amber-50 dark:bg-amber-900/20 rounded-lg">
294
+ <Icon name="lucide:loader" class="w-3 h-3 text-amber-600 dark:text-amber-400 animate-spin" />
295
+ <span class="text-xs text-amber-600 dark:text-amber-400">Chat in progress...</span>
296
+ </div>
297
+ {/if}
298
+ </div>
299
+
300
+ <button
301
+ type="button"
302
+ class="p-1.5 md:p-2 rounded-lg text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
303
+ onclick={onClose}
304
+ aria-label="Close modal"
305
+ >
306
+ <svg class="w-4 h-4 md:w-5 md:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
307
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
308
+ </svg>
309
+ </button>
310
+ </div>
311
+ {/snippet}
312
+
313
+ {#snippet children()}
314
+ {#if loading}
315
+ <div class="flex items-center justify-center py-12">
316
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-violet-600"></div>
317
+ <span class="ml-3 text-slate-600 dark:text-slate-400">Loading timeline...</span>
318
+ </div>
319
+ {:else if timelineData && graphNodes.length > 0}
320
+ <div class="transition-opacity duration-200" style="opacity: {isContentReady ? 1 : 0}">
321
+ <!-- SVG Timeline Graph -->
322
+ <TimelineGraph
323
+ {graphNodes}
324
+ {graphEdges}
325
+ {versionGroups}
326
+ {svgWidth}
327
+ {svgHeight}
328
+ {animationState}
329
+ {readonly}
330
+ isDisabled={appState.isLoading}
331
+ onNodeClick={handleNodeClick}
332
+ />
333
+
334
+ <!-- Legend -->
335
+ <!-- <div class="mt-4 p-4 bg-white dark:bg-slate-800 rounded-lg border border-slate-200 dark:border-slate-700 flex flex-wrap items-center gap-6 text-xs">
336
+ <div class="flex items-center gap-2">
337
+ <div class="w-3 h-3 rounded-full bg-green-500 ring-2 ring-green-300 dark:ring-green-700"></div>
338
+ <span class="text-slate-600 dark:text-slate-400">Current Position</span>
339
+ </div>
340
+ <div class="flex items-center gap-2">
341
+ <div class="w-3 h-3 rounded-full bg-blue-500"></div>
342
+ <span class="text-slate-600 dark:text-slate-400">Active Path</span>
343
+ </div>
344
+ <div class="flex items-center gap-2">
345
+ <div class="w-3 h-3 rounded-full bg-slate-300 dark:bg-slate-600 opacity-60"></div>
346
+ <span class="text-slate-600 dark:text-slate-400">Orphaned</span>
347
+ </div>
348
+ </div> -->
349
+ </div>
350
+ {:else if timelineData && graphNodes.length === 0}
351
+ <div class="text-center py-12">
352
+ <Icon
353
+ name="lucide:git-branch"
354
+ class="w-12 h-12 text-slate-300 dark:text-slate-600 mx-auto mb-3"
355
+ />
356
+ <p class="text-slate-600 dark:text-slate-400">No checkpoints yet</p>
357
+ <p class="text-sm text-slate-500 dark:text-slate-500 mt-1">
358
+ Checkpoints are created automatically with each message
359
+ </p>
360
+ </div>
361
+ {:else}
362
+ <div class="text-center py-12">
363
+ <Icon
364
+ name="lucide:circle-x"
365
+ class="w-12 h-12 text-slate-300 dark:text-slate-600 mx-auto mb-3"
366
+ />
367
+ <p class="text-slate-600 dark:text-slate-400">Failed to load timeline</p>
368
+ </div>
369
+ {/if}
370
+ {/snippet}
371
+ </Modal>
372
+
373
+ <!-- Confirmation Dialog -->
374
+ <Dialog
375
+ bind:isOpen={showConfirmDialog}
376
+ type="warning"
377
+ title="Restore Checkpoint"
378
+ message={pendingNode
379
+ ? `Are you sure you want to restore to this checkpoint?
380
+ "${getTruncatedMessage(pendingNode.checkpoint.messageText)}"
381
+ This will restore your conversation and files to this point.`
382
+ : ''}
383
+ confirmText="Restore"
384
+ cancelText="Cancel"
385
+ showCancel={true}
386
+ onConfirm={confirmRestore}
387
+ onClose={() => {
388
+ showConfirmDialog = false;
389
+ pendingNode = null;
390
+ }}
391
+ />
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ import { SIZE, STYLE } from './config';
3
+ import type { GraphEdge, GraphNode, AnimationState } from './types';
4
+ import { getInterpolatedEdgePath } from './animation';
5
+
6
+ const {
7
+ edge,
8
+ graphNodes,
9
+ animationState
10
+ }: {
11
+ edge: GraphEdge;
12
+ graphNodes: GraphNode[];
13
+ animationState: AnimationState;
14
+ } = $props();
15
+
16
+ const interpolatedPath = $derived(getInterpolatedEdgePath(edge, graphNodes, animationState));
17
+ </script>
18
+
19
+ <path
20
+ d={interpolatedPath}
21
+ fill="none"
22
+ stroke={STYLE.lineColor}
23
+ stroke-width={SIZE.line}
24
+ stroke-dasharray={edge.type === 'branch' ? STYLE.lineDash : '0'}
25
+ opacity={STYLE.lineOpacity}
26
+ />
@@ -0,0 +1,87 @@
1
+ <script lang="ts">
2
+ import { getSortedNodes } from './utils';
3
+ import TimelineNode from './TimelineNode.svelte';
4
+ import TimelineEdge from './TimelineEdge.svelte';
5
+ import TimelineVersionGroup from './TimelineVersionGroup.svelte';
6
+ import type { GraphNode, GraphEdge, VersionGroup, AnimationState } from './types';
7
+
8
+ const {
9
+ graphNodes,
10
+ graphEdges,
11
+ versionGroups,
12
+ svgWidth,
13
+ svgHeight,
14
+ animationState,
15
+ isDisabled,
16
+ readonly,
17
+ onNodeClick
18
+ }: {
19
+ graphNodes: GraphNode[];
20
+ graphEdges: GraphEdge[];
21
+ versionGroups: VersionGroup[];
22
+ svgWidth: number;
23
+ svgHeight: number;
24
+ animationState: AnimationState;
25
+ isDisabled: boolean;
26
+ readonly: boolean;
27
+ onNodeClick: (node: GraphNode) => void;
28
+ } = $props();
29
+
30
+ const sortedNodes = $derived(getSortedNodes(graphNodes, animationState.isAnimating, animationState.restoringNodeId));
31
+ </script>
32
+
33
+ <div>
34
+ <svg
35
+ width={svgWidth}
36
+ height={svgHeight}
37
+ >
38
+ <!-- Define gradients and filters -->
39
+ <defs>
40
+ <linearGradient id="mainLineGradient" x1="0%" y1="0%" x2="0%" y2="100%">
41
+ <stop offset="0%" style="stop-color:#94a3b8" />
42
+ <stop offset="100%" style="stop-color:#94a3b8" />
43
+ </linearGradient>
44
+ <linearGradient id="branchLineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
45
+ <stop offset="0%" style="stop-color:#a78bfa" />
46
+ <stop offset="100%" style="stop-color:#a78bfa" />
47
+ </linearGradient>
48
+ <filter id="nodeShadow">
49
+ <feDropShadow dx="0" dy="1" stdDeviation="1" flood-opacity="0.1"/>
50
+ </filter>
51
+ </defs>
52
+
53
+ <!-- Draw version group brackets (right side) -->
54
+ <!-- <g id="version-groups">
55
+ {#each versionGroups as group (`group-${group.branchName}`)}
56
+ <TimelineVersionGroup
57
+ {group}
58
+ {animationState}
59
+ />
60
+ {/each}
61
+ </g> -->
62
+
63
+ <!-- Draw edges (lines) -->
64
+ <g id="edges">
65
+ {#each graphEdges as edge (`edge-${edge.from}-${edge.to}`)}
66
+ <TimelineEdge
67
+ {edge}
68
+ {graphNodes}
69
+ {animationState}
70
+ />
71
+ {/each}
72
+ </g>
73
+
74
+ <!-- Draw nodes on top -->
75
+ <g id="nodes">
76
+ {#each sortedNodes as node (node.id)}
77
+ <TimelineNode
78
+ {node}
79
+ {animationState}
80
+ {isDisabled}
81
+ {readonly}
82
+ {onNodeClick}
83
+ />
84
+ {/each}
85
+ </g>
86
+ </svg>
87
+ </div>
@@ -0,0 +1,108 @@
1
+ <script lang="ts">
2
+ import { SIZE, SPACING } from './config';
3
+ import { formatTime } from './utils';
4
+ import type { GraphNode, AnimationState } from './types';
5
+ import { getInterpolatedPosition, getInterpolatedNodeClass } from './animation';
6
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
7
+
8
+ const {
9
+ node,
10
+ animationState,
11
+ isDisabled,
12
+ readonly,
13
+ onNodeClick
14
+ }: {
15
+ node: GraphNode;
16
+ animationState: AnimationState;
17
+ isDisabled: boolean;
18
+ readonly: boolean;
19
+ onNodeClick: (node: GraphNode) => void;
20
+ } = $props();
21
+
22
+ const pos = $derived(getInterpolatedPosition(node, animationState));
23
+ const nodeClass = $derived(getInterpolatedNodeClass(node));
24
+ </script>
25
+
26
+ <!-- Node group -->
27
+ <g
28
+ class="cursor-pointer group {isDisabled ? 'opacity-40' : ''} {readonly || isDisabled || animationState.isAnimating ? '!pointer-events-none' : 'pointer-events-auto'}"
29
+ onclick={() => onNodeClick(node)}
30
+ onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') onNodeClick(node); }}
31
+ role="button"
32
+ tabindex="0"
33
+ aria-label={`${node.type === 'main' ? 'Checkpoint' : 'Version'} - ${node.checkpoint.messageText}`}
34
+ >
35
+ <title>{node.checkpoint.messageText}</title>
36
+ <!-- Node circle -->
37
+ <circle
38
+ cx={pos.x}
39
+ cy={pos.y}
40
+ r={SIZE.node}
41
+ class={nodeClass}
42
+ stroke-width="1.5"
43
+ opacity={node.isOrphaned ? '0.6' : '1'}
44
+ filter="url(#nodeShadow)"
45
+ />
46
+
47
+ <!-- Current indicator ring -->
48
+ {#if node.isCurrent}
49
+ <circle
50
+ cx={pos.x}
51
+ cy={pos.y}
52
+ r={SIZE.node + SIZE.nodeRing}
53
+ fill="none"
54
+ stroke="currentColor"
55
+ stroke-width="1.5"
56
+ class="text-green-400 animate-pulse"
57
+ />
58
+ {/if}
59
+
60
+ <!-- Node inner dot -->
61
+ <circle
62
+ cx={pos.x}
63
+ cy={pos.y}
64
+ r={SIZE.nodeDot}
65
+ class="fill-white pointer-events-none"
66
+ />
67
+
68
+ <!-- Label container -->
69
+ <rect
70
+ x={pos.x + SIZE.node + SPACING.labelGap}
71
+ y={pos.y - (SIZE.labelHeight / 2)}
72
+ width={SIZE.labelWidth - 15}
73
+ height={SIZE.labelHeight}
74
+ rx="6"
75
+ class="fill-white dark:fill-slate-800 stroke-slate-200 dark:stroke-slate-700 group-hover:fill-slate-50 dark:group-hover:fill-slate-700 group-hover:stroke-slate-300 dark:group-hover:stroke-slate-600 transition-colors"
76
+ stroke-width="1"
77
+ />
78
+
79
+ <!-- Text content using foreignObject for CSS truncation -->
80
+ <foreignObject
81
+ x={pos.x + SIZE.node + 20}
82
+ y={pos.y - SIZE.labelHeight / 2 + 4}
83
+ width={SIZE.labelWidth - 35}
84
+ height={SIZE.labelHeight - 8}
85
+ >
86
+ <div class="flex flex-col h-full justify-center pointer-events-none">
87
+ <!-- Timestamp and file stats in one line -->
88
+ <div class="flex items-center gap-2 text-xs text-slate-500 dark:text-slate-400 leading-tight">
89
+ <span>{formatTime(node.checkpoint.timestamp)}</span>
90
+ <span class="w-px h-3 bg-slate-300 dark:bg-slate-600"></span>
91
+ <span class="flex items-center gap-0.5">
92
+ <Icon name="lucide:file-text" class="w-2.5 h-2.5" />
93
+ {node.checkpoint.filesChanged ?? 0}
94
+ </span>
95
+ <span class="text-green-600 dark:text-green-400">
96
+ +{node.checkpoint.insertions ?? 0}
97
+ </span>
98
+ <span class="text-red-600 dark:text-red-400">
99
+ -{node.checkpoint.deletions ?? 0}
100
+ </span>
101
+ </div>
102
+ <!-- Message text below timestamp with auto truncation -->
103
+ <div class="text-sm text-slate-900 dark:text-slate-100 leading-tight truncate mt-0.5">
104
+ {node.checkpoint.messageText}
105
+ </div>
106
+ </div>
107
+ </foreignObject>
108
+ </g>
@@ -0,0 +1,59 @@
1
+ <script lang="ts">
2
+ import { SIZE, SPACING } from './config';
3
+ import { getInterpolatedPosition } from './animation';
4
+ import type { VersionGroup, AnimationState } from './types';
5
+
6
+ const {
7
+ group,
8
+ animationState
9
+ }: {
10
+ group: VersionGroup;
11
+ animationState: AnimationState;
12
+ } = $props();
13
+
14
+ // Calculate bracket positions based on first and last node
15
+ const firstNodePos = $derived(getInterpolatedPosition(group.nodes[0], animationState));
16
+ const lastNodePos = $derived(getInterpolatedPosition(group.nodes[group.nodes.length - 1], animationState));
17
+
18
+ // Bracket positioning calculations
19
+ const bracketX = $derived(firstNodePos.x + SIZE.node + SIZE.labelWidth + SPACING.bracketGap);
20
+ const quarterHeight = $derived(SIZE.labelHeight / 4);
21
+ const topY = $derived(firstNodePos.y - quarterHeight);
22
+ const bottomY = $derived(lastNodePos.y + quarterHeight);
23
+ const bracketEndX = $derived(bracketX + SPACING.bracketLength);
24
+ </script>
25
+
26
+ <g class="version-group-bracket">
27
+ <!-- Top horizontal line -->
28
+ <line
29
+ x1={bracketX}
30
+ y1={topY}
31
+ x2={bracketEndX}
32
+ y2={topY}
33
+ stroke="#a78bfa"
34
+ stroke-width="1.5"
35
+ opacity="0.6"
36
+ />
37
+
38
+ <!-- Vertical line -->
39
+ <line
40
+ x1={bracketEndX}
41
+ y1={topY}
42
+ x2={bracketEndX}
43
+ y2={bottomY}
44
+ stroke="#a78bfa"
45
+ stroke-width="4"
46
+ opacity="0.6"
47
+ />
48
+
49
+ <!-- Bottom horizontal line -->
50
+ <line
51
+ x1={bracketX}
52
+ y1={bottomY}
53
+ x2={bracketEndX}
54
+ y2={bottomY}
55
+ stroke="#a78bfa"
56
+ stroke-width="1.5"
57
+ opacity="0.6"
58
+ />
59
+ </g>