@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.
- package/.env.example +6 -0
- package/.github/workflows/release.yml +60 -0
- package/.github/workflows/test.yml +40 -0
- package/CONTRIBUTING.md +499 -0
- package/LICENSE +21 -0
- package/README.md +209 -0
- package/backend/index.ts +156 -0
- package/backend/lib/chat/helpers.ts +42 -0
- package/backend/lib/chat/index.ts +2 -0
- package/backend/lib/chat/stream-manager.ts +1126 -0
- package/backend/lib/database/README.md +77 -0
- package/backend/lib/database/index.ts +119 -0
- package/backend/lib/database/migrations/001_create_projects_table.ts +31 -0
- package/backend/lib/database/migrations/002_create_chat_sessions_table.ts +33 -0
- package/backend/lib/database/migrations/003_create_messages_table.ts +32 -0
- package/backend/lib/database/migrations/004_create_prompt_templates_table.ts +34 -0
- package/backend/lib/database/migrations/005_create_settings_table.ts +24 -0
- package/backend/lib/database/migrations/006_add_user_to_messages.ts +58 -0
- package/backend/lib/database/migrations/007_create_stream_states_table.ts +41 -0
- package/backend/lib/database/migrations/008_create_message_snapshots_table.ts +62 -0
- package/backend/lib/database/migrations/009_add_delta_snapshot_fields.ts +41 -0
- package/backend/lib/database/migrations/010_add_soft_delete_and_branch_support.ts +70 -0
- package/backend/lib/database/migrations/011_git_like_commit_graph.ts +156 -0
- package/backend/lib/database/migrations/012_add_file_change_statistics.ts +41 -0
- package/backend/lib/database/migrations/013_checkpoint_tree_state.ts +118 -0
- package/backend/lib/database/migrations/014_add_engine_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/015_add_model_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/016_create_user_projects_table.ts +34 -0
- package/backend/lib/database/migrations/017_add_current_session_to_user_projects.ts +32 -0
- package/backend/lib/database/migrations/018_create_claude_accounts_table.ts +24 -0
- package/backend/lib/database/migrations/019_add_claude_account_to_sessions.ts +18 -0
- package/backend/lib/database/migrations/020_add_snapshot_tree_hash.ts +32 -0
- package/backend/lib/database/migrations/021_drop_prompt_templates_table.ts +33 -0
- package/backend/lib/database/migrations/index.ts +154 -0
- package/backend/lib/database/queries/checkpoint-queries.ts +87 -0
- package/backend/lib/database/queries/engine-queries.ts +75 -0
- package/backend/lib/database/queries/index.ts +9 -0
- package/backend/lib/database/queries/message-queries.ts +472 -0
- package/backend/lib/database/queries/project-queries.ts +117 -0
- package/backend/lib/database/queries/session-queries.ts +271 -0
- package/backend/lib/database/queries/settings-queries.ts +34 -0
- package/backend/lib/database/queries/snapshot-queries.ts +326 -0
- package/backend/lib/database/queries/utils-queries.ts +59 -0
- package/backend/lib/database/seeders/index.ts +13 -0
- package/backend/lib/database/seeders/settings_seeder.ts +84 -0
- package/backend/lib/database/utils/connection.ts +174 -0
- package/backend/lib/database/utils/index.ts +4 -0
- package/backend/lib/database/utils/migration-runner.ts +118 -0
- package/backend/lib/database/utils/seeder-runner.ts +121 -0
- package/backend/lib/engine/adapters/claude/environment.ts +164 -0
- package/backend/lib/engine/adapters/claude/error-handler.ts +60 -0
- package/backend/lib/engine/adapters/claude/index.ts +1 -0
- package/backend/lib/engine/adapters/claude/path-utils.ts +38 -0
- package/backend/lib/engine/adapters/claude/stream.ts +177 -0
- package/backend/lib/engine/adapters/opencode/index.ts +2 -0
- package/backend/lib/engine/adapters/opencode/message-converter.ts +862 -0
- package/backend/lib/engine/adapters/opencode/server.ts +104 -0
- package/backend/lib/engine/adapters/opencode/stream.ts +755 -0
- package/backend/lib/engine/index.ts +196 -0
- package/backend/lib/engine/types.ts +58 -0
- package/backend/lib/files/file-operations.ts +478 -0
- package/backend/lib/files/file-reading.ts +308 -0
- package/backend/lib/files/file-watcher.ts +383 -0
- package/backend/lib/files/path-browsing.ts +382 -0
- package/backend/lib/git/git-executor.ts +88 -0
- package/backend/lib/git/git-parser.ts +411 -0
- package/backend/lib/git/git-service.ts +505 -0
- package/backend/lib/mcp/README.md +1144 -0
- package/backend/lib/mcp/config.ts +316 -0
- package/backend/lib/mcp/index.ts +35 -0
- package/backend/lib/mcp/project-context.ts +236 -0
- package/backend/lib/mcp/servers/browser-automation/actions.ts +156 -0
- package/backend/lib/mcp/servers/browser-automation/browser.ts +419 -0
- package/backend/lib/mcp/servers/browser-automation/index.ts +791 -0
- package/backend/lib/mcp/servers/browser-automation/inspection.ts +501 -0
- package/backend/lib/mcp/servers/helper.ts +143 -0
- package/backend/lib/mcp/servers/index.ts +45 -0
- package/backend/lib/mcp/servers/weather/get-temperature.ts +56 -0
- package/backend/lib/mcp/servers/weather/index.ts +31 -0
- package/backend/lib/mcp/stdio-server.ts +103 -0
- package/backend/lib/mcp/types.ts +65 -0
- package/backend/lib/preview/browser/browser-audio-capture.ts +86 -0
- package/backend/lib/preview/browser/browser-console-manager.ts +263 -0
- package/backend/lib/preview/browser/browser-dialog-handler.ts +222 -0
- package/backend/lib/preview/browser/browser-interaction-handler.ts +421 -0
- package/backend/lib/preview/browser/browser-mcp-control.ts +415 -0
- package/backend/lib/preview/browser/browser-native-ui-handler.ts +512 -0
- package/backend/lib/preview/browser/browser-navigation-tracker.ts +104 -0
- package/backend/lib/preview/browser/browser-pool.ts +357 -0
- package/backend/lib/preview/browser/browser-preview-service.ts +882 -0
- package/backend/lib/preview/browser/browser-tab-manager.ts +935 -0
- package/backend/lib/preview/browser/browser-video-capture.ts +695 -0
- package/backend/lib/preview/browser/scripts/audio-stream.ts +292 -0
- package/backend/lib/preview/browser/scripts/cursor-tracking.ts +85 -0
- package/backend/lib/preview/browser/scripts/video-stream.ts +438 -0
- package/backend/lib/preview/browser/types.ts +359 -0
- package/backend/lib/preview/index.ts +24 -0
- package/backend/lib/project/index.ts +2 -0
- package/backend/lib/project/status-manager.ts +182 -0
- package/backend/lib/shared/index.ts +2 -0
- package/backend/lib/shared/port-utils.ts +25 -0
- package/backend/lib/shared/process-manager.ts +281 -0
- package/backend/lib/snapshot/blob-store.ts +227 -0
- package/backend/lib/snapshot/gitignore.ts +307 -0
- package/backend/lib/snapshot/helpers.ts +397 -0
- package/backend/lib/snapshot/snapshot-service.ts +483 -0
- package/backend/lib/terminal/helpers.ts +14 -0
- package/backend/lib/terminal/index.ts +8 -0
- package/backend/lib/terminal/pty-manager.ts +4 -0
- package/backend/lib/terminal/pty-session-manager.ts +387 -0
- package/backend/lib/terminal/shell-utils.ts +313 -0
- package/backend/lib/terminal/stream-manager.ts +293 -0
- package/backend/lib/tunnel/global-tunnel-manager.ts +243 -0
- package/backend/lib/tunnel/project-tunnel-manager.ts +311 -0
- package/backend/lib/user/helpers.ts +87 -0
- package/backend/lib/utils/ws.ts +944 -0
- package/backend/lib/vite-dev.ts +353 -0
- package/backend/middleware/cors.ts +15 -0
- package/backend/middleware/error-handler.ts +49 -0
- package/backend/middleware/logger.ts +9 -0
- package/backend/types/api.ts +24 -0
- package/backend/ws/README.md +1505 -0
- package/backend/ws/chat/background.ts +198 -0
- package/backend/ws/chat/index.ts +21 -0
- package/backend/ws/chat/stream.ts +707 -0
- package/backend/ws/engine/claude/accounts.ts +401 -0
- package/backend/ws/engine/claude/index.ts +13 -0
- package/backend/ws/engine/claude/status.ts +43 -0
- package/backend/ws/engine/index.ts +14 -0
- package/backend/ws/engine/opencode/index.ts +11 -0
- package/backend/ws/engine/opencode/status.ts +30 -0
- package/backend/ws/engine/utils.ts +36 -0
- package/backend/ws/files/index.ts +30 -0
- package/backend/ws/files/read.ts +189 -0
- package/backend/ws/files/search.ts +453 -0
- package/backend/ws/files/watch.ts +124 -0
- package/backend/ws/files/write.ts +143 -0
- package/backend/ws/git/branch.ts +106 -0
- package/backend/ws/git/commit.ts +39 -0
- package/backend/ws/git/conflict.ts +68 -0
- package/backend/ws/git/diff.ts +69 -0
- package/backend/ws/git/index.ts +24 -0
- package/backend/ws/git/log.ts +41 -0
- package/backend/ws/git/remote.ts +214 -0
- package/backend/ws/git/staging.ts +84 -0
- package/backend/ws/git/status.ts +90 -0
- package/backend/ws/index.ts +69 -0
- package/backend/ws/mcp/index.ts +61 -0
- package/backend/ws/messages/crud.ts +74 -0
- package/backend/ws/messages/index.ts +14 -0
- package/backend/ws/preview/browser/cleanup.ts +129 -0
- package/backend/ws/preview/browser/console.ts +114 -0
- package/backend/ws/preview/browser/interact.ts +513 -0
- package/backend/ws/preview/browser/mcp.ts +129 -0
- package/backend/ws/preview/browser/native-ui.ts +235 -0
- package/backend/ws/preview/browser/stats.ts +55 -0
- package/backend/ws/preview/browser/tab-info.ts +126 -0
- package/backend/ws/preview/browser/tab.ts +166 -0
- package/backend/ws/preview/browser/webcodecs.ts +293 -0
- package/backend/ws/preview/index.ts +146 -0
- package/backend/ws/projects/crud.ts +113 -0
- package/backend/ws/projects/index.ts +25 -0
- package/backend/ws/projects/presence.ts +46 -0
- package/backend/ws/projects/status.ts +116 -0
- package/backend/ws/sessions/crud.ts +327 -0
- package/backend/ws/sessions/index.ts +33 -0
- package/backend/ws/settings/crud.ts +112 -0
- package/backend/ws/settings/index.ts +14 -0
- package/backend/ws/snapshot/index.ts +17 -0
- package/backend/ws/snapshot/restore.ts +173 -0
- package/backend/ws/snapshot/timeline.ts +141 -0
- package/backend/ws/system/index.ts +14 -0
- package/backend/ws/system/operations.ts +49 -0
- package/backend/ws/terminal/index.ts +40 -0
- package/backend/ws/terminal/persistence.ts +153 -0
- package/backend/ws/terminal/session.ts +382 -0
- package/backend/ws/terminal/stream.ts +79 -0
- package/backend/ws/tunnel/index.ts +14 -0
- package/backend/ws/tunnel/operations.ts +91 -0
- package/backend/ws/types.ts +20 -0
- package/backend/ws/user/crud.ts +156 -0
- package/backend/ws/user/index.ts +14 -0
- package/bin/clopen.ts +307 -0
- package/bun.lock +1352 -0
- package/frontend/App.svelte +34 -0
- package/frontend/app.css +313 -0
- package/frontend/lib/app-environment.ts +10 -0
- package/frontend/lib/components/chat/ChatInterface.svelte +407 -0
- package/frontend/lib/components/chat/formatters/ErrorMessage.svelte +57 -0
- package/frontend/lib/components/chat/formatters/MessageFormatter.svelte +224 -0
- package/frontend/lib/components/chat/formatters/TextMessage.svelte +395 -0
- package/frontend/lib/components/chat/formatters/Tools.svelte +70 -0
- package/frontend/lib/components/chat/formatters/index.ts +3 -0
- package/frontend/lib/components/chat/input/ChatInput.svelte +421 -0
- package/frontend/lib/components/chat/input/components/ChatInputActions.svelte +78 -0
- package/frontend/lib/components/chat/input/components/DragDropOverlay.svelte +30 -0
- package/frontend/lib/components/chat/input/components/EditModeIndicator.svelte +33 -0
- package/frontend/lib/components/chat/input/components/EngineModelPicker.svelte +619 -0
- package/frontend/lib/components/chat/input/components/FileAttachmentPreview.svelte +48 -0
- package/frontend/lib/components/chat/input/components/LoadingIndicator.svelte +31 -0
- package/frontend/lib/components/chat/input/composables/use-animations.svelte.ts +201 -0
- package/frontend/lib/components/chat/input/composables/use-chat-actions.svelte.ts +148 -0
- package/frontend/lib/components/chat/input/composables/use-file-handling.svelte.ts +216 -0
- package/frontend/lib/components/chat/input/composables/use-input-state.svelte.ts +357 -0
- package/frontend/lib/components/chat/input/composables/use-textarea-resize.svelte.ts +57 -0
- package/frontend/lib/components/chat/message/ChatMessage.svelte +478 -0
- package/frontend/lib/components/chat/message/ChatMessages.svelte +541 -0
- package/frontend/lib/components/chat/message/DateSeparator.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageBubble.svelte +86 -0
- package/frontend/lib/components/chat/message/MessageHeader.svelte +157 -0
- package/frontend/lib/components/chat/modal/DebugModal.svelte +59 -0
- package/frontend/lib/components/chat/modal/TokenUsageModal.svelte +124 -0
- package/frontend/lib/components/chat/shared/index.ts +2 -0
- package/frontend/lib/components/chat/shared/utils.ts +116 -0
- package/frontend/lib/components/chat/tools/BashOutputTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/BashTool.svelte +46 -0
- package/frontend/lib/components/chat/tools/CustomMcpTool.svelte +139 -0
- package/frontend/lib/components/chat/tools/EditTool.svelte +48 -0
- package/frontend/lib/components/chat/tools/ExitPlanModeTool.svelte +32 -0
- package/frontend/lib/components/chat/tools/GlobTool.svelte +51 -0
- package/frontend/lib/components/chat/tools/GrepTool.svelte +90 -0
- package/frontend/lib/components/chat/tools/KillShellTool.svelte +26 -0
- package/frontend/lib/components/chat/tools/ListMcpResourcesTool.svelte +31 -0
- package/frontend/lib/components/chat/tools/NotebookEditTool.svelte +38 -0
- package/frontend/lib/components/chat/tools/ReadMcpResourceTool.svelte +34 -0
- package/frontend/lib/components/chat/tools/ReadTool.svelte +41 -0
- package/frontend/lib/components/chat/tools/TaskTool.svelte +64 -0
- package/frontend/lib/components/chat/tools/TodoWriteTool.svelte +75 -0
- package/frontend/lib/components/chat/tools/WebFetchTool.svelte +35 -0
- package/frontend/lib/components/chat/tools/WebSearchTool.svelte +84 -0
- package/frontend/lib/components/chat/tools/WriteTool.svelte +33 -0
- package/frontend/lib/components/chat/tools/components/CodeBlock.svelte +79 -0
- package/frontend/lib/components/chat/tools/components/DiffBlock.svelte +408 -0
- package/frontend/lib/components/chat/tools/components/FileHeader.svelte +45 -0
- package/frontend/lib/components/chat/tools/components/InfoLine.svelte +19 -0
- package/frontend/lib/components/chat/tools/components/StatsBadges.svelte +27 -0
- package/frontend/lib/components/chat/tools/components/TerminalCommand.svelte +54 -0
- package/frontend/lib/components/chat/tools/components/index.ts +7 -0
- package/frontend/lib/components/chat/tools/index.ts +26 -0
- package/frontend/lib/components/chat/widgets/FloatingTodoList.svelte +249 -0
- package/frontend/lib/components/chat/widgets/TokenUsage.svelte +78 -0
- package/frontend/lib/components/checkpoint/TimelineModal.svelte +391 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineEdge.svelte +26 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineGraph.svelte +87 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineNode.svelte +108 -0
- package/frontend/lib/components/checkpoint/timeline/TimelineVersionGroup.svelte +59 -0
- package/frontend/lib/components/checkpoint/timeline/animation.ts +168 -0
- package/frontend/lib/components/checkpoint/timeline/config.ts +44 -0
- package/frontend/lib/components/checkpoint/timeline/graph-builder.ts +304 -0
- package/frontend/lib/components/checkpoint/timeline/types.ts +65 -0
- package/frontend/lib/components/checkpoint/timeline/utils.ts +53 -0
- package/frontend/lib/components/common/Alert.svelte +139 -0
- package/frontend/lib/components/common/AvatarBubble.svelte +56 -0
- package/frontend/lib/components/common/Button.svelte +71 -0
- package/frontend/lib/components/common/Card.svelte +102 -0
- package/frontend/lib/components/common/Checkbox.svelte +48 -0
- package/frontend/lib/components/common/Dialog.svelte +249 -0
- package/frontend/lib/components/common/FolderBrowser.svelte +843 -0
- package/frontend/lib/components/common/Icon.svelte +58 -0
- package/frontend/lib/components/common/Input.svelte +72 -0
- package/frontend/lib/components/common/Lightbox.svelte +233 -0
- package/frontend/lib/components/common/LoadingScreen.svelte +52 -0
- package/frontend/lib/components/common/LoadingSpinner.svelte +48 -0
- package/frontend/lib/components/common/Modal.svelte +177 -0
- package/frontend/lib/components/common/ModalProvider.svelte +28 -0
- package/frontend/lib/components/common/ModelSelector.svelte +110 -0
- package/frontend/lib/components/common/MonacoEditor.svelte +569 -0
- package/frontend/lib/components/common/NotificationToast.svelte +113 -0
- package/frontend/lib/components/common/PageTemplate.svelte +76 -0
- package/frontend/lib/components/common/ProjectUserAvatars.svelte +79 -0
- package/frontend/lib/components/common/Select.svelte +98 -0
- package/frontend/lib/components/common/Textarea.svelte +80 -0
- package/frontend/lib/components/common/ThemeToggle.svelte +44 -0
- package/frontend/lib/components/common/lucide-icons.ts +1642 -0
- package/frontend/lib/components/common/material-icons.ts +1082 -0
- package/frontend/lib/components/common/xterm/XTerm.svelte +796 -0
- package/frontend/lib/components/common/xterm/index.ts +16 -0
- package/frontend/lib/components/common/xterm/terminal-config.ts +68 -0
- package/frontend/lib/components/common/xterm/types.ts +31 -0
- package/frontend/lib/components/common/xterm/xterm-service.ts +353 -0
- package/frontend/lib/components/files/FileNode.svelte +384 -0
- package/frontend/lib/components/files/FileTree.svelte +681 -0
- package/frontend/lib/components/files/FileViewer.svelte +728 -0
- package/frontend/lib/components/files/SearchResults.svelte +303 -0
- package/frontend/lib/components/git/BranchManager.svelte +458 -0
- package/frontend/lib/components/git/ChangesSection.svelte +107 -0
- package/frontend/lib/components/git/CommitForm.svelte +76 -0
- package/frontend/lib/components/git/ConflictResolver.svelte +158 -0
- package/frontend/lib/components/git/DiffViewer.svelte +364 -0
- package/frontend/lib/components/git/FileChangeItem.svelte +97 -0
- package/frontend/lib/components/git/GitButton.svelte +33 -0
- package/frontend/lib/components/git/GitLog.svelte +361 -0
- package/frontend/lib/components/git/GitModal.svelte +80 -0
- package/frontend/lib/components/history/HistoryModal.svelte +563 -0
- package/frontend/lib/components/history/HistoryView.svelte +615 -0
- package/frontend/lib/components/index.ts +34 -0
- package/frontend/lib/components/preview/browser/BrowserPreview.svelte +549 -0
- package/frontend/lib/components/preview/browser/components/Canvas.svelte +1058 -0
- package/frontend/lib/components/preview/browser/components/ConsolePanel.svelte +757 -0
- package/frontend/lib/components/preview/browser/components/Container.svelte +450 -0
- package/frontend/lib/components/preview/browser/components/ContextMenu.svelte +236 -0
- package/frontend/lib/components/preview/browser/components/SelectDropdown.svelte +224 -0
- package/frontend/lib/components/preview/browser/components/Toolbar.svelte +339 -0
- package/frontend/lib/components/preview/browser/components/VirtualCursor.svelte +36 -0
- package/frontend/lib/components/preview/browser/core/cleanup.svelte.ts +155 -0
- package/frontend/lib/components/preview/browser/core/coordinator.svelte.ts +837 -0
- package/frontend/lib/components/preview/browser/core/interactions.svelte.ts +113 -0
- package/frontend/lib/components/preview/browser/core/mcp-handlers.svelte.ts +296 -0
- package/frontend/lib/components/preview/browser/core/native-ui-handlers.svelte.ts +391 -0
- package/frontend/lib/components/preview/browser/core/stream-handler.svelte.ts +231 -0
- package/frontend/lib/components/preview/browser/core/tab-manager.svelte.ts +210 -0
- package/frontend/lib/components/preview/browser/core/tab-operations.svelte.ts +239 -0
- package/frontend/lib/components/preview/index.ts +2 -0
- package/frontend/lib/components/settings/SettingsModal.svelte +235 -0
- package/frontend/lib/components/settings/SettingsView.svelte +36 -0
- package/frontend/lib/components/settings/appearance/AppearanceSettings.svelte +51 -0
- package/frontend/lib/components/settings/appearance/LayoutPresetSettings.svelte +160 -0
- package/frontend/lib/components/settings/appearance/LayoutPreview.svelte +76 -0
- package/frontend/lib/components/settings/engines/AIEnginesSettings.svelte +917 -0
- package/frontend/lib/components/settings/general/AdvancedSettings.svelte +187 -0
- package/frontend/lib/components/settings/general/DataManagementSettings.svelte +203 -0
- package/frontend/lib/components/settings/general/GeneralSettings.svelte +10 -0
- package/frontend/lib/components/settings/model/ModelSettings.svelte +357 -0
- package/frontend/lib/components/settings/notifications/NotificationSettings.svelte +205 -0
- package/frontend/lib/components/settings/user/UserSettings.svelte +197 -0
- package/frontend/lib/components/terminal/Terminal.svelte +368 -0
- package/frontend/lib/components/terminal/TerminalTabs.svelte +87 -0
- package/frontend/lib/components/terminal/TerminalView.svelte +55 -0
- package/frontend/lib/components/tunnel/TunnelActive.svelte +142 -0
- package/frontend/lib/components/tunnel/TunnelButton.svelte +54 -0
- package/frontend/lib/components/tunnel/TunnelInactive.svelte +284 -0
- package/frontend/lib/components/tunnel/TunnelModal.svelte +47 -0
- package/frontend/lib/components/tunnel/TunnelQRCode.svelte +49 -0
- package/frontend/lib/components/workspace/DesktopNavigator.svelte +382 -0
- package/frontend/lib/components/workspace/MobileNavigator.svelte +403 -0
- package/frontend/lib/components/workspace/PanelContainer.svelte +100 -0
- package/frontend/lib/components/workspace/PanelHeader.svelte +505 -0
- package/frontend/lib/components/workspace/ViewMenu.svelte +162 -0
- package/frontend/lib/components/workspace/WorkspaceLayout.svelte +169 -0
- package/frontend/lib/components/workspace/layout/DesktopLayout.svelte +15 -0
- package/frontend/lib/components/workspace/layout/MobileLayout.svelte +17 -0
- package/frontend/lib/components/workspace/layout/split-pane/Container.svelte +42 -0
- package/frontend/lib/components/workspace/layout/split-pane/Handle.svelte +85 -0
- package/frontend/lib/components/workspace/layout/split-pane/Layout.svelte +37 -0
- package/frontend/lib/components/workspace/panels/ChatPanel.svelte +274 -0
- package/frontend/lib/components/workspace/panels/FilesPanel.svelte +1261 -0
- package/frontend/lib/components/workspace/panels/GitPanel.svelte +1560 -0
- package/frontend/lib/components/workspace/panels/PreviewPanel.svelte +150 -0
- package/frontend/lib/components/workspace/panels/TerminalPanel.svelte +73 -0
- package/frontend/lib/constants/preview.ts +45 -0
- package/frontend/lib/services/chat/chat.service.ts +704 -0
- package/frontend/lib/services/chat/index.ts +7 -0
- package/frontend/lib/services/notification/global-stream-monitor.ts +86 -0
- package/frontend/lib/services/notification/index.ts +8 -0
- package/frontend/lib/services/notification/push.service.ts +144 -0
- package/frontend/lib/services/notification/sound.service.ts +127 -0
- package/frontend/lib/services/preview/browser/browser-console.service.ts +61 -0
- package/frontend/lib/services/preview/browser/browser-webcodecs.service.ts +1499 -0
- package/frontend/lib/services/preview/browser/mcp-integration.svelte.ts +67 -0
- package/frontend/lib/services/preview/index.ts +23 -0
- package/frontend/lib/services/project/index.ts +8 -0
- package/frontend/lib/services/project/status.service.ts +159 -0
- package/frontend/lib/services/snapshot/snapshot.service.ts +47 -0
- package/frontend/lib/services/terminal/background/index.ts +130 -0
- package/frontend/lib/services/terminal/background/session-restore.ts +274 -0
- package/frontend/lib/services/terminal/background/stream-manager.ts +286 -0
- package/frontend/lib/services/terminal/index.ts +14 -0
- package/frontend/lib/services/terminal/persistence.service.ts +260 -0
- package/frontend/lib/services/terminal/project.service.ts +953 -0
- package/frontend/lib/services/terminal/session.service.ts +364 -0
- package/frontend/lib/services/terminal/terminal.service.ts +369 -0
- package/frontend/lib/stores/core/app.svelte.ts +117 -0
- package/frontend/lib/stores/core/files.svelte.ts +73 -0
- package/frontend/lib/stores/core/presence.svelte.ts +48 -0
- package/frontend/lib/stores/core/projects.svelte.ts +317 -0
- package/frontend/lib/stores/core/sessions.svelte.ts +383 -0
- package/frontend/lib/stores/features/claude-accounts.svelte.ts +58 -0
- package/frontend/lib/stores/features/models.svelte.ts +89 -0
- package/frontend/lib/stores/features/settings.svelte.ts +87 -0
- package/frontend/lib/stores/features/terminal.svelte.ts +701 -0
- package/frontend/lib/stores/features/tunnel.svelte.ts +161 -0
- package/frontend/lib/stores/features/user.svelte.ts +96 -0
- package/frontend/lib/stores/ui/chat-input.svelte.ts +57 -0
- package/frontend/lib/stores/ui/chat-model.svelte.ts +61 -0
- package/frontend/lib/stores/ui/dialog.svelte.ts +59 -0
- package/frontend/lib/stores/ui/edit-mode.svelte.ts +214 -0
- package/frontend/lib/stores/ui/notification.svelte.ts +166 -0
- package/frontend/lib/stores/ui/settings-modal.svelte.ts +88 -0
- package/frontend/lib/stores/ui/theme.svelte.ts +179 -0
- package/frontend/lib/stores/ui/workspace.svelte.ts +754 -0
- package/frontend/lib/types/native-ui.ts +73 -0
- package/frontend/lib/utils/chat/date-separator.ts +39 -0
- package/frontend/lib/utils/chat/message-grouper.ts +219 -0
- package/frontend/lib/utils/chat/message-processor.ts +135 -0
- package/frontend/lib/utils/chat/tool-handler.ts +161 -0
- package/frontend/lib/utils/chat/virtual-scroll.svelte.ts +142 -0
- package/frontend/lib/utils/click-outside.ts +20 -0
- package/frontend/lib/utils/context-manager.ts +257 -0
- package/frontend/lib/utils/file-icon-mappings.ts +769 -0
- package/frontend/lib/utils/folder-icon-mappings.ts +1030 -0
- package/frontend/lib/utils/git-status.ts +68 -0
- package/frontend/lib/utils/platform.ts +113 -0
- package/frontend/lib/utils/port-check.ts +65 -0
- package/frontend/lib/utils/terminalFormatter.ts +207 -0
- package/frontend/lib/utils/theme.ts +6 -0
- package/frontend/lib/utils/tree-visualizer.ts +320 -0
- package/frontend/lib/utils/ws.ts +44 -0
- package/frontend/main.ts +13 -0
- package/index.html +70 -0
- package/package.json +111 -0
- package/scripts/generate-icons.ts +87 -0
- package/scripts/pre-publish-check.sh +142 -0
- package/scripts/setup-hooks.sh +134 -0
- package/scripts/validate-branch-name.sh +47 -0
- package/scripts/validate-commit-msg.sh +42 -0
- package/shared/constants/engines.ts +134 -0
- package/shared/types/database/connection.ts +16 -0
- package/shared/types/database/index.ts +6 -0
- package/shared/types/database/schema.ts +141 -0
- package/shared/types/engine/index.ts +45 -0
- package/shared/types/filesystem/index.ts +22 -0
- package/shared/types/git.ts +171 -0
- package/shared/types/messaging/index.ts +239 -0
- package/shared/types/messaging/tool.ts +526 -0
- package/shared/types/network/api.ts +18 -0
- package/shared/types/network/index.ts +5 -0
- package/shared/types/stores/app.ts +23 -0
- package/shared/types/stores/dialog.ts +21 -0
- package/shared/types/stores/index.ts +3 -0
- package/shared/types/stores/settings.ts +15 -0
- package/shared/types/terminal/index.ts +44 -0
- package/shared/types/ui/components.ts +61 -0
- package/shared/types/ui/icons.ts +23 -0
- package/shared/types/ui/index.ts +22 -0
- package/shared/types/ui/notifications.ts +14 -0
- package/shared/types/ui/theme.ts +12 -0
- package/shared/types/websocket/index.ts +43 -0
- package/shared/types/window.d.ts +13 -0
- package/shared/utils/anonymous-user.ts +168 -0
- package/shared/utils/async.ts +10 -0
- package/shared/utils/diff-calculator.ts +184 -0
- package/shared/utils/file-type-detection.ts +166 -0
- package/shared/utils/logger.ts +158 -0
- package/shared/utils/message-formatter.ts +79 -0
- package/shared/utils/path.ts +47 -0
- package/shared/utils/ws-client.ts +768 -0
- package/shared/utils/ws-server.ts +660 -0
- package/static/audio/notification.ogg +0 -0
- package/static/favicon.svg +8 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-italic-latin.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin-ext.woff2 +0 -0
- package/static/fonts/dm-sans/dm-sans-normal-latin.woff2 +0 -0
- package/static/fonts/dm-sans.css +96 -0
- package/svelte.config.js +20 -0
- 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>
|