@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,421 @@
1
+ <!--
2
+ Modern SDK-Based Input Area with File Upload Support
3
+
4
+ Features:
5
+ - Server-Sent Events streaming
6
+ - Real-time message display
7
+ - Enhanced error handling
8
+ - Modern AI-first UI design
9
+ - Proper cancellation support
10
+ - File upload support (images, PDFs, documents)
11
+ -->
12
+
13
+ <script lang="ts">
14
+ import { sessionState } from '$frontend/lib/stores/core/sessions.svelte';
15
+ import { projectState } from '$frontend/lib/stores/core/projects.svelte';
16
+ import { appState } from '$frontend/lib/stores/core/app.svelte';
17
+ import { settings } from '$frontend/lib/stores/features/settings.svelte';
18
+ import { onDestroy, untrack } from 'svelte';
19
+ import { ChatService } from '$frontend/lib/services/chat';
20
+ import { chatService } from '$frontend/lib/services/chat/chat.service';
21
+ import { getEngineInfo } from '$shared/constants/engines';
22
+ import { modelStore } from '$frontend/lib/stores/features/models.svelte';
23
+ import { chatModelState } from '$frontend/lib/stores/ui/chat-model.svelte';
24
+ import { presenceState } from '$frontend/lib/stores/core/presence.svelte';
25
+ import { editModeState } from '$frontend/lib/stores/ui/edit-mode.svelte';
26
+ import { claudeAccountsStore } from '$frontend/lib/stores/features/claude-accounts.svelte';
27
+ import type { IconName } from '$shared/types/ui/icons';
28
+ import ws from '$frontend/lib/utils/ws';
29
+ import { debug } from '$shared/utils/logger';
30
+
31
+ // Components
32
+ import FileAttachmentPreview from './components/FileAttachmentPreview.svelte';
33
+ import EditModeIndicator from './components/EditModeIndicator.svelte';
34
+ import ChatInputActions from './components/ChatInputActions.svelte';
35
+ import LoadingIndicator from './components/LoadingIndicator.svelte';
36
+ import DragDropOverlay from './components/DragDropOverlay.svelte';
37
+ import EngineModelPicker from './components/EngineModelPicker.svelte';
38
+
39
+ // Composables
40
+ import { useFileHandling } from './composables/use-file-handling.svelte';
41
+ import { usePlaceholderAnimation, useLoadingTextAnimation } from './composables/use-animations.svelte';
42
+ import { useTextareaResize } from './composables/use-textarea-resize.svelte';
43
+ import { useChatActions } from './composables/use-chat-actions.svelte';
44
+ import { useInputState } from './composables/use-input-state.svelte';
45
+
46
+ let messageText = $state('');
47
+ let textareaElement: HTMLTextAreaElement;
48
+ let fileInputElement: HTMLInputElement;
49
+
50
+ // Initialize composables
51
+ const fileHandling = useFileHandling();
52
+ const placeholderTexts = ChatService.placeholderTexts;
53
+ const loadingTexts = ChatService.loadingTexts;
54
+ const placeholderAnimation = usePlaceholderAnimation(placeholderTexts);
55
+ const loadingTextAnimation = useLoadingTextAnimation(loadingTexts);
56
+ const textareaResize = useTextareaResize();
57
+
58
+ // Helper functions for composables
59
+ const setMessageText = (text: string) => {
60
+ messageText = text;
61
+ };
62
+ const getTextareaElement = () => textareaElement;
63
+ const adjustTextareaHeight = () =>
64
+ textareaResize.adjustTextareaHeight(textareaElement, messageText);
65
+ const focusTextarea = () => textareaElement?.focus();
66
+
67
+ // Chat actions params
68
+ const chatActionsParams = {
69
+ get messageText() {
70
+ return messageText;
71
+ },
72
+ get attachedFiles() {
73
+ return fileHandling.attachedFiles;
74
+ },
75
+ clearAllAttachments: fileHandling.clearAllAttachments,
76
+ adjustTextareaHeight,
77
+ focusTextarea,
78
+ startLoadingAnimation: loadingTextAnimation.startAnimation,
79
+ stopLoadingAnimation: loadingTextAnimation.stopAnimation,
80
+ clearDraft: () => inputState.clearDraft()
81
+ };
82
+
83
+ // Initialize input state management (restoration, template loading, sync)
84
+ // Declared before chatActions so clearDraft is available
85
+ const inputState = useInputState({
86
+ setMessageText,
87
+ getTextareaElement,
88
+ adjustTextareaHeight,
89
+ focusTextarea,
90
+ setAttachedFiles: (files) => {
91
+ fileHandling.attachedFiles = files;
92
+ }
93
+ });
94
+
95
+ const chatActions = useChatActions(chatActionsParams);
96
+
97
+ // Enhanced model info based on user settings
98
+ const modelInfo = $derived.by(() => {
99
+ const selectedModel = settings.selectedModel;
100
+ const model = modelStore.getById(selectedModel);
101
+ const modelName = model?.name || 'AI Assistant';
102
+ const engineInfo = getEngineInfo(settings.selectedEngine);
103
+
104
+ return {
105
+ name: modelName,
106
+ description: engineInfo?.description || 'AI-powered development assistant',
107
+ icon: 'lucide:brain-circuit' as IconName,
108
+ model: selectedModel
109
+ };
110
+ });
111
+
112
+ // Check if the current model supports file attachments
113
+ const modelSupportsAttachments = $derived.by(() => {
114
+ const model = modelStore.getById(chatModelState.model);
115
+ if (!model) return true; // Default to enabled if model not found
116
+ return model.capabilities.some(c => c.toLowerCase() === 'attachments');
117
+ });
118
+
119
+ // Check if we're in welcome state (no messages)
120
+ const isWelcomeState = $derived(sessionState.messages.length === 0);
121
+
122
+ // Project-aware state
123
+ const hasActiveProject = $derived(projectState.currentProject !== null);
124
+
125
+ // Reason why chat input is blocked (aside from isLoading / no project)
126
+ const chatBlockedReason = $derived.by(() => {
127
+ const engine = chatModelState.engine;
128
+ if (engine === 'claude-code') {
129
+ if (claudeAccountsStore.loaded && claudeAccountsStore.accounts.length === 0) {
130
+ return 'no-claude-account' as const;
131
+ }
132
+ } else {
133
+ if (!chatModelState.model) {
134
+ return 'no-model' as const;
135
+ }
136
+ }
137
+ return null;
138
+ });
139
+
140
+ const isInputDisabled = $derived(appState.isLoading || !hasActiveProject || !!chatBlockedReason);
141
+
142
+ const chatPlaceholder = $derived.by(() => {
143
+ if (chatBlockedReason === 'no-claude-account') {
144
+ return 'No Claude Code account connected. Configure it in Settings → AI Engine → Claude Code → Accounts.';
145
+ }
146
+ if (chatBlockedReason === 'no-model') {
147
+ return 'No model selected. Please select a model to start chatting.';
148
+ }
149
+ return placeholderAnimation.placeholderText;
150
+ });
151
+
152
+ // Wrapper functions for event handlers
153
+ const handleTextareaInput = () => {
154
+ textareaResize.handleTextareaInput(textareaElement, messageText);
155
+ // Sync input text to other collaborators (includes draft save)
156
+ inputState.emitInputSync(messageText, fileHandling.attachedFiles);
157
+ };
158
+ const handleKeyDown = (event: KeyboardEvent) =>
159
+ textareaResize.handleKeyDown(event, textareaElement, messageText);
160
+ const handleKeyPress = (event: KeyboardEvent) =>
161
+ chatActions.handleKeyPress(event, messageText, setMessageText);
162
+ const handleSendMessage = () => {
163
+ chatActions.sendMessage(messageText, setMessageText);
164
+ };
165
+ const handleCancelEdit = () => {
166
+ chatActions.handleCancelEdit();
167
+ messageText = '';
168
+ fileHandling.clearAllAttachments();
169
+ adjustTextareaHeight();
170
+ };
171
+
172
+ // Reactive effect for placeholder animation
173
+ $effect(() => {
174
+ if (isWelcomeState) {
175
+ placeholderAnimation.startAnimation();
176
+ } else {
177
+ placeholderAnimation.setStaticPlaceholder('Continue the conversation...');
178
+ }
179
+ });
180
+
181
+ // Sync appState.isLoading from presence data (single source of truth for all users)
182
+ // Also fetch partial text and reconnect to stream for late-joining users / refresh
183
+ let lastCatchupProjectId: string | undefined;
184
+ let lastPresenceProjectId: string | undefined;
185
+ $effect(() => {
186
+ const projectId = projectState.currentProject?.id;
187
+ const sessionId = sessionState.currentSession?.id; // Reactive dep: retry catchup when session loads
188
+ if (!projectId) return;
189
+
190
+ // When switching projects, clear isCancelling from the previous project.
191
+ // The cancel is project-scoped: cancelling Project A must NOT block Project B.
192
+ // Also, after project switch the WS room changes, so the chat:cancelled event
193
+ // for the old project will never arrive — we must clear it here.
194
+ if (projectId !== lastPresenceProjectId) {
195
+ if (lastPresenceProjectId && appState.isCancelling) {
196
+ appState.isCancelling = false;
197
+ }
198
+ lastPresenceProjectId = projectId;
199
+ }
200
+
201
+ const status = presenceState.statuses.get(projectId);
202
+ // Check if the active stream is for the CURRENT session (not just any session in the project)
203
+ const hasActiveForSession = status?.streams?.some(
204
+ (s: any) => s.status === 'active' && s.chatSessionId === sessionId
205
+ ) ?? false;
206
+ if (hasActiveForSession && !appState.isLoading) {
207
+ // Don't re-enable loading if user just cancelled locally
208
+ if (appState.isCancelling) return;
209
+
210
+ appState.isLoading = true;
211
+
212
+ // Catch up on active stream's partial text for late-joining users
213
+ // Only do this once per project switch to avoid repeated fetches
214
+ // Only attempt if session is available (may not be on initial load)
215
+ if (projectId !== lastCatchupProjectId && sessionId) {
216
+ lastCatchupProjectId = projectId;
217
+ catchupActiveStream(status);
218
+ }
219
+ } else if (hasActiveForSession && appState.isLoading && sessionId && projectId !== lastCatchupProjectId) {
220
+ // Session became available after loading was already set (e.g. page refresh)
221
+ // The first run set isLoading=true but couldn't catch up because session wasn't loaded yet
222
+ lastCatchupProjectId = projectId;
223
+ catchupActiveStream(status);
224
+ } else if (!hasActiveForSession && appState.isLoading && !appState.isCancelling) {
225
+ // Only clear loading if not in the middle of a cancel operation
226
+ appState.isLoading = false;
227
+ lastCatchupProjectId = undefined;
228
+ } else if (!hasActiveForSession && !appState.isLoading) {
229
+ // No active streams for this session — just reset catchup tracking.
230
+ lastCatchupProjectId = undefined;
231
+ }
232
+ });
233
+
234
+ /**
235
+ * Fetch current stream state, inject partial text, and reconnect to live events
236
+ * for late-joining users (browser refresh, project switch, long absence)
237
+ */
238
+ async function catchupActiveStream(status: any) {
239
+ if (!status?.streams?.length || !sessionState.currentSession?.id) return;
240
+
241
+ // Find the active stream
242
+ const activeStream = status.streams.find((s: any) => s.status === 'active');
243
+ if (!activeStream) return;
244
+
245
+ try {
246
+ const streamState = await ws.http('chat:background-state', {
247
+ chatSessionId: sessionState.currentSession.id
248
+ });
249
+
250
+ if (streamState && streamState.status === 'active' && streamState.processId) {
251
+ // Check if we already have a streaming message for this processId
252
+ const hasStreamingMessage = sessionState.messages.some(
253
+ (m: any) => m.type === 'stream_event' && m.processId === streamState.processId
254
+ );
255
+
256
+ if (!hasStreamingMessage) {
257
+ // Inject streaming message with the current partial text from the server
258
+ const streamingMessage = {
259
+ type: 'stream_event' as const,
260
+ processId: streamState.processId,
261
+ partialText: streamState.currentPartialText || '',
262
+ timestamp: new Date().toISOString()
263
+ };
264
+ (sessionState.messages as any[]).push(streamingMessage);
265
+ } else {
266
+ // Update existing stream_event with latest partial text
267
+ const existingMsg = sessionState.messages.find(
268
+ (m: any) => m.type === 'stream_event' && m.processId === streamState.processId
269
+ );
270
+ if (existingMsg && streamState.currentPartialText) {
271
+ (existingMsg as any).partialText = streamState.currentPartialText;
272
+ }
273
+ }
274
+
275
+ // Reconnect to live stream events so future partials/messages/complete flow in
276
+ chatService.reconnectToStream(
277
+ sessionState.currentSession.id,
278
+ streamState.processId
279
+ );
280
+
281
+ debug.log('chat', 'Caught up with active stream:', {
282
+ processId: streamState.processId,
283
+ partialLength: streamState.currentPartialText?.length || 0
284
+ });
285
+ }
286
+ } catch (error) {
287
+ debug.error('chat', 'Failed to catch up with active stream:', error);
288
+ }
289
+ }
290
+
291
+ // Sync loading animation with appState.isLoading (works for all users, not just sender)
292
+ $effect(() => {
293
+ if (appState.isLoading) {
294
+ loadingTextAnimation.startAnimation();
295
+ } else {
296
+ loadingTextAnimation.stopAnimation();
297
+ }
298
+ });
299
+
300
+ // When edit mode exits (remote cancel, user cancel), reset input and attachments
301
+ // Skip during project transition (isRestoring) - server restore will handle it
302
+ // Use untrack for isRestoring to avoid this effect re-running when isRestoring changes
303
+ let wasEditing = false;
304
+ $effect(() => {
305
+ const isEditing = editModeState.isEditing;
306
+ if (wasEditing && !isEditing) {
307
+ const restoring = untrack(() => appState.isRestoring);
308
+ if (!restoring) {
309
+ messageText = '';
310
+ fileHandling.clearAllAttachments();
311
+ setTimeout(() => adjustTextareaHeight(), 0);
312
+ }
313
+ }
314
+ wasEditing = isEditing;
315
+ });
316
+
317
+ // Sync file attachments to other collaborators when they change
318
+ // Use untrack for isRestoring so this effect ONLY re-runs when files change,
319
+ // not when isRestoring transitions (which would emit stale data to new project)
320
+ $effect(() => {
321
+ // Access attachedFiles to create reactive dependency
322
+ const files = fileHandling.attachedFiles;
323
+ const restoring = untrack(() => appState.isRestoring);
324
+ if (!restoring) {
325
+ // Emit attachment sync with current text
326
+ inputState.emitAttachmentSync(messageText, files);
327
+ }
328
+ });
329
+
330
+ onDestroy(() => {
331
+ fileHandling.clearAllAttachments();
332
+ });
333
+ </script>
334
+
335
+ <div class="">
336
+ <!-- Hidden file input -->
337
+ <input
338
+ bind:this={fileInputElement}
339
+ type="file"
340
+ multiple
341
+ accept={[...fileHandling.SUPPORTED_IMAGE_TYPES, ...fileHandling.SUPPORTED_DOCUMENT_TYPES].join(
342
+ ','
343
+ )}
344
+ onchange={fileHandling.handleFileInputChange}
345
+ class="hidden"
346
+ />
347
+
348
+ <!-- Input container with modern design -->
349
+ <div class="{isWelcomeState ? 'max-w-3xl mx-auto' : 'max-w-4xl mx-auto'} relative">
350
+ <!-- File attachments preview -->
351
+ <FileAttachmentPreview
352
+ attachedFiles={fileHandling.attachedFiles}
353
+ onRemove={fileHandling.removeAttachment}
354
+ />
355
+
356
+ <!-- Main input area with drag and drop support -->
357
+ <div
358
+ class="
359
+ relative z-10 flex items-end gap-3 lg:gap-4 overflow-hidden bg-white dark:bg-slate-900
360
+ border border-slate-200 dark:border-slate-700 rounded-xl transition-all duration-200
361
+ focus-within:ring-2 focus-within:ring-violet-500 {fileHandling.isDragging && 'ring-2 ring-violet-500'}"
362
+ role="region"
363
+ aria-label="Message input with file drop zone"
364
+ ondragover={fileHandling.handleDragOver}
365
+ ondragleave={fileHandling.handleDragLeave}
366
+ ondrop={fileHandling.handleDrop}
367
+ >
368
+ <div class="flex-1">
369
+ <!-- Engine/Model Picker -->
370
+ <EngineModelPicker />
371
+
372
+ <!-- Edit Mode Indicator -->
373
+ <EditModeIndicator onCancel={handleCancelEdit} />
374
+
375
+ <div class="relative">
376
+ <textarea
377
+ bind:this={textareaElement}
378
+ bind:value={messageText}
379
+ placeholder={chatPlaceholder}
380
+ class="flex w-full p-4 pr-24 border-0 bg-transparent resize-none focus:outline-none text-slate-900 dark:text-slate-100 placeholder-slate-500 dark:placeholder-slate-400 text-base leading-relaxed disabled:opacity-50 disabled:cursor-not-allowed"
381
+ rows="1"
382
+ style="max-height: 22.5rem; overflow-y: hidden;"
383
+ disabled={isInputDisabled}
384
+ oninput={handleTextareaInput}
385
+ onkeydown={handleKeyDown}
386
+ onkeypress={handleKeyPress}
387
+ onpaste={fileHandling.handlePaste}
388
+ oncompositionstart={chatActions.handleCompositionStart}
389
+ oncompositionend={chatActions.handleCompositionEnd}
390
+ autocomplete="off"
391
+ ></textarea>
392
+
393
+ <!-- Action buttons -->
394
+ <ChatInputActions
395
+ isLoading={appState.isLoading}
396
+ hasActiveProject={hasActiveProject}
397
+ messageText={messageText}
398
+ attachedFiles={fileHandling.attachedFiles}
399
+ isProcessingFiles={fileHandling.isProcessingFiles}
400
+ {modelSupportsAttachments}
401
+ onSend={handleSendMessage}
402
+ onCancel={chatActions.cancelRequest}
403
+ onAttachFile={() => fileHandling.handleFileSelect(fileInputElement)}
404
+ />
405
+ </div>
406
+ </div>
407
+ </div>
408
+
409
+ <!-- Overlays -->
410
+ <DragDropOverlay
411
+ isDragging={fileHandling.isDragging}
412
+ isProcessingFiles={fileHandling.isProcessingFiles}
413
+ />
414
+
415
+ <!-- Loading indicator -->
416
+ <LoadingIndicator
417
+ visibleLoadingText={loadingTextAnimation.visibleLoadingText}
418
+ isWelcomeState={isWelcomeState}
419
+ />
420
+ </div>
421
+ </div>
@@ -0,0 +1,78 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import type { FileAttachment } from '../composables/use-file-handling.svelte';
4
+
5
+ interface Props {
6
+ isLoading: boolean;
7
+ hasActiveProject: boolean;
8
+ messageText: string;
9
+ attachedFiles: FileAttachment[];
10
+ isProcessingFiles: boolean;
11
+ modelSupportsAttachments: boolean;
12
+ onSend: () => void;
13
+ onCancel: () => void;
14
+ onAttachFile: () => void;
15
+ }
16
+
17
+ const {
18
+ isLoading,
19
+ hasActiveProject,
20
+ messageText,
21
+ attachedFiles,
22
+ isProcessingFiles,
23
+ modelSupportsAttachments,
24
+ onSend,
25
+ onCancel,
26
+ onAttachFile
27
+ }: Props = $props();
28
+
29
+ const attachDisabled = $derived(
30
+ isProcessingFiles || !hasActiveProject || !modelSupportsAttachments
31
+ );
32
+
33
+ const attachTitle = $derived(
34
+ !modelSupportsAttachments
35
+ ? 'File attachments are not supported by this model'
36
+ : 'Attach files'
37
+ );
38
+ </script>
39
+
40
+ <div class="absolute bottom-2 right-2 flex items-center gap-1.5">
41
+ {#if !isLoading}
42
+ <!-- Attach file button -->
43
+ <button
44
+ onclick={onAttachFile}
45
+ disabled={attachDisabled}
46
+ class="w-8 h-8 hover:bg-slate-100 dark:hover:bg-slate-800 rounded-lg flex items-center justify-center transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed group relative"
47
+ title={attachTitle}
48
+ aria-label={attachTitle}
49
+ >
50
+ <Icon name="lucide:paperclip" class="text-slate-600 dark:text-slate-400 w-4.5 h-4.5" />
51
+ {#if attachedFiles.length > 0}
52
+ <span class="absolute -top-1 -right-1 w-4 h-4 bg-violet-500 text-white text-xs rounded-full flex items-center justify-center">
53
+ {attachedFiles.length}
54
+ </span>
55
+ {/if}
56
+ </button>
57
+ {/if}
58
+
59
+ <!-- Send/Cancel button -->
60
+ {#if isLoading}
61
+ <button
62
+ onclick={onCancel}
63
+ class="w-10 h-10 bg-red-500 hover:bg-red-600 rounded-xl flex items-center justify-center transition-all duration-200 group"
64
+ aria-label="Cancel request"
65
+ >
66
+ <Icon name="lucide:circle-stop" class="text-white w-5 h-5" />
67
+ </button>
68
+ {:else}
69
+ <button
70
+ onclick={onSend}
71
+ disabled={(!messageText.trim() && attachedFiles.length === 0) || !hasActiveProject || isProcessingFiles}
72
+ class="w-10 h-10 bg-gradient-to-r from-violet-600 to-violet-600 hover:from-violet-700 hover:to-violet-700 disabled:from-slate-300 disabled:to-slate-300 dark:disabled:from-slate-700 dark:disabled:to-slate-700 rounded-xl flex items-center justify-center transition-all duration-200 disabled:cursor-not-allowed group"
73
+ aria-label="Send message"
74
+ >
75
+ <Icon name="lucide:send-horizontal" class="text-white w-5 h-5" />
76
+ </button>
77
+ {/if}
78
+ </div>
@@ -0,0 +1,30 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ isDragging: boolean;
4
+ isProcessingFiles: boolean;
5
+ }
6
+
7
+ const { isDragging, isProcessingFiles }: Props = $props();
8
+ </script>
9
+
10
+ <!-- Drag and drop overlay -->
11
+ {#if isDragging}
12
+ <div class="absolute inset-0 bg-violet-500/10 rounded-xl flex items-center justify-center pointer-events-none">
13
+ <div class="bg-white dark:bg-slate-800 px-4 py-2 rounded-lg shadow-lg border border-violet-500">
14
+ <span class="text-sm font-medium text-violet-700 dark:text-violet-300">Drop files here to attach</span>
15
+ </div>
16
+ </div>
17
+ {/if}
18
+
19
+ <!-- Processing files overlay -->
20
+ {#if isProcessingFiles}
21
+ <div class="absolute top-0 left-0 right-0 -mt-10 flex justify-center">
22
+ <div class="bg-white dark:bg-slate-800 px-3 py-1.5 rounded-lg shadow-lg border border-slate-200 dark:border-slate-700 flex items-center gap-2">
23
+ <svg class="animate-spin h-3 w-3 text-violet-500" viewBox="0 0 24 24">
24
+ <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" fill="none"></circle>
25
+ <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
26
+ </svg>
27
+ <span class="text-xs text-slate-600 dark:text-slate-400">Processing files...</span>
28
+ </div>
29
+ </div>
30
+ {/if}
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import { editModeState } from '$frontend/lib/stores/ui/edit-mode.svelte';
4
+
5
+ interface Props {
6
+ onCancel: () => void;
7
+ }
8
+
9
+ const { onCancel }: Props = $props();
10
+ </script>
11
+
12
+ {#if editModeState.isEditing}
13
+ <div class="px-4 py-2 bg-amber-50 dark:bg-amber-900/20 border-b border-slate-200 dark:border-slate-600">
14
+ <div class="flex items-center justify-between">
15
+ <div class="flex flex-col gap-1">
16
+ <div class="flex items-center gap-2">
17
+ <Icon name="lucide:pencil" class="w-4 h-4 text-amber-600 dark:text-amber-400" />
18
+ <span class="text-sm text-amber-700 dark:text-amber-300 font-medium">
19
+ Editing message
20
+ </span>
21
+ </div>
22
+ </div>
23
+ <button
24
+ onclick={onCancel}
25
+ class="px-2 py-1 text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-800/40 rounded-md text-sm font-medium transition-colors flex items-center gap-1 flex-shrink-0"
26
+ aria-label="Cancel edit"
27
+ >
28
+ <Icon name="lucide:x" class="w-3.5 h-3.5" />
29
+ Cancel
30
+ </button>
31
+ </div>
32
+ </div>
33
+ {/if}