@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,55 @@
1
+ <script lang="ts">
2
+ import Terminal from './Terminal.svelte';
3
+ import PageTemplate from '../common/PageTemplate.svelte';
4
+ import Button from '../common/Button.svelte';
5
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
6
+ import { terminalStore } from '$frontend/lib/stores/features/terminal.svelte';
7
+ import { projectState } from '$frontend/lib/stores/core/projects.svelte';
8
+
9
+ // Project-aware state
10
+ const hasActiveProject = $derived(projectState.currentProject !== null);
11
+ const projectPath = $derived(projectState.currentProject?.path || '');
12
+ const projectId = $derived(projectState.currentProject?.id || '');
13
+
14
+ function createNewSession() {
15
+ terminalStore.createNewSession(undefined, hasActiveProject ? projectPath : undefined, hasActiveProject ? projectId : undefined);
16
+ }
17
+ </script>
18
+
19
+ <PageTemplate
20
+ title="Terminal"
21
+ description="Interactive command-line interface"
22
+ >
23
+ {#snippet actions()}
24
+ <Button variant="primary" onclick={createNewSession} disabled={!hasActiveProject} class="rounded-lg px-2 sm:px-4 py-1.5 sm:py-2">
25
+ <Icon name="lucide:plus" class="w-3.5 h-3.5 sm:w-4 sm:h-4 sm:mr-2" />
26
+ <span class="hidden sm:inline">New Terminal</span>
27
+ </Button>
28
+ {/snippet}
29
+
30
+ {#if hasActiveProject}
31
+ <!-- Terminal content -->
32
+ <div class="flex flex-col h-full">
33
+ <div class="flex-1 min-h-0">
34
+ <Terminal />
35
+ </div>
36
+ </div>
37
+ {:else}
38
+ <!-- No Active Project Warning -->
39
+ <div class="flex-1 min-h-0 h-96 sm:h-128 lg:h-full bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 rounded-xl overflow-hidden">
40
+ <div class="h-full flex items-center justify-center">
41
+ <div class="text-center p-12">
42
+ <div class="bg-amber-100 dark:bg-amber-900/30 rounded-full w-20 h-20 flex items-center justify-center mx-auto mb-6">
43
+ <Icon name="lucide:triangle-alert" class="w-10 h-10 text-amber-600 dark:text-amber-400" />
44
+ </div>
45
+ <h3 class="text-lg font-bold text-slate-900 dark:text-slate-100 mb-2">
46
+ No Active Project
47
+ </h3>
48
+ <p class="text-sm text-slate-600 dark:text-slate-400 font-medium">
49
+ Select a project from the sidebar to use the terminal
50
+ </p>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ {/if}
55
+ </PageTemplate>
@@ -0,0 +1,142 @@
1
+ <script lang="ts">
2
+ import { tunnelStore } from '$frontend/lib/stores/features/tunnel.svelte';
3
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
4
+ import TunnelQRCode from './TunnelQRCode.svelte';
5
+ import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
6
+
7
+ interface Props {
8
+ port: number;
9
+ publicUrl: string;
10
+ startedAt: string;
11
+ }
12
+
13
+ const { port, publicUrl, startedAt }: Props = $props();
14
+
15
+ let showQR = $state(false);
16
+ let copied = $state(false);
17
+
18
+ async function copyUrl() {
19
+ try {
20
+ await navigator.clipboard.writeText(publicUrl);
21
+ copied = true;
22
+ addNotification({
23
+ type: 'success',
24
+ title: 'Success',
25
+ message: 'URL copied to clipboard'
26
+ });
27
+
28
+ // Reset icon after 2 seconds
29
+ setTimeout(() => {
30
+ copied = false;
31
+ }, 2000);
32
+ } catch (error) {
33
+ console.error('Failed to copy:', error);
34
+ addNotification({
35
+ type: 'error',
36
+ title: 'Error',
37
+ message: 'Failed to copy URL'
38
+ });
39
+ }
40
+ }
41
+
42
+ async function handleStopTunnel() {
43
+ try {
44
+ await tunnelStore.stopTunnel(port);
45
+ addNotification({
46
+ type: 'success',
47
+ title: 'Success',
48
+ message: `Tunnel on port ${port} stopped`
49
+ });
50
+ } catch (error) {
51
+ console.error('Failed to stop tunnel:', error);
52
+ addNotification({
53
+ type: 'error',
54
+ title: 'Error',
55
+ message: 'Failed to stop tunnel'
56
+ });
57
+ }
58
+ }
59
+
60
+ function getTimeAgo(startedAt: string): string {
61
+ const start = new Date(startedAt);
62
+ const now = new Date();
63
+ const diffMs = now.getTime() - start.getTime();
64
+
65
+ const diffMins = Math.floor(diffMs / 60000);
66
+ const diffHours = Math.floor(diffMs / 3600000);
67
+
68
+ if (diffMins < 1) return 'Just now';
69
+ if (diffMins < 60) return `${diffMins}m ago`;
70
+ if (diffHours < 24) return `${diffHours}h ago`;
71
+ return `${Math.floor(diffHours / 24)}d ago`;
72
+ }
73
+ </script>
74
+
75
+ <div class="bg-white dark:bg-slate-800/50 border border-slate-200 dark:border-slate-700 rounded-lg p-4">
76
+ <!-- Header -->
77
+ <div class="flex items-start justify-between gap-3 mb-3">
78
+ <div class="flex items-center gap-2">
79
+ <div class="flex items-center gap-1.5">
80
+ <div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
81
+ <span class="text-sm font-semibold text-slate-900 dark:text-slate-100">Port {port}</span>
82
+ </div>
83
+ <span class="text-xs text-slate-500 dark:text-slate-400">{getTimeAgo(startedAt)}</span>
84
+ </div>
85
+ <button
86
+ onclick={handleStopTunnel}
87
+ class="flex items-center gap-1.5 px-2 py-1 text-xs font-medium text-red-600 dark:text-red-400 hover:bg-red-50 dark:hover:bg-red-900/20 rounded transition-colors"
88
+ >
89
+ <Icon name="lucide:circle-x" class="w-3.5 h-3.5" />
90
+ Stop
91
+ </button>
92
+ </div>
93
+
94
+ <!-- Public URL -->
95
+ <div class="space-y-3">
96
+ <div class="flex items-center gap-2">
97
+ <Icon name="lucide:link" class="w-4 h-4 text-violet-600 dark:text-violet-400 flex-shrink-0" />
98
+ <a
99
+ href={publicUrl}
100
+ target="_blank"
101
+ rel="noopener noreferrer"
102
+ class="text-sm text-violet-600 dark:text-violet-400 hover:underline truncate flex-1 font-medium"
103
+ >
104
+ {publicUrl}
105
+ </a>
106
+ </div>
107
+
108
+ <!-- Action Buttons -->
109
+ <div class="flex gap-2">
110
+ <button
111
+ onclick={copyUrl}
112
+ class={`flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium transition-colors rounded ${
113
+ copied
114
+ ? 'text-green-700 dark:text-green-400 bg-green-100 dark:bg-green-900/20'
115
+ : 'text-slate-700 dark:text-slate-300 bg-slate-100 dark:bg-slate-700/50 hover:bg-slate-200 dark:hover:bg-slate-700'
116
+ }`}
117
+ >
118
+ {#if copied}
119
+ <Icon name="lucide:check" class="w-3.5 h-3.5" />
120
+ Copied!
121
+ {:else}
122
+ <Icon name="lucide:copy" class="w-3.5 h-3.5" />
123
+ Copy
124
+ {/if}
125
+ </button>
126
+ <button
127
+ onclick={() => (showQR = !showQR)}
128
+ class="flex items-center gap-1.5 px-3 py-1.5 text-xs font-medium text-slate-700 dark:text-slate-300 bg-slate-100 dark:bg-slate-700/50 hover:bg-slate-200 dark:hover:bg-slate-700 rounded transition-colors"
129
+ >
130
+ <Icon name="lucide:qr-code" class="w-3.5 h-3.5" />
131
+ {showQR ? 'Hide' : 'Show'} QR
132
+ </button>
133
+ </div>
134
+
135
+ <!-- QR Code -->
136
+ {#if showQR}
137
+ <div class="pt-2 border-t border-slate-200 dark:border-slate-700">
138
+ <TunnelQRCode value={publicUrl} />
139
+ </div>
140
+ {/if}
141
+ </div>
142
+ </div>
@@ -0,0 +1,54 @@
1
+ <script lang="ts">
2
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
3
+ import { tunnelStore } from '$frontend/lib/stores/features/tunnel.svelte';
4
+
5
+ interface Props {
6
+ collapsed?: boolean;
7
+ onClick: () => void;
8
+ }
9
+
10
+ const { collapsed = false, onClick }: Props = $props();
11
+
12
+ const activeTunnels = $derived(tunnelStore.tunnels);
13
+ const hasActiveTunnels = $derived(activeTunnels.length > 0);
14
+ </script>
15
+
16
+ {#if collapsed}
17
+ <!-- Collapsed: Icon Only -->
18
+ <button
19
+ type="button"
20
+ class="flex items-center justify-center w-9 h-9 bg-transparent border-none rounded-lg text-slate-500 cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100 relative"
21
+ onclick={onClick}
22
+ aria-label="Public Tunnel"
23
+ title="Public Tunnel"
24
+ >
25
+ <Icon name="lucide:cloud-upload" class="w-5 h-5" />
26
+ {#if hasActiveTunnels}
27
+ <span
28
+ class="absolute top-1 right-1 w-2 h-2 bg-green-500 rounded-full border-2 border-slate-50 dark:border-slate-900/95"
29
+ ></span>
30
+ {/if}
31
+ </button>
32
+ {:else}
33
+ <!-- Expanded: Full Width -->
34
+ <button
35
+ type="button"
36
+ class="flex items-center gap-2.5 w-full py-2.5 px-3 bg-transparent border-none rounded-lg text-slate-500 text-sm cursor-pointer transition-all duration-150 hover:bg-violet-500/10 hover:text-slate-900 dark:hover:text-slate-100"
37
+ onclick={onClick}
38
+ >
39
+ <div class="relative">
40
+ <Icon name="lucide:cloud-upload" class="w-4 h-4" />
41
+ {#if hasActiveTunnels}
42
+ <span
43
+ class="absolute -top-1 -right-1 w-2 h-2 bg-green-500 rounded-full border-2 border-slate-50 dark:border-slate-900/95 animate-pulse"
44
+ ></span>
45
+ {/if}
46
+ </div>
47
+ <span class="flex-1 text-left">Public Tunnel</span>
48
+ {#if hasActiveTunnels}
49
+ <span class="text-xs text-green-600 dark:text-green-400 font-semibold">
50
+ {activeTunnels.length}
51
+ </span>
52
+ {/if}
53
+ </button>
54
+ {/if}
@@ -0,0 +1,284 @@
1
+ <script lang="ts">
2
+ import { tunnelStore } from '$frontend/lib/stores/features/tunnel.svelte';
3
+ import Icon from '$frontend/lib/components/common/Icon.svelte';
4
+ import Modal from '$frontend/lib/components/common/Modal.svelte';
5
+ import Checkbox from '$frontend/lib/components/common/Checkbox.svelte';
6
+
7
+ let port = $state(9141);
8
+ let autoStopMinutes = $state(60);
9
+ let showWarning = $state(false);
10
+ let dontShowWarningAgain = $state(false);
11
+ let errorDismissed = $state(false);
12
+ let warningMessage = $state<string | null>(null);
13
+ let warningDismissed = $state(false);
14
+
15
+ // Info box visibility - load from localStorage
16
+ let showInfoBox = $state(
17
+ typeof window !== 'undefined'
18
+ ? localStorage.getItem('tunnel-info-dismissed') !== 'true'
19
+ : true
20
+ );
21
+
22
+ async function handleStartTunnel() {
23
+ // Check if tunnel already exists for this port
24
+ if (tunnelStore.getTunnel(port)) {
25
+ warningMessage = `Tunnel already active on port ${port}`;
26
+ warningDismissed = false;
27
+ showWarning = false;
28
+ return;
29
+ }
30
+
31
+ // Save "don't show again" preference
32
+ if (dontShowWarningAgain) {
33
+ localStorage.setItem('tunnel-warning-dismissed', 'true');
34
+ }
35
+
36
+ // Close modal immediately
37
+ showWarning = false;
38
+
39
+ try {
40
+ await tunnelStore.startTunnel(port, autoStopMinutes);
41
+ } catch (error) {
42
+ console.error('Failed to start tunnel:', error);
43
+ }
44
+ }
45
+
46
+ // Get loading and progress state for current port
47
+ const isLoading = $derived(tunnelStore.isLoading(port));
48
+ const progress = $derived(tunnelStore.getProgress(port));
49
+ const error = $derived(tunnelStore.getError(port));
50
+
51
+ function openWarningModal() {
52
+ // Clear any previous warning messages
53
+ warningMessage = null;
54
+
55
+ // Reset error and warning dismissed state when user tries to start again
56
+ errorDismissed = false;
57
+ warningDismissed = false;
58
+
59
+ // Check if user has dismissed warning permanently
60
+ const securityWarningDismissed = localStorage.getItem('tunnel-warning-dismissed') === 'true';
61
+ if (securityWarningDismissed) {
62
+ // Skip warning and start tunnel directly
63
+ handleStartTunnel();
64
+ } else {
65
+ showWarning = true;
66
+ }
67
+ }
68
+
69
+ function closeInfoBox() {
70
+ showInfoBox = false;
71
+ localStorage.setItem('tunnel-info-dismissed', 'true');
72
+ }
73
+
74
+ function dismissError() {
75
+ errorDismissed = true;
76
+ }
77
+
78
+ function dismissWarning() {
79
+ warningDismissed = true;
80
+ }
81
+ </script>
82
+
83
+ <div class="space-y-6">
84
+ <!-- Info Card -->
85
+ {#if showInfoBox}
86
+ <div
87
+ class="p-4 bg-violet-500/5 dark:bg-violet-500/10 border border-violet-500/20 rounded-xl"
88
+ >
89
+ <div class="flex gap-3">
90
+ <Icon name="lucide:info" class="w-5 h-5 text-violet-600 dark:text-violet-400 flex-shrink-0 mt-0.5" />
91
+ <div class="flex-1 text-sm text-slate-700 dark:text-slate-300">
92
+ <p class="font-semibold mb-1">What is Public Tunnel?</p>
93
+ <p class="text-slate-600 dark:text-slate-400">
94
+ Create a secure HTTPS tunnel to share your local development server with anyone on the
95
+ internet.
96
+ </p>
97
+ </div>
98
+ <button
99
+ onclick={closeInfoBox}
100
+ class="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors flex-shrink-0"
101
+ title="Dismiss"
102
+ >
103
+ <Icon name="lucide:x" class="w-5 h-5" />
104
+ </button>
105
+ </div>
106
+ </div>
107
+ {/if}
108
+
109
+ <!-- Input Fields -->
110
+ <div class="space-y-4">
111
+ <!-- Port Input -->
112
+ <div class="space-y-2">
113
+ <label for="port" class="block text-sm font-semibold text-slate-700 dark:text-slate-300">
114
+ Local Port
115
+ </label>
116
+ <input
117
+ id="port"
118
+ type="number"
119
+ bind:value={port}
120
+ min="1"
121
+ max="65535"
122
+ placeholder="3000"
123
+ class="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 focus:border-violet-500 focus:ring-2 focus:ring-violet-200 dark:focus:ring-violet-900/20 bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 placeholder-slate-400 dark:placeholder-slate-500"
124
+ />
125
+ <!-- <p class="text-xs text-slate-500 dark:text-slate-400">
126
+ Port number where your app is running (e.g., 3000 for Vite, 8080 for API)
127
+ </p> -->
128
+ </div>
129
+
130
+ <!-- Auto-Stop Timer -->
131
+ <div class="space-y-2">
132
+ <label for="autoStop" class="block text-sm font-semibold text-slate-700 dark:text-slate-300">
133
+ Auto-Stop After
134
+ </label>
135
+ <select
136
+ id="autoStop"
137
+ bind:value={autoStopMinutes}
138
+ class="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 focus:border-violet-500 focus:ring-2 focus:ring-violet-200 dark:focus:ring-violet-900/20 bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100"
139
+ >
140
+ <option value={30}>30 minutes</option>
141
+ <option value={60}>1 hour</option>
142
+ <option value={120}>2 hours</option>
143
+ <option value={180}>3 hours</option>
144
+ </select>
145
+ <!-- <p class="text-xs text-slate-500 dark:text-slate-400">
146
+ Tunnel will automatically stop after this duration
147
+ </p> -->
148
+ </div>
149
+
150
+ <!-- Start Button -->
151
+ <button
152
+ onclick={openWarningModal}
153
+ disabled={isLoading}
154
+ class="inline-flex items-center justify-center font-semibold transition-colors duration-200 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed w-full px-3 md:px-4 py-2.5 text-sm rounded-lg bg-violet-600 hover:bg-violet-700 text-white gap-2"
155
+ >
156
+ {#if isLoading}
157
+ <div
158
+ class="animate-spin rounded-full h-4 w-4 border-2 border-current border-t-transparent"
159
+ ></div>
160
+ <span>
161
+ {#if progress.stage === 'checking-binary'}
162
+ Checking binary...
163
+ {:else if progress.stage === 'downloading-binary'}
164
+ Downloading binary...
165
+ {:else if progress.stage === 'binary-ready'}
166
+ Binary ready
167
+ {:else if progress.stage === 'starting-tunnel'}
168
+ Starting tunnel...
169
+ {:else if progress.stage === 'generating-url'}
170
+ Generating URL...
171
+ {:else if progress.stage === 'connected'}
172
+ Connected!
173
+ {:else}
174
+ Setting up tunnel...
175
+ {/if}
176
+ </span>
177
+ {:else}
178
+ <Icon name="lucide:cloud-upload" class="w-4 h-4" />
179
+ <span>Start Public Tunnel</span>
180
+ {/if}
181
+ </button>
182
+
183
+ <!-- Warning Message (e.g., tunnel already active) -->
184
+ {#if warningMessage && !warningDismissed}
185
+ <div
186
+ class="bg-yellow-500/10 border border-yellow-500/20 rounded-lg p-4 text-sm"
187
+ >
188
+ <div class="flex items-start gap-2 text-yellow-600 dark:text-yellow-400">
189
+ <Icon name="lucide:triangle-alert" class="w-5 h-5 flex-shrink-0 mt-0.5" />
190
+ <div class="flex-1 space-y-1">
191
+ <div class="font-semibold">Warning</div>
192
+ <div class="text-yellow-500 dark:text-yellow-300">
193
+ {warningMessage}
194
+ </div>
195
+ </div>
196
+ <button
197
+ onclick={dismissWarning}
198
+ class="text-yellow-400 hover:text-yellow-600 dark:hover:text-yellow-300 transition-colors flex-shrink-0"
199
+ title="Dismiss"
200
+ >
201
+ <Icon name="lucide:x" class="w-5 h-5" />
202
+ </button>
203
+ </div>
204
+ </div>
205
+ {/if}
206
+
207
+ {#if (error || progress.stage === 'failed') && !errorDismissed}
208
+ <div
209
+ class="bg-red-500/10 border border-red-500/20 rounded-lg p-4 text-sm space-y-3"
210
+ >
211
+ <div class="flex items-start gap-2 text-red-600 dark:text-red-400">
212
+ <Icon name="lucide:circle-alert" class="w-5 h-5 flex-shrink-0 mt-0.5" />
213
+ <div class="flex-1 space-y-1">
214
+ <div class="font-semibold">Tunnel Failed</div>
215
+ <div class="text-red-500 dark:text-red-300">
216
+ {error}
217
+ </div>
218
+ </div>
219
+ <button
220
+ onclick={dismissError}
221
+ class="text-red-400 hover:text-red-600 dark:hover:text-red-300 transition-colors flex-shrink-0"
222
+ title="Dismiss"
223
+ >
224
+ <Icon name="lucide:x" class="w-5 h-5" />
225
+ </button>
226
+ </div>
227
+
228
+ <!-- Common solutions -->
229
+ <div class="text-xs text-slate-700 dark:text-slate-300 space-y-1 pt-2 border-t border-red-500/20">
230
+ <div class="font-semibold">Common solutions:</div>
231
+ <ul class="list-disc list-inside space-y-0.5 text-slate-600 dark:text-slate-400">
232
+ <li>Ensure your app is running on port {port}</li>
233
+ <li>Check your internet connection</li>
234
+ <li>Temporarily disable firewall/antivirus</li>
235
+ </ul>
236
+ </div>
237
+ </div>
238
+ {/if}
239
+ </div>
240
+ </div>
241
+
242
+ <!-- Warning Modal -->
243
+ {#if showWarning}
244
+ <Modal isOpen={showWarning} onClose={() => (showWarning = false)} title="Security Warning">
245
+ <div class="space-y-4">
246
+ <div class="flex items-start gap-3">
247
+ <Icon name="lucide:triangle-alert" class="w-8 h-8 text-yellow-500 flex-shrink-0" />
248
+ <div class="text-sm text-slate-700 dark:text-slate-300">
249
+ <p class="mb-3">Your project will be publicly accessible on the internet.</p>
250
+ <p class="font-semibold mb-2">Please ensure:</p>
251
+ <ul class="list-disc list-inside space-y-1 text-slate-600 dark:text-slate-400">
252
+ <li>No sensitive data is exposed</li>
253
+ <li>Environment variables are properly configured</li>
254
+ <li>This is for <strong class="text-slate-900 dark:text-slate-100">testing only</strong>, not production</li>
255
+ </ul>
256
+ </div>
257
+ </div>
258
+
259
+ <!-- Don't show again checkbox -->
260
+ <div class="pt-2">
261
+ <Checkbox
262
+ id="dontShowWarning"
263
+ bind:checked={dontShowWarningAgain}
264
+ label="Don't show this warning again"
265
+ />
266
+ </div>
267
+
268
+ <div class="flex gap-2 justify-end pt-2">
269
+ <button
270
+ onclick={() => (showWarning = false)}
271
+ class="inline-flex items-center justify-center font-semibold transition-colors duration-200 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed px-3 md:px-4 py-2.5 text-sm rounded-lg border border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800"
272
+ >
273
+ Cancel
274
+ </button>
275
+ <button
276
+ onclick={handleStartTunnel}
277
+ class="inline-flex items-center justify-center font-semibold transition-colors duration-200 focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed px-3 md:px-4 py-2.5 text-sm rounded-lg bg-violet-600 hover:bg-violet-700 text-white"
278
+ >
279
+ I Understand, Continue
280
+ </button>
281
+ </div>
282
+ </div>
283
+ </Modal>
284
+ {/if}
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import Modal from '$frontend/lib/components/common/Modal.svelte';
3
+ import TunnelInactive from './TunnelInactive.svelte';
4
+ import TunnelActive from './TunnelActive.svelte';
5
+ import { tunnelStore } from '$frontend/lib/stores/features/tunnel.svelte';
6
+
7
+ interface Props {
8
+ isOpen: boolean;
9
+ onClose: () => void;
10
+ }
11
+
12
+ let { isOpen = $bindable(), onClose }: Props = $props();
13
+
14
+ const activeTunnels = $derived(tunnelStore.tunnels);
15
+
16
+ // Load tunnels when modal opens
17
+ $effect(() => {
18
+ if (isOpen) {
19
+ tunnelStore.checkStatus();
20
+ }
21
+ });
22
+ </script>
23
+
24
+ <Modal {isOpen} {onClose} title="Public Tunnel" size="md">
25
+ <div class="space-y-6">
26
+ <!-- Add new tunnel form -->
27
+ <TunnelInactive />
28
+
29
+ <!-- Active tunnels list -->
30
+ {#if activeTunnels.length > 0}
31
+ <div>
32
+ <div class="text-sm font-semibold text-slate-700 dark:text-slate-300 mb-3">
33
+ Active Tunnels
34
+ </div>
35
+ <div class="space-y-3">
36
+ {#each activeTunnels as tunnel (tunnel.port)}
37
+ <TunnelActive
38
+ port={tunnel.port}
39
+ publicUrl={tunnel.publicUrl}
40
+ startedAt={tunnel.startedAt}
41
+ />
42
+ {/each}
43
+ </div>
44
+ </div>
45
+ {/if}
46
+ </div>
47
+ </Modal>
@@ -0,0 +1,49 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import QRCode from 'qrcode';
4
+
5
+ interface Props {
6
+ value: string;
7
+ size?: number;
8
+ }
9
+
10
+ const { value, size = 200 }: Props = $props();
11
+ let canvas: HTMLCanvasElement;
12
+
13
+ onMount(() => {
14
+ generateQR();
15
+ });
16
+
17
+ $effect(() => {
18
+ if (value) {
19
+ generateQR();
20
+ }
21
+ });
22
+
23
+ async function generateQR() {
24
+ if (!canvas) return;
25
+
26
+ try {
27
+ await QRCode.toCanvas(canvas, value, {
28
+ width: size,
29
+ margin: 2,
30
+ color: {
31
+ dark: '#000000',
32
+ light: '#FFFFFF'
33
+ }
34
+ });
35
+ } catch (error) {
36
+ console.error('Failed to generate QR code:', error);
37
+ }
38
+ }
39
+ </script>
40
+
41
+ <div class="qr-wrapper flex justify-center p-2">
42
+ <canvas bind:this={canvas} class="qr-canvas rounded-lg"></canvas>
43
+ </div>
44
+
45
+ <style>
46
+ .qr-canvas {
47
+ box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
48
+ }
49
+ </style>