@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,58 @@
1
+ <script lang="ts">
2
+ // Import organized icon registries
3
+ import { lucideIconRegistry } from './lucide-icons';
4
+ import { materialIconRegistry } from './material-icons';
5
+ import lucideData from '@iconify-json/lucide/icons.json';
6
+ import materialData from '@iconify-json/material-icon-theme/icons.json';
7
+ import type { IconName } from '$shared/types/ui/icons';
8
+ import { debug } from '$shared/utils/logger';
9
+
10
+ const iconRegistry = {
11
+ // ALL LUCIDE ICONS
12
+ ...lucideIconRegistry,
13
+
14
+ // ALL MATERIAL ICON THEME ICONS
15
+ ...materialIconRegistry,
16
+ } as const;
17
+
18
+ // Default dimensions for each icon library
19
+ const defaultDimensions = {
20
+ lucide: { width: lucideData.width || 24, height: lucideData.height || 24 },
21
+ material: { width: materialData.width || 32, height: materialData.height || 32 }
22
+ };
23
+
24
+ interface Props {
25
+ name: IconName;
26
+ class?: string;
27
+ }
28
+
29
+ const { name, class: className = '' }: Props = $props();
30
+
31
+ const icon = $derived(iconRegistry[name]);
32
+
33
+ const svgContent = $derived.by(() => {
34
+ if (!icon) {
35
+ debug.warn('session', `Icon not found: ${name}`);
36
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="iconify ${className}" fill="currentColor"><rect x="3" y="3" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"/><path d="M9 9l6 6m-6 0l6-6"/></svg>`;
37
+ }
38
+
39
+ // Extract SVG body from icon data
40
+ const body = icon.body || '';
41
+
42
+ // Determine icon dimensions
43
+ const iconPrefix = name.split(':')[0] as 'lucide' | 'material';
44
+ const defaults = defaultDimensions[iconPrefix] || { width: 24, height: 24 };
45
+
46
+ // Use icon-specific dimensions if available, otherwise use library defaults
47
+ const width = icon.width || defaults.width;
48
+ const height = icon.height || defaults.height;
49
+ const top = icon.top || 0;
50
+
51
+ // Create viewBox
52
+ const viewBox = `0 ${top} ${width} ${height}`;
53
+
54
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBox}" class="iconify ${className}" fill="currentColor">${body}</svg>`;
55
+ });
56
+ </script>
57
+
58
+ {@html svgContent}
@@ -0,0 +1,72 @@
1
+ <script lang="ts">
2
+ import type { InputProps } from '$shared/types/ui/components';
3
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
4
+
5
+ let {
6
+ value = $bindable(''),
7
+ placeholder = '',
8
+ type = 'text',
9
+ disabled = false,
10
+ error = '',
11
+ label = '',
12
+ required = false,
13
+ onchange,
14
+ class: className = '',
15
+ ...props
16
+ }: InputProps & { class?: string } = $props();
17
+
18
+ // Generate unique ID for input
19
+ const inputId = `input-${Math.random().toString(36).substr(2, 9)}`;
20
+
21
+ // Generate modern AI input classes
22
+ const baseClasses =
23
+ 'block w-full px-3 py-3 border border-slate-300 dark:border-slate-600 rounded-lg transition-colors duration-200 focus:outline-none text-sm font-medium';
24
+
25
+ const stateClasses = error
26
+ ? 'border-red-400 focus:border-red-500 focus:ring-2 focus:ring-red-200 dark:focus:ring-red-900/20'
27
+ : 'focus:border-violet-500 focus:ring-2 focus:ring-violet-200 dark:focus:ring-violet-900/20';
28
+
29
+ const backgroundClasses =
30
+ 'bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 placeholder-slate-400 dark:placeholder-slate-500';
31
+
32
+ const inputClasses = `${baseClasses} ${stateClasses} ${backgroundClasses} ${className}`;
33
+
34
+ function handleInput(event: Event) {
35
+ const target = event.target as HTMLInputElement;
36
+ value = target.value;
37
+ if (onchange) {
38
+ onchange(value);
39
+ }
40
+ }
41
+ </script>
42
+
43
+ <div class="space-y-1">
44
+ {#if label}
45
+ <label for={inputId} class="block text-sm font-semibold text-slate-700 dark:text-slate-300">
46
+ {label}
47
+ {#if required}
48
+ <span class="text-red-500 ml-1">*</span>
49
+ {/if}
50
+ </label>
51
+ {/if}
52
+
53
+ <input
54
+ id={inputId}
55
+ {type}
56
+ {value}
57
+ {placeholder}
58
+ {disabled}
59
+ {required}
60
+ class={inputClasses}
61
+ oninput={handleInput}
62
+ autocomplete="off"
63
+ {...props}
64
+ />
65
+
66
+ {#if error}
67
+ <p class="text-sm text-red-600 dark:text-red-400 flex items-center mt-1 font-medium">
68
+ <Icon name="lucide:circle-alert" class="w-4 h-4 mr-1" />
69
+ {error}
70
+ </p>
71
+ {/if}
72
+ </div>
@@ -0,0 +1,233 @@
1
+ <script lang="ts">
2
+ import { fade, scale } from 'svelte/transition';
3
+ import { cubicOut } from 'svelte/easing';
4
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
5
+ import { getFileIcon } from '$frontend/lib/utils/file-icon-mappings';
6
+ import { formatFileSize } from '$frontend/lib/components/chat/shared/utils';
7
+ import { debug } from '$shared/utils/logger';
8
+
9
+ interface Props {
10
+ isOpen: boolean;
11
+ type: 'image' | 'document';
12
+ mediaType: string;
13
+ data: string;
14
+ onClose: () => void;
15
+ fileName?: string;
16
+ }
17
+
18
+ let {
19
+ isOpen = $bindable(),
20
+ type,
21
+ mediaType,
22
+ data,
23
+ onClose,
24
+ fileName = 'Document'
25
+ }: Props = $props();
26
+
27
+ let modalElement = $state<HTMLDivElement>();
28
+
29
+ function handleKeydown(event: KeyboardEvent) {
30
+ if (event.key === 'Escape' && isOpen) {
31
+ onClose();
32
+ }
33
+ }
34
+
35
+ function handleBackdropClick(event: MouseEvent) {
36
+ if (event.target === modalElement) {
37
+ onClose();
38
+ }
39
+ }
40
+
41
+ function openInNewTab() {
42
+ try {
43
+ // Create a blob from the base64 data
44
+ const byteCharacters = atob(data);
45
+ const byteNumbers = new Array(byteCharacters.length);
46
+ for (let i = 0; i < byteCharacters.length; i++) {
47
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
48
+ }
49
+ const byteArray = new Uint8Array(byteNumbers);
50
+ const blob = new Blob([byteArray], { type: mediaType });
51
+
52
+ // Create object URL and open it
53
+ const blobUrl = URL.createObjectURL(blob);
54
+ const newWindow = window.open(blobUrl, '_blank');
55
+
56
+ // Clean up the object URL after a delay to ensure the new tab has loaded
57
+ if (newWindow) {
58
+ setTimeout(() => {
59
+ URL.revokeObjectURL(blobUrl);
60
+ }, 1000);
61
+ }
62
+ } catch (error) {
63
+ debug.error('session', 'Failed to open attachment in new tab:', error);
64
+ // Fallback to data URL approach if blob creation fails
65
+ const dataUrl = `data:${mediaType};base64,${data}`;
66
+ window.open(dataUrl, '_blank');
67
+ }
68
+
69
+ onClose();
70
+ }
71
+
72
+ function downloadFile() {
73
+ try {
74
+ // Create a blob from the base64 data
75
+ const byteCharacters = atob(data);
76
+ const byteNumbers = new Array(byteCharacters.length);
77
+ for (let i = 0; i < byteCharacters.length; i++) {
78
+ byteNumbers[i] = byteCharacters.charCodeAt(i);
79
+ }
80
+ const byteArray = new Uint8Array(byteNumbers);
81
+ const blob = new Blob([byteArray], { type: mediaType });
82
+
83
+ // Create object URL and trigger download
84
+ const blobUrl = URL.createObjectURL(blob);
85
+ const link = document.createElement('a');
86
+ link.href = blobUrl;
87
+
88
+ // Determine file extension based on media type
89
+ let extension = '.bin';
90
+ if (mediaType.includes('pdf')) extension = '.pdf';
91
+ else if (mediaType.includes('image/png')) extension = '.png';
92
+ else if (mediaType.includes('image/jpeg') || mediaType.includes('image/jpg')) extension = '.jpg';
93
+ else if (mediaType.includes('image/gif')) extension = '.gif';
94
+ else if (mediaType.includes('image/svg')) extension = '.svg';
95
+ else if (mediaType.includes('image/webp')) extension = '.webp';
96
+
97
+ link.download = fileName || `attachment_${Date.now()}${extension}`;
98
+ document.body.appendChild(link);
99
+ link.click();
100
+ document.body.removeChild(link);
101
+
102
+ // Clean up
103
+ setTimeout(() => {
104
+ URL.revokeObjectURL(blobUrl);
105
+ }, 100);
106
+ } catch (error) {
107
+ debug.error('session', 'Failed to download attachment:', error);
108
+ }
109
+
110
+ onClose();
111
+ }
112
+
113
+ $effect(() => {
114
+ if (isOpen) {
115
+ document.body.style.overflow = 'hidden';
116
+ } else {
117
+ document.body.style.overflow = '';
118
+ }
119
+ });
120
+ </script>
121
+
122
+ <svelte:window on:keydown={handleKeydown} />
123
+
124
+ {#if isOpen}
125
+ <div
126
+ bind:this={modalElement}
127
+ class="fixed inset-0 z-50 flex items-center justify-center bg-black/80 backdrop-blur-sm"
128
+ role="dialog"
129
+ aria-modal="true"
130
+ aria-label="View attachment"
131
+ tabindex="-1"
132
+ onclick={handleBackdropClick}
133
+ onkeydown={handleKeydown}
134
+ in:fade={{ duration: 200, easing: cubicOut }}
135
+ out:fade={{ duration: 150, easing: cubicOut }}
136
+ >
137
+ <!-- Close button (always visible) -->
138
+ <button
139
+ onclick={onClose}
140
+ class="absolute top-4 right-4 z-10 p-2.5 rounded-full bg-white/90 dark:bg-slate-800/90 hover:bg-white dark:hover:bg-slate-800 transition-all shadow-lg"
141
+ aria-label="Close lightbox"
142
+ >
143
+ <svg class="w-5 h-5 text-slate-700 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
144
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
145
+ </svg>
146
+ </button>
147
+
148
+ <!-- Download button -->
149
+ <button
150
+ onclick={downloadFile}
151
+ class="absolute top-4 right-16 z-10 p-2.5 rounded-full bg-white/90 dark:bg-slate-800/90 hover:bg-white dark:hover:bg-slate-800 transition-all shadow-lg"
152
+ aria-label="Download file"
153
+ title="Download file"
154
+ >
155
+ <svg class="w-5 h-5 text-slate-700 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
156
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
157
+ </svg>
158
+ </button>
159
+
160
+ <!-- Open in new tab button -->
161
+ <button
162
+ onclick={openInNewTab}
163
+ class="absolute top-4 right-28 z-10 p-2.5 rounded-full bg-white/90 dark:bg-slate-800/90 hover:bg-white dark:hover:bg-slate-800 transition-all shadow-lg"
164
+ aria-label="Open in new tab"
165
+ title="Open in new tab"
166
+ >
167
+ <svg class="w-5 h-5 text-slate-700 dark:text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
168
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
169
+ </svg>
170
+ </button>
171
+
172
+ <!-- Content container -->
173
+ <div
174
+ class="relative max-w-[95vw] max-h-[95vh] flex items-center justify-center"
175
+ onclick={(e) => e.stopPropagation()}
176
+ onkeydown={(e) => e.stopPropagation()}
177
+ role="document"
178
+ tabindex="-1"
179
+ in:scale={{ duration: 200, easing: cubicOut, start: 0.9 }}
180
+ out:scale={{ duration: 150, easing: cubicOut, start: 0.95 }}
181
+ >
182
+ {#if type === 'image'}
183
+ <!-- Image viewer -->
184
+ <img
185
+ src="data:{mediaType};base64,{data}"
186
+ alt="Full size view"
187
+ class="max-w-full max-h-[90vh] object-contain rounded-lg shadow-2xl"
188
+ loading="eager"
189
+ />
190
+ {:else if type === 'document'}
191
+ <!-- Document preview -->
192
+ <div class="bg-white dark:bg-slate-900 rounded-lg shadow-2xl p-8 min-w-80 max-w-md text-center">
193
+ <div class="mb-6">
194
+ <Icon
195
+ name={fileName ? getFileIcon(fileName) : 'material:document'}
196
+ class="w-20 h-20 mx-auto text-slate-600 dark:text-slate-400"
197
+ />
198
+ </div>
199
+
200
+ <h3 class="text-xl font-semibold text-slate-900 dark:text-slate-100 mb-2">
201
+ {fileName}
202
+ </h3>
203
+
204
+ <p class="text-sm text-slate-600 dark:text-slate-400 mb-6">
205
+ {formatFileSize(data.length * 0.75)}
206
+ </p>
207
+
208
+ <div class="flex flex-col gap-3">
209
+ <button
210
+ onclick={downloadFile}
211
+ class="w-full px-4 py-2.5 bg-violet-600 hover:bg-violet-700 text-white rounded-lg transition-colors font-medium flex items-center justify-center gap-2"
212
+ >
213
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
214
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
215
+ </svg>
216
+ Download File
217
+ </button>
218
+
219
+ <button
220
+ onclick={openInNewTab}
221
+ class="w-full px-4 py-2.5 bg-slate-600 hover:bg-slate-700 text-white rounded-lg transition-colors font-medium flex items-center justify-center gap-2"
222
+ >
223
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
224
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
225
+ </svg>
226
+ Open in New Tab
227
+ </button>
228
+ </div>
229
+ </div>
230
+ {/if}
231
+ </div>
232
+ </div>
233
+ {/if}
@@ -0,0 +1,52 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+ import { cubicOut } from 'svelte/easing';
4
+ import Icon from './Icon.svelte';
5
+
6
+ let { isVisible = $bindable(), progress = 0, loadingText = 'Initializing workspace...' } = $props();
7
+ </script>
8
+
9
+ {#if isVisible}
10
+ <div
11
+ class="fixed inset-0 z-[9999] bg-white dark:bg-slate-950 flex items-center justify-center"
12
+ out:fade={{ duration: 300, easing: cubicOut }}
13
+ >
14
+ <div class="flex flex-col items-center gap-4 text-center px-4">
15
+ <!-- Logo and Brand -->
16
+ <div class="relative">
17
+ <img src="/favicon.svg" alt="Clopen" class="w-20 h-20 rounded-3xl shadow-2xl" />
18
+ <!-- Loading animation ring -->
19
+ <div class="absolute inset-0 rounded-3xl border-4 border-violet-600/20 animate-pulse"></div>
20
+ </div>
21
+
22
+ <!-- App Title -->
23
+ <div class="space-y-2">
24
+ <h1 class="text-3xl font-bold bg-gradient-to-r from-violet-600 to-violet-600 bg-clip-text text-transparent">
25
+ Clopen
26
+ </h1>
27
+ </div>
28
+
29
+ <!-- Loading Progress -->
30
+ <div class="space-y-2">
31
+ <!-- Dynamic loading text -->
32
+ <p class="text-slate-500 dark:text-slate-500 text-sm font-medium min-h-5">
33
+ {loadingText}
34
+ </p>
35
+
36
+ <!-- Progress bar with smooth JS animation -->
37
+ <div class="w-72 h-1 bg-slate-200 dark:bg-slate-800 rounded-full overflow-hidden">
38
+ <div
39
+ class="h-full bg-gradient-to-r from-violet-600 to-violet-600 rounded-full transition-all duration-300 ease-out"
40
+ style="width: {progress}%"
41
+ ></div>
42
+ </div>
43
+
44
+ <!-- Progress percentage -->
45
+ <p class="text-xs text-slate-400 dark:text-slate-600 font-mono">
46
+ {Math.round(progress)}%
47
+ </p>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ {/if}
52
+
@@ -0,0 +1,48 @@
1
+ <script lang="ts">
2
+ const {
3
+ size = 'md',
4
+ color = 'primary',
5
+ text = '',
6
+ class: className = '',
7
+ ...props
8
+ }: {
9
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
10
+ color?: '' | 'primary' | 'secondary' | 'neutral' | 'white';
11
+ text?: string;
12
+ class?: string;
13
+ } = $props();
14
+
15
+ const sizeClasses = {
16
+ xs: 'w-3 h-3',
17
+ sm: 'w-4 h-4',
18
+ md: 'w-6 h-6',
19
+ lg: 'w-8 h-8',
20
+ xl: 'w-12 h-12'
21
+ };
22
+
23
+ const colorClasses = {
24
+ primary: 'border-violet-600',
25
+ secondary: 'border-violet-600',
26
+ neutral: 'border-slate-500',
27
+ white: 'border-white'
28
+ };
29
+
30
+ const textSizeClasses = {
31
+ xs: 'text-xs',
32
+ sm: 'text-sm',
33
+ md: 'text-base',
34
+ lg: 'text-lg',
35
+ xl: 'text-xl'
36
+ };
37
+
38
+ const spinnerClasses = `animate-spin rounded-full border-2 ${sizeClasses[size]} ${color ? colorClasses[color] : ''} border-t-transparent ${className}`;
39
+ </script>
40
+
41
+ <div class="flex flex-col items-center space-y-2" {...props}>
42
+ <div class={spinnerClasses}></div>
43
+ {#if text}
44
+ <span class="text-slate-600 dark:text-slate-400 font-medium {textSizeClasses[size]}">
45
+ {text}
46
+ </span>
47
+ {/if}
48
+ </div>
@@ -0,0 +1,177 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { fade, scale } from 'svelte/transition';
4
+ import { cubicOut } from 'svelte/easing';
5
+
6
+ interface Props {
7
+ isOpen: boolean;
8
+ onClose: () => void;
9
+ title?: string;
10
+ size?: 'sm' | 'md' | 'lg' | 'xl' | 'full';
11
+ closable?: boolean;
12
+ className?: string;
13
+ children?: import('svelte').Snippet;
14
+ header?: import('svelte').Snippet;
15
+ footer?: import('svelte').Snippet;
16
+ contentRef?: HTMLElement;
17
+ }
18
+
19
+ let {
20
+ isOpen = $bindable(),
21
+ onClose,
22
+ title,
23
+ size = 'md',
24
+ closable = true,
25
+ className = '',
26
+ children,
27
+ header,
28
+ footer,
29
+ contentRef = $bindable()
30
+ }: Props = $props();
31
+
32
+ let modalElement = $state<HTMLDivElement>();
33
+
34
+ // Handle escape key
35
+ function handleKeydown(event: KeyboardEvent) {
36
+ if (event.key === 'Escape' && closable && isOpen) {
37
+ onClose();
38
+ }
39
+ }
40
+
41
+ // Handle backdrop click
42
+ function handleBackdropClick(event: MouseEvent) {
43
+ if (closable && event.target === modalElement) {
44
+ onClose();
45
+ }
46
+ }
47
+
48
+ // Size classes mapping
49
+ const sizeClasses = {
50
+ sm: 'max-w-[95vw] md:max-w-md',
51
+ md: 'max-w-[95vw] md:max-w-lg',
52
+ lg: 'max-w-[95vw] md:max-w-2xl',
53
+ xl: 'max-w-[95vw] md:max-w-4xl',
54
+ full: 'max-w-[95vw] md:max-w-[90vw] max-h-[90vh]'
55
+ };
56
+
57
+ // Auto-focus management
58
+ onMount(() => {
59
+ if (isOpen) {
60
+ // Focus the modal when it opens
61
+ const focusableElement = modalElement?.querySelector(
62
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
63
+ ) as HTMLElement;
64
+ focusableElement?.focus();
65
+ }
66
+ });
67
+
68
+ // Watch for isOpen changes to manage focus
69
+ $effect(() => {
70
+ if (isOpen) {
71
+ // Prevent body scroll when modal is open
72
+ document.body.style.overflow = 'hidden';
73
+
74
+ // Focus management
75
+ setTimeout(() => {
76
+ const focusableElement = modalElement?.querySelector(
77
+ 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
78
+ ) as HTMLElement;
79
+ focusableElement?.focus();
80
+ }, 50);
81
+ } else {
82
+ // Restore body scroll when modal is closed
83
+ document.body.style.overflow = '';
84
+ }
85
+ });
86
+ </script>
87
+
88
+ <!-- Keyboard event listener -->
89
+ <svelte:window on:keydown={handleKeydown} />
90
+
91
+ {#if isOpen}
92
+ <!-- Modal backdrop -->
93
+ <div
94
+ bind:this={modalElement}
95
+ class="fixed inset-0 z-[150] bg-black/60 dark:bg-slate-900/70 backdrop-blur-sm flex items-center justify-center p-2 md:p-4"
96
+ role="dialog"
97
+ aria-modal="true"
98
+ aria-labelledby={title ? 'modal-title' : undefined}
99
+ tabindex="-1"
100
+ onclick={handleBackdropClick}
101
+ onkeydown={handleKeydown}
102
+ in:fade={{ duration: 200, easing: cubicOut }}
103
+ out:fade={{ duration: 150, easing: cubicOut }}
104
+ >
105
+ <!-- Modal content -->
106
+ <div
107
+ class="bg-white dark:bg-slate-900 rounded-lg md:rounded-xl border border-slate-200 dark:border-slate-800 shadow-2xl w-full {sizeClasses[
108
+ size
109
+ ]} max-h-[95vh] md:max-h-[90vh] overflow-hidden flex flex-col {className}"
110
+ role="document"
111
+ onclick={(e) => e.stopPropagation()}
112
+ onkeydown={(e) => e.stopPropagation()}
113
+ tabindex="-1"
114
+ in:scale={{ duration: 200, easing: cubicOut, start: 0.95 }}
115
+ out:scale={{ duration: 150, easing: cubicOut, start: 0.95 }}
116
+ >
117
+ {#if header || title || closable}
118
+ <!-- Modal header -->
119
+ <div class="border-b border-slate-200 dark:border-slate-800 flex-shrink-0">
120
+ {#if header}
121
+ {@render header()}
122
+ {:else}
123
+ <div class="flex items-center justify-between px-4 py-3 md:px-6 md:py-4">
124
+ {#if title}
125
+ <h2
126
+ id="modal-title"
127
+ class="text-base md:text-lg font-bold text-slate-900 dark:text-slate-100"
128
+ >
129
+ {title}
130
+ </h2>
131
+ {/if}
132
+
133
+ {#if closable}
134
+ <button
135
+ type="button"
136
+ class="p-1.5 md:p-2 rounded-lg text-slate-500 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-violet-500/10 transition-colors"
137
+ onclick={onClose}
138
+ aria-label="Close modal"
139
+ >
140
+ <svg
141
+ class="w-4 h-4 md:w-5 md:h-5"
142
+ fill="none"
143
+ stroke="currentColor"
144
+ viewBox="0 0 24 24"
145
+ >
146
+ <path
147
+ stroke-linecap="round"
148
+ stroke-linejoin="round"
149
+ stroke-width="2"
150
+ d="M6 18L18 6M6 6l12 12"
151
+ />
152
+ </svg>
153
+ </button>
154
+ {/if}
155
+ </div>
156
+ {/if}
157
+ </div>
158
+ {/if}
159
+
160
+ <!-- Modal body -->
161
+ <div bind:this={contentRef} class="flex-1 overflow-y-auto p-4 md:p-6">
162
+ {#if children}
163
+ {@render children()}
164
+ {/if}
165
+ </div>
166
+
167
+ <!-- Modal footer slot (optional) -->
168
+ {#if footer}
169
+ <div
170
+ class="flex items-center justify-end gap-2 md:gap-3 p-4 md:p-6 border-t border-slate-200 dark:border-slate-800 flex-shrink-0"
171
+ >
172
+ {@render footer()}
173
+ </div>
174
+ {/if}
175
+ </div>
176
+ </div>
177
+ {/if}
@@ -0,0 +1,28 @@
1
+ <script lang="ts">
2
+ import Alert from './Alert.svelte';
3
+ import Dialog from './Dialog.svelte';
4
+ import { dialogStore, closeAlert, closeConfirm } from '$frontend/lib/stores/ui/dialog.svelte';
5
+
6
+ const alertState = $derived(dialogStore.alert);
7
+ const confirmState = $derived(dialogStore.confirm);
8
+ </script>
9
+
10
+ <Alert
11
+ isOpen={alertState.isOpen}
12
+ title={alertState.options.title}
13
+ message={alertState.options.message}
14
+ type={alertState.options.type}
15
+ onClose={closeAlert}
16
+ />
17
+
18
+ <Dialog
19
+ isOpen={confirmState.isOpen}
20
+ title={confirmState.options.title}
21
+ message={confirmState.options.message}
22
+ type={confirmState.options.type}
23
+ confirmText={confirmState.options.confirmText}
24
+ cancelText={confirmState.options.cancelText}
25
+ showCancel={true}
26
+ onConfirm={() => closeConfirm(true)}
27
+ onClose={() => closeConfirm(false)}
28
+ />