@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,168 @@
1
+ /**
2
+ * Timeline animation utilities
3
+ * Handles FLIP animation technique and interpolation
4
+ */
5
+
6
+ import { ANIMATION, SIZE } from './config';
7
+ import type { GraphNode, GraphEdge, VersionGroup, AnimationState } from './types';
8
+
9
+ /**
10
+ * Cubic bezier easing function (ease-in-out)
11
+ */
12
+ export function cubicBezier(t: number): number {
13
+ // ease-in-out: cubic-bezier(0.42, 0, 0.58, 1)
14
+ const x1 = 0.42, x2 = 0.58;
15
+
16
+ const c = 3 * x1;
17
+ const b = 3 * (x2 - x1) - c;
18
+ const a = 1 - c - b;
19
+
20
+ const t2 = t * t;
21
+ const t3 = t2 * t;
22
+
23
+ return a * t3 + b * t2 + c * t;
24
+ }
25
+
26
+ /**
27
+ * Get interpolated position for a node
28
+ */
29
+ export function getInterpolatedPosition(
30
+ node: GraphNode,
31
+ animationState: AnimationState
32
+ ): { x: number, y: number } {
33
+ if (!animationState.isAnimating) {
34
+ return { x: node.x, y: node.y };
35
+ }
36
+
37
+ const oldPos = animationState.oldNodePositions.get(node.id);
38
+ if (!oldPos) {
39
+ // Node is new, just use current position
40
+ return { x: node.x, y: node.y };
41
+ }
42
+
43
+ // Interpolate between old and new position
44
+ const x = oldPos.x + (node.x - oldPos.x) * animationState.progress;
45
+ const y = oldPos.y + (node.y - oldPos.y) * animationState.progress;
46
+
47
+ return { x, y };
48
+ }
49
+
50
+ /**
51
+ * Get interpolated color for a node
52
+ */
53
+ export function getInterpolatedNodeClass(node: GraphNode): string {
54
+ if (node.isCurrent) {
55
+ return 'fill-green-500 stroke-green-300';
56
+ }
57
+
58
+ // Orphaned nodes (descendants of current active checkpoint) are always gray
59
+ if (node.isOrphaned) {
60
+ return 'fill-slate-300 stroke-slate-200 dark:fill-slate-600 dark:stroke-slate-500';
61
+ }
62
+
63
+ // All non-orphaned nodes use blue (Active Path)
64
+ return 'fill-blue-500 stroke-blue-300';
65
+ }
66
+
67
+ /**
68
+ * Get interpolated path for an edge
69
+ * Recalculates path using interpolated node positions for smooth transitions
70
+ */
71
+ export function getInterpolatedEdgePath(
72
+ edge: GraphEdge,
73
+ graphNodes: GraphNode[],
74
+ animationState: AnimationState
75
+ ): string {
76
+ if (!animationState.isAnimating) {
77
+ return edge.path;
78
+ }
79
+
80
+ // Find the from and to nodes
81
+ const fromNode = graphNodes.find(n => n.id === edge.from);
82
+ const toNode = graphNodes.find(n => n.id === edge.to);
83
+
84
+ if (!fromNode || !toNode) {
85
+ return edge.path;
86
+ }
87
+
88
+ // Get interpolated positions
89
+ const fromPos = getInterpolatedPosition(fromNode, animationState);
90
+ const toPos = getInterpolatedPosition(toNode, animationState);
91
+
92
+ // Recalculate path with interpolated positions
93
+ if (edge.type === 'straight') {
94
+ // Straight vertical line
95
+ return `M ${fromPos.x} ${fromPos.y + SIZE.node} L ${toPos.x} ${toPos.y - SIZE.node}`;
96
+ } else if (edge.type === 'branch') {
97
+ // Horizontal line from main vertical line to branch
98
+ const startX = fromPos.x + 1;
99
+ const endX = toPos.x - SIZE.node;
100
+ const lineY = toPos.y;
101
+ return `M ${startX} ${lineY} L ${endX} ${lineY}`;
102
+ }
103
+
104
+ return edge.path;
105
+ }
106
+
107
+ /**
108
+ * Get interpolated bracket position for version group
109
+ */
110
+ export function getInterpolatedBracket(
111
+ group: VersionGroup,
112
+ animationState: AnimationState
113
+ ): { minY: number, maxY: number, height: number } {
114
+ if (!animationState.isAnimating) {
115
+ return {
116
+ minY: group.minY,
117
+ maxY: group.maxY,
118
+ height: group.height
119
+ };
120
+ }
121
+
122
+ // Find old group with same branch name
123
+ const oldGroup = animationState.oldVersionGroups.find(g => g.branchName === group.branchName);
124
+ if (!oldGroup) {
125
+ // New group, just use current values
126
+ return {
127
+ minY: group.minY,
128
+ maxY: group.maxY,
129
+ height: group.height
130
+ };
131
+ }
132
+
133
+ // Interpolate bracket position
134
+ const minY = oldGroup.minY + (group.minY - oldGroup.minY) * animationState.progress;
135
+ const maxY = oldGroup.maxY + (group.maxY - oldGroup.maxY) * animationState.progress;
136
+ const height = oldGroup.height + (group.height - oldGroup.height) * animationState.progress;
137
+
138
+ return { minY, maxY, height };
139
+ }
140
+
141
+ /**
142
+ * Start animation loop
143
+ */
144
+ export function startAnimation(
145
+ targetNodeId: string,
146
+ onProgressUpdate: (progress: number) => void,
147
+ onComplete: () => void
148
+ ): void {
149
+ const startTime = performance.now();
150
+
151
+ function animate(currentTime: number) {
152
+ const elapsed = currentTime - startTime;
153
+ const rawProgress = Math.min(elapsed / ANIMATION.duration, 1);
154
+
155
+ // Apply easing
156
+ const easedProgress = cubicBezier(rawProgress);
157
+ onProgressUpdate(easedProgress);
158
+
159
+ if (rawProgress < 1) {
160
+ requestAnimationFrame(animate);
161
+ } else {
162
+ // Animation complete
163
+ onComplete();
164
+ }
165
+ }
166
+
167
+ requestAnimationFrame(animate);
168
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Timeline visualization configuration
3
+ * Contains all constants for spacing, sizing, layout, style, and animation
4
+ */
5
+
6
+ // Spacing (distances between elements)
7
+ export const SPACING = {
8
+ nodeGap: 52, // Vertical gap between nodes in same timeline
9
+ branchGap: 0, // Gap between different branches from same point
10
+ checkpointToBranch: 52, // Gap from checkpoint to first branch
11
+ branchToCheckpoint: 52, // Gap from last branch to next checkpoint
12
+ branchIndent: 32, // Horizontal indent for branches
13
+ labelGap: 12, // Gap between node and label
14
+ bracketGap: 10, // Gap from label to bracket
15
+ bracketLength: 15 // Length of bracket lines
16
+ };
17
+
18
+ // Size (element dimensions)
19
+ export const SIZE = {
20
+ node: 6, // Node radius
21
+ nodeDot: 2, // Inner dot radius
22
+ nodeRing: 3, // Current node ring radius
23
+ line: 2, // Line thickness
24
+ labelWidth: 280, // Label width
25
+ labelHeight: 44 // Label height
26
+ };
27
+
28
+ // Layout (starting positions)
29
+ export const LAYOUT = {
30
+ startX: 10, // Starting X position
31
+ startY: 30 // Starting Y position
32
+ };
33
+
34
+ // Style (colors & opacity)
35
+ export const STYLE = {
36
+ lineColor: '#94a3b8',
37
+ lineOpacity: 0.4,
38
+ lineDash: '0' // Dash pattern for branch lines
39
+ };
40
+
41
+ // Animation configuration
42
+ export const ANIMATION = {
43
+ duration: 300 // ms
44
+ };
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Timeline Graph Builder (Rewritten)
3
+ *
4
+ * Builds visual tree from checkpoint nodes with parent-child relationships.
5
+ * Uses activeChildId to determine which child continues straight (main line)
6
+ * and which children become branches (indented).
7
+ */
8
+
9
+ import { SPACING, SIZE, LAYOUT } from './config';
10
+ import type { TimelineResponse, GraphNode, GraphEdge, VersionGroup, CheckpointNode } from './types';
11
+ import { debug } from '$shared/utils/logger';
12
+
13
+ export interface GraphData {
14
+ nodes: GraphNode[];
15
+ edges: GraphEdge[];
16
+ versionGroups: VersionGroup[];
17
+ svgWidth: number;
18
+ svgHeight: number;
19
+ }
20
+
21
+ interface TreeNode {
22
+ checkpoint: CheckpointNode;
23
+ children: TreeNode[];
24
+ activeChild: TreeNode | null; // the child that continues straight
25
+ branchChildren: TreeNode[]; // children that are branches (indented)
26
+ }
27
+
28
+ /**
29
+ * Build a tree structure from flat checkpoint nodes
30
+ */
31
+ function buildTree(nodes: CheckpointNode[]): TreeNode[] {
32
+ const nodeMap = new Map<string, TreeNode>();
33
+
34
+ // Create TreeNode for each checkpoint
35
+ for (const cp of nodes) {
36
+ nodeMap.set(cp.id, {
37
+ checkpoint: cp,
38
+ children: [],
39
+ activeChild: null,
40
+ branchChildren: []
41
+ });
42
+ }
43
+
44
+ // Build parent-child relationships
45
+ const roots: TreeNode[] = [];
46
+ for (const cp of nodes) {
47
+ const treeNode = nodeMap.get(cp.id)!;
48
+
49
+ if (cp.parentId && nodeMap.has(cp.parentId)) {
50
+ const parent = nodeMap.get(cp.parentId)!;
51
+ parent.children.push(treeNode);
52
+ } else {
53
+ roots.push(treeNode);
54
+ }
55
+ }
56
+
57
+ // Determine active child vs branch children for each node
58
+ for (const cp of nodes) {
59
+ const treeNode = nodeMap.get(cp.id)!;
60
+ if (treeNode.children.length === 0) continue;
61
+
62
+ if (treeNode.children.length === 1) {
63
+ // Only one child - it's the active/straight continuation
64
+ treeNode.activeChild = treeNode.children[0];
65
+ treeNode.branchChildren = [];
66
+ } else {
67
+ // Multiple children - use activeChildId to determine which goes straight
68
+ const activeChildNode = cp.activeChildId
69
+ ? treeNode.children.find(c => c.checkpoint.id === cp.activeChildId)
70
+ : null;
71
+
72
+ if (activeChildNode) {
73
+ treeNode.activeChild = activeChildNode;
74
+ treeNode.branchChildren = treeNode.children.filter(c => c !== activeChildNode);
75
+ } else {
76
+ // No activeChildId set or not found.
77
+ // Use the child on the active path, or fallback to the first child
78
+ const activePathChild = treeNode.children.find(c => c.checkpoint.isOnActivePath);
79
+ if (activePathChild) {
80
+ treeNode.activeChild = activePathChild;
81
+ treeNode.branchChildren = treeNode.children.filter(c => c !== activePathChild);
82
+ } else {
83
+ // Fallback: first child by timestamp goes straight
84
+ const sorted = [...treeNode.children].sort(
85
+ (a, b) => a.checkpoint.timestamp.localeCompare(b.checkpoint.timestamp)
86
+ );
87
+ treeNode.activeChild = sorted[sorted.length - 1]; // last created goes straight
88
+ treeNode.branchChildren = sorted.slice(0, sorted.length - 1);
89
+ }
90
+ }
91
+
92
+ // Sort branch children: reverse creation order (newest first)
93
+ treeNode.branchChildren.sort(
94
+ (a, b) => b.checkpoint.timestamp.localeCompare(a.checkpoint.timestamp)
95
+ );
96
+ }
97
+ }
98
+
99
+ return roots;
100
+ }
101
+
102
+ /**
103
+ * Calculate the total height of a subtree (for spacing)
104
+ */
105
+ function calculateSubtreeHeight(node: TreeNode, depth: number): number {
106
+ let height = SPACING.nodeGap;
107
+
108
+ // Add height for branch children
109
+ for (const branch of node.branchChildren) {
110
+ height += calculateBranchHeight(branch, depth + 1);
111
+ height += SPACING.branchGap;
112
+ }
113
+
114
+ // Add height for active child's subtree
115
+ if (node.activeChild) {
116
+ height += calculateSubtreeHeight(node.activeChild, depth);
117
+ }
118
+
119
+ return height;
120
+ }
121
+
122
+ /**
123
+ * Calculate the height of a branch (including its own subtree)
124
+ */
125
+ function calculateBranchHeight(node: TreeNode, depth: number): number {
126
+ let height = SPACING.nodeGap; // This node itself
127
+
128
+ // Add height for this branch's sub-branches
129
+ for (const branch of node.branchChildren) {
130
+ height += calculateBranchHeight(branch, depth + 1);
131
+ height += SPACING.branchGap;
132
+ }
133
+
134
+ // Add height for active child continuation
135
+ if (node.activeChild) {
136
+ height += calculateBranchHeight(node.activeChild, depth);
137
+ }
138
+
139
+ return height;
140
+ }
141
+
142
+ /**
143
+ * Build graph visualization from timeline data
144
+ */
145
+ export function buildGraph(timelineData: TimelineResponse | null): GraphData {
146
+ if (!timelineData || timelineData.nodes.length === 0) {
147
+ return {
148
+ nodes: [],
149
+ edges: [],
150
+ versionGroups: [],
151
+ svgWidth: 800,
152
+ svgHeight: 100
153
+ };
154
+ }
155
+
156
+ debug.log('snapshot', `Graph builder: ${timelineData.nodes.length} nodes`);
157
+
158
+ // Build tree structure
159
+ const roots = buildTree(timelineData.nodes);
160
+
161
+ if (roots.length === 0) {
162
+ return { nodes: [], edges: [], versionGroups: [], svgWidth: 800, svgHeight: 100 };
163
+ }
164
+
165
+ const graphNodes: GraphNode[] = [];
166
+ const graphEdges: GraphEdge[] = [];
167
+
168
+ /**
169
+ * Recursively layout the tree.
170
+ * Returns the Y position after this subtree.
171
+ */
172
+ function layoutNode(
173
+ treeNode: TreeNode,
174
+ x: number,
175
+ startY: number,
176
+ parentId: string | null,
177
+ isMainLine: boolean,
178
+ isBranchEdge: boolean
179
+ ): number {
180
+ const nodeY = startY;
181
+
182
+ // Create graph node
183
+ const graphNode: GraphNode = {
184
+ id: treeNode.checkpoint.id,
185
+ checkpoint: treeNode.checkpoint,
186
+ x,
187
+ y: nodeY,
188
+ type: isMainLine ? 'main' : 'branch',
189
+ isCurrent: treeNode.checkpoint.isCurrent,
190
+ isOrphaned: treeNode.checkpoint.isOrphaned
191
+ };
192
+ graphNodes.push(graphNode);
193
+
194
+ // Create edge from parent
195
+ if (parentId) {
196
+ graphEdges.push({
197
+ from: parentId,
198
+ to: treeNode.checkpoint.id,
199
+ type: isBranchEdge ? 'branch' : 'straight',
200
+ path: '' // Will be calculated later
201
+ });
202
+ }
203
+
204
+ let currentY = nodeY;
205
+
206
+ // Layout branch children first (they go between this node and the active child)
207
+ for (let i = 0; i < treeNode.branchChildren.length; i++) {
208
+ const branch = treeNode.branchChildren[i];
209
+ const branchX = x + SPACING.branchIndent;
210
+ const branchStartY = currentY + SPACING.checkpointToBranch;
211
+
212
+ const branchEndY = layoutNode(
213
+ branch,
214
+ branchX,
215
+ branchStartY,
216
+ treeNode.checkpoint.id,
217
+ false, // not main line
218
+ true // is a branch edge
219
+ );
220
+
221
+ currentY = branchEndY;
222
+
223
+ // Add gap after branch
224
+ if (i < treeNode.branchChildren.length - 1) {
225
+ currentY += SPACING.branchGap;
226
+ }
227
+ }
228
+
229
+ // Layout active child (straight continuation)
230
+ if (treeNode.activeChild) {
231
+ // If we had branches, add transition spacing
232
+ if (treeNode.branchChildren.length > 0) {
233
+ currentY += SPACING.branchToCheckpoint;
234
+ } else {
235
+ currentY += SPACING.nodeGap;
236
+ }
237
+
238
+ currentY = layoutNode(
239
+ treeNode.activeChild,
240
+ x, // same X (straight continuation)
241
+ currentY,
242
+ treeNode.checkpoint.id,
243
+ isMainLine, // inherit main line status
244
+ false // straight edge
245
+ );
246
+ }
247
+
248
+ return currentY;
249
+ }
250
+
251
+ // Layout from root
252
+ let currentY = LAYOUT.startY;
253
+ for (let i = 0; i < roots.length; i++) {
254
+ currentY = layoutNode(
255
+ roots[i],
256
+ LAYOUT.startX,
257
+ currentY,
258
+ null,
259
+ true, // root is main line
260
+ false
261
+ );
262
+
263
+ if (i < roots.length - 1) {
264
+ currentY += SPACING.nodeGap;
265
+ }
266
+ }
267
+
268
+ // Calculate SVG paths for edges
269
+ for (const edge of graphEdges) {
270
+ const fromNode = graphNodes.find(n => n.id === edge.from);
271
+ const toNode = graphNodes.find(n => n.id === edge.to);
272
+
273
+ if (fromNode && toNode) {
274
+ if (edge.type === 'straight') {
275
+ // Vertical line
276
+ edge.path = `M ${fromNode.x} ${fromNode.y + SIZE.node} L ${toNode.x} ${toNode.y - SIZE.node}`;
277
+ } else if (edge.type === 'branch') {
278
+ // Horizontal line from main line to branch node
279
+ const startX = fromNode.x + 1;
280
+ const endX = toNode.x - SIZE.node;
281
+ const lineY = toNode.y;
282
+ edge.path = `M ${startX} ${lineY} L ${endX} ${lineY}`;
283
+ }
284
+ }
285
+ }
286
+
287
+ // Calculate SVG dimensions
288
+ const maxX = graphNodes.length > 0
289
+ ? Math.max(...graphNodes.map(n => n.x)) + SIZE.labelWidth + 30
290
+ : 800;
291
+ const maxY = graphNodes.length > 0
292
+ ? Math.max(...graphNodes.map(n => n.y)) + 50
293
+ : 100;
294
+
295
+ debug.log('snapshot', `Graph: ${graphNodes.length} nodes, ${graphEdges.length} edges, ${maxX}x${maxY}`);
296
+
297
+ return {
298
+ nodes: graphNodes,
299
+ edges: graphEdges,
300
+ versionGroups: [], // No longer using version groups
301
+ svgWidth: maxX,
302
+ svgHeight: maxY
303
+ };
304
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Timeline data structures and type definitions
3
+ */
4
+
5
+ export interface CheckpointNode {
6
+ id: string;
7
+ messageId: string;
8
+ parentId: string | null; // parent checkpoint ID in the tree
9
+ activeChildId: string | null; // which child continues straight
10
+ timestamp: string;
11
+ messageText: string;
12
+ isOnActivePath: boolean;
13
+ isOrphaned: boolean;
14
+ isCurrent: boolean;
15
+ hasSnapshot: boolean;
16
+ senderName?: string | null;
17
+ // File change statistics (git-like)
18
+ filesChanged?: number;
19
+ insertions?: number;
20
+ deletions?: number;
21
+ }
22
+
23
+ export interface TimelineResponse {
24
+ nodes: CheckpointNode[];
25
+ currentHeadId: string | null;
26
+ }
27
+
28
+ // Graph visualization interfaces
29
+ export interface GraphNode {
30
+ id: string;
31
+ checkpoint: CheckpointNode;
32
+ x: number;
33
+ y: number;
34
+ type: 'main' | 'branch';
35
+ isCurrent: boolean;
36
+ isOrphaned: boolean;
37
+ }
38
+
39
+ export interface GraphEdge {
40
+ from: string;
41
+ to: string;
42
+ type: 'straight' | 'branch';
43
+ path: string;
44
+ }
45
+
46
+ export interface VersionGroup {
47
+ branchName: string;
48
+ versionNumber: number;
49
+ nodes: GraphNode[];
50
+ x: number;
51
+ minY: number;
52
+ maxY: number;
53
+ width: number;
54
+ height: number;
55
+ }
56
+
57
+ // Animation state
58
+ export interface AnimationState {
59
+ isAnimating: boolean;
60
+ restoringNodeId: string | null;
61
+ progress: number;
62
+ oldNodePositions: Map<string, {x: number, y: number}>;
63
+ oldNodeStyles: Map<string, {type: string}>;
64
+ oldVersionGroups: VersionGroup[];
65
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Timeline utility functions
3
+ */
4
+
5
+ import type { TimelineResponse, GraphNode } from './types';
6
+
7
+ /**
8
+ * Format timestamp to time string (HH:MM)
9
+ */
10
+ export function formatTime(timestamp: string): string {
11
+ const date = new Date(timestamp);
12
+ return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
13
+ }
14
+
15
+ /**
16
+ * Format timestamp to date string (MMM DD, HH:MM)
17
+ */
18
+ export function formatDate(timestamp: string): string {
19
+ const date = new Date(timestamp);
20
+ return date.toLocaleDateString('en-US', {
21
+ month: 'short',
22
+ day: 'numeric',
23
+ hour: '2-digit',
24
+ minute: '2-digit'
25
+ });
26
+ }
27
+
28
+ /**
29
+ * Check if checkpoint is current HEAD
30
+ */
31
+ export function isCurrentHead(
32
+ timelineData: TimelineResponse | null,
33
+ checkpointId: string
34
+ ): boolean {
35
+ return timelineData?.currentHeadId === checkpointId;
36
+ }
37
+
38
+ /**
39
+ * Get sorted nodes for Z-index (restoring node rendered last, on top)
40
+ */
41
+ export function getSortedNodes(
42
+ graphNodes: GraphNode[],
43
+ isAnimating: boolean,
44
+ restoringNodeId: string | null
45
+ ): GraphNode[] {
46
+ if (isAnimating && restoringNodeId) {
47
+ return [
48
+ ...graphNodes.filter(n => n.id !== restoringNodeId),
49
+ ...graphNodes.filter(n => n.id === restoringNodeId)
50
+ ];
51
+ }
52
+ return graphNodes;
53
+ }