@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,357 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { settings, updateSettings } from '$frontend/lib/stores/features/settings.svelte';
|
|
3
|
+
import { modelStore } from '$frontend/lib/stores/features/models.svelte';
|
|
4
|
+
import { ENGINES, type EngineType } from '$shared/constants/engines';
|
|
5
|
+
import type { EngineModel } from '$shared/types/engine';
|
|
6
|
+
|
|
7
|
+
let searchQuery = $state('');
|
|
8
|
+
let refreshing = $state(false);
|
|
9
|
+
let collapsedProviders = $state<Set<string>>(new Set());
|
|
10
|
+
|
|
11
|
+
// Handle engine selection — restore remembered model or pick first
|
|
12
|
+
async function selectEngine(engineType: EngineType) {
|
|
13
|
+
updateSettings({ selectedEngine: engineType });
|
|
14
|
+
searchQuery = '';
|
|
15
|
+
|
|
16
|
+
// Restore remembered model for this engine
|
|
17
|
+
const memory = settings.engineModelMemory || {};
|
|
18
|
+
const remembered = memory[engineType];
|
|
19
|
+
|
|
20
|
+
if (engineType !== 'claude-code') {
|
|
21
|
+
const models = await modelStore.fetchModels(engineType);
|
|
22
|
+
const target = (remembered && models.find(m => m.id === remembered))
|
|
23
|
+
|| models.find(m => m.recommended)
|
|
24
|
+
|| models[0];
|
|
25
|
+
if (target) {
|
|
26
|
+
updateSettings({
|
|
27
|
+
selectedModel: target.id,
|
|
28
|
+
engineModelMemory: { ...memory, [engineType]: target.id }
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
// No models available — clear the model selection
|
|
32
|
+
updateSettings({ selectedModel: '' });
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
const models = modelStore.getByEngine('claude-code');
|
|
36
|
+
const target = (remembered && models.find(m => m.id === remembered))
|
|
37
|
+
|| models.find(m => m.recommended)
|
|
38
|
+
|| models[0];
|
|
39
|
+
if (target) {
|
|
40
|
+
updateSettings({
|
|
41
|
+
selectedModel: target.id,
|
|
42
|
+
engineModelMemory: { ...memory, [engineType]: target.id }
|
|
43
|
+
});
|
|
44
|
+
} else {
|
|
45
|
+
// No models available — clear the model selection
|
|
46
|
+
updateSettings({ selectedModel: '' });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Open accordion for the selected model's provider
|
|
51
|
+
syncAccordionState();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Handle model selection — also save to per-engine memory
|
|
55
|
+
function selectModel(modelId: string) {
|
|
56
|
+
const memory = settings.engineModelMemory || {};
|
|
57
|
+
updateSettings({
|
|
58
|
+
selectedModel: modelId,
|
|
59
|
+
engineModelMemory: { ...memory, [settings.selectedEngine]: modelId }
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Refresh models (bypass cache)
|
|
64
|
+
async function handleRefresh() {
|
|
65
|
+
refreshing = true;
|
|
66
|
+
try {
|
|
67
|
+
await modelStore.refreshModels(settings.selectedEngine);
|
|
68
|
+
} finally {
|
|
69
|
+
refreshing = false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Toggle provider accordion
|
|
74
|
+
function toggleProvider(provider: string) {
|
|
75
|
+
const next = new Set(collapsedProviders);
|
|
76
|
+
if (next.has(provider)) {
|
|
77
|
+
next.delete(provider);
|
|
78
|
+
} else {
|
|
79
|
+
next.add(provider);
|
|
80
|
+
}
|
|
81
|
+
collapsedProviders = next;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Sync accordion state: open only the provider containing the selected model
|
|
85
|
+
function syncAccordionState() {
|
|
86
|
+
const allProviders = [...groupedModels.keys()];
|
|
87
|
+
const selectedModel = settings.selectedModel;
|
|
88
|
+
let selectedProvider: string | null = null;
|
|
89
|
+
|
|
90
|
+
for (const [provider, models] of groupedModels) {
|
|
91
|
+
if (models.some(m => m.id === selectedModel)) {
|
|
92
|
+
selectedProvider = provider;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Collapse all, then open the one with selected model
|
|
98
|
+
const collapsed = new Set(allProviders);
|
|
99
|
+
if (selectedProvider) {
|
|
100
|
+
collapsed.delete(selectedProvider);
|
|
101
|
+
}
|
|
102
|
+
collapsedProviders = collapsed;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Get models for the currently selected engine, filtered by search
|
|
106
|
+
const filteredModels = $derived.by(() => {
|
|
107
|
+
const models = modelStore.getByEngine(settings.selectedEngine);
|
|
108
|
+
if (!searchQuery.trim()) return models;
|
|
109
|
+
|
|
110
|
+
const q = searchQuery.toLowerCase();
|
|
111
|
+
return models.filter(m =>
|
|
112
|
+
m.name.toLowerCase().includes(q) ||
|
|
113
|
+
m.modelId.toLowerCase().includes(q) ||
|
|
114
|
+
m.provider.toLowerCase().includes(q) ||
|
|
115
|
+
m.capabilities.some(c => c.toLowerCase().includes(q))
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Group models by provider
|
|
120
|
+
const groupedModels = $derived.by(() => {
|
|
121
|
+
const groups = new Map<string, EngineModel[]>();
|
|
122
|
+
for (const model of filteredModels) {
|
|
123
|
+
const key = model.provider;
|
|
124
|
+
if (!groups.has(key)) groups.set(key, []);
|
|
125
|
+
groups.get(key)!.push(model);
|
|
126
|
+
}
|
|
127
|
+
return groups;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Fetch models on mount for non-claude-code, then sync accordion
|
|
131
|
+
$effect(() => {
|
|
132
|
+
if (settings.selectedEngine !== 'claude-code') {
|
|
133
|
+
modelStore.fetchModels(settings.selectedEngine);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Sync accordion: open all when searching, restore default when cleared
|
|
138
|
+
$effect(() => {
|
|
139
|
+
if (searchQuery.trim()) {
|
|
140
|
+
// Searching — open all accordions
|
|
141
|
+
collapsedProviders = new Set();
|
|
142
|
+
} else if (groupedModels.size > 0) {
|
|
143
|
+
// Not searching — only open the one with selected model
|
|
144
|
+
syncAccordionState();
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
function formatContext(tokens: number): string {
|
|
149
|
+
return tokens >= 1000000
|
|
150
|
+
? `${(tokens / 1000000).toFixed(1)}M`
|
|
151
|
+
: `${(tokens / 1000).toFixed(0)}K`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function formatProvider(provider: string): string {
|
|
155
|
+
return provider
|
|
156
|
+
.split(/[-_]/)
|
|
157
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
158
|
+
.join(' ');
|
|
159
|
+
}
|
|
160
|
+
</script>
|
|
161
|
+
|
|
162
|
+
<!-- Claude SVG logo (Anthropic) -->
|
|
163
|
+
{#snippet claudeLogo(active: boolean)}
|
|
164
|
+
<svg viewBox="0 0 24 24" fill="none" class="w-5 h-5" aria-hidden="true">
|
|
165
|
+
<path d="M16.091 4L9.115 20h-1.19L14.901 4h1.19zm-5.726 5.2L14.8 20h-1.218L9.2 9.6l1.165-.4z"
|
|
166
|
+
fill={active ? '#8b5cf6' : 'currentColor'} />
|
|
167
|
+
</svg>
|
|
168
|
+
{/snippet}
|
|
169
|
+
|
|
170
|
+
<!-- OpenCode SVG logo -->
|
|
171
|
+
{#snippet opencodeLogo(active: boolean)}
|
|
172
|
+
<svg viewBox="0 0 24 24" fill="none" class="w-5 h-5" aria-hidden="true">
|
|
173
|
+
<path d="M8.5 6L3 12l5.5 6M15.5 6L21 12l-5.5 6M13.5 4l-3 16"
|
|
174
|
+
stroke={active ? '#8b5cf6' : 'currentColor'}
|
|
175
|
+
stroke-width="2"
|
|
176
|
+
stroke-linecap="round"
|
|
177
|
+
stroke-linejoin="round" />
|
|
178
|
+
</svg>
|
|
179
|
+
{/snippet}
|
|
180
|
+
|
|
181
|
+
<div class="py-1">
|
|
182
|
+
<!-- Engine Selection -->
|
|
183
|
+
<h3 class="text-base font-bold text-slate-900 dark:text-slate-100 mb-1.5">AI Engine</h3>
|
|
184
|
+
<p class="text-sm text-slate-600 dark:text-slate-500 mb-4">
|
|
185
|
+
Select the AI engine to power your conversations
|
|
186
|
+
</p>
|
|
187
|
+
|
|
188
|
+
<div class="flex gap-3 mb-6">
|
|
189
|
+
{#each ENGINES as engine (engine.type)}
|
|
190
|
+
{@const isActive = settings.selectedEngine === engine.type}
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
class="flex-1 flex items-center gap-3 p-3.5 overflow-hidden border-2 rounded-xl text-left cursor-pointer transition-all duration-200
|
|
194
|
+
{isActive
|
|
195
|
+
? 'border-violet-600 bg-gradient-to-br from-violet-500/10 to-purple-500/5 dark:from-violet-500/12 dark:to-purple-500/8'
|
|
196
|
+
: 'border-slate-200 dark:border-slate-800 bg-slate-100/80 dark:bg-slate-800/80 hover:border-violet-500/20 dark:hover:border-violet-500/35'}"
|
|
197
|
+
onclick={() => selectEngine(engine.type)}
|
|
198
|
+
>
|
|
199
|
+
<div>
|
|
200
|
+
<div class="flex dark:hidden items-center justify-center w-5 h-5">{@html engine.icon.light}</div>
|
|
201
|
+
<div class="hidden dark:flex items-center justify-center w-5 h-5">{@html engine.icon.dark}</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div>
|
|
204
|
+
<div class="font-bold text-sm text-slate-900 dark:text-slate-100">{engine.name}</div>
|
|
205
|
+
<div class="text-xs text-slate-500 dark:text-slate-400 mt-0.5">{engine.description}</div>
|
|
206
|
+
</div>
|
|
207
|
+
{#if isActive}
|
|
208
|
+
<div class="flex items-center justify-center w-5 h-5 bg-gradient-to-br from-violet-600 to-purple-600 rounded-full text-white ml-auto flex-shrink-0">
|
|
209
|
+
<svg viewBox="0 0 24 24" fill="none" class="w-3 h-3" aria-hidden="true">
|
|
210
|
+
<path d="M5 13l4 4L19 7" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
|
|
211
|
+
</svg>
|
|
212
|
+
</div>
|
|
213
|
+
{/if}
|
|
214
|
+
</button>
|
|
215
|
+
{/each}
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<!-- Model Selection -->
|
|
219
|
+
<div class="flex items-center justify-between mb-1.5">
|
|
220
|
+
<h3 class="text-base font-bold text-slate-900 dark:text-slate-100">Model</h3>
|
|
221
|
+
<button
|
|
222
|
+
type="button"
|
|
223
|
+
class="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-lg transition-colors cursor-pointer
|
|
224
|
+
text-slate-500 hover:text-violet-600 hover:bg-violet-500/10 dark:hover:text-violet-400 dark:hover:bg-violet-500/15
|
|
225
|
+
disabled:opacity-50 disabled:cursor-not-allowed"
|
|
226
|
+
onclick={handleRefresh}
|
|
227
|
+
disabled={refreshing || modelStore.loading}
|
|
228
|
+
>
|
|
229
|
+
<svg viewBox="0 0 24 24" fill="none" class="w-3.5 h-3.5 {refreshing ? 'animate-spin' : ''}" aria-hidden="true">
|
|
230
|
+
<path d="M21 12a9 9 0 11-2.636-6.364M21 3v5h-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
231
|
+
</svg>
|
|
232
|
+
{refreshing ? 'Refreshing...' : 'Refresh'}
|
|
233
|
+
</button>
|
|
234
|
+
</div>
|
|
235
|
+
<p class="text-sm text-slate-600 dark:text-slate-500 mb-3">
|
|
236
|
+
Select the AI model for the {ENGINES.find(e => e.type === settings.selectedEngine)?.name || 'selected'} engine
|
|
237
|
+
</p>
|
|
238
|
+
|
|
239
|
+
<!-- Search -->
|
|
240
|
+
<div class="relative mb-3">
|
|
241
|
+
<svg viewBox="0 0 24 24" fill="none" class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" aria-hidden="true">
|
|
242
|
+
<circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="2" />
|
|
243
|
+
<path d="M21 21l-4.35-4.35" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
|
|
244
|
+
</svg>
|
|
245
|
+
<input
|
|
246
|
+
type="text"
|
|
247
|
+
bind:value={searchQuery}
|
|
248
|
+
placeholder="Search models..."
|
|
249
|
+
class="w-full pl-9 pr-3 py-2 text-sm bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-lg outline-none focus:ring-2 focus:ring-violet-500/20 focus:border-violet-600 transition-colors text-slate-900 dark:text-slate-100 placeholder-slate-400"
|
|
250
|
+
/>
|
|
251
|
+
</div>
|
|
252
|
+
|
|
253
|
+
<!-- Model List -->
|
|
254
|
+
<div class="flex flex-col gap-1.5">
|
|
255
|
+
{#if modelStore.loading && settings.selectedEngine !== 'claude-code' && !refreshing}
|
|
256
|
+
<!-- Loading skeleton for Open Code only -->
|
|
257
|
+
<div class="border border-slate-200/80 dark:border-slate-700/50 rounded-lg overflow-hidden">
|
|
258
|
+
<div class="bg-white/80 dark:bg-slate-800/40 px-3 py-3 flex items-center gap-3">
|
|
259
|
+
<div class="w-4 h-4 rounded-full bg-slate-200 dark:bg-slate-700 animate-pulse"></div>
|
|
260
|
+
<div class="h-3.5 w-32 rounded bg-slate-200 dark:bg-slate-700 animate-pulse"></div>
|
|
261
|
+
</div>
|
|
262
|
+
<div class="px-4 py-2.5 space-y-2.5">
|
|
263
|
+
{#each Array(3) as _}
|
|
264
|
+
<div class="flex items-center gap-3 py-2">
|
|
265
|
+
<div class="w-4 h-4 rounded-full bg-slate-200/80 dark:bg-slate-700/60 animate-pulse"></div>
|
|
266
|
+
<div class="flex-1 space-y-1.5">
|
|
267
|
+
<div class="h-3.5 w-40 rounded bg-slate-200/80 dark:bg-slate-700/60 animate-pulse"></div>
|
|
268
|
+
<div class="flex gap-1.5">
|
|
269
|
+
<div class="h-3 w-14 rounded bg-slate-200/60 dark:bg-slate-700/40 animate-pulse"></div>
|
|
270
|
+
<div class="h-3 w-12 rounded bg-slate-200/60 dark:bg-slate-700/40 animate-pulse"></div>
|
|
271
|
+
</div>
|
|
272
|
+
</div>
|
|
273
|
+
</div>
|
|
274
|
+
{/each}
|
|
275
|
+
</div>
|
|
276
|
+
</div>
|
|
277
|
+
{:else if filteredModels.length === 0}
|
|
278
|
+
<div class="py-4 text-sm text-slate-500 text-center">
|
|
279
|
+
{searchQuery ? 'No models matching your search.' : 'No models available for this engine.'}
|
|
280
|
+
</div>
|
|
281
|
+
{:else}
|
|
282
|
+
<!-- Grouped by provider with accordion -->
|
|
283
|
+
{#each [...groupedModels.entries()] as [provider, providerModels] (provider)}
|
|
284
|
+
{@const isCollapsed = collapsedProviders.has(provider)}
|
|
285
|
+
{@const hasSelectedModel = providerModels.some(m => m.id === settings.selectedModel)}
|
|
286
|
+
<div class="border border-slate-200/80 dark:border-slate-700/50 rounded-lg overflow-hidden">
|
|
287
|
+
<!-- Accordion header -->
|
|
288
|
+
<button
|
|
289
|
+
type="button"
|
|
290
|
+
class="flex items-center gap-2.5 w-full px-3 py-2.5 text-left cursor-pointer transition-colors
|
|
291
|
+
bg-white/80 dark:bg-slate-800/40 hover:bg-white dark:hover:bg-slate-800/60"
|
|
292
|
+
onclick={() => toggleProvider(provider)}
|
|
293
|
+
>
|
|
294
|
+
<svg viewBox="0 0 24 24" fill="none"
|
|
295
|
+
class="w-4 h-4 text-slate-400 transition-transform duration-200 flex-shrink-0
|
|
296
|
+
{isCollapsed ? '' : 'rotate-90'}"
|
|
297
|
+
aria-hidden="true">
|
|
298
|
+
<path d="M9 18l6-6-6-6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
|
299
|
+
</svg>
|
|
300
|
+
<span class="text-sm font-semibold text-slate-800 dark:text-slate-200">
|
|
301
|
+
{formatProvider(provider)}
|
|
302
|
+
</span>
|
|
303
|
+
<span class="text-xs text-slate-400 dark:text-slate-500">
|
|
304
|
+
{providerModels.length} {providerModels.length === 1 ? 'model' : 'models'}
|
|
305
|
+
</span>
|
|
306
|
+
{#if hasSelectedModel}
|
|
307
|
+
<div class="w-1.5 h-1.5 rounded-full bg-violet-500 ml-auto flex-shrink-0"></div>
|
|
308
|
+
{/if}
|
|
309
|
+
</button>
|
|
310
|
+
|
|
311
|
+
<!-- Accordion body -->
|
|
312
|
+
{#if !isCollapsed}
|
|
313
|
+
<div class="flex flex-col bg-white/40 dark:bg-slate-800/20">
|
|
314
|
+
{#each providerModels as model (model.id)}
|
|
315
|
+
{@const isSelected = settings.selectedModel === model.id}
|
|
316
|
+
{@const caps = model.capabilities}
|
|
317
|
+
<button
|
|
318
|
+
type="button"
|
|
319
|
+
class="flex items-start gap-3 px-3 py-2.5 text-left cursor-pointer transition-all duration-150
|
|
320
|
+
{isSelected
|
|
321
|
+
? 'bg-violet-500/10 dark:bg-violet-500/12'
|
|
322
|
+
: 'hover:bg-slate-100/80 dark:hover:bg-slate-700/30'}"
|
|
323
|
+
onclick={() => selectModel(model.id)}
|
|
324
|
+
>
|
|
325
|
+
<!-- Radio indicator -->
|
|
326
|
+
<div class="flex-shrink-0 w-4 h-4 rounded-full border-2 flex items-center justify-center mt-0.5
|
|
327
|
+
{isSelected ? 'border-violet-600' : 'border-slate-300 dark:border-slate-600'}">
|
|
328
|
+
{#if isSelected}
|
|
329
|
+
<div class="w-2 h-2 rounded-full bg-violet-600"></div>
|
|
330
|
+
{/if}
|
|
331
|
+
</div>
|
|
332
|
+
|
|
333
|
+
<!-- Model info -->
|
|
334
|
+
<div class="flex-1 min-w-0">
|
|
335
|
+
<div class="flex items-center gap-2">
|
|
336
|
+
<span class="text-sm font-medium text-slate-900 dark:text-slate-100">{model.name}</span>
|
|
337
|
+
<!-- <span class="text-2xs text-slate-400 dark:text-slate-500">{formatContext(model.contextWindow)}</span> -->
|
|
338
|
+
</div>
|
|
339
|
+
{#if caps.length > 0}
|
|
340
|
+
<div class="flex flex-wrap gap-1 mt-1.5">
|
|
341
|
+
{#each caps as cap}
|
|
342
|
+
<span class="px-1.5 py-0.5 text-2xs rounded bg-slate-100 dark:bg-slate-700/50 text-slate-500 dark:text-slate-400 leading-none">
|
|
343
|
+
{cap}
|
|
344
|
+
</span>
|
|
345
|
+
{/each}
|
|
346
|
+
</div>
|
|
347
|
+
{/if}
|
|
348
|
+
</div>
|
|
349
|
+
</button>
|
|
350
|
+
{/each}
|
|
351
|
+
</div>
|
|
352
|
+
{/if}
|
|
353
|
+
</div>
|
|
354
|
+
{/each}
|
|
355
|
+
{/if}
|
|
356
|
+
</div>
|
|
357
|
+
</div>
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { addNotification } from '$frontend/lib/stores/ui/notification.svelte';
|
|
3
|
+
import { settings, updateSettings } from '$frontend/lib/stores/features/settings.svelte';
|
|
4
|
+
import { soundNotification, pushNotification } from '$frontend/lib/services/notification';
|
|
5
|
+
import Icon from '../../common/Icon.svelte';
|
|
6
|
+
|
|
7
|
+
let isTestingSound = $state(false);
|
|
8
|
+
let isTestingPush = $state(false);
|
|
9
|
+
|
|
10
|
+
// Test sound notification
|
|
11
|
+
async function testSoundNotification() {
|
|
12
|
+
isTestingSound = true;
|
|
13
|
+
try {
|
|
14
|
+
soundNotification.initialize();
|
|
15
|
+
|
|
16
|
+
const success = await soundNotification.testSound();
|
|
17
|
+
if (success) {
|
|
18
|
+
addNotification({
|
|
19
|
+
type: 'success',
|
|
20
|
+
title: 'Sound Test',
|
|
21
|
+
message: 'Sound notification is working correctly',
|
|
22
|
+
duration: 3000
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
throw new Error('Sound test failed');
|
|
26
|
+
}
|
|
27
|
+
} catch (error) {
|
|
28
|
+
addNotification({
|
|
29
|
+
type: 'error',
|
|
30
|
+
title: 'Sound Test Failed',
|
|
31
|
+
message: soundNotification.isSupported()
|
|
32
|
+
? 'Unable to play sound notification'
|
|
33
|
+
: 'Sound notifications not supported on this browser',
|
|
34
|
+
duration: 4000
|
|
35
|
+
});
|
|
36
|
+
} finally {
|
|
37
|
+
isTestingSound = false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Test push notification
|
|
42
|
+
async function testPushNotification() {
|
|
43
|
+
isTestingPush = true;
|
|
44
|
+
try {
|
|
45
|
+
const initialized = await pushNotification.initialize();
|
|
46
|
+
|
|
47
|
+
if (initialized) {
|
|
48
|
+
const success = await pushNotification.testNotification();
|
|
49
|
+
if (success) {
|
|
50
|
+
addNotification({
|
|
51
|
+
type: 'success',
|
|
52
|
+
title: 'Push Notification Test',
|
|
53
|
+
message: 'Native push notification is working correctly',
|
|
54
|
+
duration: 3000
|
|
55
|
+
});
|
|
56
|
+
} else {
|
|
57
|
+
throw new Error('Push test failed');
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error('Push notification permission denied or not supported');
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const permissionStatus = pushNotification.getPermissionStatus();
|
|
64
|
+
let message = 'Unable to send push notification';
|
|
65
|
+
|
|
66
|
+
if (!pushNotification.isSupported()) {
|
|
67
|
+
message = 'Push notifications not supported on this browser';
|
|
68
|
+
} else if (permissionStatus === 'denied') {
|
|
69
|
+
message =
|
|
70
|
+
'Push notification permission denied. Please allow notifications in browser settings.';
|
|
71
|
+
} else if (permissionStatus === 'default') {
|
|
72
|
+
message = 'Push notification permission not granted';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
addNotification({
|
|
76
|
+
type: 'error',
|
|
77
|
+
title: 'Push Notification Test Failed',
|
|
78
|
+
message,
|
|
79
|
+
duration: 5000
|
|
80
|
+
});
|
|
81
|
+
} finally {
|
|
82
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
83
|
+
isTestingPush = false;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</script>
|
|
87
|
+
|
|
88
|
+
<div class="py-1">
|
|
89
|
+
<h3 class="text-base font-bold text-slate-900 dark:text-slate-100 mb-1.5">Notifications</h3>
|
|
90
|
+
<p class="text-sm text-slate-600 dark:text-slate-500 mb-5">Configure sound and push notification preferences</p>
|
|
91
|
+
|
|
92
|
+
<div class="flex flex-col gap-3.5">
|
|
93
|
+
<!-- Sound notifications -->
|
|
94
|
+
<div
|
|
95
|
+
class="p-4 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-xl transition-all duration-150 hover:border-violet-500/20"
|
|
96
|
+
>
|
|
97
|
+
<div class="flex items-center justify-between">
|
|
98
|
+
<div class="flex items-center gap-3.5 flex-1">
|
|
99
|
+
<div
|
|
100
|
+
class="flex items-center justify-center w-10 h-10 rounded-lg shrink-0 bg-violet-500/10 dark:bg-violet-500/15 text-violet-600 dark:text-violet-400"
|
|
101
|
+
>
|
|
102
|
+
<Icon name="lucide:volume-2" class="w-5 h-5" />
|
|
103
|
+
</div>
|
|
104
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
105
|
+
<div class="text-sm font-semibold text-slate-900 dark:text-slate-100">
|
|
106
|
+
Sound notifications
|
|
107
|
+
</div>
|
|
108
|
+
<div class="text-xs text-slate-600 dark:text-slate-500">
|
|
109
|
+
Play sound when response is completed
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<label class="relative inline-block w-12 h-6.5 shrink-0">
|
|
114
|
+
<input
|
|
115
|
+
type="checkbox"
|
|
116
|
+
checked={settings.soundNotifications}
|
|
117
|
+
onchange={() => updateSettings({ soundNotifications: !settings.soundNotifications })}
|
|
118
|
+
class="opacity-0 w-0 h-0"
|
|
119
|
+
/>
|
|
120
|
+
<span
|
|
121
|
+
class="absolute cursor-pointer inset-0 bg-slate-600/40 dark:bg-slate-600/40 rounded-3xl transition-all duration-200
|
|
122
|
+
before:absolute before:content-[''] before:h-5 before:w-5 before:left-0.75 before:bottom-0.75 before:bg-white before:rounded-full before:transition-all before:duration-200
|
|
123
|
+
{settings.soundNotifications
|
|
124
|
+
? 'bg-gradient-to-br from-violet-600 to-purple-600 before:translate-x-5.5'
|
|
125
|
+
: ''}"
|
|
126
|
+
></span>
|
|
127
|
+
</label>
|
|
128
|
+
</div>
|
|
129
|
+
<div class="mt-3 pt-3 border-t border-slate-200 dark:border-slate-800">
|
|
130
|
+
<button
|
|
131
|
+
type="button"
|
|
132
|
+
class="inline-flex items-center gap-1.5 py-2 px-3.5 bg-violet-500/10 dark:bg-violet-500/10 border border-violet-500/20 dark:border-violet-500/25 rounded-lg text-violet-600 dark:text-violet-400 text-xs font-semibold cursor-pointer transition-all duration-150 hover:bg-violet-500/20 dark:hover:bg-violet-500/20 hover:border-violet-600 dark:hover:border-violet-500/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
133
|
+
onclick={testSoundNotification}
|
|
134
|
+
disabled={isTestingSound}
|
|
135
|
+
>
|
|
136
|
+
{#if isTestingSound}
|
|
137
|
+
<div
|
|
138
|
+
class="w-3 h-3 border-2 border-violet-600/30 dark:border-violet-400/30 border-t-violet-600 dark:border-t-violet-400 rounded-full animate-spin"
|
|
139
|
+
></div>
|
|
140
|
+
<span>Testing...</span>
|
|
141
|
+
{:else}
|
|
142
|
+
<Icon name="lucide:play" class="w-3.5 h-3.5" />
|
|
143
|
+
<span>Test Sound</span>
|
|
144
|
+
{/if}
|
|
145
|
+
</button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- Push notifications -->
|
|
150
|
+
<div
|
|
151
|
+
class="p-4 bg-slate-100/80 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-800 rounded-xl transition-all duration-150 hover:border-violet-500/20"
|
|
152
|
+
>
|
|
153
|
+
<div class="flex items-center justify-between">
|
|
154
|
+
<div class="flex items-center gap-3.5 flex-1">
|
|
155
|
+
<div
|
|
156
|
+
class="flex items-center justify-center w-10 h-10 rounded-lg shrink-0 bg-orange-400/15 text-orange-400"
|
|
157
|
+
>
|
|
158
|
+
<Icon name="lucide:bell" class="w-5 h-5" />
|
|
159
|
+
</div>
|
|
160
|
+
<div class="flex flex-col gap-0.5 min-w-0">
|
|
161
|
+
<div class="text-sm font-semibold text-slate-900 dark:text-slate-100">
|
|
162
|
+
Push notifications
|
|
163
|
+
</div>
|
|
164
|
+
<div class="text-xs text-slate-600 dark:text-slate-500">
|
|
165
|
+
Show native browser notifications when response is completed
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<label class="relative inline-block w-12 h-6.5 shrink-0">
|
|
170
|
+
<input
|
|
171
|
+
type="checkbox"
|
|
172
|
+
checked={settings.pushNotifications}
|
|
173
|
+
onchange={() => updateSettings({ pushNotifications: !settings.pushNotifications })}
|
|
174
|
+
class="opacity-0 w-0 h-0"
|
|
175
|
+
/>
|
|
176
|
+
<span
|
|
177
|
+
class="absolute cursor-pointer inset-0 bg-slate-600/40 dark:bg-slate-600/40 rounded-3xl transition-all duration-200
|
|
178
|
+
before:absolute before:content-[''] before:h-5 before:w-5 before:left-0.75 before:bottom-0.75 before:bg-white before:rounded-full before:transition-all before:duration-200
|
|
179
|
+
{settings.pushNotifications
|
|
180
|
+
? 'bg-gradient-to-br from-violet-600 to-purple-600 before:translate-x-5.5'
|
|
181
|
+
: ''}"
|
|
182
|
+
></span>
|
|
183
|
+
</label>
|
|
184
|
+
</div>
|
|
185
|
+
<div class="mt-3 pt-3 border-t border-slate-200 dark:border-slate-800">
|
|
186
|
+
<button
|
|
187
|
+
type="button"
|
|
188
|
+
class="inline-flex items-center gap-1.5 py-2 px-3.5 bg-violet-500/10 dark:bg-violet-500/10 border border-violet-500/20 dark:border-violet-500/25 rounded-lg text-violet-600 dark:text-violet-400 text-xs font-semibold cursor-pointer transition-all duration-150 hover:bg-violet-500/20 dark:hover:bg-violet-500/20 hover:border-violet-600 dark:hover:border-violet-500/40 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
189
|
+
onclick={testPushNotification}
|
|
190
|
+
disabled={isTestingPush}
|
|
191
|
+
>
|
|
192
|
+
{#if isTestingPush}
|
|
193
|
+
<div
|
|
194
|
+
class="w-3 h-3 border-2 border-violet-600/30 dark:border-violet-400/30 border-t-violet-600 dark:border-t-violet-400 rounded-full animate-spin"
|
|
195
|
+
></div>
|
|
196
|
+
<span>Testing...</span>
|
|
197
|
+
{:else}
|
|
198
|
+
<Icon name="lucide:send" class="w-3.5 h-3.5" />
|
|
199
|
+
<span>Test Push</span>
|
|
200
|
+
{/if}
|
|
201
|
+
</button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|