From 25576b0be6da409d2009636dbbb9df28b4933071 Mon Sep 17 00:00:00 2001 From: wehub-resource-sync Date: Mon, 13 Jul 2026 12:24:32 +0800 Subject: [PATCH] chore: import upstream snapshot with attribution --- .github/ISSUE_TEMPLATE/bug_report.yml | 74 + .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 29 + .github/dependabot.yml | 79 + .github/pull_request_template.md | 27 + .github/workflows/checks.yaml | 195 + .github/workflows/codeql.yml | 102 + .github/workflows/release.yml | 76 + .gitignore | 232 + .python-version | 1 + .vscode/extensions.json | 8 + .vscode/settings.json | 33 + CODE_OF_CONDUCT.md | 9 + CONTRIBUTING.md | 92 + LICENSE | 21 + NOTICE | 43 + README.md | 103 + README.wehub.md | 7 + SECURITY.md | 41 + SUPPORT.md | 14 + config.yaml.example | 84 + docs/TRANSPARENCY_NOTE.md | 172 + docs/_assets/magentic-lite-logo-dark.png | Bin 0 -> 139495 bytes docs/_assets/magentic-lite-logo-light.png | Bin 0 -> 138406 bytes docs/build-from-source.md | 99 + docs/configuration.md | 145 + docs/installation.md | 135 + docs/limitations.md | 12 + docs/model-hosting-guide.md | 84 + docs/troubleshooting.md | 87 + frontend/.nvmrc | 1 + frontend/.prettierignore | 4 + frontend/.prettierrc | 8 + frontend/components.json | 20 + frontend/eslint.config.js | 25 + frontend/index.html | 23 + frontend/package.json | 87 + frontend/pnpm-lock.yaml | 6701 +++++++++++++++++ frontend/public/magentic-logo.svg | 29 + frontend/src/App.tsx | 406 + frontend/src/api/agentSettings.ts | 51 + frontend/src/api/auth.ts | 39 + frontend/src/api/client.ts | 199 + frontend/src/api/files.ts | 59 + frontend/src/api/filesystem.ts | 48 + frontend/src/api/health.ts | 12 + frontend/src/api/index.ts | 48 + frontend/src/api/onboarding.ts | 174 + frontend/src/api/sessions.ts | 180 + frontend/src/api/settings.ts | 51 + .../components/browser/ActionDescription.tsx | 40 + .../src/components/browser/BrowserEmbed.tsx | 174 + .../components/browser/BrowserExpanded.tsx | 199 + .../src/components/browser/BrowserHeader.tsx | 107 + .../src/components/browser/BrowserViewer.tsx | 220 + .../components/browser/DescriptionOverlay.tsx | 57 + .../browser/ExpandableActionText.tsx | 151 + .../components/browser/PlaybackButtons.tsx | 109 + .../browser/PlaybackControlsRow.tsx | 175 + .../src/components/browser/ProgressBar.tsx | 185 + .../src/components/browser/TakeoverNotice.tsx | 51 + frontend/src/components/browser/index.ts | 13 + .../components/browser/usePlaybackControls.ts | 159 + frontend/src/components/chat/ChatInput.tsx | 437 ++ .../src/components/chat/ChatInputBanner.tsx | 33 + frontend/src/components/chat/ChatView.tsx | 824 ++ .../components/chat/FolderBrowserDialog.tsx | 214 + .../src/components/chat/FolderMountDialog.tsx | 82 + frontend/src/components/chat/MessageList.tsx | 202 + .../src/components/chat/MessageRenderer.tsx | 274 + frontend/src/components/chat/chatViewUtils.ts | 35 + frontend/src/components/chat/index.ts | 1 + .../src/components/chat/messageListUtils.ts | 597 ++ .../components/chat/messages/CodeBlock.tsx | 77 + .../chat/messages/CodeExecutionMessage.tsx | 65 + .../chat/messages/CollapsibleHeader.tsx | 90 + .../components/chat/messages/CuaMessage.tsx | 374 + .../components/chat/messages/ErrorMessage.tsx | 40 + .../components/chat/messages/FileMessage.tsx | 138 + .../chat/messages/FinalAnswerMessage.tsx | 60 + .../chat/messages/HeaderedMessage.tsx | 37 + .../chat/messages/InputRequestMessage.tsx | 319 + .../chat/messages/MemorizedFactMessage.tsx | 36 + .../chat/messages/OrchestratorToolMessage.tsx | 142 + .../src/components/chat/messages/PreBlock.tsx | 44 + .../chat/messages/ReasoningMessage.tsx | 57 + .../chat/messages/ReasoningPlaceholder.tsx | 26 + .../chat/messages/SessionStatusIndicator.tsx | 41 + .../chat/messages/SystemStatusMessage.tsx | 58 + .../chat/messages/ToolResultMessage.tsx | 57 + .../src/components/chat/messages/index.ts | 25 + .../common/ConnectionStatusBanner.tsx | 30 + .../src/components/common/ErrorBoundary.tsx | 59 + frontend/src/components/common/FileChip.tsx | 261 + frontend/src/components/common/FolderChip.tsx | 31 + frontend/src/components/common/Markdown.tsx | 203 + .../src/components/common/ModelConfigCard.tsx | 189 + .../src/components/common/SampleTaskCards.tsx | 45 + .../components/common/WrapToggleButton.tsx | 51 + frontend/src/components/common/index.ts | 8 + frontend/src/components/file/FileExpanded.tsx | 114 + .../src/components/file/FileViewContent.tsx | 252 + .../src/components/file/FileViewControls.tsx | 124 + .../src/components/file/FileViewHeader.tsx | 123 + frontend/src/components/file/index.ts | 4 + frontend/src/components/layout/Header.tsx | 150 + .../components/layout/NotificationCenter.tsx | 158 + frontend/src/components/layout/index.ts | 2 + .../onboarding/CustomEndpointStep.tsx | 152 + .../onboarding/OnboardingLayout.tsx | 39 + .../src/components/onboarding/WelcomeStep.tsx | 71 + frontend/src/components/onboarding/index.ts | 3 + frontend/src/components/session/Dashboard.tsx | 59 + .../src/components/session/DashboardTabs.tsx | 28 + .../src/components/session/SessionCard.tsx | 204 + .../src/components/session/SessionDialogs.tsx | 301 + .../src/components/session/SessionView.tsx | 650 ++ frontend/src/components/session/Sidebar.tsx | 72 + frontend/src/components/session/index.ts | 8 + .../settings/AgentMaxRoundsSettings.tsx | 157 + .../src/components/settings/ConfirmDialog.tsx | 67 + .../components/settings/FolderSettings.tsx | 71 + .../components/settings/GeneralSettings.tsx | 111 + .../src/components/settings/ModelSettings.tsx | 327 + .../components/settings/SettingsDialog.tsx | 211 + .../components/settings/SettingsTrigger.tsx | 23 + frontend/src/components/settings/index.ts | 2 + .../settings/modelSettingsReducer.ts | 322 + frontend/src/components/ui/README.md | 174 + frontend/src/components/ui/alert.tsx | 59 + frontend/src/components/ui/button.tsx | 55 + frontend/src/components/ui/card.tsx | 55 + frontend/src/components/ui/checkbox.tsx | 28 + frontend/src/components/ui/dialog.tsx | 103 + frontend/src/components/ui/dropdown-menu.tsx | 187 + frontend/src/components/ui/input.tsx | 22 + frontend/src/components/ui/select.tsx | 150 + frontend/src/components/ui/switch.tsx | 35 + frontend/src/components/ui/tabs.tsx | 53 + frontend/src/components/ui/tooltip.tsx | 30 + frontend/src/hooks/index.ts | 19 + frontend/src/hooks/useAutoScrollToBottom.ts | 132 + frontend/src/hooks/useBackendHealthPolling.ts | 70 + frontend/src/hooks/useCollapsibleGroup.ts | 62 + frontend/src/hooks/useConnectionIssue.ts | 35 + frontend/src/hooks/useEnsureSessionData.ts | 97 + frontend/src/hooks/useFileAttachments.ts | 137 + frontend/src/hooks/useFolderMount.ts | 118 + frontend/src/hooks/useMediaQuery.ts | 27 + frontend/src/hooks/useNow.ts | 25 + frontend/src/hooks/useResponsiveLayout.ts | 40 + frontend/src/hooks/useScrollRestoration.ts | 217 + .../useScrollToImportantBackgroundMessage.ts | 138 + frontend/src/hooks/useSessionPlayback.ts | 140 + frontend/src/hooks/useTheme.ts | 35 + frontend/src/hooks/useWebSocketManager.tsx | 1039 +++ frontend/src/hooks/useWrapState.ts | 31 + frontend/src/index.css | 466 ++ frontend/src/lib/connectionStatus.ts | 52 + frontend/src/lib/constants.ts | 272 + frontend/src/lib/fileUtils.ts | 273 + frontend/src/lib/inputDrafts.ts | 26 + frontend/src/lib/messages/constants.tsx | 291 + frontend/src/lib/messages/index.ts | 51 + frontend/src/lib/messages/parser.ts | 454 ++ frontend/src/lib/messages/utils.ts | 81 + frontend/src/lib/prismConfig.ts | 85 + frontend/src/lib/sampleTasks.ts | 98 + frontend/src/lib/sessionRedirect.ts | 70 + frontend/src/lib/timeFormat.ts | 161 + frontend/src/lib/utils.ts | 54 + frontend/src/lib/wsLogger.ts | 212 + frontend/src/main.tsx | 13 + frontend/src/pages/OnboardingPage.tsx | 38 + frontend/src/pages/SampleTasksPage.tsx | 77 + frontend/src/stores/backendHealthStore.ts | 29 + frontend/src/stores/chatStore.ts | 717 ++ frontend/src/stores/folderPreferencesStore.ts | 92 + frontend/src/stores/index.ts | 32 + frontend/src/stores/notificationStore.ts | 145 + frontend/src/stores/onboardingStore.ts | 293 + frontend/src/stores/uiStore.ts | 164 + frontend/src/test/ChatInput.test.tsx | 74 + frontend/src/test/auth.test.ts | 66 + frontend/src/test/chatStore.test.ts | 827 ++ frontend/src/test/chatViewUtils.test.ts | 145 + frontend/src/test/client.test.ts | 324 + frontend/src/test/fileUtils.test.ts | 375 + .../src/test/folderPreferencesStore.test.ts | 172 + frontend/src/test/messageList.test.tsx | 104 + frontend/src/test/messageListUtils.test.ts | 1291 ++++ frontend/src/test/modelSettings.test.ts | 364 + frontend/src/test/notificationStore.test.ts | 235 + frontend/src/test/onboardingStore.test.ts | 345 + frontend/src/test/parser.test.ts | 1306 ++++ frontend/src/test/sessionRedirect.test.ts | 200 + frontend/src/test/setup.ts | 18 + frontend/src/test/timeFormat.test.ts | 180 + .../src/test/useEnsureSessionData.test.tsx | 178 + ...ScrollToImportantBackgroundMessage.test.ts | 270 + frontend/src/test/useSessionPlayback.test.ts | 455 ++ .../src/test/useWebSocketManager.test.tsx | 398 + frontend/src/test/verificationAlert.test.tsx | 46 + frontend/src/test/wsLogger.test.ts | 116 + frontend/src/types/api.ts | 145 + frontend/src/types/browser.ts | 11 + frontend/src/types/file.ts | 92 + frontend/src/types/folder.ts | 14 + frontend/src/types/index.ts | 110 + frontend/src/types/message.ts | 338 + frontend/src/types/state.ts | 189 + frontend/src/types/store.ts | 147 + frontend/src/types/ui.ts | 24 + frontend/src/types/websocket.ts | 263 + frontend/src/vite-env.d.ts | 19 + frontend/tailwind.config.ts | 12 + frontend/tsconfig.json | 35 + frontend/vite.config.ts | 48 + frontend/vitest.config.ts | 18 + pyproject.toml | 131 + src/magentic_ui/__init__.py | 11 + src/magentic_ui/_ai_client.py | 241 + src/magentic_ui/agents/__init__.py | 5 + src/magentic_ui/agents/base.py | 59 + src/magentic_ui/agents/message_schemas.py | 518 ++ src/magentic_ui/agents/registry.py | 160 + src/magentic_ui/agents/web_surfer/__init__.py | 5 + .../agents/web_surfer/fara/__init__.py | 3 + .../agents/web_surfer/fara/_browser_env.py | 174 + .../agents/web_surfer/fara/_fara_qwen3.py | 763 ++ .../web_surfer/fara/_fara_qwen3_next.py | 307 + .../web_surfer/fara/_fara_web_surfer.py | 984 +++ .../agents/web_surfer/fara/_prompts.py | 446 ++ .../agents/web_surfer/fara/_state_io.py | 52 + .../agents/web_surfer/fara/_types.py | 204 + .../agents/web_surfer/fara/find_overlay.js | 204 + .../web_surfer/fara/qwen_helpers/__init__.py | 0 .../web_surfer/fara/qwen_helpers/base_tool.py | 156 + .../fara/qwen_helpers/fncall_prompt.py | 293 + .../web_surfer/fara/qwen_helpers/schema.py | 166 + .../web_surfer/fara/qwen_helpers/utils.py | 24 + src/magentic_ui/approval.py | 46 + src/magentic_ui/backend/__init__.py | 5 + src/magentic_ui/backend/cli.py | 264 + src/magentic_ui/backend/database/__init__.py | 5 + .../backend/database/config_file_loader.py | 118 + .../backend/database/db_manager.py | 370 + .../backend/database/schema_manager.py | 521 ++ src/magentic_ui/backend/datamodel/__init__.py | 32 + src/magentic_ui/backend/datamodel/db.py | 206 + src/magentic_ui/backend/datamodel/types.py | 48 + .../backend/teammanager/__init__.py | 4 + .../backend/teammanager/_upload_validation.py | 106 + .../backend/teammanager/teammanager.py | 685 ++ src/magentic_ui/backend/utils/__init__.py | 0 src/magentic_ui/backend/utils/utils.py | 265 + src/magentic_ui/backend/web/__init__.py | 0 src/magentic_ui/backend/web/app.py | 287 + src/magentic_ui/backend/web/auth.py | 142 + src/magentic_ui/backend/web/config.py | 15 + src/magentic_ui/backend/web/deps.py | 226 + src/magentic_ui/backend/web/initialization.py | 101 + .../backend/web/managers/__init__.py | 3 + .../web/managers/_continuation_response.py | 33 + .../backend/web/managers/_pool_full.py | 138 + .../backend/web/managers/connection.py | 1210 +++ .../backend/web/routes/__init__.py | 0 .../backend/web/routes/filesystem.py | 226 + .../backend/web/routes/onboarding.py | 444 ++ src/magentic_ui/backend/web/routes/runs.py | 159 + .../backend/web/routes/sessions.py | 282 + .../backend/web/routes/settings.py | 193 + .../backend/web/routes/trusted_folders.py | 85 + src/magentic_ui/backend/web/routes/ws.py | 213 + src/magentic_ui/magentic_ui_config.py | 237 + src/magentic_ui/sandbox/__init__.py | 104 + src/magentic_ui/sandbox/_local.py | 66 + src/magentic_ui/sandbox/_null.py | 67 + src/magentic_ui/sandbox/_path_normalizer.py | 149 + src/magentic_ui/sandbox/_path_validator.py | 269 + src/magentic_ui/sandbox/_quicksand.py | 372 + src/magentic_ui/sandbox/requirements.txt | 1 + src/magentic_ui/sandbox/resources/init.sh | 16 + src/magentic_ui/task_team.py | 144 + src/magentic_ui/teams/__init__.py | 1 + src/magentic_ui/teams/omniagent/__init__.py | 5 + .../teams/omniagent/_bash_observers.py | 289 + .../teams/omniagent/_command_policy.py | 1149 +++ .../teams/omniagent/_compaction.py | 152 + .../teams/omniagent/_continuation.py | 58 + src/magentic_ui/teams/omniagent/_errors.py | 20 + src/magentic_ui/teams/omniagent/_handoff.py | 79 + .../teams/omniagent/_harness/__init__.py | 5 + .../teams/omniagent/_harness/_format.py | 131 + src/magentic_ui/teams/omniagent/_messages.py | 29 + .../teams/omniagent/_omni_agent.py | 1053 +++ src/magentic_ui/teams/omniagent/_parse.py | 135 + src/magentic_ui/teams/omniagent/_registry.py | 858 +++ src/magentic_ui/teams/omniagent/_responses.py | 859 +++ .../teams/omniagent/_sandbox_utils.py | 23 + .../teams/omniagent/_shell_quoting.py | 75 + src/magentic_ui/teams/omniagent/_state_io.py | 84 + .../teams/omniagent/_system_prompt.py | 169 + .../teams/omniagent/_user_interjection.py | 55 + src/magentic_ui/teams/omniagent/_verifier.py | 451 ++ .../teams/omniagent/tools/__init__.py | 1 + .../tools/guest/magui_tools/__init__.py | 1 + .../tools/guest/magui_tools/_path_utils.py | 22 + .../omniagent/tools/guest/magui_tools/edit.py | 89 + .../tools/guest/magui_tools/insert.py | 88 + .../omniagent/tools/guest/magui_tools/lint.py | 95 + .../omniagent/tools/guest/magui_tools/read.py | 124 + .../omniagent/tools/guest/scripts/search.sh | 141 + .../teams/omniagent/tools/host/__init__.py | 41 + .../teams/omniagent/tools/host/_bash.py | 30 + .../teams/omniagent/tools/host/_create.py | 21 + .../teams/omniagent/tools/host/_edit.py | 87 + .../teams/omniagent/tools/host/_insert.py | 83 + .../teams/omniagent/tools/host/_open.py | 21 + .../teams/omniagent/tools/host/_output.py | 12 + .../teams/omniagent/tools/host/_scroll.py | 24 + .../teams/omniagent/tools/host/_search.py | 52 + .../teams/omniagent/tools/host/_state.py | 153 + src/magentic_ui/tools/__init__.py | 7 + src/magentic_ui/tools/playwright/__init__.py | 11 + .../tools/playwright/browser/__init__.py | 19 + .../browser/base_playwright_browser.py | 115 + .../browser/local_playwright_browser.py | 137 + .../browser/quicksand_browser_manager.py | 513 ++ .../browser/quicksand_playwright_browser.py | 132 + .../tools/playwright/browser/utils.py | 62 + .../tools/playwright/page_script.js | 623 ++ .../playwright/playwright_controller_fara.py | 716 ++ .../tools/playwright/playwright_state.py | 140 + .../tools/playwright/utils/__init__.py | 0 .../tools/playwright/utils/animation_utils.py | 190 + .../playwright/utils/webpage_text_utils.py | 224 + src/magentic_ui/types.py | 131 + src/magentic_ui/version.py | 3 + tests/agents/fara/__init__.py | 1 + tests/agents/fara/test_fara_continuation.py | 399 + tests/agents/fara/test_fara_qwen3.py | 1138 +++ tests/agents/fara/test_state_io.py | 262 + tests/agents/omni/__init__.py | 0 tests/agents/omni/_stream_mock.py | 122 + tests/agents/omni/conftest.py | 88 + tests/agents/omni/test_context_compaction.py | 1141 +++ tests/agents/omni/test_delegate_cua.py | 484 ++ .../omni/test_guest_tools_integration.py | 844 +++ tests/agents/omni/test_handoff.py | 156 + tests/agents/omni/test_multi_call.py | 306 + tests/agents/omni/test_multi_turn_context.py | 314 + tests/agents/omni/test_omni_continuation.py | 285 + tests/agents/omni/test_orphan_tool_call.py | 324 + tests/agents/omni/test_parse.py | 165 + tests/agents/omni/test_prompts.py | 120 + tests/agents/omni/test_state_io.py | 132 + tests/agents/omni/test_thinking_state.py | 133 + tests/agents/omni/test_user_inbox.py | 399 + tests/agents/test_registry.py | 169 + tests/test_agent_settings_route.py | 272 + tests/test_ai_client.py | 113 + tests/test_auth.py | 169 + tests/test_auth_middleware.py | 321 + tests/test_bash_observers.py | 234 + tests/test_command_policy.py | 732 ++ tests/test_config_file_loader.py | 251 + tests/test_continuation_handler.py | 129 + .../test_datamodel_datetime_serialization.py | 146 + tests/test_file_upload_tracking.py | 1095 +++ tests/test_filesystem_route.py | 85 + tests/test_harness_config_merge.py | 135 + tests/test_input_response_files.py | 355 + tests/test_live_browser_pool_full.py | 247 + tests/test_log_scrub.py | 127 + .../test_magentic_ui_config_serialization.py | 260 + tests/test_onboarding.py | 254 + tests/test_playwright_state.py | 209 + tests/test_quicksand_browser.py | 640 ++ tests/test_quicksand_sandbox.py | 211 + tests/test_run_agent_mode_persistence.py | 157 + tests/test_sandbox_bash_only.py | 30 + tests/test_sandbox_guest_tools_dir.py | 40 + tests/test_save_message_scrub.py | 101 + tests/test_start_mounted_folder_metadata.py | 63 + tests/test_types.py | 197 + tests/test_verifier.py | 373 + uv.lock | 2715 +++++++ 388 files changed, 76588 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/dependabot.yml create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/checks.yaml create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 NOTICE create mode 100644 README.md create mode 100644 README.wehub.md create mode 100644 SECURITY.md create mode 100644 SUPPORT.md create mode 100644 config.yaml.example create mode 100644 docs/TRANSPARENCY_NOTE.md create mode 100644 docs/_assets/magentic-lite-logo-dark.png create mode 100644 docs/_assets/magentic-lite-logo-light.png create mode 100644 docs/build-from-source.md create mode 100644 docs/configuration.md create mode 100644 docs/installation.md create mode 100644 docs/limitations.md create mode 100644 docs/model-hosting-guide.md create mode 100644 docs/troubleshooting.md create mode 100644 frontend/.nvmrc create mode 100644 frontend/.prettierignore create mode 100644 frontend/.prettierrc create mode 100644 frontend/components.json create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package.json create mode 100644 frontend/pnpm-lock.yaml create mode 100644 frontend/public/magentic-logo.svg create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/api/agentSettings.ts create mode 100644 frontend/src/api/auth.ts create mode 100644 frontend/src/api/client.ts create mode 100644 frontend/src/api/files.ts create mode 100644 frontend/src/api/filesystem.ts create mode 100644 frontend/src/api/health.ts create mode 100644 frontend/src/api/index.ts create mode 100644 frontend/src/api/onboarding.ts create mode 100644 frontend/src/api/sessions.ts create mode 100644 frontend/src/api/settings.ts create mode 100644 frontend/src/components/browser/ActionDescription.tsx create mode 100644 frontend/src/components/browser/BrowserEmbed.tsx create mode 100644 frontend/src/components/browser/BrowserExpanded.tsx create mode 100644 frontend/src/components/browser/BrowserHeader.tsx create mode 100644 frontend/src/components/browser/BrowserViewer.tsx create mode 100644 frontend/src/components/browser/DescriptionOverlay.tsx create mode 100644 frontend/src/components/browser/ExpandableActionText.tsx create mode 100644 frontend/src/components/browser/PlaybackButtons.tsx create mode 100644 frontend/src/components/browser/PlaybackControlsRow.tsx create mode 100644 frontend/src/components/browser/ProgressBar.tsx create mode 100644 frontend/src/components/browser/TakeoverNotice.tsx create mode 100644 frontend/src/components/browser/index.ts create mode 100644 frontend/src/components/browser/usePlaybackControls.ts create mode 100644 frontend/src/components/chat/ChatInput.tsx create mode 100644 frontend/src/components/chat/ChatInputBanner.tsx create mode 100644 frontend/src/components/chat/ChatView.tsx create mode 100644 frontend/src/components/chat/FolderBrowserDialog.tsx create mode 100644 frontend/src/components/chat/FolderMountDialog.tsx create mode 100644 frontend/src/components/chat/MessageList.tsx create mode 100644 frontend/src/components/chat/MessageRenderer.tsx create mode 100644 frontend/src/components/chat/chatViewUtils.ts create mode 100644 frontend/src/components/chat/index.ts create mode 100644 frontend/src/components/chat/messageListUtils.ts create mode 100644 frontend/src/components/chat/messages/CodeBlock.tsx create mode 100644 frontend/src/components/chat/messages/CodeExecutionMessage.tsx create mode 100644 frontend/src/components/chat/messages/CollapsibleHeader.tsx create mode 100644 frontend/src/components/chat/messages/CuaMessage.tsx create mode 100644 frontend/src/components/chat/messages/ErrorMessage.tsx create mode 100644 frontend/src/components/chat/messages/FileMessage.tsx create mode 100644 frontend/src/components/chat/messages/FinalAnswerMessage.tsx create mode 100644 frontend/src/components/chat/messages/HeaderedMessage.tsx create mode 100644 frontend/src/components/chat/messages/InputRequestMessage.tsx create mode 100644 frontend/src/components/chat/messages/MemorizedFactMessage.tsx create mode 100644 frontend/src/components/chat/messages/OrchestratorToolMessage.tsx create mode 100644 frontend/src/components/chat/messages/PreBlock.tsx create mode 100644 frontend/src/components/chat/messages/ReasoningMessage.tsx create mode 100644 frontend/src/components/chat/messages/ReasoningPlaceholder.tsx create mode 100644 frontend/src/components/chat/messages/SessionStatusIndicator.tsx create mode 100644 frontend/src/components/chat/messages/SystemStatusMessage.tsx create mode 100644 frontend/src/components/chat/messages/ToolResultMessage.tsx create mode 100644 frontend/src/components/chat/messages/index.ts create mode 100644 frontend/src/components/common/ConnectionStatusBanner.tsx create mode 100644 frontend/src/components/common/ErrorBoundary.tsx create mode 100644 frontend/src/components/common/FileChip.tsx create mode 100644 frontend/src/components/common/FolderChip.tsx create mode 100644 frontend/src/components/common/Markdown.tsx create mode 100644 frontend/src/components/common/ModelConfigCard.tsx create mode 100644 frontend/src/components/common/SampleTaskCards.tsx create mode 100644 frontend/src/components/common/WrapToggleButton.tsx create mode 100644 frontend/src/components/common/index.ts create mode 100644 frontend/src/components/file/FileExpanded.tsx create mode 100644 frontend/src/components/file/FileViewContent.tsx create mode 100644 frontend/src/components/file/FileViewControls.tsx create mode 100644 frontend/src/components/file/FileViewHeader.tsx create mode 100644 frontend/src/components/file/index.ts create mode 100644 frontend/src/components/layout/Header.tsx create mode 100644 frontend/src/components/layout/NotificationCenter.tsx create mode 100644 frontend/src/components/layout/index.ts create mode 100644 frontend/src/components/onboarding/CustomEndpointStep.tsx create mode 100644 frontend/src/components/onboarding/OnboardingLayout.tsx create mode 100644 frontend/src/components/onboarding/WelcomeStep.tsx create mode 100644 frontend/src/components/onboarding/index.ts create mode 100644 frontend/src/components/session/Dashboard.tsx create mode 100644 frontend/src/components/session/DashboardTabs.tsx create mode 100644 frontend/src/components/session/SessionCard.tsx create mode 100644 frontend/src/components/session/SessionDialogs.tsx create mode 100644 frontend/src/components/session/SessionView.tsx create mode 100644 frontend/src/components/session/Sidebar.tsx create mode 100644 frontend/src/components/session/index.ts create mode 100644 frontend/src/components/settings/AgentMaxRoundsSettings.tsx create mode 100644 frontend/src/components/settings/ConfirmDialog.tsx create mode 100644 frontend/src/components/settings/FolderSettings.tsx create mode 100644 frontend/src/components/settings/GeneralSettings.tsx create mode 100644 frontend/src/components/settings/ModelSettings.tsx create mode 100644 frontend/src/components/settings/SettingsDialog.tsx create mode 100644 frontend/src/components/settings/SettingsTrigger.tsx create mode 100644 frontend/src/components/settings/index.ts create mode 100644 frontend/src/components/settings/modelSettingsReducer.ts create mode 100644 frontend/src/components/ui/README.md create mode 100644 frontend/src/components/ui/alert.tsx create mode 100644 frontend/src/components/ui/button.tsx create mode 100644 frontend/src/components/ui/card.tsx create mode 100644 frontend/src/components/ui/checkbox.tsx create mode 100644 frontend/src/components/ui/dialog.tsx create mode 100644 frontend/src/components/ui/dropdown-menu.tsx create mode 100644 frontend/src/components/ui/input.tsx create mode 100644 frontend/src/components/ui/select.tsx create mode 100644 frontend/src/components/ui/switch.tsx create mode 100644 frontend/src/components/ui/tabs.tsx create mode 100644 frontend/src/components/ui/tooltip.tsx create mode 100644 frontend/src/hooks/index.ts create mode 100644 frontend/src/hooks/useAutoScrollToBottom.ts create mode 100644 frontend/src/hooks/useBackendHealthPolling.ts create mode 100644 frontend/src/hooks/useCollapsibleGroup.ts create mode 100644 frontend/src/hooks/useConnectionIssue.ts create mode 100644 frontend/src/hooks/useEnsureSessionData.ts create mode 100644 frontend/src/hooks/useFileAttachments.ts create mode 100644 frontend/src/hooks/useFolderMount.ts create mode 100644 frontend/src/hooks/useMediaQuery.ts create mode 100644 frontend/src/hooks/useNow.ts create mode 100644 frontend/src/hooks/useResponsiveLayout.ts create mode 100644 frontend/src/hooks/useScrollRestoration.ts create mode 100644 frontend/src/hooks/useScrollToImportantBackgroundMessage.ts create mode 100644 frontend/src/hooks/useSessionPlayback.ts create mode 100644 frontend/src/hooks/useTheme.ts create mode 100644 frontend/src/hooks/useWebSocketManager.tsx create mode 100644 frontend/src/hooks/useWrapState.ts create mode 100644 frontend/src/index.css create mode 100644 frontend/src/lib/connectionStatus.ts create mode 100644 frontend/src/lib/constants.ts create mode 100644 frontend/src/lib/fileUtils.ts create mode 100644 frontend/src/lib/inputDrafts.ts create mode 100644 frontend/src/lib/messages/constants.tsx create mode 100644 frontend/src/lib/messages/index.ts create mode 100644 frontend/src/lib/messages/parser.ts create mode 100644 frontend/src/lib/messages/utils.ts create mode 100644 frontend/src/lib/prismConfig.ts create mode 100644 frontend/src/lib/sampleTasks.ts create mode 100644 frontend/src/lib/sessionRedirect.ts create mode 100644 frontend/src/lib/timeFormat.ts create mode 100644 frontend/src/lib/utils.ts create mode 100644 frontend/src/lib/wsLogger.ts create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/pages/OnboardingPage.tsx create mode 100644 frontend/src/pages/SampleTasksPage.tsx create mode 100644 frontend/src/stores/backendHealthStore.ts create mode 100644 frontend/src/stores/chatStore.ts create mode 100644 frontend/src/stores/folderPreferencesStore.ts create mode 100644 frontend/src/stores/index.ts create mode 100644 frontend/src/stores/notificationStore.ts create mode 100644 frontend/src/stores/onboardingStore.ts create mode 100644 frontend/src/stores/uiStore.ts create mode 100644 frontend/src/test/ChatInput.test.tsx create mode 100644 frontend/src/test/auth.test.ts create mode 100644 frontend/src/test/chatStore.test.ts create mode 100644 frontend/src/test/chatViewUtils.test.ts create mode 100644 frontend/src/test/client.test.ts create mode 100644 frontend/src/test/fileUtils.test.ts create mode 100644 frontend/src/test/folderPreferencesStore.test.ts create mode 100644 frontend/src/test/messageList.test.tsx create mode 100644 frontend/src/test/messageListUtils.test.ts create mode 100644 frontend/src/test/modelSettings.test.ts create mode 100644 frontend/src/test/notificationStore.test.ts create mode 100644 frontend/src/test/onboardingStore.test.ts create mode 100644 frontend/src/test/parser.test.ts create mode 100644 frontend/src/test/sessionRedirect.test.ts create mode 100644 frontend/src/test/setup.ts create mode 100644 frontend/src/test/timeFormat.test.ts create mode 100644 frontend/src/test/useEnsureSessionData.test.tsx create mode 100644 frontend/src/test/useScrollToImportantBackgroundMessage.test.ts create mode 100644 frontend/src/test/useSessionPlayback.test.ts create mode 100644 frontend/src/test/useWebSocketManager.test.tsx create mode 100644 frontend/src/test/verificationAlert.test.tsx create mode 100644 frontend/src/test/wsLogger.test.ts create mode 100644 frontend/src/types/api.ts create mode 100644 frontend/src/types/browser.ts create mode 100644 frontend/src/types/file.ts create mode 100644 frontend/src/types/folder.ts create mode 100644 frontend/src/types/index.ts create mode 100644 frontend/src/types/message.ts create mode 100644 frontend/src/types/state.ts create mode 100644 frontend/src/types/store.ts create mode 100644 frontend/src/types/ui.ts create mode 100644 frontend/src/types/websocket.ts create mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/tailwind.config.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 frontend/vitest.config.ts create mode 100644 pyproject.toml create mode 100644 src/magentic_ui/__init__.py create mode 100644 src/magentic_ui/_ai_client.py create mode 100644 src/magentic_ui/agents/__init__.py create mode 100644 src/magentic_ui/agents/base.py create mode 100644 src/magentic_ui/agents/message_schemas.py create mode 100644 src/magentic_ui/agents/registry.py create mode 100644 src/magentic_ui/agents/web_surfer/__init__.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/__init__.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_browser_env.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_fara_qwen3.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_fara_qwen3_next.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_fara_web_surfer.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_prompts.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_state_io.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/_types.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/find_overlay.js create mode 100644 src/magentic_ui/agents/web_surfer/fara/qwen_helpers/__init__.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/qwen_helpers/base_tool.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/qwen_helpers/fncall_prompt.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/qwen_helpers/schema.py create mode 100644 src/magentic_ui/agents/web_surfer/fara/qwen_helpers/utils.py create mode 100644 src/magentic_ui/approval.py create mode 100644 src/magentic_ui/backend/__init__.py create mode 100644 src/magentic_ui/backend/cli.py create mode 100644 src/magentic_ui/backend/database/__init__.py create mode 100644 src/magentic_ui/backend/database/config_file_loader.py create mode 100644 src/magentic_ui/backend/database/db_manager.py create mode 100644 src/magentic_ui/backend/database/schema_manager.py create mode 100644 src/magentic_ui/backend/datamodel/__init__.py create mode 100644 src/magentic_ui/backend/datamodel/db.py create mode 100644 src/magentic_ui/backend/datamodel/types.py create mode 100644 src/magentic_ui/backend/teammanager/__init__.py create mode 100644 src/magentic_ui/backend/teammanager/_upload_validation.py create mode 100644 src/magentic_ui/backend/teammanager/teammanager.py create mode 100644 src/magentic_ui/backend/utils/__init__.py create mode 100644 src/magentic_ui/backend/utils/utils.py create mode 100644 src/magentic_ui/backend/web/__init__.py create mode 100644 src/magentic_ui/backend/web/app.py create mode 100644 src/magentic_ui/backend/web/auth.py create mode 100644 src/magentic_ui/backend/web/config.py create mode 100644 src/magentic_ui/backend/web/deps.py create mode 100644 src/magentic_ui/backend/web/initialization.py create mode 100644 src/magentic_ui/backend/web/managers/__init__.py create mode 100644 src/magentic_ui/backend/web/managers/_continuation_response.py create mode 100644 src/magentic_ui/backend/web/managers/_pool_full.py create mode 100644 src/magentic_ui/backend/web/managers/connection.py create mode 100644 src/magentic_ui/backend/web/routes/__init__.py create mode 100644 src/magentic_ui/backend/web/routes/filesystem.py create mode 100644 src/magentic_ui/backend/web/routes/onboarding.py create mode 100644 src/magentic_ui/backend/web/routes/runs.py create mode 100644 src/magentic_ui/backend/web/routes/sessions.py create mode 100644 src/magentic_ui/backend/web/routes/settings.py create mode 100644 src/magentic_ui/backend/web/routes/trusted_folders.py create mode 100644 src/magentic_ui/backend/web/routes/ws.py create mode 100644 src/magentic_ui/magentic_ui_config.py create mode 100644 src/magentic_ui/sandbox/__init__.py create mode 100644 src/magentic_ui/sandbox/_local.py create mode 100644 src/magentic_ui/sandbox/_null.py create mode 100644 src/magentic_ui/sandbox/_path_normalizer.py create mode 100644 src/magentic_ui/sandbox/_path_validator.py create mode 100644 src/magentic_ui/sandbox/_quicksand.py create mode 100644 src/magentic_ui/sandbox/requirements.txt create mode 100644 src/magentic_ui/sandbox/resources/init.sh create mode 100644 src/magentic_ui/task_team.py create mode 100644 src/magentic_ui/teams/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/_bash_observers.py create mode 100644 src/magentic_ui/teams/omniagent/_command_policy.py create mode 100644 src/magentic_ui/teams/omniagent/_compaction.py create mode 100644 src/magentic_ui/teams/omniagent/_continuation.py create mode 100644 src/magentic_ui/teams/omniagent/_errors.py create mode 100644 src/magentic_ui/teams/omniagent/_handoff.py create mode 100644 src/magentic_ui/teams/omniagent/_harness/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/_harness/_format.py create mode 100644 src/magentic_ui/teams/omniagent/_messages.py create mode 100644 src/magentic_ui/teams/omniagent/_omni_agent.py create mode 100644 src/magentic_ui/teams/omniagent/_parse.py create mode 100644 src/magentic_ui/teams/omniagent/_registry.py create mode 100644 src/magentic_ui/teams/omniagent/_responses.py create mode 100644 src/magentic_ui/teams/omniagent/_sandbox_utils.py create mode 100644 src/magentic_ui/teams/omniagent/_shell_quoting.py create mode 100644 src/magentic_ui/teams/omniagent/_state_io.py create mode 100644 src/magentic_ui/teams/omniagent/_system_prompt.py create mode 100644 src/magentic_ui/teams/omniagent/_user_interjection.py create mode 100644 src/magentic_ui/teams/omniagent/_verifier.py create mode 100644 src/magentic_ui/teams/omniagent/tools/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/_path_utils.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/edit.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/insert.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/lint.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/magui_tools/read.py create mode 100644 src/magentic_ui/teams/omniagent/tools/guest/scripts/search.sh create mode 100644 src/magentic_ui/teams/omniagent/tools/host/__init__.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_bash.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_create.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_edit.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_insert.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_open.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_output.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_scroll.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_search.py create mode 100644 src/magentic_ui/teams/omniagent/tools/host/_state.py create mode 100644 src/magentic_ui/tools/__init__.py create mode 100644 src/magentic_ui/tools/playwright/__init__.py create mode 100644 src/magentic_ui/tools/playwright/browser/__init__.py create mode 100644 src/magentic_ui/tools/playwright/browser/base_playwright_browser.py create mode 100644 src/magentic_ui/tools/playwright/browser/local_playwright_browser.py create mode 100644 src/magentic_ui/tools/playwright/browser/quicksand_browser_manager.py create mode 100644 src/magentic_ui/tools/playwright/browser/quicksand_playwright_browser.py create mode 100644 src/magentic_ui/tools/playwright/browser/utils.py create mode 100644 src/magentic_ui/tools/playwright/page_script.js create mode 100644 src/magentic_ui/tools/playwright/playwright_controller_fara.py create mode 100644 src/magentic_ui/tools/playwright/playwright_state.py create mode 100644 src/magentic_ui/tools/playwright/utils/__init__.py create mode 100644 src/magentic_ui/tools/playwright/utils/animation_utils.py create mode 100644 src/magentic_ui/tools/playwright/utils/webpage_text_utils.py create mode 100644 src/magentic_ui/types.py create mode 100644 src/magentic_ui/version.py create mode 100644 tests/agents/fara/__init__.py create mode 100644 tests/agents/fara/test_fara_continuation.py create mode 100644 tests/agents/fara/test_fara_qwen3.py create mode 100644 tests/agents/fara/test_state_io.py create mode 100644 tests/agents/omni/__init__.py create mode 100644 tests/agents/omni/_stream_mock.py create mode 100644 tests/agents/omni/conftest.py create mode 100644 tests/agents/omni/test_context_compaction.py create mode 100644 tests/agents/omni/test_delegate_cua.py create mode 100644 tests/agents/omni/test_guest_tools_integration.py create mode 100644 tests/agents/omni/test_handoff.py create mode 100644 tests/agents/omni/test_multi_call.py create mode 100644 tests/agents/omni/test_multi_turn_context.py create mode 100644 tests/agents/omni/test_omni_continuation.py create mode 100644 tests/agents/omni/test_orphan_tool_call.py create mode 100644 tests/agents/omni/test_parse.py create mode 100644 tests/agents/omni/test_prompts.py create mode 100644 tests/agents/omni/test_state_io.py create mode 100644 tests/agents/omni/test_thinking_state.py create mode 100644 tests/agents/omni/test_user_inbox.py create mode 100644 tests/agents/test_registry.py create mode 100644 tests/test_agent_settings_route.py create mode 100644 tests/test_ai_client.py create mode 100644 tests/test_auth.py create mode 100644 tests/test_auth_middleware.py create mode 100644 tests/test_bash_observers.py create mode 100644 tests/test_command_policy.py create mode 100644 tests/test_config_file_loader.py create mode 100644 tests/test_continuation_handler.py create mode 100644 tests/test_datamodel_datetime_serialization.py create mode 100644 tests/test_file_upload_tracking.py create mode 100644 tests/test_filesystem_route.py create mode 100644 tests/test_harness_config_merge.py create mode 100644 tests/test_input_response_files.py create mode 100644 tests/test_live_browser_pool_full.py create mode 100644 tests/test_log_scrub.py create mode 100644 tests/test_magentic_ui_config_serialization.py create mode 100644 tests/test_onboarding.py create mode 100644 tests/test_playwright_state.py create mode 100644 tests/test_quicksand_browser.py create mode 100644 tests/test_quicksand_sandbox.py create mode 100644 tests/test_run_agent_mode_persistence.py create mode 100644 tests/test_sandbox_bash_only.py create mode 100644 tests/test_sandbox_guest_tools_dir.py create mode 100644 tests/test_save_message_scrub.py create mode 100644 tests/test_start_mounted_folder_metadata.py create mode 100644 tests/test_types.py create mode 100644 tests/test_verifier.py create mode 100644 uv.lock diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..c861d7c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,74 @@ +name: Bug report +description: Report a bug or unexpected behavior +title: "[bug] " +labels: [bug] +body: + - type: textarea + id: description + attributes: + label: Describe the bug + description: A clear, concise description of what's wrong. + validations: + required: true + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: The minimum sequence that triggers the bug. + placeholder: | + 1. + 2. + 3. + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What you expected to happen. + validations: + required: true + - type: textarea + id: actual + attributes: + label: Actual behavior + description: What actually happened. Include error messages, screenshots, or log excerpts. + validations: + required: true + - type: input + id: version + attributes: + label: MagenticLite version + description: Output of `magentic-ui --version`. + placeholder: 0.2.0 + validations: + required: true + - type: dropdown + id: os + attributes: + label: Operating system + options: + - Linux + - macOS + - Windows + - Windows (WSL) + - Other + validations: + required: true + - type: input + id: python + attributes: + label: Python version + placeholder: "3.12" + validations: + required: true + - type: input + id: model + attributes: + label: Model client / provider + description: Which orchestrator / web-surfer model you're running. + - type: textarea + id: context + attributes: + label: Additional context + description: Config snippets (with secrets redacted), recent changes, workarounds tried. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..1a4a34e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question or discussion + url: https://github.com/microsoft/magentic-ui/discussions + about: For usage questions and general discussion, please use Discussions instead of an issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..9fc3354 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,29 @@ +name: Feature request +description: Suggest a new feature or improvement +title: "[feature] " +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: What problem does this solve? + description: Describe the use case or pain point. What are you trying to do? + validations: + required: true + - type: textarea + id: solution + attributes: + label: Proposed solution + description: How would you like it to work? UI, API, or behavior. + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Other approaches you thought about and why they're not ideal. + - type: textarea + id: context + attributes: + label: Additional context + description: Mockups, links to similar projects, related issues. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b77dcaa --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,79 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" # project root (where pyproject.toml and uv.lock live) + schedule: + interval: "weekly" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + + - package-ecosystem: "npm" + directory: "/frontend" + schedule: + interval: "weekly" + open-pull-requests-limit: 10 + # Suppress major-version PRs for packages that need to track an external baseline. + ignore: + # Keep @types/node on the current Node major (24) to match the runtime baseline + # declared in package.json (>=24); bump it manually when we intentionally raise + # that baseline to a new major. + - dependency-name: "@types/node" + update-types: ["version-update:semver-major"] + # TypeScript majors often introduce stricter narrowing across the whole project. + # Bump manually when we're ready to handle the fallout. + - dependency-name: "typescript" + update-types: ["version-update:semver-major"] + # Vite 8 swaps the bundler/transform stack to Rolldown + Oxc. Wait for the + # ecosystem (vitest, @tailwindcss/vite, @vitejs/plugin-react) to stabilize + # — re-evaluate around vite 8.2 or when we hit a feature we need. + - dependency-name: "vite" + update-types: ["version-update:semver-major"] + # @vitejs/plugin-react v6 drops Vite 7 support; bump in lockstep with vite. + - dependency-name: "@vitejs/plugin-react" + update-types: ["version-update:semver-major"] + # ESLint 10 majors can ripple through plugins (typescript-eslint, + # eslint-plugin-react, etc.); bump manually once peer deps are aligned. + - dependency-name: "eslint" + update-types: ["version-update:semver-major"] + - dependency-name: "@eslint/js" + update-types: ["version-update:semver-major"] + # Test/build tooling that we intentionally pin to its current major to + # avoid noisy PRs without real benefit: + # - jsdom: vitest's environment; follow vitest's recommended version + # - globals: ESLint plugin support; staying on 16.x is fine + # - rollup-plugin-visualizer: dev-only build analyzer + - dependency-name: "jsdom" + update-types: ["version-update:semver-major"] + - dependency-name: "globals" + update-types: ["version-update:semver-major"] + - dependency-name: "rollup-plugin-visualizer" + update-types: ["version-update:semver-major"] + # Project doesn't use Redux — package will be removed from package.json + # in a separate PR. Suppress all updates in the meantime. + - dependency-name: "@redux-devtools/extension" + update-types: + - "version-update:semver-major" + - "version-update:semver-minor" + - "version-update:semver-patch" + # Group routine version bumps so we don't drown in PRs. + # Security updates always open as standalone PRs regardless of grouping. + groups: + frontend-dev-dependencies: + dependency-type: "development" + update-types: + - "minor" + - "patch" + frontend-prod-dependencies: + dependency-type: "production" + update-types: + - "minor" + - "patch" + # Quiet down dev-only major bumps (vite, jsdom, rollup plugins, etc.) + # into a single weekly PR. Prod majors stay standalone for individual review. + frontend-dev-majors: + dependency-type: "development" + update-types: + - "major" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..1dbb090 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,27 @@ +## Related Issue + + + +## Summary + + + +## Changes + + + +- ... + +## How to Verify + + + +1. ... +2. ... +3. ... + +## Checklist + +- [ ] Tests added or updated (if applicable) +- [ ] Documentation updated (if needed) +- [ ] Verified using the steps above diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml new file mode 100644 index 0000000..0b8dbd1 --- /dev/null +++ b/.github/workflows/checks.yaml @@ -0,0 +1,195 @@ +name: Checks (magui2.0) + +on: + push: + branches: + - main + - 'release/**' + pull_request: + branches: + - main + - 'release/**' + schedule: + - cron: "0 3 * * 1" # Every Monday at 03:00 UTC + +permissions: + contents: read + +jobs: + # Python checks + python-format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + version: "0.7.11" + - uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + - run: uv sync --all-extras + working-directory: . + - name: Run format check + run: | + source .venv/bin/activate + poe fmt --check + + python-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + version: "0.7.11" + - uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + - run: uv sync --all-extras + working-directory: . + - name: Run lint + run: | + source .venv/bin/activate + poe lint + + python-pyright: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + version: "0.7.11" + - uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + - run: uv sync --all-extras + working-directory: . + - name: Run pyright + run: | + source .venv/bin/activate + poe pyright + + python-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + version: "0.7.11" + - uses: actions/setup-python@v5 + with: + python-version-file: ".python-version" + - run: uv sync --all-extras + working-directory: . + - name: Get Playwright version + id: playwright-version + run: | + source .venv/bin/activate + echo "version=$(playwright --version | awk '{print $2}')" >> "$GITHUB_OUTPUT" + - name: Cache Playwright browsers + uses: actions/cache@v6 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ steps.playwright-version.outputs.version }} + - name: Install Playwright browsers + run: | + source .venv/bin/activate + playwright install --with-deps + - name: Run tests + run: | + source .venv/bin/activate + pytest tests --cov=src --cov-report=xml --cov-report=term-missing + - name: Upload coverage artifact + uses: actions/upload-artifact@v7 + with: + name: coverage-magentic-ui + path: coverage.xml + + # Frontend checks + frontend-format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: npm install -g pnpm@10.24.0 + - uses: actions/setup-node@v6 + with: + node-version: "24" + cache: "pnpm" + cache-dependency-path: "frontend/pnpm-lock.yaml" + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + - name: Format check + run: pnpm run format:check + working-directory: frontend + + frontend-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: npm install -g pnpm@10.24.0 + - uses: actions/setup-node@v6 + with: + node-version: "24" + cache: "pnpm" + cache-dependency-path: "frontend/pnpm-lock.yaml" + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + - name: Lint + run: pnpm run lint + working-directory: frontend + + frontend-typecheck: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: npm install -g pnpm@10.24.0 + - uses: actions/setup-node@v6 + with: + node-version: "24" + cache: "pnpm" + cache-dependency-path: "frontend/pnpm-lock.yaml" + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + - name: Type check + run: pnpm run type-check + working-directory: frontend + + frontend-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - run: npm install -g pnpm@10.24.0 + - uses: actions/setup-node@v6 + with: + node-version: "24" + cache: "pnpm" + cache-dependency-path: "frontend/pnpm-lock.yaml" + - name: Install dependencies + run: pnpm install --frozen-lockfile + working-directory: frontend + - name: Test + run: pnpm run test:run + working-directory: frontend +# Turn on this step after open source. +# codecov: +# runs-on: ubuntu-latest +# needs: [python-test] +# steps: +# - uses: actions/checkout@v7 +# - uses: actions/download-artifact@v4 +# with: +# name: coverage-magentic-ui +# path: ./ +# - uses: codecov/codecov-action@v5 +# with: +# files: coverage.xml +# flags: unittests +# name: codecov-umbrella +# fail_ci_if_error: true +# token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..0aae52c --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,102 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "main", "release/**" ] + pull_request: + branches: [ "main", "release/**" ] + schedule: + - cron: '34 1 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: actions + build-mode: none + - language: javascript-typescript + build-mode: none + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'actions', 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v7 + + # Add any setup steps before running the `github/codeql-action/init` action. + # This includes steps like installing compilers or runtimes (`actions/setup-node` + # or others). This is typically only required for manual builds. + # - name: Setup runtime (example) + # uses: actions/setup-example@v1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..66607e0 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: Build & Release Wheel + +on: + workflow_dispatch: + inputs: + create_release: + description: "Create a GitHub Release with the wheel" + type: boolean + default: false + overwrite_existing: + description: "Overwrite existing release with same version tag" + type: boolean + default: false + +permissions: + contents: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: "24" + + - name: Setup pnpm + uses: pnpm/action-setup@v6 + with: + version: 10 + + - name: Install frontend dependencies + working-directory: frontend + run: pnpm install --frozen-lockfile + + - name: Build frontend + working-directory: frontend + run: pnpm build + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Build wheel + run: | + pip install build + python -m build --wheel + + - name: Create GitHub Release + if: ${{ inputs.create_release }} + env: + GH_TOKEN: ${{ github.token }} + run: | + VERSION=$(python -c "exec(open('src/magentic_ui/version.py').read()); print(VERSION)") + TAG="v$VERSION" + # Mark as prerelease when the version has an alpha/beta/rc suffix. + PRERELEASE_FLAG="" + if [[ "$VERSION" =~ (a|b|rc)[0-9]+$ ]]; then + PRERELEASE_FLAG="--prerelease" + fi + # Check if release already exists + if gh release view "$TAG" >/dev/null 2>&1; then + if [ "${{ inputs.overwrite_existing }}" != "true" ]; then + echo "::error::Release $TAG already exists. Bump version in version.py or enable 'Overwrite existing release'." + exit 1 + fi + echo "Overwriting existing release $TAG..." + gh release delete "$TAG" --yes --cleanup-tag + fi + gh release create "$TAG" dist/*.whl \ + --generate-notes \ + $PRERELEASE_FLAG diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4bbf0b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,232 @@ +# VS Code - ignore user-specific settings but keep shared config +.vscode/* +!.vscode/settings.json +!.vscode/extensions.json + +# User config file — see config.yaml.example at repo root +/config.yaml +config/ + +# AI assistant local docs +.claude/ +CLAUDE.md +.copilot/ +.github/copilot-instructions.md +.github/frontend/ +.github/skills/ +.github/plans/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +!frontend/src/lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +scratch.py + +.DS_Store +.magentic_ui_state.json +debug + +data +runs + +node_modules + +# Frontend +pnpm-debug.log* + +# Autogen Studio +database.sqlite +.cache/* +src/magentic_ui/backend/web/files/user/* +src/magentic_ui/backend/test +src/magentic_ui/backend/database/alembic.ini +src/magentic_ui/backend/database/alembic/* +src/magentic_ui/backend/web/files/ui/* +OAI_CONFIG_LIST +scratch/ +src/magentic_ui/backend/web/workdir/* +src/magentic_ui/backend/web/ui/* +src/magentic_ui/backend/web/skills/user/* +.release.sh +.nightly.sh +notebooks/test + +notebooks/work_dir/* +notebooks/test.db + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Task centric memory related db and logs +**/memory_bank/ +**/pagelogs/ diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..2b6bee9 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,8 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss", + "vitest.explorer" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..da721c4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,33 @@ +{ + "eslint.workingDirectories": ["frontend"], + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "css.lint.unknownAtRules": "ignore", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "typescript.tsdk": "frontend/node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true, + "tailwindCSS.includeLanguages": { + "typescriptreact": "html" + }, + "tailwindCSS.experimental.classRegex": [ + ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], + ["cn\\(([^)]*)\\)", "(?:'|\"|`)([^'\"`]*)(?:'|\"|`)"] + ], + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f9ba8cf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9f8b728 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,92 @@ +# Contributing to MagenticLite + +Thank you for your interest in MagenticLite! We genuinely value your ideas, bug reports, and engagement with the project. This document explains how community contributions work and why we've structured things the way we have. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact with any additional questions or comments. + +## How We Work: Issues First + +**GitHub Issues are the primary way to contribute to MagenticLite.** Whether you've found a bug, have a feature idea, or want to propose a change to the codebase, please start with an issue before writing any code. + +This isn't bureaucracy — it's how we avoid duplicated effort, catch design problems early, and make sure contributions align with where the project is headed. An issue that sparks a great discussion is a valuable contribution in its own right. + +### Reporting a Bug + +Open a new issue using the **Bug Report** template. Please include: + +- A clear description of what you expected vs. what happened +- Steps to reproduce the problem +- Your environment (OS, Python version, relevant dependencies) +- Any relevant logs or screenshots + +The more context you provide, the faster we can triage and address it. + +### Suggesting a Feature or Improvement + +Open a new issue using the **Feature Request** template. Describe the problem you're trying to solve, not just the solution you have in mind — this helps us understand the need and explore the design space together. + +If you have a concrete proposal (an API shape, a workflow change, an architecture sketch), include it. We love detailed proposals. + +### Asking Questions + +If you're not sure whether something is a bug or you just need help, open a **Discussion** rather than an issue. The Discussions tab is a great place for open-ended questions and community conversation. + +--- + +## Pull Requests + +MagenticLite is a research prototype in active development. To maintain quality and protect the integrity of the codebase, **we currently limit code contributions via pull requests to a vetted set of contributors.** + +This is a deliberate choice, not an oversight. Open-source AI projects are increasingly targeted by supply chain attacks — where malicious code is introduced through seemingly legitimate contributions. High-profile incidents like the xz Utils backdoor have made the entire ecosystem more cautious, and we think that caution is warranted, especially for a project that runs agentic AI workflows on users' machines. We'd rather be transparent about this policy than leave contributors wondering why their PRs sit unreviewed. + +We know this isn't the fully open model some contributors prefer, and we appreciate your understanding. + +### A Note on AI-Generated Code + +If you are a vetted contributor submitting a PR, please disclose if substantial portions of the code were generated by an AI coding assistant. We don't prohibit AI-assisted development, but we need to know so we can apply the appropriate level of scrutiny. Code is code regardless of how it was produced — the review bar is the same either way. + +--- + +## Other Ways to Contribute + +You don't need to write code to make a meaningful impact on MagenticLite: + +- **Triage issues** — comment on open issues with reproduction steps, additional context, or a note that you can or cannot reproduce a problem. This directly accelerates our work. +- **Improve documentation** — if something in the docs is unclear, missing, or wrong, open an issue describing the gap (or, if you're a vetted contributor, a PR). +- **Share your experience** — open a Discussion about how you're using MagenticLite, what's working, and what isn't. That feedback shapes our roadmap. +- **Answer questions** — if you know the answer to someone's question in Discussions, jump in. + +--- + +## Contributor License Agreement (CLA) + +If you are a vetted contributor and submit a pull request, you'll need to agree to a Contributor License Agreement declaring that you have the right to grant us the rights to use your contribution. For details, visit . + +When you submit a pull request, a CLA bot will automatically determine whether you need to sign and will guide you through the process. You only need to do this once across all Microsoft repos. + +--- + +## Running Tests and Checks + +All code changes must pass the continuous integration checks. You can run them locally with: + +```bash +poe check +``` + +See the `tests` directory for examples of how tests are structured. + +--- + +## Security Vulnerabilities + +Please do **not** file security vulnerabilities as public GitHub issues. Report them privately using [GitHub's private vulnerability reporting](https://github.com/microsoft/magentic-ui/security/advisories/new) or follow the instructions in [SECURITY.md](./SECURITY.md). + +--- + +## Questions? + +Open a [Discussion](https://github.com/microsoft/magentic-ui/discussions) or comment on a relevant issue. Thank you for helping make MagenticLite better. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..269a897 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..294a244 --- /dev/null +++ b/NOTICE @@ -0,0 +1,43 @@ +Magentic-UI +Copyright (c) Microsoft Corporation. + +This product includes software developed at Microsoft Corporation +(https://www.microsoft.com/). + + +=============================================================================== +Third-party software licenses +=============================================================================== + + +------------------------------------------------------------------------------- +SWE-agent v0.7 +------------------------------------------------------------------------------- + +Portions of `src/magentic_ui/teams/omniagent/tools/` are derived from +SWE-agent v0.7: + + https://github.com/SWE-agent/SWE-agent/tree/v0.7/config/commands + +MIT License + +Copyright (c) 2024 John Yang, Carlos E. Jimenez, Alexander Wettig, +Shunyu Yao, Karthik Narasimhan, Ofir Press + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..a991038 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +

+ + + MagenticLite + +

+ +

+ Big tasks. Small models. +

+ +--- + +**MagenticLite** is the next generation of Magentic-UI — an agentic application from **Microsoft AI Frontiers**, redesigned to do more with less. It pairs an on-device-friendly orchestrator model ([MagenticBrain](https://aka.ms/MagenticBrain-foundry)) with a specialized browser-use model ([Fara](https://aka.ms/fara-foundry)) so you can automate real work without depending on frontier-scale compute. + +- **Built for small models, efficient by design.** Strong agentic performance without heavy compute — no frontier-scale models required. +- **Works across the browser and your local file system.** Web research, form filling, file management — in one workflow. +- **Keeps you in the loop and in control.** Steer, approve, or take over at any point. MagenticLite stops and checks in before taking critical actions. +- **Safe by design.** Browser sessions run inside a lightweight VM sandbox ([Quicksand](https://microsoft.github.io/quicksand/)) so the agent can't reach the rest of your machine without your say-so. + +## See it in action + +Click any task below to expand and watch MagenticLite handle it end to end. + +
+ Fill expense forms + + https://github.com/user-attachments/assets/a982b60d-127e-4310-96e6-9f094dffb7f0 + +
+ +
+ Find prices for recipe ingredients + + https://github.com/user-attachments/assets/7ff87917-752f-4e40-b319-8036e08ab8f9 + +
+ +
+ Find and book a restaurant + + https://github.com/user-attachments/assets/ee5f967e-ee1b-4554-b329-36b56b595aa4 + +
+ +
+ Organize local files + + https://github.com/user-attachments/assets/aff385dd-f856-4db4-8f26-08fd0f940aa4 + +
+ +## Quick start + +These steps get you running on macOS or Windows (WSL). Need more detail or a different platform? See the [Installation Guide](./docs/installation.md). + +### 1. Install MagenticLite + +```bash +# Create a project directory +mkdir magentic-lite && cd magentic-lite + +# Create and activate a virtual environment +uv venv --python=3.12 --seed .venv +source .venv/bin/activate + +# Install the latest 0.2.x release from PyPI +uv pip install "magentic_ui>=0.2.0" +``` + +### 2. Run + +```bash +magentic-ui --port 8081 +``` + +Open and follow the in-app onboarding to connect a model endpoint. If you don't have one yet, see the [Model Hosting Guide](./docs/model-hosting-guide.md). + +> **Looking for the previous Magentic-UI 0.1 release** (optimized to run with frontier models)? It lives on the [`magentic-ui-0.1`](https://github.com/microsoft/magentic-ui/tree/magentic-ui-0.1) branch. + +## Documentation + +| Doc | What's in it | +| ---------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| [Installation](./docs/installation.md) | Supported platforms, macOS / WSL prerequisites, install + run commands | +| [Build from source](./docs/build-from-source.md) | How to run MagenticLite from a clone of this repo (`uv sync`, `pnpm dev`, etc.) | +| [Model hosting guide](./docs/model-hosting-guide.md) | End-to-end walkthrough of standing up a model endpoint and connecting MagenticLite to it | +| [Configuration](./docs/configuration.md) | Sandbox, agent mode, tool approval, full `config.yaml` example | +| [Troubleshooting](./docs/troubleshooting.md) | Common errors and how to fix them | +| [Limitations](./docs/limitations.md) | Tasks and usage patterns MagenticLite doesn't handle well today | +| [Transparency Note](./docs/TRANSPARENCY_NOTE.md) | Intended uses, responsible-use guidance, risks, and mitigations | + +## License + +Microsoft, and any contributors, grant you a license to any code in the repository under the [MIT License](https://opensource.org/licenses/MIT). See the [LICENSE](LICENSE) file. + +Microsoft, Windows, Microsoft Azure, and/or other Microsoft products and services referenced in the documentation may be either trademarks or registered trademarks of Microsoft in the United States and/or other countries. The licenses for this project do not grant you rights to use any Microsoft names, logos, or trademarks. Microsoft's general trademark guidelines can be found at . + +Any use of third-party trademarks or logos are subject to those third-party's policies. + +Privacy information can be found at . + +Microsoft and any contributors reserve all other rights, whether under their respective copyrights, patents, or trademarks, whether by implication, estoppel, or otherwise. diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..24657c3 --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub æĨæēč¯´æ˜Ž + +- åŽŸå§‹éĄšį›Žīŧš`microsoft/magentic-ui` +- 原始äģ“åē“īŧšhttps://github.com/microsoft/magentic-ui +- å¯ŧå…Ĩæ–šåŧīŧšä¸Šæ¸¸éģ˜čŽ¤åˆ†æ”¯įš„æœ€æ–°åŋĢį…§ +- 原äŊœč€…ã€į‰ˆæƒå’ŒčŽ¸å¯č¯äŋĄæ¯äģĨ原始äģ“åē“及æœŦäģ“åē“ LICENSE ä¸ē准 +- æœŦ文äģļäģ…ᔍäēŽčްåŊ•æĨæēīŧŒä¸äģŖčĄ¨ WeHub æ˜¯åŽŸéĄšį›ŽäŊœč€… diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..14c8971 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you find any issues, please flag by emailing magui@service.microsoft.com. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..bbb53c1 --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,14 @@ +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or +feature request as a new Issue. + +For help and questions about using this project, please post questions to GitHub Issues as +above and assign them the label "question". + +## Microsoft Support Policy + +Support for MagenticLite is limited to the resources listed above. diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..95a8e3e --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,84 @@ +# Magentic-UI config template +# +# Copy this file to `config.yaml` at the repo root and edit to your needs. +# `config.yaml` is gitignored. +# +# `magentic-ui` picks up `./config.yaml` automatically if `--config` isn't +# passed explicitly. +# +# API keys: set OPENAI_API_KEY (or AZURE_OPENAI_API_KEY) as env vars instead +# of putting them in this file. _ai_client.py falls back to the env var when +# `api_key` is omitted from the yaml. + +# --------------------------------------------------------------------------- +# Reusable model client snippets (YAML anchors) +# --------------------------------------------------------------------------- + +# OmniAgent orchestrator — plans and produces the final answer. +model_config_omni: &omni + provider: OpenAIChatCompletionClient + config: + model: your-orchestrator-model + base_url: https://api.openai.com/v1 # or your own endpoint + # api_key: set via OPENAI_API_KEY env var + max_retries: 5 + +# Fara web surfer — drives the browser via CDP. +model_config_fara: &fara + provider: OpenAIChatCompletionClient + config: + model: your-web-surfer-model + base_url: https://api.openai.com/v1 # or your own endpoint + # api_key: set via OPENAI_API_KEY env var + max_retries: 5 + +# Optional Azure example (defined but unused by default). Swap *omni or +# *fara below for *azure_example to route either agent through an +# Azure-hosted model instead. +model_config_azure_example: &azure_example + provider: AzureOpenAIChatCompletionClient + config: + model: your-azure-model + api_version: "2024-12-01-preview" + azure_endpoint: https://your-resource.openai.azure.com/ + azure_deployment: your-deployment-name + azure_ad_token_provider: + config: + scopes: + - "https://your-token-scope.example.com/.default" + max_retries: 5 + +# --------------------------------------------------------------------------- +# MagenticUIConfig +# --------------------------------------------------------------------------- + +# Model clients used by the agent pipeline. +model_client_configs: + orchestrator: *omni + web_surfer: *fara + +# Sandbox configuration. +sandbox: + type: quicksand # "quicksand" | "null" + # memory: "6G" + # cpus: 3 + # pool_size: 5 + +# Agent mode: +# "all" — OmniAgent + Fara (default; both local tasks and web browsing) +# "omniagent_only" — OmniAgent only (local tasks only) +# "websurfer_only" — Fara only (web browsing only; orchestrator skipped) +agent_mode: all + +# Harness behavior knobs. Many of these are also editable from Settings → +# General at runtime. Any field set here will overwrite the user's UI value +# on the next backend restart with this --config — this is the same +# behavior as `agent_mode`, `approval_policy`, etc. +# +# harness_config: +# orchestrator: +# approval_policy: require_approval_untrusted +# temperature: 0.6 +# max_rounds: 30 # OmniAgent rounds per batch (default 100) +# web_surfer: +# max_rounds: 100 # Browser use actions per batch (default 100) diff --git a/docs/TRANSPARENCY_NOTE.md b/docs/TRANSPARENCY_NOTE.md new file mode 100644 index 0000000..f3599ef --- /dev/null +++ b/docs/TRANSPARENCY_NOTE.md @@ -0,0 +1,172 @@ +# MagenticLite (aka Magentic 2.0) Transparency Notes + +## Overview + +MagenticLite (aka Magentic 2.0) is a powerful open-source agentic application that works with you to help you complete tasks across the web browser and your local file system. Built as the successor to [Magentic-UI](https://www.microsoft.com/en-us/research/publication/magentic-ui-report/), MagenticLite was optimized to work with small language models (SLMs) — making it leaner, faster, and more accessible without sacrificing capability. + +Unlike its predecessor, which required SOTA frontier models to achieve desired performance, MagenticLite delivers strong agentic capabilities at a fraction of the cost and compute, while keeping you in control at critical steps. + +> Looking for the previous version of Magentic-UI optimized to run with frontier models? It lives on the `magentic-ui-0.1.x` branch. + +--- + +## What Can MagenticLite Do? + +MagenticLite operates its own browser and can access folders that the user grants it access to, working fluidly across both to complete useful, real-world tasks. It can handle a wide range of tasks — from web research and form filling, to file management, data analysis, and code writing and execution. + +Every reasoning trace and action is fully visible to the user. Users can steer the agent at any point, either in natural language or by directly taking control of the browser, ensuring they remain in the driver's seat throughout. + +--- + +## Intended Uses + +MagenticLite is a research prototype best suited to explore, experience, and deploy agentic assistance for tasks that require web navigation and local file system interaction. + +> **MagenticLite should always be used with human supervision.** + +Examples of tasks it can accomplish: + +- Fill online forms and make bookings on your behalf +- Research and analyze information across the browser and your local file system +- Manage and analyze your local file system +- Complete simple coding tasks locally + +MagenticLite is being shared with the research community to foster further research on agentic systems that keep people in control. It is intended to be used by domain experts who are independently capable of evaluating the quality of outputs, safety issues, and potential harms before acting on them. + +--- + +## Out-of-Scope Uses + +We do not recommend using MagenticLite in commercial or real-world applications without further testing and development. It is being released for research purposes. + +MagenticLite may not work as expected if used with models other than the recommended setup: **Fara1.5** for browser use and **MagenticBrain** for orchestration and coding. + +MagenticLite is **not** well suited for tasks that: + +- Rely on audio or video data +- Involve long-duration tasks (e.g., summarizing 100+ papers) +- Require real-time fast actions such as playing online games + +MagenticLite should always be used with a human in the loop. It was not designed or evaluated for all possible downstream purposes. Developers should consider its inherent limitations as they select use cases, and evaluate and mitigate for accuracy, safety, and fairness concerns specific to each intended downstream use. + +MagenticLite should **not** be used in: + +- Highly regulated domains or high-stakes situations where inaccurate outputs could suggest actions that lead to injury or negatively impact an individual's health, legal, financial, or life opportunities +- High-risk decision making (e.g., in law enforcement, legal, finance, or healthcare) + +--- + +## How to Get Started + +To begin using MagenticLite, follow instructions in the README page or check our installation guide under `/docs`. + +--- + +## Evaluation + +MagenticLite was evaluated on its ability to solve complex agentic tasks, both in standard benchmark settings and on a custom evaluation dataset designed around priority use cases. + +### Evaluation Methods + +Evaluations were driven by hero use cases reflecting real everyday tasks — including form filling, browser research, and file system management. A custom evaluation dataset was built around these scenarios to measure performance on tasks that reflect actual user value, complementing standard benchmarks rather than simply optimizing for them. + +**Recommended models for evaluation:** MagenticLite was developed and tested using **Fara1.5** and **MagenticBrain** as the recommended model configuration. These are the models on which all evaluations and safety testing were conducted. Users may substitute other models, but performance, safety behavior, and benchmark results are not guaranteed outside of the tested configuration. + +In addition to quality performance testing, MagenticLite was assessed from a Responsible AI perspective. Based on these results, mitigations were implemented to minimize susceptibility to misuse. See the [Risks and Mitigations](#risks-and-mitigations) section below. + +--- + +## Limitations + +- MagenticLite was developed for research and experimental purposes. Further testing and validation are needed before considering its application in commercial or real-world scenarios. +- MagenticLite was designed and tested primarily using the **English language**. Performance in other languages may vary and should be assessed by someone who is both an expert in the expected outputs and a native speaker of that language. +- Outputs generated by AI may include factual errors, fabrication, or speculation. Users are responsible for assessing the accuracy of generated content. All decisions leveraging outputs of the system should be made with human oversight and not be based solely on system outputs. +- All evaluations and safety testing — including critical point handling, XPIA, and code-harm tests — were conducted on the **Fara1.5 + MagenticBrain** configuration. Performance and safety behavior have not been tested on other model combinations. +- Users with limited GPU capacity may run only one of the two models, but not all use cases will be unlocked. Users who run MagenticLite without Fara should be aware that critical point detection for browser actions relies on Fara's trained behavior. +- MagenticLite inherits any biases, errors, or omissions produced by the underlying model used. +- There has not been a systematic effort to ensure that all deployment configurations are protected from all security vulnerabilities such as indirect prompt injection attacks. +- MagenticLite plans one step at a time rather than committing to a full upfront plan, making it more adaptive and easier to course-correct — a deliberate design choice for SLM-based orchestration. + +A list of tasks and usage patterns that are not well supported is documented in `docs/limitations.md`. + +--- + +## Best Practices + +MagenticLite is a highly capable agent, proficient at interacting with websites, operating over local files, and writing or executing Python code. Like all LLM-based systems, it can and will make mistakes. To safely operate MagenticLite: + +- **Always run it within Quicksand**, a Python wrapper for QEMU VM that provides strong isolation boundaries and is available as part of this open-source release. Strictly limit its access to only essential resources — avoid exposing unnecessary files, folders, or credentials to the agent. +- **Avoid logging into websites** through the agent unnecessarily. +- **Never share sensitive data** you would not confidently send to external providers. MagenticLite shares browser screenshots with model providers, including all data entered on websites within its browser session. +- **Ensure careful human oversight:** meticulously review proposed actions and monitor progress before giving approval. +- **Approach outputs with appropriate skepticism** — MagenticLite can hallucinate, misattribute sources, or be misled by deceptive or low-quality online content. + +We strongly encourage users to pair MagenticLite with models that support robust Responsible AI mitigations. For reference on responsible AI best practices: + +- [Announcing new AI safety & Responsible AI features in Azure](https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/announcing-new-ai-safety-amp-responsible-ai-features-in-azure/ba-p/3983686) +- [Azure OpenAI Overview](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/overview) +- [Azure OpenAI Transparency Note](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/transparency-note) +- [OpenAI Usage Policies](https://openai.com/policies/usage-policies) +- [Azure OpenAI Code of Conduct](https://learn.microsoft.com/en-us/legal/cognitive-services/openai/code-of-conduct) + +Users are reminded to be mindful of data privacy concerns and are encouraged to review the privacy policies associated with any models and data storage solutions interfacing with MagenticLite. It is the user's responsibility to ensure that the use of MagenticLite complies with relevant data protection regulations and organizational guidelines. + +--- + +## Risks and Mitigations + +The risk surface spans the files and websites the agents have access to. The two primary risk categories are: + +- **Data leakage from prompt injection.** Untrusted content encountered in the browser or in user-supplied files may attempt to manipulate the agent into exfiltrating data or taking unintended actions. MagenticLite shares browser screenshots with model providers, including any data entered on websites within its session. +- **Undesired or destructive actions** across the user's local file system, the operated browser, or executed code — including irreversible operations the user did not intend to authorize. + +### Mitigations + +MagenticLite mitigates these risks through a layered approach: + +- **Sandboxed execution environment.** All browser sessions and code execution happen inside Quicksand, a Python wrapper around a QEMU VM that provides strong isolation boundaries from the host system. This limits the blast radius of both prompt injection and undesired actions. +- **Human intervention at critical points in the browser.** Fara's training surfaces actions that warrant user approval (e.g., transactions, irreversible submissions, login flows), pausing for explicit user confirmation before proceeding. +- **Code action classification at the MagenticLite harness level.** The allow / require_approval / deny tables categorize every tool call and bash command from the Orchestrator model, blocking destructive operations and routing risky ones through user approval. +- **User guidance on limiting data exposure.** We recommend that users: + - Grant the agent access to only the folders strictly necessary for the task + - Avoid logging into websites through the agent unless required + - Never share sensitive data they would not confidently send to external model providers + - Use the recommended model configuration (Fara1.5 + MagenticBrain), where critical point detection has been tested + +Users who choose to disable Fara, substitute alternative models, or run MagenticLite outside the sandboxed VM forfeit one or more of these mitigations and should evaluate the residual risk for their use case. + +### How Critical Point Detection Works + +In Magentic-UI 0.1, critical point detection was handled by prompting the configured frontier model (e.g., GPT-4o) to flag actions that warranted user review. This prompting-based safety layer no longer exists in this release. Instead, critical point detection has been moved closer to where each kind of action originates: + +**Browser actions → handled by Fara's trained behavior.** +The need for human intervention in browser use is rarely black-and-white and contains many gray areas. Rather than rely on post-hoc prompting, we trained Fara on traces that include correctly calibrated critical points, so the model itself learns when to surface an action for user approval. This generalizes better than prompting and avoids the latency overhead of routing each step through a separate large model. Users who do not run Fara should be aware that this safeguard depends on Fara's training and is not provided by MagenticLite itself. + +**Code and tool actions → handled by the MagenticLite harness.** +MagenticBrain works with a discrete set of tools and bash commands, which makes a deterministic, list-based approach well-suited. The harness applies an **allow / require_approval / deny** classification to every tool call and bash command before execution: + +| Classification | Description | +|---|---| +| **Allow** | Low-risk, reversible actions execute automatically (e.g., reading a file, listing a directory) | +| **Require approval** | Actions with potential side effects pause and prompt the user for explicit approval before running | +| **Deny** | Destructive or irreversible actions (e.g., recursive deletes outside the working folder, certain network operations) are blocked outright | + +This split — model-level for browser, harness-level for code — reflects the different shapes of the two action spaces: continuous and ambiguous in the browser, discrete and enumerable in code. + +> **Important note for users familiar with Magentic-UI 0.1:** Because critical point detection is no longer provided by MagenticLite itself, running this release with a browser-use model other than Fara, or with a coding model that bypasses the harness, will not give you the same safety behavior that was tested and approved in 0.1. We strongly recommend the recommended model configuration described above. + +--- + +## License + +**MIT License** + +Nothing disclosed here, including the Out of Scope Uses section, should be interpreted as or deemed a restriction or modification to the license the code is released under. + +--- + +## Contact + +We welcome feedback and collaboration from our audience. If you have suggestions, questions, or observe unexpected/offensive behavior in our technology, please contact us at [magui@service.microsoft.com](mailto:magui@service.microsoft.com). + +If the team receives reports of undesired behavior or identifies issues independently, we will update this repository with appropriate mitigations. diff --git a/docs/_assets/magentic-lite-logo-dark.png b/docs/_assets/magentic-lite-logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..34813e1e4415148a9591b4d0a1d82f2426d591fe GIT binary patch literal 139495 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

8?!buBzT4Gl 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 M4CHjNt#|By0d$2SPyhe` literal 0 HcmV?d00001 diff --git a/docs/_assets/magentic-lite-logo-light.png b/docs/_assets/magentic-lite-logo-light.png new file mode 100644 index 0000000000000000000000000000000000000000..d59d11a9907c452d00f6f2fc57d3923a48c69994 GIT binary patch literal 138406 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 z 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≠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

1ns99?H~8aW$BXI^ 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 literal 0 HcmV?d00001 diff --git a/docs/build-from-source.md b/docs/build-from-source.md new file mode 100644 index 0000000..4f3b7fa --- /dev/null +++ b/docs/build-from-source.md @@ -0,0 +1,99 @@ +# Build from Source + +This guide is for anyone who wants to run MagenticLite from a clone of this +repo — for example to make local changes to the backend or frontend, to test +an unreleased commit, or to dig into the code. + +If you only want to run MagenticLite, the released `magentic_ui` package on +PyPI is what you want — see [Installation](./installation.md) instead. + +## Prerequisites + +Platform-level prerequisites (Homebrew / WSL2 / KVM / `uv` / Python 3.12) are +the same as a regular install — follow the [Installation guide](./installation.md#supported-platforms) +through the **macOS** or **Windows (WSL)** prerequisites section, then come +back here. + +In addition, building the frontend requires: + +- **Node.js v24 or later** +- **pnpm v10+** + +```bash +# Install Node via nvm +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash +nvm install 24 + +# Install pnpm +npm install -g pnpm +``` + +## Clone the repo + +```bash +git clone https://github.com/microsoft/magentic-ui.git +cd magentic-ui +``` + +## Backend setup + +```bash +uv venv --python=3.12 --seed .venv +source .venv/bin/activate +uv sync --all-extras +``` + +`uv sync` installs MagenticLite in editable mode along with all dev +dependencies. The Quicksand VM image is downloaded automatically the first time +you launch `magentic-ui`; you don't need to run a separate install step for it. + +## Frontend setup + +The frontend is a Vite + React app under `frontend/`. Production builds are +written into `src/magentic_ui/backend/web/ui/`, where the backend serves them as +static files. + +```bash +cd frontend +pnpm install +``` + +You then have two ways to run the UI, depending on what you're working on. + +### Option 1: Production-style run + +Build the frontend once into the backend's static directory, then launch the +backend and let it serve the bundle. Use this when you're working only on +backend code. + +```bash +# from frontend/ +pnpm build # outputs to ../src/magentic_ui/backend/web/ui/ +cd .. + +magentic-ui --port 8081 +``` + +Open . Re-run `pnpm build` whenever the frontend +changes. + +### Option 2: Frontend dev mode + +Run the Vite dev server with hot reload, and run the backend separately. Use +this when you're iterating on the UI. + +```bash +# Terminal 1 — backend +source .venv/bin/activate +magentic-ui --port 8081 + +# Terminal 2 — frontend dev server +cd frontend +pnpm dev # serves at http://localhost:5173 +``` + +Open . The Vite dev server proxies API and WebSocket +calls to the backend on port 8081. + +For UI component conventions, see +[`frontend/src/components/ui/README.md`](../frontend/src/components/ui/README.md). diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..3563245 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,145 @@ +# Configuration + +For most users, the **Settings** panel inside the app is all you need. It walks you through model endpoints during the first-launch onboarding flow, and lets you change everything later from **Settings → Models** (and the other Settings tabs). + +If you'd rather use a YAML file — for example to share a setup across machines or check it into source control — the same options are also exposed via `config.yaml`. The repo ships a [`config.yaml.example`](../config.yaml.example) at the project root that you can copy as a starting point: + +```bash +cp config.yaml.example config.yaml +# then edit config.yaml to taste, and pass it on launch: +magentic-ui --port 8081 --config config.yaml +``` + +Each option below shows both the YAML key and (where applicable) the equivalent place in the UI. + +## How configuration is stored + +MagenticLite keeps your effective configuration in a local database, not in the YAML file. There are three ways that database can be populated, and the rule is simple — **whichever source wrote last wins, and the result persists across restarts**: + +- **Onboarding UI** — runs once on first launch and writes your answers to the database. +- **Settings UI** — change anything at any time; the new values overwrite the database immediately. +- **YAML file via `--config`** — at startup, MagenticLite reads `config.yaml` and **merges** it into the database (only fields you explicitly set are overwritten; the rest are left alone). This means starting with `--config` every time effectively pins those YAML fields back to your file values on every launch, regardless of what the UI changed in between. + +A handful of **Settings → General** options — the display preferences like theme, "show reasoning details", "show tool call details" — are saved to your browser's local storage instead of the backend database. They're per-browser, not per-installation, and they don't show up in `config.yaml`. Other items in the same panel (e.g. agent step limits) do write to the database like the rest of Settings. + +```bash +magentic-ui --port 8081 --config config.yaml +``` + +To start fresh — clear the saved model endpoints and re-run the onboarding flow — pass `--reset-config`: + +```bash +magentic-ui --port 8081 --reset-config +``` + +This only clears the model endpoints (orchestrator and browser-use); other configuration (sandbox, agent mode, tool approval) is preserved. + +You can combine the two: `--reset-config` first clears the model endpoints, then `--config` (if also passed) seeds them from your YAML file. + +## Model clients + +`model_client_configs` tells MagenticLite which model serves which agent role. There are two roles: + +- `orchestrator` — used in the `all` and `omniagent_only` agent modes. +- `web_surfer` — used in the `all` and `websurfer_only` agent modes. + +Each entry is an OpenAI-compatible client config — any server that speaks `/v1/chat/completions` (vLLM, an OpenAI-compatible managed endpoint, your own gateway, â€Ļ) will work. The full set of fields: + +```yaml +model_client_configs: + orchestrator: + provider: OpenAIChatCompletionClient + config: + model: + base_url: + api_key: + max_retries: 5 + model_info: + vision: false + function_calling: false + json_output: true + family: unknown + structured_output: false + multiple_system_messages: false + + web_surfer: + provider: OpenAIChatCompletionClient + config: + model: + base_url: + api_key: + max_retries: 5 + model_info: + vision: true # browser-use models are vision-language + function_calling: false + json_output: true + family: unknown + structured_output: false + multiple_system_messages: false +``` + +Notes: + +- `model_info` describes the capabilities of the model behind the endpoint. The values shown above are the ones MagenticLite has been tested with for the orchestrator (text-only) and browser-use (vision) roles; use them as-is unless you have a reason to differ for your specific model. +- **MagenticLite is tuned for the recommended models ([MagenticBrain](https://aka.ms/MagenticBrain-foundry) for the orchestrator, [Fara](https://aka.ms/fara-foundry) for browser use).** Pointing the same fields at a different model will probably work, but expect to tweak prompts and run your own evals; the orchestrator and browser-use code paths are not generic across arbitrary models. +- **Azure OpenAI** is supported via `config.yaml` only (the in-app Settings UI doesn't expose it yet): set `provider: AzureOpenAIChatCompletionClient` and use Azure-specific keys (`azure_endpoint`, `azure_deployment`, `api_version`, `azure_ad_token_provider`) under `config`. See [`config.yaml.example`](../config.yaml.example) for a worked example. +- If you don't have an endpoint to point at yet, see the [Model Hosting Guide](./model-hosting-guide.md) for one end-to-end way to stand one up. + +## Agent mode + +`agent_mode` controls which agents are active. It can also be changed in **Settings → Models** without restarting. The three modes let you trade capability for setup cost — you can run with both agents, or with only one of them if that's all your task needs. + +| Mode | Description | +| ---------------- | ------------------------------------------------------------------------------------------- | +| `all` | Orchestrator + Browser use — capable of both local tasks and web browsing (default) | +| `omniagent_only` | Orchestrator only — local tasks only; only `model_client_configs.orchestrator` required | +| `websurfer_only` | Browser use only — web browsing tasks only; only `model_client_configs.web_surfer` required | + +Which mode to pick: + +- **`all`** is the default and gives you the full product. You need both an orchestrator endpoint and a browser-use endpoint. +- **`omniagent_only`** is useful if you only want local file / code-execution work and don't have a browser-use endpoint to point at. The agent can't use web browser. +- **`websurfer_only`** is useful if you only want web automation and don't have an orchestrator endpoint to point at. The agent can't read or write local files. + +```yaml +agent_mode: all +``` + +## Sandbox + +`sandbox.type` controls how agent code runs: + +| Type | Description | +| ----------- | ------------------------------------------------------------- | +| `quicksand` | Lightweight QEMU VM with browser isolation (recommended) | +| `null` | No isolation — agent runs on host directly (dev/testing only) | + +```yaml +sandbox: + type: quicksand +``` + +See [Quicksand browser architecture](./dev/quicksand-browser-architecture.md) for the technical details and environment variables. + +## Tool approval + +MagenticLite's safety harness prompts the user before executing potentially dangerous tool calls. Three policies are available: + +| Policy | Behavior | +| ---------------------------- | --------------------------------------------------------------------------- | +| `auto_approve` | Execute all tool calls without prompting (eval / trusted setups only) | +| `require_approval_untrusted` | Prompt before tool calls deemed untrusted; auto-approve read-only (default) | +| `require_approval_all` | Prompt before every tool call | + +Set the policy in YAML: + +```yaml +harness_config: + orchestrator: + approval_policy: require_approval_untrusted +``` + +## Next steps + +- [Model Hosting Guide](./model-hosting-guide.md) — stand up a model endpoint to point `model_client_configs` at. +- [Troubleshooting](./troubleshooting.md) — what to do when something doesn't work. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..ae20c9a --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,135 @@ +# Installation + +This guide walks you through installing MagenticLite. It covers macOS and Windows (WSL); Linux is similar to the Windows/WSL path. + +## Supported platforms + +| Platform | Status | Notes | +| ------------------ | -------------------------------- | ------------------------------------------------------------------ | +| macOS ARM64 | ✅ tested | Apple Silicon. | +| Windows x64 + WSL2 | ✅ tested | Run everything inside the Ubuntu shell. Requires KVM enabled. | +| Linux x64 (native) | âš ī¸ untested but expected to work | Same path as WSL2 minus the `wsl --install` step. | +| Windows ARM64 | ❌ not supported | Not currently supported. Support may be added in a future release. | + +Pick the section that matches your machine and follow it end to end. The "Install and run" steps at the bottom apply to both platforms. + +## Install prerequisites on macOS + +Tested on macOS ARM64 (Apple Silicon). + +```bash +# Install Homebrew if you don't have it +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + +# If you just installed Homebrew, add it to your shell PATH +# (Apple Silicon path; for Intel Macs use /usr/local/bin/brew) +eval "$(/opt/homebrew/bin/brew shellenv)" + +# Python 3.12+ +brew install python@3.12 + +# uv (Python package manager) +curl -LsSf https://astral.sh/uv/install.sh | sh +source $HOME/.local/bin/env # add uv to PATH for this shell +``` + +Skip ahead to [Install and run MagenticLite](#install-and-run-magenticlite). + +## Install prerequisites on Windows (WSL) + +Tested on Windows 11 x64 with WSL2 + Ubuntu. + +### 1. Install WSL and Ubuntu + +In **PowerShell as Administrator**: + +```powershell +wsl --install +``` + +Reboot if prompted, then launch Ubuntu — either from the Start menu, or by running `wsl` (or `ubuntu`) in a new PowerShell window — and complete the first-time user setup. **Every command from here on runs inside the Ubuntu (WSL) shell.** + +### 2. Enable KVM + +```bash +sudo usermod -aG kvm $USER +``` + +Close and reopen the WSL terminal for the group change to take effect. + +KVM gives the [Quicksand](https://microsoft.github.io/quicksand/) VM hardware acceleration. Without it the VM falls back to software emulation, which is significantly slower. + +### 3. Install tools + +```bash +# uv (Python package manager) — installed first because we use uv to manage Python +curl -LsSf https://astral.sh/uv/install.sh | sh +source $HOME/.local/bin/env # add uv to PATH for this shell + +# Python 3.12 (managed by uv; the apt python3.12 package isn't available on Ubuntu 22.04) +uv python install 3.12 +``` + +## Install and run MagenticLite + +Once the platform-specific prerequisites are in place, the install + run steps are the same on both platforms. + +```bash +# Create a project directory +mkdir magentic-lite && cd magentic-lite + +# Create and activate a virtual environment +uv venv --python=3.12 --seed .venv +source .venv/bin/activate + +# Install the latest 0.2.x release from PyPI +uv pip install "magentic_ui>=0.2.0" + +# Run +magentic-ui --port 8081 +``` + +Then visit in your browser. + +For subsequent runs: + +```bash +cd magentic-lite +source .venv/bin/activate +magentic-ui --port 8081 +``` + +> Only one MagenticLite instance can run at a time on the same port (default 8081). + +## Coming from Magentic-UI 0.1.x? + +If you previously ran a 0.1.x release of Magentic-UI on the same machine, two things to know: + +- **Pin the version when you install.** PyPI still hosts the 0.1.x line under the same `magentic_ui` package name, so a plain `uv pip install magentic_ui` may pick up an older release. Pin to a 0.2.x version explicitly: + + ```bash + uv pip install "magentic_ui>=0.2.0" + ``` + +- **Use a fresh data directory.** MagenticLite (0.2.x) does not migrate the 0.1.x database. To keep the two installs side-by-side, point this run at a different `--appdir`: + + ```bash + magentic-ui --port 8081 --appdir ~/.magentic-lite + ``` + + Without `--appdir`, MagenticLite uses the same default data directory as 1.0, which can lead to confusing state. + +## A note on running MagenticLite as a shared service + +MagenticLite is designed to be installed and run **locally on your own machine** by the same person who uses it. We don't recommend hosting it as a shared service for other users: + +- **Concurrent multi-user sessions weren't a design goal**, so the UX degrades when several people share one instance. +- **The app exposes host-level capabilities to whoever can reach it** — most notably the file-system mounting controls used for browser uploads and downloads. Running it as a multi-user service effectively grants every user of that service the same file-system access as the host account. + +If you do choose to host it, treat the resulting URL as you would shell access to the host machine. + +## Next steps + +- [Model Hosting Guide](./model-hosting-guide.md) — get a model endpoint to point MagenticLite at. +- [Configuration](./configuration.md) — sandbox, agent mode, and tool approval policies. +- [Troubleshooting](./troubleshooting.md) — common issues and fixes. diff --git a/docs/limitations.md b/docs/limitations.md new file mode 100644 index 0000000..3dd4b69 --- /dev/null +++ b/docs/limitations.md @@ -0,0 +1,12 @@ +# Limitations + +MagenticLite is a research prototype. Some things it doesn't handle well today: + +- **Summarization is limited.** Tasks that ask MagenticLite to read a long source and produce a faithful summary often miss important content or oversimplify. +- **Long multi-turn conversations degrade.** Quality drops as the conversation history grows, especially for tasks that require keeping many earlier details in mind. +- **Steering may not always stick.** Even when you successfully redirect the browser-use agent ([Fara](https://aka.ms/fara-foundry)) mid-task, [MagenticBrain](https://aka.ms/MagenticBrain-foundry) may ignore your correction once Fara returns and dispatch Fara back to the original sub-task. The agent's overall plan can be stubborn that way. +- **Very large files or contexts don't fit.** Tasks that require reading or producing very large documents (well beyond a typical chat-sized prompt) will fail or truncate. +- **Uploading files inside the browser isn't supported.** The browser-use agent cannot complete flows that require attaching a file from your local disk through a webpage's upload control. +- **Images aren't supported as task inputs.** Any task that hinges on the agent opening an image file you provide (e.g. "describe this picture", "extract text from this screenshot") will not succeed. + +If you hit something not on this list and aren't sure whether it's expected, please file an issue. diff --git a/docs/model-hosting-guide.md b/docs/model-hosting-guide.md new file mode 100644 index 0000000..008ad63 --- /dev/null +++ b/docs/model-hosting-guide.md @@ -0,0 +1,84 @@ +# Model Hosting Guide + +MagenticLite talks to models through an **OpenAI-compatible `/v1/chat/completions` endpoint**. This guide walks through hosting the recommended models with **Microsoft Foundry Managed Compute** on Azure. + +After deployment, you end up with three values to paste into MagenticLite's onboarding (or into **Settings → Models**): an OpenAI-compatible URL, a model name, and an API key. + +> **Each model needs its own endpoint.** MagenticLite uses one model for the orchestrator role and another for browser use. In Foundry, that means **two deployments**, each with its own URL and key. + +--- + +## Microsoft Foundry Managed Compute + +### Prerequisites + +- An [Azure subscription](https://azure.microsoft.com/free/) with a valid payment method. Free and trial subscriptions don't work for GPU deployments. +- A **hub-based** project in Foundry. The newer "Foundry project" type does not support Managed Compute. If you don't have one, create it from the [Foundry portal](https://ai.azure.com/) under **+ New project → Hub-based project**. Pick a region with H100 or A100 inventory (East US 2 and Sweden Central are good defaults). +- Quota for enough dedicated vCPUs in the chosen region. **Standard_NC24ads_A100_v4** is a good VM SKU for both [Fara1.5-9B](https://aka.ms/fara-foundry) and [MagenticBrain](https://aka.ms/MagenticBrain-foundry) for testing and typical single-user use, and each instance consumes 24 vCPUs from the quota family. In [Azure Quotas](https://portal.azure.com/#view/Microsoft_Azure_Capacity/QuotaMenuBlade/~/overview), select **Machine learning**, then request **Standard NCADSA100v4 Family Cluster Dedicated vCPUs** in the same region as your Foundry project. For the usual two-deployment setup with Fara and MagenticBrain running concurrently at instance count 1, request 48 dedicated vCPUs. Larger A100 or H100 SKUs also work if you want extra headroom or have them readily available, but they cost more. Approval can take 24–48 hours. + +### 1. Deploy the model + +You'll repeat this once per model role you want to use (browser use and/or orchestrator). + +1. Open the model card in [Foundry Explore models](https://ai.azure.com/explore/models): [Fara1.5-9B](https://aka.ms/fara-foundry) for browser use or [MagenticBrain-14B](https://aka.ms/MagenticBrain-foundry) for orchestration. + +2. On the model card, click **Use this model**. If Foundry asks you to select a project, choose an existing hub-based project or create a new one. For a new project, keep the default hub unless you already have a shared hub for this work, and pick a region with GPU inventory such as East US 2 or Sweden Central. + + If project creation fails with a `Microsoft.Resources/subscriptions/resourcegroups/write` authorization error, your account can see the model but cannot create the Azure resource group behind the Foundry project. Use an existing project where you have access, or ask the subscription owner to grant you a role such as Contributor on the subscription or target resource group, then refresh your credentials and try again. + +3. Continue to the deployment wizard. If you're presented with purchase options, pick **Managed Compute**. + +4. Configure the deployment: + + | Field | Value | + | --------------- | ---------------------------------------------------------------------------------------------------------- | + | Endpoint name | anything, e.g. `fara-15-9b-magentic-lite`. Becomes part of the URL. | + | Deployment name | anything, e.g. `fara1-5-9b-1` or `magenticbrain-14b-1`. This is for tracking the deployment in Foundry. | + | Virtual machine | **Standard_NC24ads_A100_v4**. Larger A100 or H100 SKUs also work, but they are usually unnecessary for testing. | + | Instance count | **1**. Foundry may default to 3 instances; reduce it to 1 for testing or typical single-user use to avoid unnecessary cost. | + + Both Fara and MagenticBrain are served by vLLM under the hood, so the deployed endpoint exposes a fully OpenAI-compatible `/v1/chat/completions` route — text and vision-language requests both work. + +5. Click **Deploy**. + + Provisioning takes ~15–20 minutes per model: Foundry allocates the VM, pulls the container, and warms up vLLM. **Billing starts when the VM is allocated**, not when the endpoint reaches `Healthy`. + +### 2. Connect MagenticLite + +For each deployment, open **Models + endpoints** in your Foundry project and click into the deployment: + +- **REST endpoint** (Details tab): copy the endpoint through `/v1`, for example `https://..inference.ml.azure.com/v1`. +- **Model ID** (deployment details): use the model name segment after `/models/`, for example `Fara1.5-9B` or `MagenticBrain-14B`. Do not use the deployment name here. +- **Primary key** (Consume tab): the API key Foundry generated for that endpoint. + +Open MagenticLite and fill in the **Browser use model** card (and/or the **Orchestrator** card). On first launch this is part of the onboarding flow; if you've already onboarded, find the same fields under **Settings → Models**. + +| Field | Browser use model (Fara) | Orchestrator model (MagenticBrain) | +| ------------ | ------------------------------------------------------------- | -------------------------------------------------------------- | +| Endpoint URL | `https://..inference.ml.azure.com/v1` | `https://..inference.ml.azure.com/v1` | +| Model Name | `Fara1.5-9B` | `MagenticBrain-14B` | +| API Key | the primary key from the Fara endpoint's Consume tab | the primary key from the MagenticBrain endpoint's Consume tab | + +Click **Verify & Save**. See [Verification fails](#verification-fails) below if you hit an error. + +### 3. Idle behavior and cost + +Foundry Managed Compute deployments **do not scale to zero**. The VM stays allocated and billed by the hour for as long as the deployment exists, whether or not traffic is flowing. An A100 deployment in East US 2 runs roughly $3–4 per hour at list price (H100 is roughly twice that); check the [Azure VM pricing page](https://azure.microsoft.com/pricing/details/virtual-machines/linux/) for current rates in your region. Multiply by the number of deployments you keep running. + +To stop the meter, **delete the deployment** from the **Models + endpoints** page. Redeploying from the catalog later takes the same ~15–20 minutes. + +--- + +## Verification fails + +When you click **Verify & Save**, MagenticLite sends a probe request to the endpoint: + +- During onboarding, a successful verification finishes the onboarding flow and sends you to the sample-tasks page. +- In Settings, the button updates to **Connection Verified** (with a check icon) once the endpoint responds. + +If verification fails, the banner usually pinpoints the problem: + +| Symptom (banner) | Likely cause | +| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | +| `Endpoint returned HTTP 401` or `403` | API Key field is empty or wrong (different endpoints return one or the other for the same problem) | +| `Connection refused — is the server running?` or other network errors | Endpoint URL is wrong (typo in the host, missing `https://`, VPN/firewall issue) | diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..57bc347 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,87 @@ +# Troubleshooting + +## Model endpoint cold starts + +If you're using a managed model endpoint that scales to zero when idle (Hugging Face Inference Endpoints with scale-to-zero, for example), the **first call after the endpoint has been idle** may take **30–90 seconds** while the platform brings a replica back up. During that window you may see: + +- a `503` response or a `Verify & Save` error in MagenticLite's Settings, or +- the first chat turn appearing to hang. + +Wait a minute and try again. Subsequent requests respond at normal speed until the endpoint scales to zero again. + +See the [Model Hosting Guide](./model-hosting-guide.md#a3-scale-to-zero-and-cold-starts) for the full explanation. + +## Settings → Models verification fails + +When you click **Verify & Save**, MagenticLite sends a probe request to the endpoint. Common failures: + +| Symptom (banner) | Likely cause | +| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `Endpoint returned HTTP 401` or `403` | API Key field is empty or wrong (different endpoints return one or the other for the same problem) | +| `Endpoint returned HTTP 503` on the first attempt | Cold start — see the section above; wait a minute and click Verify again | +| `Connection refused — is the server running?` or other network errors | Endpoint URL is wrong (typo in the host, missing `https://`, VPN/firewall issue) | + +Re-check the values against the source you copied them from (the Hugging Face dashboard, the Foundry deployment page, etc.) and try again. + +## Installation and runtime + +### `magentic-ui` command not found + +Your virtual environment isn't activated. Re-activate it and try again: + +```bash +deactivate # if another env is active +source .venv/bin/activate +magentic-ui --port 8081 +``` + +### Port 8081 already in use + +Another MagenticLite process is already running on the same port. Stop it, or run on a different port: + +```bash +magentic-ui --port 8082 +``` + +### Quicksand VM fails to start (Linux/WSL2) + +- Confirm KVM is available: `[ -e /dev/kvm ] && echo ok` should print `ok`. +- Confirm your user is in the `kvm` group: `groups | grep -q kvm && echo ok`. If not, run `sudo usermod -aG kvm $USER` and restart your shell. +- Without KVM the VM falls back to software emulation, which is significantly slower but should still work. + +### Browser viewer is blank or unresponsive + +- Make sure the Quicksand VM is healthy (check the MagenticLite logs for `quicksand` errors). +- Check that any local firewall isn't blocking the noVNC port the app picks for the embedded browser viewer. +- Restart MagenticLite — the browser is recreated on each session. + +## Coming from Magentic-UI 0.1.x? + +The 0.1.x line of Magentic-UI is still on PyPI under the same `magentic_ui` package name, and its on-disk data lives in the same default app directory. A few things to be aware of: + +### `pip install` picks up Magentic-UI 0.1.x instead of MagenticLite + +A plain `uv pip install magentic_ui` (no version pin) may resolve to a 0.1.x release. Pin to a 0.2.x version explicitly: + +```bash +uv pip install "magentic_ui>=0.2.0" +``` + +(Adjust the version to whatever 0.2.x release you intend to run.) + +### MagenticLite reads / writes my old Magentic-UI 0.1.x data + +MagenticLite (0.2.x) doesn't migrate the 0.1.x database. By default both versions use the same app directory, which can lead to confusing state. To keep them separate, point MagenticLite at a different `--appdir`: + +```bash +magentic-ui --port 8081 --appdir ~/.magentic-lite +``` + +## Still having issues? + +- Re-check the [Installation Guide](./installation.md) and [Configuration](./configuration.md). +- Search [GitHub Issues](https://github.com/microsoft/magentic-ui/issues) for similar problems. +- Open a new issue and include: + 1. A detailed description of your problem + 2. Information about your system (OS, hardware acceleration support, MagenticLite version) + 3. Steps to reproduce the issue diff --git a/frontend/.nvmrc b/frontend/.nvmrc new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/frontend/.nvmrc @@ -0,0 +1 @@ +24 diff --git a/frontend/.prettierignore b/frontend/.prettierignore new file mode 100644 index 0000000..c341c69 --- /dev/null +++ b/frontend/.prettierignore @@ -0,0 +1,4 @@ +dist +node_modules +pnpm-lock.yaml +*.min.js diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..e999e95 --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": false, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100, + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/frontend/components.json b/frontend/components.json new file mode 100644 index 0000000..55159c0 --- /dev/null +++ b/frontend/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components/ui", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..91d3441 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,25 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': ['warn', { allowConstantExport: true }], + }, + } +) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..bf53186 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,23 @@ + + + + + + + MagenticLite + + + +

+ + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..c06aa52 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,87 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "packageManager": "pnpm@10.24.0", + "engines": { + "node": ">=24" + }, + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"src/**/*.{ts,tsx,css}\"", + "format:check": "prettier --check \"src/**/*.{ts,tsx,css}\"", + "test": "vitest", + "test:ui": "vitest --ui", + "test:run": "vitest run", + "type-check": "tsc --noEmit" + }, + "devDependencies": { + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "^9.39.4", + "@tailwindcss/vite": "^4.3.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/node": "^24.13.2", + "@types/prismjs": "^1.26.6", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.2.0", + "@vitest/ui": "^4.1.9", + "eslint": "^9.39.4", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^16.5.0", + "jsdom": "^27.4.0", + "prettier": "^3.8.4", + "prettier-plugin-tailwindcss": "^0.8.0", + "rollup-plugin-visualizer": "^6.0.11", + "tailwindcss": "^4.3.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.62.0", + "vite": "^7.3.5", + "vitest": "^4.1.9" + }, + "dependencies": { + "@fontsource-variable/inter": "^5.2.8", + "@radix-ui/react-checkbox": "^1.3.5", + "@radix-ui/react-dialog": "^1.1.17", + "@radix-ui/react-dropdown-menu": "^2.1.18", + "@radix-ui/react-select": "^2.3.1", + "@radix-ui/react-slot": "^1.3.0", + "@radix-ui/react-switch": "^1.3.1", + "@radix-ui/react-tabs": "^1.1.15", + "@radix-ui/react-tooltip": "^1.2.10", + "@tanstack/react-query": "^5.101.0", + "@tanstack/react-query-devtools": "^5.101.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.21.0", + "prismjs": "^1.30.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-markdown": "^10.1.0", + "react-router": "^7.18.0", + "react-router-dom": "^7.18.0", + "react-vnc": "^3.2.0", + "remark-gfm": "^4.0.1", + "tailwind-merge": "^3.6.0", + "zustand": "^5.0.14" + }, + "pnpm": { + "overrides": { + "yaml@<2.8.3": "^2.8.3", + "ws@<8.21.0": "^8.21.0", + "js-yaml@<4.2.0": "^4.2.0", + "@babel/core@<7.29.6": "^7.29.6", + "esbuild@<0.28.1": "^0.28.1" + } + } +} diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml new file mode 100644 index 0000000..cdd2e02 --- /dev/null +++ b/frontend/pnpm-lock.yaml @@ -0,0 +1,6701 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + yaml@<2.8.3: ^2.8.3 + ws@<8.21.0: ^8.21.0 + js-yaml@<4.2.0: ^4.2.0 + '@babel/core@<7.29.6': ^7.29.6 + esbuild@<0.28.1: ^0.28.1 + +importers: + + .: + dependencies: + '@fontsource-variable/inter': + specifier: ^5.2.8 + version: 5.2.8 + '@radix-ui/react-checkbox': + specifier: ^1.3.5 + version: 1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dialog': + specifier: ^1.1.17 + version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.18 + version: 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-select': + specifier: ^2.3.1 + version: 2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': + specifier: ^1.3.0 + version: 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-switch': + specifier: ^1.3.1 + version: 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tabs': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-tooltip': + specifier: ^1.2.10 + version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@tanstack/react-query': + specifier: ^5.101.0 + version: 5.101.0(react@19.2.7) + '@tanstack/react-query-devtools': + specifier: ^5.101.0 + version: 5.101.0(@tanstack/react-query@5.101.0(react@19.2.7))(react@19.2.7) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + lucide-react: + specifier: ^1.21.0 + version: 1.21.0(react@19.2.7) + prismjs: + specifier: ^1.30.0 + version: 1.30.0 + react: + specifier: ^19.2.7 + version: 19.2.7 + react-dom: + specifier: ^19.2.7 + version: 19.2.7(react@19.2.7) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.2.17)(react@19.2.7) + react-router: + specifier: ^7.18.0 + version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-router-dom: + specifier: ^7.18.0 + version: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react-vnc: + specifier: ^3.2.0 + version: 3.2.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + tailwind-merge: + specifier: ^3.6.0 + version: 3.6.0 + zustand: + specifier: ^5.0.14 + version: 5.0.14(@types/react@19.2.17)(react@19.2.7) + devDependencies: + '@eslint/eslintrc': + specifier: ^3.3.5 + version: 3.3.5 + '@eslint/js': + specifier: ^9.39.4 + version: 9.39.4 + '@tailwindcss/vite': + specifier: ^4.3.1 + version: 4.3.1(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@testing-library/user-event': + specifier: ^14.6.1 + version: 14.6.1(@testing-library/dom@10.4.1) + '@types/node': + specifier: ^24.13.2 + version: 24.13.2 + '@types/prismjs': + specifier: ^1.26.6 + version: 1.26.6 + '@types/react': + specifier: ^19.2.17 + version: 19.2.17 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.17) + '@vitejs/plugin-react': + specifier: ^5.2.0 + version: 5.2.0(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitest/ui': + specifier: ^4.1.9 + version: 4.1.9(vitest@4.1.9) + eslint: + specifier: ^9.39.4 + version: 9.39.4(jiti@2.7.0) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react: + specifier: ^7.37.5 + version: 7.37.5(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-hooks: + specifier: ^7.1.1 + version: 7.1.1(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-refresh: + specifier: ^0.5.3 + version: 0.5.3(eslint@9.39.4(jiti@2.7.0)) + globals: + specifier: ^16.5.0 + version: 16.5.0 + jsdom: + specifier: ^27.4.0 + version: 27.4.0 + prettier: + specifier: ^3.8.4 + version: 3.8.4 + prettier-plugin-tailwindcss: + specifier: ^0.8.0 + version: 0.8.0(prettier@3.8.4) + rollup-plugin-visualizer: + specifier: ^6.0.11 + version: 6.0.11(rollup@4.61.0) + tailwindcss: + specifier: ^4.3.1 + version: 4.3.1 + typescript: + specifier: ~5.9.3 + version: 5.9.3 + typescript-eslint: + specifier: ^8.62.0 + version: 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + vite: + specifier: ^7.3.5 + version: 7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0) + vitest: + specifier: ^4.1.9 + version: 4.1.9(@types/node@24.13.2)(@vitest/ui@4.1.9)(jsdom@27.4.0)(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + +packages: + + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@asamuzakjp/css-color@4.1.2': + resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==} + + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.29.6 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.29.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.29.6 + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} + + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@fontsource-variable/inter@5.2.8': + resolution: {integrity: sha512-kOfP2D+ykbcX/P3IFnokOhVRNoTozo5/JxhAIVYLpea/UBmCQ/YWPBfWIDuBImXX/15KH+eKh4xpEUyS2sQQGQ==} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@novnc/novnc@1.6.0': + resolution: {integrity: sha512-CJrmdSe9Yt2ZbLsJpVFoVkEu0KICEvnr3njW25Nz0jodaiFJtg8AYLGZogRYy0/N5HUWkGUsCmegKXYBSqwygw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} + + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} + + '@radix-ui/react-arrow@1.1.10': + resolution: {integrity: sha512-j2VTDz1vgCsmuG0k5lBfOcM8n5JPFqZBcMryasFjHYMhwxYL5SRUV5lMSUpRdNtw3D/Sv8pzJtrlAgkssYSsQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.5': + resolution: {integrity: sha512-pREzrmNnVwGvYaBoM64huTRK7B3lrTRuwj8A9nwhPiEtMb+yudiWh6zWAqEtP0Dzd5+iBa1Ki7V1pCxV8ExMdA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.10': + resolution: {integrity: sha512-IVVz4EvBcKjrzKgof714qDnz/SzQAkLA2Emh5edlHbgcE6fNd3Un6CJLlaYcnm8N4JmAtzQgse4dOKxcD2yc9g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.17': + resolution: {integrity: sha512-TDTYmpdq8dI2+Xgvgj9AJ8Ghqq+Eph/TRVEdaFQPDItIY+6QSkU7MJMeevw1568Yw/2Ijz8BTphPSP2XejKphw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.13': + resolution: {integrity: sha512-2v+zNAWWe0ySxgC0D0yeXMPQ23xZVgXZTerTz+JKlmdRj6gfTqmCcR29jb6d290DezXPGgruHWDX/vYUebtErg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.18': + resolution: {integrity: sha512-PZGV82gFk0WltDRI//SsG28ZIjlo9ANTmoNYg0jLNzXXiDsAy5PkOOYQaVD1pPxY6t7gxffb1QMD6qaUvsBZdw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.10': + resolution: {integrity: sha512-Fas/lXQqhVvqwAb64s5RFeHiHYElZ6SUQbZaNd6EkfhP/Al7wTIQ9WIR4QVX475tlu5yFCEdDcJH6/UwsZjMWw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.18': + resolution: {integrity: sha512-lj8Rxjtn6zJq1oSbE/uDtAwCbB9BnxgHD+8MwJMuTh6u1dPamYhW9iuELr/Z8d0D/UysFblYYHeBPwi7T4k0YQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.3.1': + resolution: {integrity: sha512-bhnq/0DEPTi2lsOD3J5rTL65qUKHbKbhqHsmN9TMiclSXpipi651ooUKPPp6G5lF/WiHBdn1s0Wuqsn+myVAvw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.12': + resolution: {integrity: sha512-m309havGzsjLHHaIX50G5PlvRs3xkgPCsGk/5PTvYm8D5q33yG0J7w/712PTOhid7NTaFETtnSXjngHQavvhVw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.6': + resolution: {integrity: sha512-wetd0QI77DbvrPpTAvH1SqOxsYF2wZe5TNxqwOd5Ty4XDpV3dpV0s8K/1MGMJBeY5o7lg8ub5VIt1Ub+yVen6g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.13': + resolution: {integrity: sha512-9gkwneI0guf8JDmrFxPjJF6Ozzgioyw+/lonYNCwefS9ZHA05er0BVHiXr+LbWGHxUfczvMY6G1oiZZi1VzjRw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.3.1': + resolution: {integrity: sha512-w6eDvY78LE9ZUiNnXCA1QVK8RYN7k9galFv09kjVydJqBAgHd7Y9A6h0UJ/6DCZNGZMZrB2ohcSW1Bo9d8+wWA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.3.0': + resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.1': + resolution: {integrity: sha512-55bQtCnOB0BohomSHi6qvQXpJEEqUGDm6hRrM0Bph5OXwhSegqkd8IqgBAQkM1IlgUlWZIxpxRcpOEfRIgimyw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.15': + resolution: {integrity: sha512-kxc9gI6/HfcU4nfMMVS3AmQK414kbU1IE6UCJmMmxjhO3cRPXOyYnmvyKD+ODt7q56nRq9l7Wovi6uaGwKgMlg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.2.10': + resolution: {integrity: sha512-NlNe8D0dWEpVfXFli90IO6X07Josx/b1iu98tDnx9Xv0HT4wLIL+m2VOheMHhK7qbp2HoTBqALEFzGyZs/levw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.2': + resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.6': + resolution: {integrity: sha512-jCE0WljWifTI4niIMCll06kGpsJTAPiZVU9H4WR1N6qW7At9ystHbN7dDB+we2xH535roFHj7qKS+RGj0FMDWQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} + + '@rolldown/pluginutils@1.0.0-rc.3': + resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} + + '@rollup/rollup-android-arm-eabi@4.61.0': + resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.61.0': + resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.61.0': + resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.61.0': + resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.61.0': + resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.61.0': + resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.61.0': + resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.61.0': + resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.61.0': + resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.61.0': + resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.61.0': + resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.61.0': + resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.61.0': + resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.61.0': + resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.61.0': + resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.61.0': + resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.61.0': + resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.61.0': + resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.61.0': + resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.61.0': + resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.61.0': + resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.61.0': + resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.61.0': + resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.61.0': + resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.61.0': + resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tailwindcss/node@4.3.1': + resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + + '@tailwindcss/oxide-android-arm64@4.3.1': + resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.1': + resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.1': + resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.1': + resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tanstack/query-core@5.101.0': + resolution: {integrity: sha512-cQetA74EB+seWySv1TTKr828TnP0u39m6LykwDXIo84SNortpDkp30TMEjkqtYCNP9c40uT/iwl6MLiufEt0Ow==} + + '@tanstack/query-devtools@5.101.0': + resolution: {integrity: sha512-MVqw17k08RQtGGLEL654+dX/btbX9p/8WjkznO//zusLTMaObxi3Q+MoFwGVkC9K3tqjn8qrrNhJevXx4fJTeQ==} + + '@tanstack/react-query-devtools@5.101.0': + resolution: {integrity: sha512-cpZA0+WqKXwrwMfiWZEGGF6QrIWVQFbhBtxqDF5sQsAfrFf47HIE6fiPbQU3wyAUEN2+7UNqLCQe7oG6m3f93w==} + peerDependencies: + '@tanstack/react-query': ^5.101.0 + react: ^18 || ^19 + + '@tanstack/react-query@5.101.0': + resolution: {integrity: sha512-rLlJXSpkqfizLWgkR5+eLeIk0MvTx/meEIR7LRjxic+qxiQP8zVjq7BqQkiCMNLQBlLfuOLqqr6KO5GtrDlmSg==} + peerDependencies: + react: ^18 || ^19 + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@testing-library/user-event@14.6.1': + resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@24.13.2': + resolution: {integrity: sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==} + + '@types/novnc__novnc@1.6.0': + resolution: {integrity: sha512-kIH/PQACbf9qj6whiSXRKECpyzgs6IFNVsW4wohyqQUZfQHdLhuGRMLi9XEhBiISSh0lKCSbBa7kScuPu9Gk+Q==} + + '@types/prismjs@1.26.6': + resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@typescript-eslint/eslint-plugin@8.62.0': + resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.62.0 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.62.0': + resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.62.0': + resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.62.0': + resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.62.0': + resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.62.0': + resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.62.0': + resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.62.0': + resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.62.0': + resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.62.0': + resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + deprecated: Potential CWE-502 - Update to 1.3.1 or higher + + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/ui@4.1.9': + resolution: {integrity: sha512-U/cRvtqfEPj27FI1n9cyUvi4vXXdcLhjJiI+InYKdk8hP4VrS6RXOjGL7rfFaeBc37iRKANsR6eEzIoC7lmgBQ==} + peerDependencies: + vitest: 4.1.9 + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.23: + resolution: {integrity: sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==} + engines: {node: '>=6.0.0'} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001791: + resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + cssstyle@5.3.7: + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + data-urls@6.0.1: + resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} + engines: {node: '>=20'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.344: + resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.3.2: + resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react-refresh@0.5.3: + resolution: {integrity: sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==} + peerDependencies: + eslint: ^9 || ^10 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} + hasBin: true + + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lucide-react@1.21.0: + resolution: {integrity: sha512-reEZMXq8Qdd5jg5XYkQ5TR1fB/GiQ7ih4vcrthYDtgjSDwh0i6/YLiGjsWsIwgN49gpAnd4J2elSNzncMEEUUQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-plugin-tailwindcss@0.8.0: + resolution: {integrity: sha512-V8ITGH87yuBDF6JpEZTOVlUz/saAwqb8f3HRgUj8Lh+tGCcrmorhsLpYqzygwFwK0PE2Ib6Mv3M7T/uE2tZV1g==} + engines: {node: '>=20.19'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.8.4: + resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} + engines: {node: '>=14'} + hasBin: true + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} + peerDependencies: + react: ^19.2.7 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-refresh@0.18.0: + resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@7.18.0: + resolution: {integrity: sha512-Fi0yY6kgtKae/Th2xibdWK0KSdYZ4B53Gyf6wRtomOKWgpNm7H7+DyfDhncdz9FKbpS+1jmDhg3F4WoGJ+yFOA==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.18.0: + resolution: {integrity: sha512-pTTGt8J+ji1NOmYnjzT+bAJy/1zD+Jp4ziO6cL7T3ZLvXKtusO7BpFqlRXitqpcPVqllsIXFHRMt+2/k3Xn6HQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-vnc@3.2.0: + resolution: {integrity: sha512-PnlcleDjVI7gUKaSX5+94MMadxee8avAJP10vlHvT2JBLVd2vEGdyWcs3KQ/9STK7mj/Ht6gFQpwEc3BYh/4GQ==} + peerDependencies: + react: '>=19.0.0' + react-dom: '>=19.0.0' + + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} + engines: {node: '>=0.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup-plugin-visualizer@6.0.11: + resolution: {integrity: sha512-TBwVHVY7buHjIKVLqr9scTVFwqZqMXINcCphPwIWKPDCOBIa+jCQfafvbjRJDZgXdq/A996Dy6yGJ/+/NtAXDQ==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.61.0: + resolution: {integrity: sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tailwind-merge@3.6.0: + resolution: {integrity: sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==} + + tailwindcss@4.3.1: + resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + hasBin: true + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.62.0: + resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.8.3 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@acemir/cssom@0.9.31': {} + + '@adobe/css-tools@4.4.4': {} + + '@asamuzakjp/css-color@4.1.2': + dependencies: + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + lru-cache: 11.3.5 + + '@asamuzakjp/dom-selector@6.8.1': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.5 + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/runtime@7.29.2': {} + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@csstools/color-helpers@6.0.2': {} + + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.2.0 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@exodus/bytes@1.15.0': {} + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.6': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@floating-ui/utils@0.2.11': {} + + '@fontsource-variable/inter@5.2.8': {} + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@novnc/novnc@1.6.0': {} + + '@polka/url@1.0.0-next.29': {} + + '@radix-ui/number@1.1.2': {} + + '@radix-ui/primitive@1.1.4': {} + + '@radix-ui/react-arrow@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-checkbox@1.3.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-collection@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dialog@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-dismissable-layer@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-dropdown-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-focus-scope@1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-menu@2.1.18(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-popper@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-portal@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-primitive@2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-roving-focus@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-select@2.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-slot@1.3.0(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-switch@1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-tabs@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-tooltip@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.3.0(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + optionalDependencies: + '@types/react': 19.2.17 + + '@radix-ui/react-visually-hidden@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@radix-ui/react-primitive': 2.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@radix-ui/rect@1.1.2': {} + + '@rolldown/pluginutils@1.0.0-rc.3': {} + + '@rollup/rollup-android-arm-eabi@4.61.0': + optional: true + + '@rollup/rollup-android-arm64@4.61.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.61.0': + optional: true + + '@rollup/rollup-darwin-x64@4.61.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.61.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.61.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.61.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.61.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.61.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.61.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.61.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.61.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.61.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.61.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.61.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.61.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.61.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.61.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.61.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.61.0': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@tailwindcss/node@4.3.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.1 + + '@tailwindcss/oxide-android-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.1': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.1': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + optional: true + + '@tailwindcss/oxide@4.3.1': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-arm64': 4.3.1 + '@tailwindcss/oxide-darwin-x64': 4.3.1 + '@tailwindcss/oxide-freebsd-x64': 4.3.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 + '@tailwindcss/oxide-linux-x64-musl': 4.3.1 + '@tailwindcss/oxide-wasm32-wasi': 4.3.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + + '@tailwindcss/vite@4.3.1(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@tailwindcss/node': 4.3.1 + '@tailwindcss/oxide': 4.3.1 + tailwindcss: 4.3.1 + vite: 7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0) + + '@tanstack/query-core@5.101.0': {} + + '@tanstack/query-devtools@5.101.0': {} + + '@tanstack/react-query-devtools@5.101.0(@tanstack/react-query@5.101.0(react@19.2.7))(react@19.2.7)': + dependencies: + '@tanstack/query-devtools': 5.101.0 + '@tanstack/react-query': 5.101.0(react@19.2.7) + react: 19.2.7 + + '@tanstack/react-query@5.101.0(react@19.2.7)': + dependencies: + '@tanstack/query-core': 5.101.0 + react: 19.2.7 + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) + + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/json-schema@7.0.15': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@24.13.2': + dependencies: + undici-types: 7.18.2 + + '@types/novnc__novnc@1.6.0': {} + + '@types/prismjs@1.26.6': {} + + '@types/react-dom@19.2.3(@types/react@19.2.17)': + dependencies: + '@types/react': 19.2.17 + + '@types/react@19.2.17': + dependencies: + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/type-utils': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.0 + eslint: 9.39.4(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.62.0 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.62.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.62.0': + dependencies: + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 + + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.62.0': {} + + '@typescript-eslint/typescript-estree@8.62.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.62.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.62.0': + dependencies: + '@typescript-eslint/types': 8.62.0 + eslint-visitor-keys: 5.0.1 + + '@ungap/structured-clone@1.3.0': {} + + '@vitejs/plugin-react@5.2.0(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-rc.3 + '@types/babel__core': 7.20.5 + react-refresh: 0.18.0 + vite: 7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.9(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0) + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.9': {} + + '@vitest/ui@4.1.9(vitest@4.1.9)': + dependencies: + '@vitest/utils': 4.1.9 + fflate: 0.8.3 + flatted: 3.4.2 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@24.13.2)(@vitest/ui@4.1.9)(jsdom@27.4.0)(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + agent-base@7.1.4: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.23: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.23 + caniuse-lite: 1.0.30001791 + electron-to-chromium: 1.5.344 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001791: {} + + ccount@2.0.1: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + comma-separated-tokens@2.0.3: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css.escape@1.5.1: {} + + cssstyle@5.3.7: + dependencies: + '@asamuzakjp/css-color': 4.1.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + css-tree: 3.2.1 + lru-cache: 11.3.5 + + csstype@3.2.3: {} + + data-urls@6.0.1: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 15.1.0 + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.344: {} + + emoji-regex@8.0.0: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + entities@8.0.0: {} + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.2: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-module-lexer@2.1.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.3 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-config-prettier@10.1.8(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.2 + eslint: 9.39.4(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.5.3(eslint@9.39.4(jiti@2.7.0)): + dependencies: + eslint: 9.39.4(jiti@2.7.0) + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.2 + eslint: 9.39.4(jiti@2.7.0) + estraverse: 5.3.0 + hasown: 2.0.3 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + esutils@2.0.3: {} + + expect-type@1.3.0: {} + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fflate@0.8.3: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.3 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.5.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.15.0 + transitivePeerDependencies: + - '@noble/hashes' + + html-url-attributes@3.0.1: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + inline-style-parser@0.2.7: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.3 + side-channel: 1.1.0 + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.3 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-decimal@2.0.1: {} + + is-docker@2.2.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-plain-obj@4.1.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.2.0: + dependencies: + argparse: 2.0.1 + + jsdom@27.4.0: + dependencies: + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.8.1 + '@exodus/bytes': 1.15.0 + cssstyle: 5.3.7 + data-urls: 6.0.1 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.21.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + - bufferutil + - supports-color + - utf-8-validate + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@11.3.5: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lucide-react@1.21.0(react@19.2.7): + dependencies: + react: 19.2.7 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + markdown-table@3.0.4: {} + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + mdn-data@2.27.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.38: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + obug@2.1.3: {} + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse5@8.0.1: + dependencies: + entities: 8.0.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-plugin-tailwindcss@0.8.0(prettier@3.8.4): + dependencies: + prettier: 3.8.4 + + prettier@3.8.4: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + prismjs@1.30.0: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + property-information@7.1.0: {} + + punycode@2.3.1: {} + + react-dom@19.2.7(react@19.2.7): + dependencies: + react: 19.2.7 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react-is@17.0.2: {} + + react-markdown@10.1.0(@types/react@19.2.17)(react@19.2.7): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.2.17 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 19.2.7 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-refresh@0.18.0: {} + + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) + optionalDependencies: + '@types/react': 19.2.17 + + react-router-dom@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-router: 7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + + react-router@7.18.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + cookie: 1.1.1 + react: 19.2.7 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.7(react@19.2.7) + + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + get-nonce: 1.0.1 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + react-vnc@3.2.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + dependencies: + '@novnc/novnc': 1.6.0 + '@types/novnc__novnc': 1.6.0 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + react@19.2.7: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup-plugin-visualizer@6.0.11(rollup@4.61.0): + dependencies: + open: 8.4.2 + picomatch: 4.0.4 + source-map: 0.7.6 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.61.0 + + rollup@4.61.0: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.61.0 + '@rollup/rollup-android-arm64': 4.61.0 + '@rollup/rollup-darwin-arm64': 4.61.0 + '@rollup/rollup-darwin-x64': 4.61.0 + '@rollup/rollup-freebsd-arm64': 4.61.0 + '@rollup/rollup-freebsd-x64': 4.61.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.0 + '@rollup/rollup-linux-arm-musleabihf': 4.61.0 + '@rollup/rollup-linux-arm64-gnu': 4.61.0 + '@rollup/rollup-linux-arm64-musl': 4.61.0 + '@rollup/rollup-linux-loong64-gnu': 4.61.0 + '@rollup/rollup-linux-loong64-musl': 4.61.0 + '@rollup/rollup-linux-ppc64-gnu': 4.61.0 + '@rollup/rollup-linux-ppc64-musl': 4.61.0 + '@rollup/rollup-linux-riscv64-gnu': 4.61.0 + '@rollup/rollup-linux-riscv64-musl': 4.61.0 + '@rollup/rollup-linux-s390x-gnu': 4.61.0 + '@rollup/rollup-linux-x64-gnu': 4.61.0 + '@rollup/rollup-linux-x64-musl': 4.61.0 + '@rollup/rollup-openbsd-x64': 4.61.0 + '@rollup/rollup-openharmony-arm64': 4.61.0 + '@rollup/rollup-win32-arm64-msvc': 4.61.0 + '@rollup/rollup-win32-ia32-msvc': 4.61.0 + '@rollup/rollup-win32-x64-gnu': 4.61.0 + '@rollup/rollup-win32-x64-msvc': 4.61.0 + fsevents: 2.3.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.5: {} + + set-cookie-parser@2.7.2: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + space-separated-tokens@2.0.2: {} + + stackback@0.0.2: {} + + std-env@4.1.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + symbol-tree@3.2.4: {} + + tailwind-merge@3.6.0: {} + + tailwindcss@4.3.1: {} + + tapable@2.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + tldts-core@7.0.28: {} + + tldts@7.0.28: + dependencies: + tldts-core: 7.0.28 + + totalist@3.0.1: {} + + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.28 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.62.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.62.0(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@7.18.2: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): + dependencies: + detect-node-es: 1.1.0 + react: 19.2.7 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.17 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + esbuild: 0.28.1 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.61.0 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.13.2 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + + vitest@4.1.9(@types/node@24.13.2)(@vitest/ui@4.1.9)(jsdom@27.4.0)(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 7.3.5(@types/node@24.13.2)(jiti@2.7.0)(lightningcss@1.32.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.13.2 + '@vitest/ui': 4.1.9(vitest@4.1.9) + jsdom: 27.4.0 + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + ws@8.21.0: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + + zod@4.3.6: {} + + zustand@5.0.14(@types/react@19.2.17)(react@19.2.7): + optionalDependencies: + '@types/react': 19.2.17 + react: 19.2.7 + + zwitch@2.0.4: {} diff --git a/frontend/public/magentic-logo.svg b/frontend/public/magentic-logo.svg new file mode 100644 index 0000000..de8843c --- /dev/null +++ b/frontend/public/magentic-logo.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..e2f4b6a --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,406 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { + BrowserRouter, + Routes, + Route, + useNavigate, + useParams, + useLocation, + Navigate, +} from 'react-router-dom' +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' +import { ErrorBoundary, ConnectionStatusBanner } from '@/components/common' +import { Header, type AppLayout } from '@/components/layout' +import { SessionView, Dashboard, DashboardTabs } from '@/components/session' +import { TooltipProvider } from '@/components/ui/tooltip' +import { OnboardingPage } from '@/pages/OnboardingPage' +import { SampleTasksPage } from '@/pages/SampleTasksPage' +import { getOnboardingStatus } from '@/api/onboarding' +import { + useResponsiveLayout, + WebSocketManagerProvider, + useEnsureSessionData, + useBackendHealthPolling, + type ActiveRun, +} from '@/hooks' +import { useSessionList, useSessionsWithStatus } from '@/api' +import { useUIStore, useNotificationStore } from '@/stores' +import { ACTIVE_SESSION_STATUSES, ACTIVE_RUN_STATUSES, type ServerRunStatus } from '@/types' +import { DRAFT_SESSION_ID, isDraftSession } from '@/lib/constants' +import { findInvalidSessionRedirect } from '@/lib/sessionRedirect' + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 1000 * 60, // 1 minute + retry: 1, + }, + }, +}) + +/** + * Hook to create a new session and navigate to it. + * Creates a local draft session (not persisted until first message is sent). + * If a draft already exists, navigates to it instead of creating another. + */ +function useNewSession() { + const navigate = useNavigate() + const createDraftSession = useUIStore((s) => s.createDraftSession) + const draftSession = useUIStore((s) => s.draftSession) + + return useCallback(() => { + // If a draft already exists, just navigate to it + if (draftSession) { + navigate(`/sessions/${DRAFT_SESSION_ID}`) + return + } + + createDraftSession() + navigate(`/sessions/${DRAFT_SESSION_ID}`) + }, [createDraftSession, draftSession, navigate]) +} + +/** + * Merges the in-memory draft session (if any) to the top of the API session list. + * Draft is cleared externally (ChatView on promote, SessionDialogs on delete). + */ +function useSessionListWithDraft() { + const { sessions: apiSessions, isLoading, error } = useSessionList() + const draftSession = useUIStore((s) => s.draftSession) + + const sessions = useMemo(() => { + if (!draftSession) return apiSessions + return [draftSession, ...apiSessions] + }, [apiSessions, draftSession]) + + return { sessions, isLoading, error } +} + +/** + * Dashboard page - shows all sessions in a grid + */ +function DashboardPage() { + const navigate = useNavigate() + const handleNewSession = useNewSession() + + // Tab state from store + const activeTab = useUIStore((s) => s.activeTab) + const setActiveTab = useUIStore((s) => s.setActiveTab) + + // Fetch sessions from API (includes draft session at top if exists) + const { sessions, isLoading, error } = useSessionListWithDraft() + + // Filter sessions based on active tab + const filteredSessions = useMemo(() => { + return sessions.filter((session) => { + switch (activeTab) { + case 'active': + return ACTIVE_SESSION_STATUSES.includes(session.status) + case 'stopped': + return session.status === 'completed' || session.status === 'stopped' + case 'all': + default: + return true + } + }) + }, [sessions, activeTab]) + + const handleSessionClick = useCallback( + (id: number) => { + navigate(`/sessions/${id}`) + }, + [navigate] + ) + + return ( +
+ {/* Header with tabs */} + +
} + onNewSession={handleNewSession} + /> + + + + + {/* Main content */} +
+ + {isLoading ? ( +
+

Loading sessions...

+
+ ) : error ? ( + // Real error surfaces via the global ConnectionStatusBanner. +
+

No sessions to show

+
+ ) : ( + + )} +
+
+
+ ) +} + +/** + * Session page - shows session view with optional sidebar + */ +function SessionPage() { + const navigate = useNavigate() + const { sessionId: sessionIdParam } = useParams<{ sessionId: string }>() + const handleNewSession = useNewSession() + + // Parse URL param to number (URL params are always strings) + const sessionId = sessionIdParam ? parseInt(sessionIdParam, 10) : undefined + const draftSession = useUIStore((s) => s.draftSession) + + useEnsureSessionData(sessionId) + + // Sidebar state from store (persists user preference) + const sidebarOpen = useUIStore((s) => s.sidebarOpen) + const toggleSidebar = useUIStore((s) => s.toggleSidebar) + const autoShowSidebar = useUIStore((s) => s.autoShowSidebar) + const setSelectedSessionId = useUIStore((s) => s.setSelectedSessionId) + + // Sync selected session ID to store (for notification filtering) + useEffect(() => { + setSelectedSessionId(sessionId) + // Clear on unmount (user leaves session page) + return () => setSelectedSessionId(undefined) + }, [sessionId, setSelectedSessionId]) + + // Responsive layout: auto-show/hide sidebar based on window width + const { allowSidebar } = useResponsiveLayout() + + // Auto-show sidebar when screen becomes wide enough (if user didn't manually close it) + useEffect(() => { + if (allowSidebar) { + autoShowSidebar() + } + }, [allowSidebar, autoShowSidebar]) + + // Fetch sessions from API (with draft merged in) + const { sessions, isLoading, error } = useSessionListWithDraft() + + // If the URL points to a session that doesn't exist — either the draft + // sentinel after the in-memory draft was cleared (e.g., page refresh), or + // a real session ID that isn't in the API list (e.g., deleted session, + // hand-typed ID, or stale link) — redirect to the top of the sidebar + // (the draft if one exists, otherwise the most recent real session), or + // to the dashboard if the list is empty. Without this, ChatView would + // fall through to its empty-state and leak sample prompts for a real + // but missing session. See issue #582. + useEffect(() => { + const target = findInvalidSessionRedirect({ + sessionId, + draftSession, + sessions, + isLoading, + error, + }) + if (target !== null) { + navigate(target, { replace: true }) + } + }, [sessionId, draftSession, sessions, isLoading, error, navigate]) + + // Compute layout based on sidebar state + const layout: AppLayout = sidebarOpen && allowSidebar ? 'sidebar-show' : 'sidebar-hide' + + const handleBackToDashboard = useCallback(() => { + navigate('/') + }, [navigate]) + + const handleSessionSelect = useCallback( + (id: number) => { + navigate(`/sessions/${id}`) + }, + [navigate] + ) + + const handleSessionDeselect = useCallback(() => { + navigate('/sessions') + }, [navigate]) + + return ( +
+ {/* Header */} + +
+ + + + + {/* Main content area */} +
+ + + +
+
+ ) +} + +/** + * Route guard: redirects to /onboarding if onboarding not completed. + * Fetches status on mount and re-fetches when entering /sample-tasks + * (so that a preceding completeOnboarding() call is picked up). + * Exempt paths (/onboarding, /sample-tasks) bypass the redirect. + */ +function OnboardingGuard({ children }: { children: React.ReactNode }) { + const location = useLocation() + const [status, setStatus] = useState<'loading' | 'needs-onboarding' | 'ready'>('loading') + + const isExempt = (path: string) => path === '/onboarding' || path === '/sample-tasks' + + // Fetch onboarding status when status is 'loading' + useEffect(() => { + if (status !== 'loading') return + + let cancelled = false + getOnboardingStatus() + .then((result) => { + if (!cancelled) { + setStatus(result.onboarding_completed ? 'ready' : 'needs-onboarding') + } + }) + .catch(() => { + if (!cancelled) setStatus('ready') // let user through on error + }) + return () => { + cancelled = true + } + }, [status]) + + // Re-fetch status when entering /sample-tasks (onboarding just completed). + const prevPathRef = useRef(location.pathname) + useEffect(() => { + const entering = + location.pathname === '/sample-tasks' && prevPathRef.current !== '/sample-tasks' + prevPathRef.current = location.pathname + if (entering && status !== 'loading') { + setStatus('loading') + } + }, [location.pathname]) // eslint-disable-line react-hooks/exhaustive-deps + + if (status === 'loading') { + return ( +
+

Loading...

+
+ ) + } + + if (status === 'needs-onboarding' && !isExempt(location.pathname)) { + return + } + + if (status === 'ready' && location.pathname === '/onboarding') { + return + } + + return <>{children} +} + +/** + * App routes + */ +function AppRoutes() { + return ( + + + } /> + } /> + } /> + } /> + } /> + + + ) +} + +/** + * WebSocket manager wrapper + * Provides WebSocket connections for all active sessions + */ +function WebSocketWrapper({ children }: { children: React.ReactNode }) { + const { data: sessions, isLoading } = useSessionsWithStatus() + const cleanupInvalidSessions = useNotificationStore((s) => s.cleanupInvalidSessions) + const selectedSessionId = useUIStore((s) => s.selectedSessionId) + + // Poll /health while the backend is unreachable so the banner + // dismisses on recovery even on pages with no other queries. + useBackendHealthPolling() + + // Track if we've done initial sync + const hasSyncedRef = useRef(false) + + // Clean up stale notifications on initial load + // This handles: page refresh, sessions deleted while offline + // IMPORTANT: Wait for data to actually load before cleanup + useEffect(() => { + if (isLoading || hasSyncedRef.current || !sessions) return + hasSyncedRef.current = true + + // Get valid session IDs from API + const validSessionIds = sessions.map((s) => s.session_id) + // Remove notifications for sessions that no longer exist + cleanupInvalidSessions(validSessionIds) + }, [sessions, isLoading, cleanupInvalidSessions]) + + // Extract runs that need WebSocket connections (see ACTIVE_RUN_STATUSES for details) + const activeRuns: ActiveRun[] = useMemo(() => { + if (!sessions) return [] + + return sessions + .filter((session) => session.latest_run) // Has a run + .filter((session) => + ACTIVE_RUN_STATUSES.includes(session.latest_run?.status as ServerRunStatus) + ) + .map((session) => ({ + runId: String(session.latest_run!.run_id), + sessionId: session.session_id, + sessionName: session.name, + status: session.latest_run!.status as ServerRunStatus, + })) + }, [sessions]) + + return ( + + {children} + + ) +} + +function App() { + return ( + + + + + + + + + + ) +} + +export default App diff --git a/frontend/src/api/agentSettings.ts b/frontend/src/api/agentSettings.ts new file mode 100644 index 0000000..57ffe4c --- /dev/null +++ b/frontend/src/api/agentSettings.ts @@ -0,0 +1,51 @@ +/** Client for the `/api/settings/agents` endpoints. */ +import { useQuery, useMutation, useQueryClient, type QueryClient } from '@tanstack/react-query' +import { apiClient } from './client' + +export interface OrchestratorAgentSettings { + max_rounds: number +} + +export interface WebSurferAgentSettings { + max_rounds: number +} + +export interface AgentSettings { + orchestrator: OrchestratorAgentSettings + web_surfer: WebSurferAgentSettings +} + +export function getAgentSettings(): Promise { + return apiClient.get('/settings/agents') +} + +export function updateAgentSettings(payload: AgentSettings): Promise { + return apiClient.put('/settings/agents', payload) +} + +const agentSettingsKeys = { + all: ['settings', 'agents'] as const, +} + +export function useAgentSettings() { + return useQuery({ + queryKey: agentSettingsKeys.all, + queryFn: getAgentSettings, + staleTime: 30_000, + refetchOnWindowFocus: false, + }) +} + +export function useUpdateAgentSettings() { + const queryClient = useQueryClient() + return useMutation({ + mutationFn: updateAgentSettings, + onSuccess: (data) => { + queryClient.setQueryData(agentSettingsKeys.all, data) + }, + }) +} + +export function invalidateAgentSettings(queryClient: QueryClient): Promise { + return queryClient.invalidateQueries({ queryKey: agentSettingsKeys.all }) +} diff --git a/frontend/src/api/auth.ts b/frontend/src/api/auth.ts new file mode 100644 index 0000000..1724a3d --- /dev/null +++ b/frontend/src/api/auth.ts @@ -0,0 +1,39 @@ +/** + * Browser-side session auth. + * + * The backend injects a random session token into index.html as + * `window.__MAGUI_TOKEN__`. We read it here and attach it to every + * outgoing API request and WebSocket handshake. When the token is + * absent (Vite dev server serving its own HTML) the backend accepts + * requests based on the dev-origin bypass instead. + */ + +declare global { + interface Window { + __MAGUI_TOKEN__?: string + } +} + +/** Keep in sync with `WS_PROTOCOL_TAG` in `backend/web/auth.py`. */ +export const WS_PROTOCOL_TAG = 'magui.auth.bearer' + +export function getSessionToken(): string { + return window.__MAGUI_TOKEN__ ?? '' +} + +/** Bearer header for fetch. Empty when there is no injected token (dev). */ +export function getAuthHeaders(): Record { + const token = getSessionToken() + return token ? { Authorization: `Bearer ${token}` } : {} +} + +/** + * Open a WebSocket with the token carried in `Sec-WebSocket-Protocol` + * (the only client-controlled handshake header in the browser). When + * no token is present we open an unauth'd socket and rely on the + * backend's dev-origin bypass. + */ +export function createAuthenticatedWebSocket(url: string): WebSocket { + const token = getSessionToken() + return token ? new WebSocket(url, [WS_PROTOCOL_TAG, token]) : new WebSocket(url) +} diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts new file mode 100644 index 0000000..6e61eff --- /dev/null +++ b/frontend/src/api/client.ts @@ -0,0 +1,199 @@ +/** + * API Client + * + * Base HTTP client for communicating with the backend API. + * Handles the standard response wrapper format: { status: boolean, data: T, message?: string } + */ +import { API_BASE_URL } from '@/lib/constants' +import { useBackendHealthStore } from '@/stores/backendHealthStore' +import { getAuthHeaders } from './auth' + +// ============================================================================= +// URL Helpers +// ============================================================================= + +/** + * Get the WebSocket base URL + * WebSocket requires full URL with protocol (ws:// or wss://) + */ +export function getWsBaseUrl(): string { + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:' + const host = window.location.host + return `${protocol}//${host}/api` +} + +// ============================================================================= +// Types +// ============================================================================= + +/** + * Standard API response wrapper from backend + */ +export interface ApiResponse { + status: boolean + data: T + message?: string +} + +/** + * Custom error class for API errors + */ +export class ApiError extends Error { + statusCode?: number + response?: unknown + + constructor(message: string, statusCode?: number, response?: unknown) { + super(message) + this.name = 'ApiError' + this.statusCode = statusCode + this.response = response + } +} + +// ============================================================================= +// API Client +// ============================================================================= + +/** + * Format an error response body into a user-readable message. + * + * Handles: + * - String `message` or `detail` fields (FastAPI HTTPException style) + * - Array `detail` (FastAPI Pydantic 422 — list of `{loc, msg, type, ...}`) + * + * Returns `null` when no recognizable error field is present. + */ +function formatErrorMessage(body: unknown): string | null { + if (!body || typeof body !== 'object') return null + const obj = body as Record + if (typeof obj.message === 'string') return obj.message + const { detail } = obj + if (typeof detail === 'string') return detail + if (Array.isArray(detail)) { + // Pydantic validation errors + const parts = detail + .map((e) => { + if (e && typeof e === 'object') { + const item = e as Record + const loc = Array.isArray(item.loc) ? item.loc.filter((x) => x !== 'body').join('.') : '' + const msg = typeof item.msg === 'string' ? item.msg : JSON.stringify(item) + return loc ? `${loc}: ${msg}` : msg + } + return String(e) + }) + .filter(Boolean) + if (parts.length > 0) return parts.join('; ') + } + return null +} + +type RequestOptions = Omit & { + body?: unknown +} + +/** + * Make an API request and handle the response wrapper + * + * @param endpoint - API endpoint (without /api prefix) + * @param options - Fetch options + * @returns Unwrapped data from the response + * @throws ApiError if request fails or status is false + */ +async function request(endpoint: string, options: RequestOptions = {}): Promise { + const url = `${API_BASE_URL}${endpoint}` + + const headers: HeadersInit = { + 'Content-Type': 'application/json', + ...getAuthHeaders(), + ...options.headers, + } + + const config: RequestInit = { + ...options, + headers, + body: options.body ? JSON.stringify(options.body) : undefined, + } + + let response: Response + + try { + response = await fetch(url, config) + } catch (error) { + // Skip health updates for intentional cancellations (AbortController, + // React Query unmount cleanup) — they don't say anything about backend + // reachability and would otherwise flip the global banner spuriously. + if (error instanceof DOMException && error.name === 'AbortError') { + throw error + } + // Network/CORS/offline — backend unreachable. + useBackendHealthStore.getState().setReachable(false) + throw new ApiError(error instanceof Error ? error.message : 'Network error', undefined, error) + } + + // 5xx → backend down; anything else (incl. 4xx) → backend responding. + if (response.status >= 500) { + useBackendHealthStore.getState().setReachable(false) + } else { + useBackendHealthStore.getState().setReachable(true) + } + + // Handle HTTP errors (4xx, 5xx) + if (!response.ok) { + let errorMessage = `HTTP ${response.status}: ${response.statusText}` + try { + const errorData = await response.json() + errorMessage = formatErrorMessage(errorData) ?? errorMessage + } catch { + // Response body is not JSON, use default message + } + throw new ApiError(errorMessage, response.status) + } + + // Parse JSON response + let data: ApiResponse + try { + data = await response.json() + } catch { + throw new ApiError('Invalid JSON response', response.status) + } + + // Handle API-level errors (status: false in wrapper) + if (!data.status) { + throw new ApiError(data.message || 'API request failed', response.status, data) + } + + return data.data +} + +/** + * API client with typed methods + */ +export const apiClient = { + /** + * GET request + */ + get(endpoint: string, options?: Omit): Promise { + return request(endpoint, { ...options, method: 'GET' }) + }, + + /** + * POST request + */ + post(endpoint: string, body?: unknown, options?: Omit): Promise { + return request(endpoint, { ...options, method: 'POST', body }) + }, + + /** + * PUT request + */ + put(endpoint: string, body?: unknown, options?: Omit): Promise { + return request(endpoint, { ...options, method: 'PUT', body }) + }, + + /** + * DELETE request + */ + delete(endpoint: string, options?: Omit): Promise { + return request(endpoint, { ...options, method: 'DELETE' }) + }, +} diff --git a/frontend/src/api/files.ts b/frontend/src/api/files.ts new file mode 100644 index 0000000..9c069ea --- /dev/null +++ b/frontend/src/api/files.ts @@ -0,0 +1,59 @@ +/** + * File Upload API + * + * Upload files to a run's working directory via REST API. + * Uses raw fetch (not apiClient) because file upload requires multipart/form-data. + */ + +import { API_BASE_URL } from '@/lib/constants' +import { getAuthHeaders } from './auth' + +// ============================================================================= +// Types +// ============================================================================= + +/** Single file info returned from upload endpoint */ +export interface UploadedFileInfo { + name: string + size: number + path: string + relative_path: string +} + +/** Upload response from backend */ +export interface UploadResponse { + status: boolean + message: string + files: UploadedFileInfo[] +} + +// ============================================================================= +// Upload Function +// ============================================================================= + +/** + * Upload files to a run's working directory. + * + * @param runId - Run ID to associate files with + * @param files - Array of File objects from browser file input + * @returns Upload response with file paths + * @throws Error if upload fails + */ +export async function uploadFiles(runId: string, files: File[]): Promise { + const formData = new FormData() + for (const file of files) { + formData.append('files', file) + } + + const res = await fetch(`${API_BASE_URL}/runs/${runId}/upload`, { + method: 'POST', + body: formData, // browser auto-sets Content-Type with boundary + headers: getAuthHeaders(), + }) + + if (!res.ok) { + throw new Error(`File upload failed: ${res.status} ${res.statusText}`) + } + + return res.json() +} diff --git a/frontend/src/api/filesystem.ts b/frontend/src/api/filesystem.ts new file mode 100644 index 0000000..132f018 --- /dev/null +++ b/frontend/src/api/filesystem.ts @@ -0,0 +1,48 @@ +/** + * Filesystem API + * + * Client for the server-side filesystem browsing API. + * Used by FolderBrowserDialog to let users browse and select folders. + */ +import { apiClient } from './client' + +// ============================================================================= +// Types +// ============================================================================= + +export interface DirectoryEntry { + name: string + type: 'file' | 'directory' + size?: number + modified: string +} + +export interface DirectoryListing { + path: string + parent: string | null + entries: DirectoryEntry[] +} + +export interface FilesystemRoots { + home: string +} + +// ============================================================================= +// API Functions +// ============================================================================= + +/** + * Get the user's home directory path. + */ +export function getRoots(): Promise { + return apiClient.get('/filesystem/roots') +} + +/** + * List contents of a directory + */ +export function listDirectory(path: string, showHidden = false): Promise { + const params = new URLSearchParams({ path }) + if (showHidden) params.set('show_hidden', 'true') + return apiClient.get(`/filesystem/list?${params}`) +} diff --git a/frontend/src/api/health.ts b/frontend/src/api/health.ts new file mode 100644 index 0000000..df74daf --- /dev/null +++ b/frontend/src/api/health.ts @@ -0,0 +1,12 @@ +/** + * Backend health endpoint. + * + * Used by `useBackendHealthPolling`. We don't care about the response + * shape — the `request()` wrapper in `client.ts` updates the health + * store as a side effect, so a successful call is enough. + */ +import { apiClient } from './client' + +export function pingBackend(): Promise { + return apiClient.get('/health') +} diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts new file mode 100644 index 0000000..d837253 --- /dev/null +++ b/frontend/src/api/index.ts @@ -0,0 +1,48 @@ +/** + * API Module Exports + */ +export { apiClient, ApiError } from './client' +export type { ApiResponse } from './client' + +export { + getOnboardingStatus, + getOnboardingEndpoints, + verifyEndpoints, + setAgentMode, + completeOnboarding, + resetOnboarding, + useCurrentAgentMode, + invalidateOnboardingEndpoints, +} from './onboarding' +export type { + AgentMode, + OnboardingStatus, + ModelEndpointsResponse, + ModelClientConfig, + ModelInfoConfig, + ModelVerifyResponse, + ModelEndpointVerification, + ModelEndpointInput, + VerifyEndpointsPayload, +} from './onboarding' + +export { + getAgentSettings, + updateAgentSettings, + useAgentSettings, + useUpdateAgentSettings, + invalidateAgentSettings, +} from './agentSettings' +export type { + AgentSettings, + OrchestratorAgentSettings, + WebSurferAgentSettings, +} from './agentSettings' + +export { + sessionKeys, + useSessionList, + useSessionsWithStatus, + useUpdateSession, + useDeleteSession, +} from './sessions' diff --git a/frontend/src/api/onboarding.ts b/frontend/src/api/onboarding.ts new file mode 100644 index 0000000..2be068d --- /dev/null +++ b/frontend/src/api/onboarding.ts @@ -0,0 +1,174 @@ +/** + * Onboarding API + * + * Client functions for the /api/onboarding endpoints. + */ +import { useQuery, type QueryClient } from '@tanstack/react-query' +import { apiClient } from './client' + +// ============================================================================= +// Constants +// ============================================================================= + +/** + * Sentinel value the backend returns instead of real API keys. + * When sent back to the backend, it means "keep the existing DB value". + */ +export const API_KEY_MASKED = '__MASKED__' + +/** + * Deployment mode controlling which agents are active. + * Mirrors `AgentMode` in `magentic_ui_config.py`. + */ +export type AgentMode = 'all' | 'omniagent_only' | 'websurfer_only' + +// ============================================================================= +// Types (mirror backend Pydantic models) +// ============================================================================= + +export interface ModelInfoConfig { + vision: boolean + function_calling: boolean + json_output: boolean + family: string + structured_output: boolean + multiple_system_messages: boolean +} + +export interface ModelClientConfig { + provider: string + config: { + model: string + api_key: string + base_url: string + max_retries: number + model_info: ModelInfoConfig + } +} + +export interface OnboardingStatus { + onboarding_completed: boolean +} + +export interface ModelEndpointsResponse { + orchestrator: ModelClientConfig | null + web_surfer: ModelClientConfig | null + agent_mode: AgentMode +} + +export interface ModelEndpointVerification { + success: boolean + error?: string +} + +export interface ModelVerifyResponse { + /** Present when the verify request included `orchestrator`. */ + orchestrator?: ModelEndpointVerification + /** Present when the verify request included `web_surfer`. */ + web_surfer?: ModelEndpointVerification +} + +export interface ModelEndpointInput { + base_url: string + model: string + api_key: string +} + +export interface VerifyEndpointsPayload { + /** Required when `agent_mode` includes the orchestrator. */ + orchestrator?: ModelEndpointInput + /** Required when `agent_mode` includes the web surfer. */ + web_surfer?: ModelEndpointInput + /** Defaults to 'all' on the backend. */ + agent_mode?: AgentMode +} + +// ============================================================================= +// API Functions +// ============================================================================= + +/** + * Check if onboarding is completed. + * Lightweight — reads only the Settings.onboarding_completed column. + */ +export function getOnboardingStatus(): Promise { + return apiClient.get('/onboarding/status') +} + +/** + * Get saved model endpoints (masked api_keys). + * Used by settings page. + */ +export function getOnboardingEndpoints(): Promise { + return apiClient.get('/onboarding/endpoints') +} + +/** + * Verify model endpoints and save them to DB. + * Only sends user-provided fields; backend fills role defaults. + * + * Caller must include the role(s) required by `agent_mode`. The inactive + * role may be omitted; its previously saved config is left untouched. + */ +export function verifyEndpoints(payload: VerifyEndpointsPayload): Promise { + return apiClient.post('/onboarding/verify', payload) +} + +/** + * Update only the active `agent_mode` — no model verification. + * + * The backend rejects with HTTP 400 if the new mode requires a model that + * has no saved config yet. + */ +export function setAgentMode(mode: AgentMode): Promise<{ agent_mode: AgentMode }> { + return apiClient.post<{ agent_mode: AgentMode }>('/onboarding/agent-mode', { + agent_mode: mode, + }) +} + +/** + * Mark onboarding as completed. + * Requires both endpoints to be verified. + */ +export function completeOnboarding(): Promise { + return apiClient.post('/onboarding/complete') +} + +/** + * Reset onboarding config (clear model endpoints and completed flag). + */ +export function resetOnboarding(): Promise { + return apiClient.post('/onboarding/reset') +} + +// ============================================================================= +// Query Hooks +// ============================================================================= + +const onboardingKeys = { + all: ['onboarding'] as const, + endpoints: () => [...onboardingKeys.all, 'endpoints'] as const, +} + +/** + * The user's currently-saved `agent_mode` from settings. + * + * Cached indefinitely (`gcTime: Infinity` so the data survives even with no + * observers). Invalidate via `invalidateOnboardingEndpoints` after mutations. + */ +export function useCurrentAgentMode(): { agentMode: AgentMode | null; isLoading: boolean } { + const query = useQuery({ + queryKey: onboardingKeys.endpoints(), + queryFn: getOnboardingEndpoints, + staleTime: Infinity, + gcTime: Infinity, + refetchOnWindowFocus: false, + select: (data) => data.agent_mode ?? null, + }) + return { agentMode: query.data ?? null, isLoading: query.isLoading } +} + +/** Invalidate the cached onboarding endpoints (and therefore `agent_mode`). */ +export function invalidateOnboardingEndpoints(queryClient: QueryClient): Promise { + return queryClient.invalidateQueries({ queryKey: onboardingKeys.endpoints() }) +} diff --git a/frontend/src/api/sessions.ts b/frontend/src/api/sessions.ts new file mode 100644 index 0000000..5844071 --- /dev/null +++ b/frontend/src/api/sessions.ts @@ -0,0 +1,180 @@ +/** + * Session API + * + * TanStack Query hooks for session management. + */ +import { useMemo } from 'react' +import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' +import { apiClient } from './client' +import { DEFAULT_USER_ID } from '@/lib/constants' +import type { Session, SessionListItem, SessionRuns, UISession } from '@/types' +import { serverStatusToSessionStatus } from '@/types' + +// ============================================================================= +// Query Keys +// ============================================================================= + +export const sessionKeys = { + all: ['sessions'] as const, + lists: () => [...sessionKeys.all, 'list'] as const, + list: (userId: string) => [...sessionKeys.lists(), userId] as const, + details: () => [...sessionKeys.all, 'detail'] as const, + detail: (id: number) => [...sessionKeys.details(), id] as const, + runs: (sessionId: number) => [...sessionKeys.detail(sessionId), 'runs'] as const, +} + +// ============================================================================= +// API Functions +// ============================================================================= + +/** + * Fetch all sessions for a user (with latest run status) + */ +async function fetchSessions(userId: string): Promise { + return apiClient.get(`/sessions/?user_id=${encodeURIComponent(userId)}`) +} + +/** + * Create a new session + */ +export async function createSession(data: Partial): Promise { + return apiClient.post('/sessions/', data) +} + +/** + * Update a session + */ +async function updateSession( + sessionId: number, + data: Partial, + userId: string +): Promise { + return apiClient.put(`/sessions/${sessionId}?user_id=${encodeURIComponent(userId)}`, { + ...data, + id: sessionId, + user_id: userId, + }) +} + +/** + * Delete a session + */ +async function deleteSession(sessionId: number, userId: string): Promise { + return apiClient.delete(`/sessions/${sessionId}?user_id=${encodeURIComponent(userId)}`) +} + +/** + * Fetch runs for a session + */ +export async function fetchSessionRuns(sessionId: number, userId: string): Promise { + return apiClient.get( + `/sessions/${sessionId}/runs?user_id=${encodeURIComponent(userId)}` + ) +} + +// ============================================================================= +// Query Hooks +// ============================================================================= + +/** + * Hook to fetch the run for a session (1 session = 1 run) + * Returns the run directly, or undefined if not loaded yet + */ +export function useSessionRun(sessionId: number | undefined, userId: string = DEFAULT_USER_ID) { + const query = useQuery({ + queryKey: sessionKeys.runs(sessionId!), + queryFn: () => fetchSessionRuns(sessionId!, userId), + enabled: sessionId !== undefined, + // 1 session = 1 run (never changes), status updates via WebSocket + staleTime: Infinity, + refetchOnWindowFocus: false, + select: (data) => data.runs[0] ?? null, + }) + return query +} + +/** + * Hook to fetch all sessions with their latest run status + * Returns SessionListItem[] directly from backend (no N+1 queries) + */ +export function useSessionsWithStatus(userId: string = DEFAULT_USER_ID) { + return useQuery({ + queryKey: sessionKeys.list(userId), + queryFn: () => fetchSessions(userId), + }) +} + +/** + * Hook to fetch all sessions formatted for UI display (Sidebar, Dashboard) + * Status comes from query cache, and can be updated directly by WebSocket manager + */ +export function useSessionList(userId: string = DEFAULT_USER_ID): { + sessions: UISession[] + isLoading: boolean + error: Error | null +} { + const { data, isLoading, error } = useSessionsWithStatus(userId) + + const sessions: UISession[] = useMemo(() => { + if (!data) return [] + // Sort by latest activity (run.updated_at), fallback to session.created_at. + // ISO-8601 strings sort lexically === chronologically. The backend already + // orders by the same key; this re-sort handles optimistic updates and WS + // status bumps in the cache. + return [...data] + .filter((item) => item.latest_run !== null) + .sort((a, b) => { + const at = a.latest_run?.updated_at ?? a.created_at ?? '' + const bt = b.latest_run?.updated_at ?? b.created_at ?? '' + return bt.localeCompare(at) + }) + .map((item) => ({ + id: item.session_id, + title: item.name || `Session ${item.session_id}`, + status: serverStatusToSessionStatus(item.latest_run!.status), + runId: String(item.latest_run!.run_id), + updatedAt: item.latest_run!.updated_at ?? item.created_at, + })) + }, [data]) + + return { sessions, isLoading, error: error as Error | null } +} + +// ============================================================================= +// Mutation Hooks +// ============================================================================= + +/** + * Hook to update a session + */ +export function useUpdateSession(userId: string = DEFAULT_USER_ID) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: ({ sessionId, data }: { sessionId: number; data: Partial }) => + updateSession(sessionId, data, userId), + onSuccess: (updatedSession) => { + // Update the specific session in cache + queryClient.setQueryData(sessionKeys.detail(updatedSession.id), updatedSession) + // Invalidate list to refetch + queryClient.invalidateQueries({ queryKey: sessionKeys.lists() }) + }, + }) +} + +/** + * Hook to delete a session + */ +export function useDeleteSession(userId: string = DEFAULT_USER_ID) { + const queryClient = useQueryClient() + + return useMutation({ + mutationFn: (sessionId: number) => deleteSession(sessionId, userId), + onSuccess: (_, sessionId) => { + // Remove from cache + queryClient.removeQueries({ queryKey: sessionKeys.detail(sessionId) }) + // Invalidate list to refetch + queryClient.invalidateQueries({ queryKey: sessionKeys.lists() }) + }, + }) +} diff --git a/frontend/src/api/settings.ts b/frontend/src/api/settings.ts new file mode 100644 index 0000000..3321674 --- /dev/null +++ b/frontend/src/api/settings.ts @@ -0,0 +1,51 @@ +/** + * Trusted Folders API + * + * Client for the trusted folders CRUD API. + * Each folder is an independent DB row — no read-modify-write conflicts. + */ +import { apiClient } from './client' +import { DEFAULT_USER_ID } from '@/lib/constants' + +// ============================================================================= +// Types +// ============================================================================= + +export interface TrustedFolder { + id: number + name: string + path: string +} + +// ============================================================================= +// API Functions +// ============================================================================= + +/** + * List all trusted folders for the current user + */ +export function listTrustedFolders(): Promise { + return apiClient.get( + `/trusted-folders/?user_id=${encodeURIComponent(DEFAULT_USER_ID)}` + ) +} + +/** + * Add a folder to the trusted list + */ +export function addTrustedFolder(name: string, path: string): Promise { + return apiClient.post( + `/trusted-folders/?user_id=${encodeURIComponent(DEFAULT_USER_ID)}`, + { + name, + path, + } + ) +} + +/** + * Remove a trusted folder by ID + */ +export function removeTrustedFolder(id: number): Promise { + return apiClient.delete(`/trusted-folders/${id}?user_id=${encodeURIComponent(DEFAULT_USER_ID)}`) +} diff --git a/frontend/src/components/browser/ActionDescription.tsx b/frontend/src/components/browser/ActionDescription.tsx new file mode 100644 index 0000000..37267f0 --- /dev/null +++ b/frontend/src/components/browser/ActionDescription.tsx @@ -0,0 +1,40 @@ +/** + * Action Description Component + * + * Displays CUA action thoughts and result inside the expanded browser view. + * + * Layout responsibilities live here: + * - Container has a minimum height (no shrink jitter); grows when content expands + * - Bottom-anchored so the text stays right above the toolbar; expanded content + * grows upward. + * + * Per-block interaction (click-to-expand, tooltip hint, etc.) lives in + * `ExpandableActionText`, which is shared with `DescriptionOverlay`. + */ + +import { ExpandableActionText } from './ExpandableActionText' + +// ============================================================================= +// Types +// ============================================================================= + +interface ActionDescriptionProps { + thoughts?: string + actionResult?: string +} + +// ============================================================================= +// Component +// ============================================================================= + +export function ActionDescription({ thoughts, actionResult }: ActionDescriptionProps) { + return ( +
+ +
+ ) +} diff --git a/frontend/src/components/browser/BrowserEmbed.tsx b/frontend/src/components/browser/BrowserEmbed.tsx new file mode 100644 index 0000000..e57842b --- /dev/null +++ b/frontend/src/components/browser/BrowserEmbed.tsx @@ -0,0 +1,174 @@ +/** + * BrowserEmbed Component + * + * Embedded browser view within message list. + * Uses fixed 16:10 aspect ratio (matching Docker VM screen: 1440x900). + * Supports screenshot playback with prev/next/Live controls. + * + * Playback state is read directly from chatStore via useSessionPlayback hook, + * eliminating prop drilling through ChatView → MessageList. + * + * Layout (top → bottom): + * Toolbar (title + expand button) + * Viewer area (VNC + screenshot overlay + DescriptionOverlay) + * Bottom bar: LIVE toggle + Control (live) or ProgressBar + steps (history) + */ +import { useState, useCallback } from 'react' +import { BrowserViewer } from './BrowserViewer' +import { BrowserHeader } from './BrowserHeader' +import { DescriptionOverlay } from './DescriptionOverlay' +import { PlaybackControlsRow } from './PlaybackControlsRow' +import { usePlaybackControls, getBrowserTitle } from './usePlaybackControls' +import { useSessionPlayback } from '@/hooks' +import type { BrowserViewMode } from '@/types' + +// ============================================================================= +// Types +// ============================================================================= + +interface BrowserEmbedProps { + novncUrl: string | null + /** Per-slot RFB password from the backend; required for the noVNC handshake. */ + novncPassword: string | null + /** Session ID — used to read playback state from chatStore */ + sessionId: number + /** Whether this session is selected/visible - disconnects VNC when hidden to save bandwidth */ + isSessionSelected?: boolean + /** Called when mode changes via toolbar buttons */ + onModeChange?: (mode: BrowserViewMode) => void + /** Called when Control button is clicked (switches to expanded view for takeover) */ + onControlClick?: () => void +} + +// VM screen aspect ratio (Docker config: 1440x900 = 16:10) +const ASPECT_RATIO = 16 / 10 + +// ============================================================================= +// Component +// ============================================================================= + +export function BrowserEmbed({ + novncUrl, + novncPassword, + sessionId, + isSessionSelected = true, + onModeChange, + onControlClick, +}: BrowserEmbedProps) { + // Read playback state directly from store (eliminates prop drilling) + const { + screenshotActions, + liveActionInfo, + playbackIsLive, + playbackIndex, + followLatest, + onPlaybackIndexChange, + onSetLive, + } = useSessionPlayback(sessionId) + // Track VNC connection status - used to hide Control button when disconnected + const [isVncConnected, setIsVncConnected] = useState(false) + + const handleVncConnect = useCallback(() => { + setIsVncConnected(true) + }, []) + + const handleVncDisconnect = useCallback(() => { + setIsVncConnected(false) + }, []) + + // Shared playback logic + const playbackControls = usePlaybackControls({ + screenshotActions, + isLive: playbackIsLive, + currentIndex: playbackIndex, + onIndexChange: onPlaybackIndexChange, + isVncConnected, + followLatest, + liveActionInfo, + }) + + const { + total, + hasHistory, + displayIsLive, + validCurrentIndex, + currentScreenshotUrl, + canStepBack, + canStepForward, + handleStepBack, + handleStepForward, + nextTooltip, + thoughts, + actionResult, + hasContent, + } = playbackControls + + // Toolbar title + const title = getBrowserTitle(isVncConnected, hasHistory, playbackIsLive) + + // Show bottom controls when VNC connected or has screenshot history + const showBottomControls = isVncConnected || hasHistory + + return ( + // Container with fixed width, height adapts to aspect ratio + fixed bottom section +
+ {/* Toolbar with centered title */} + {})} title={title} /> + + {/* VNC Viewer with screenshot overlay + description overlay */} +
+ {/* VNC: only mount when URL + password are available. The + password is required for the RFB handshake; without it + react-vnc would prompt the user. */} + {isSessionSelected && novncUrl && novncPassword && ( + + )} + + {/* Screenshot overlay: shown when not in live VNC mode */} + {!displayIsLive && currentScreenshotUrl && ( + {`Screenshot + )} + + {/* Description overlay: show when VNC connected or viewing screenshots */} + {(isVncConnected || !displayIsLive) && ( + + )} +
+ + {/* Bottom bar: LIVE toggle + Control (live) or ProgressBar + steps (history) */} + {showBottomControls && ( + + )} +
+ ) +} diff --git a/frontend/src/components/browser/BrowserExpanded.tsx b/frontend/src/components/browser/BrowserExpanded.tsx new file mode 100644 index 0000000..08e8568 --- /dev/null +++ b/frontend/src/components/browser/BrowserExpanded.tsx @@ -0,0 +1,199 @@ +/** + * BrowserExpanded Component + * + * Side-by-side browser view container (right side of chat). + * + * Playback state is read directly from chatStore via useSessionPlayback hook, + * eliminating prop drilling. + * + * Layout (top → bottom): + * Toolbar (title + action buttons) + * Viewer area (VNC + screenshot overlay) + * Bottom section: + * - Normal: ActionDescription + LIVE toggle + Control/ProgressBar + * - Takeover: TakeoverNotice (with Release Control button) + * + * Modes: + * - Live: Shows VNC stream with pending action info + * - Playback: Shows screenshot with completed action info + * - Takeover: User is controlling, shows TakeoverNotice instead of playback controls + */ +import { useState, useCallback, useRef } from 'react' +import { BrowserViewer } from './BrowserViewer' +import { BrowserHeader } from './BrowserHeader' +import { ActionDescription } from './ActionDescription' +import { PlaybackControlsRow } from './PlaybackControlsRow' +import { TakeoverNotice } from './TakeoverNotice' +import { usePlaybackControls, getBrowserTitle } from './usePlaybackControls' +import { useSessionPlayback } from '@/hooks' +import type { BrowserViewMode, ControlState } from '@/types' + +// ============================================================================= +// Types +// ============================================================================= + +interface BrowserExpandedProps { + /** noVNC WebSocket URL for browser streaming */ + novncUrl: string | null + /** Per-slot RFB password for the noVNC handshake. */ + novncPassword: string | null + /** Session ID — used to read playback state from chatStore */ + sessionId: number + /** Current view mode */ + mode: BrowserViewMode + /** Called when mode changes via toolbar buttons */ + onModeChange: (mode: BrowserViewMode) => void + /** Whether this session is selected/visible - disconnects VNC when hidden */ + isSessionSelected?: boolean + /** Current browser control state */ + controlState?: ControlState + /** Called when Control button is clicked */ + onControlClick?: () => void + /** Called when VNC connection is lost (parent should reset takeover state) */ + onVncDisconnect?: () => void +} + +// ============================================================================= +// Component +// ============================================================================= + +export function BrowserExpanded({ + novncUrl, + novncPassword, + sessionId, + mode, + onModeChange, + isSessionSelected = true, + controlState = 'agent', + onControlClick, + onVncDisconnect, +}: BrowserExpandedProps) { + // Read playback state directly from store (eliminates prop drilling) + const { + screenshotActions, + liveActionInfo, + playbackIsLive, + playbackIndex, + followLatest, + onPlaybackIndexChange, + onSetLive, + } = useSessionPlayback(sessionId) + // Track VNC connection status. + // - Controls Take Control button visibility (hidden until first connect) + // - On disconnect: resets takeover state via onVncDisconnect callback + const [isVncConnected, setIsVncConnected] = useState(false) + + // Track whether VNC has ever connected successfully. + // Used to distinguish "initial connection failure" from "real network drop". + // VNC may fire disconnect during its initial connection attempt (WebSocket fails + // then retries) — we must NOT reset takeover state for those transient disconnects. + const hasEverConnectedRef = useRef(false) + + const handleVncConnect = useCallback(() => { + hasEverConnectedRef.current = true + setIsVncConnected(true) + }, []) + + const handleVncDisconnect = useCallback(() => { + setIsVncConnected(false) + // Only notify parent (reset takeover state) if VNC was previously connected. + // Initial connection failures should not reset controlState. + if (hasEverConnectedRef.current) { + onVncDisconnect?.() + } + }, [onVncDisconnect]) + + // Shared playback logic (eliminates duplicated inline calculations) + const { + total, + hasHistory, + displayIsLive, + validCurrentIndex, + currentScreenshotUrl, + canStepBack, + canStepForward, + handleStepBack, + handleStepForward, + nextTooltip, + thoughts, + actionResult, + } = usePlaybackControls({ + screenshotActions, + isLive: playbackIsLive, + currentIndex: playbackIndex, + onIndexChange: onPlaybackIndexChange, + isVncConnected: isVncConnected, + followLatest, + liveActionInfo, + }) + + // Key to reset ActionDescription expansion state when content changes + const contentKey = `${thoughts ?? ''}-${actionResult ?? ''}` + + // User has or is requesting control + const isUserControl = controlState === 'user' || controlState === 'user-pending' + + // Toolbar title + const title = getBrowserTitle(isVncConnected, hasHistory, playbackIsLive, isUserControl) + + return ( +
+ {/* Toolbar */} + + + {/* Main content: VNC Viewer (always mounted) + Screenshot overlay */} +
+ {/* VNC: only mount when URL + password are available. */} + {isSessionSelected && novncUrl && novncPassword && ( + + )} + + {/* Screenshot overlay: shown when not in live VNC mode */} + {!isUserControl && !displayIsLive && currentScreenshotUrl && ( + {`Screenshot + )} +
+ + {/* Bottom section: TakeoverNotice when controlling, playback controls otherwise */} + {/* During takeover + VNC connecting: hide both (no misleading playback bar) */} + {/* When not takeover: hide controls while VNC connecting in live mode (prevents flash) */} + {isUserControl ? ( + isVncConnected ? ( + + ) : null + ) : isVncConnected || hasHistory ? ( +
+ {/* Action description (expandable) */} + + + {/* Controls row: LIVE toggle + Control (live) or ProgressBar + steps (history) */} + +
+ ) : null} +
+ ) +} diff --git a/frontend/src/components/browser/BrowserHeader.tsx b/frontend/src/components/browser/BrowserHeader.tsx new file mode 100644 index 0000000..074d0c2 --- /dev/null +++ b/frontend/src/components/browser/BrowserHeader.tsx @@ -0,0 +1,107 @@ +/** + * BrowserHeader Component + * + * Toolbar with Globe icon + centered title and action buttons. + * Aligned with FileViewHeader layout pattern. + * + * Button layout by mode: + * - embedded: [Expand] + * - expanded: [Maximize] [Close] + * - maximized: [Side-by-side] [Close] + */ +import { Globe, Maximize, Columns2, X, Expand } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { useResponsiveLayout } from '@/hooks' +import type { BrowserViewMode } from '@/types' + +// ============================================================================= +// Types +// ============================================================================= + +interface BrowserHeaderProps { + /** Current view mode */ + mode: BrowserViewMode + /** Called when mode changes via toolbar buttons */ + onModeChange: (mode: BrowserViewMode) => void + /** Centered title text (e.g., "Browser (Live)", "Browser (History)") */ + title?: string +} + +// ============================================================================= +// Component +// ============================================================================= + +export function BrowserHeader({ mode, onModeChange, title }: BrowserHeaderProps) { + const isExpanded = mode !== 'embedded' + const isMaximized = mode === 'maximized' + const { allowSideBySide } = useResponsiveLayout() + + return ( +
+ {/* Centered title with Globe icon */} + {title && ( +
+ + {title} +
+ )} + + {/* Right side action buttons */} + {isExpanded ? ( + <> + {/* Maximize / Side-by-side toggle — hidden on narrow screens in maximized mode */} + {(!isMaximized || allowSideBySide) && ( + + + + + {isMaximized ? 'Side-by-side view' : 'Maximize view'} + + )} + + {/* Close button */} + + + + + Close + + + ) : ( + /* Embedded: single expand button */ + + + + + Expand + + )} +
+ ) +} diff --git a/frontend/src/components/browser/BrowserViewer.tsx b/frontend/src/components/browser/BrowserViewer.tsx new file mode 100644 index 0000000..eb75181 --- /dev/null +++ b/frontend/src/components/browser/BrowserViewer.tsx @@ -0,0 +1,220 @@ +/** + * BrowserViewer Component + * + * Wraps react-vnc VncScreen for browser viewing. + * Handles connection status, clipboard sync, and displays appropriate + * placeholder when disconnected. + */ +import { useState, useCallback, useRef, useEffect } from 'react' +import { VncScreen, type VncScreenHandle } from 'react-vnc' +import { cn } from '@/lib/utils' + +// ============================================================================= +// Types +// ============================================================================= + +type VncConnectionStatus = 'disconnected' | 'connecting' | 'connected' + +// Connection timeout in milliseconds +const CONNECTION_TIMEOUT_MS = 10000 + +interface BrowserViewerProps { + url: string + /** Per-slot RFB password used during the noVNC handshake. */ + password: string + viewOnly?: boolean + className?: string + /** Called when connection is established */ + onConnect?: () => void + onDisconnect?: () => void +} + +// ============================================================================= +// Component +// ============================================================================= + +export function BrowserViewer({ + url, + password, + viewOnly = true, + className, + onConnect, + onDisconnect, +}: BrowserViewerProps) { + // Start in 'connecting' state since VncScreen begins connecting on mount + const [status, setStatus] = useState('connecting') + const vncRef = useRef(null) + // Track connection timeout timer for cleanup + const timeoutRef = useRef | null>(null) + + // Clear connection timeout timer + const clearConnectionTimeout = useCallback(() => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + timeoutRef.current = null + } + }, []) + + // Reset to connecting state when URL changes (render-time state adjustment) + const [prevUrl, setPrevUrl] = useState(url) + if (prevUrl !== url) { + setPrevUrl(url) + setStatus('connecting') + } + + // Connection timeout - show error if not connected within timeout + useEffect(() => { + timeoutRef.current = setTimeout(() => { + setStatus((current) => { + if (current === 'connecting') { + console.debug('[BrowserViewer] Connection timeout') + return 'disconnected' + } + return current + }) + }, CONNECTION_TIMEOUT_MS) + + return clearConnectionTimeout + }, [url, clearConnectionTimeout]) + + // Track container ref for resize detection + const containerRef = useRef(null) + + // Trigger VNC scale update when container size changes (e.g., sidebar toggle) + // noVNC's internal ResizeObserver watches its own canvas, not our container. + // We use the public scaleViewport property toggle to force a recalculation. + useEffect(() => { + const container = containerRef.current + const rfb = vncRef.current?.rfb + if (!container || !rfb) return + + const resizeObserver = new ResizeObserver(() => { + // Toggle scaleViewport to trigger noVNC's internal _updateScale() + // This is the public API way to force a scale recalculation + if (rfb.scaleViewport) { + rfb.scaleViewport = false + rfb.scaleViewport = true + } + }) + resizeObserver.observe(container) + + return () => resizeObserver.disconnect() + }, [status]) // Re-attach when status changes to 'connected' (rfb becomes available) + + // Update RFB viewOnly when prop changes + // react-vnc may not automatically update this on prop change + // Note: Modifying rfb properties is intentional - this is noVNC's API + useEffect(() => { + const rfb = vncRef.current?.rfb + if (rfb && rfb.viewOnly !== viewOnly) { + console.debug('[BrowserViewer] Updating viewOnly:', viewOnly) + // eslint-disable-next-line react-hooks/immutability -- noVNC API requires property mutation + rfb.viewOnly = viewOnly + } + // When taking control, move focus to the VNC canvas. The React re-renders + // triggered by the controlState transition often park focus on , + // and noVNC's keyboard listener is attached to the canvas — so without + // this the user has to click the canvas first before typing reaches the + // VM. Only steal focus when it is already on : if the user is + // typing into another input (e.g. the chat textarea), leave it alone. + if (!viewOnly && document.activeElement === document.body) { + vncRef.current?.focus() + } + }, [viewOnly]) + + // Clipboard sync: VM → User + // When the VM clipboard changes, write to the user's clipboard + const handleClipboard = useCallback((e: { detail: { text: string } }) => { + navigator.clipboard.writeText(e.detail.text).catch(() => { + // Clipboard write can fail if page is not focused or permission denied + }) + }, []) + + // Clipboard sync: User → VM + // Sync host clipboard to VM on canvas focus (covers keyboard paste since + // user must click into VNC first) and on right-click context menu paste. + useEffect(() => { + if (viewOnly || status !== 'connected') return + + const container = containerRef.current + const canvas = container?.querySelector('canvas') + if (!canvas) return + + // Read host clipboard and send to VM + const syncClipboardToVM = async () => { + try { + const text = await navigator.clipboard.readText() + if (text) { + vncRef.current?.clipboardPaste(text) + } + } catch { + // Permission denied or page not focused + } + } + + // Right-click menu paste (scoped to container, not document) + const handlePaste = (e: Event) => { + const clipboardEvent = e as ClipboardEvent + const text = clipboardEvent.clipboardData?.getData('text/plain') + if (text) { + vncRef.current?.clipboardPaste(text) + } + } + + canvas.addEventListener('focus', syncClipboardToVM) + container?.addEventListener('paste', handlePaste) + return () => { + canvas.removeEventListener('focus', syncClipboardToVM) + container?.removeEventListener('paste', handlePaste) + } + }, [viewOnly, status]) + + const handleConnect = useCallback(() => { + console.debug('[BrowserViewer] Connected to VNC') + clearConnectionTimeout() + setStatus('connected') + onConnect?.() + }, [onConnect, clearConnectionTimeout]) + + const handleDisconnect = useCallback(() => { + console.debug('[BrowserViewer] Disconnected from VNC') + setStatus('disconnected') + onDisconnect?.() + }, [onDisconnect]) + + const handleSecurityFailure = useCallback(() => { + console.debug('[BrowserViewer] Security failure') + setStatus('disconnected') + }, []) + + return ( +
+ + + {/* Status Overlay */} + {status !== 'connected' && ( +
+ + {status === 'connecting' ? 'Connecting...' : 'Browser not available'} + +
+ )} +
+ ) +} diff --git a/frontend/src/components/browser/DescriptionOverlay.tsx b/frontend/src/components/browser/DescriptionOverlay.tsx new file mode 100644 index 0000000..fc69da1 --- /dev/null +++ b/frontend/src/components/browser/DescriptionOverlay.tsx @@ -0,0 +1,57 @@ +/** + * DescriptionOverlay Component + * + * Semi-transparent floating overlay inside the Embedded browser viewer area. + * Shows CUA action thoughts and result text over the browser/screenshot. + * + * Truncated content is click-to-expand with a tooltip hint, mirroring the + * behavior in `ActionDescription` (expanded view) — both share + * `ExpandableActionText`. + */ + +import { cn } from '@/lib/utils' +import { ExpandableActionText } from './ExpandableActionText' + +// ============================================================================= +// Types +// ============================================================================= + +interface DescriptionOverlayProps { + /** Current action planned thought */ + thoughts?: string + /** Current action result text */ + actionResult?: string + /** Whether there's any action description content */ + hasContent: boolean +} + +// ============================================================================= +// Component +// ============================================================================= + +export function DescriptionOverlay({ + thoughts, + actionResult, + hasContent, +}: DescriptionOverlayProps) { + if (!hasContent) return null + + return ( +
+ +
+ ) +} diff --git a/frontend/src/components/browser/ExpandableActionText.tsx b/frontend/src/components/browser/ExpandableActionText.tsx new file mode 100644 index 0000000..b998672 --- /dev/null +++ b/frontend/src/components/browser/ExpandableActionText.tsx @@ -0,0 +1,151 @@ +/** + * ExpandableActionText + * + * The "thoughts + actionResult" text block shown inside browser views. + * Treats the two lines as a single interactive unit: + * - thoughts: 2 lines max when collapsed (line-clamp-2) + * - actionResult: 1 line max when collapsed (truncate) + * - If either line is truncated, the whole block becomes clickable and shows a + * "Click to expand" hover hint + * - Click anywhere → both expand together; click again → both collapse + * + * Truncation is detected via ResizeObserver, so the affordance auto-updates + * as the surrounding container resizes. + * + * Used by both `ActionDescription` (expanded view) and `DescriptionOverlay` + * (embedded overlay). + */ + +import { useEffect, useRef, useState } from 'react' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { cn } from '@/lib/utils' + +// ============================================================================= +// Helpers +// ============================================================================= + +/** + * Detects whether a block-level element's content is visually truncated by CSS + * (line-clamp or text-overflow). Re-checks on element resize and whenever + * `text` changes (e.g. when switching to a different screenshot). + * + * Pass `enabled: false` to pause the ResizeObserver (e.g. while expanded). The + * last measured value is preserved so the caller stays interactive on the same + * render that toggles back to collapsed. + */ +function useIsTruncated( + ref: React.RefObject, + enabled: boolean, + text: string | undefined +) { + const [truncated, setTruncated] = useState(false) + + useEffect(() => { + if (!enabled) return + const el = ref.current + if (!el) return + + const check = () => { + const overflow = el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth + setTruncated(overflow) + } + + const observer = new ResizeObserver(check) + observer.observe(el) + // `check()` runs synchronously here so a `text` change immediately + // overwrites any stale value from the previous render. + check() + + return () => observer.disconnect() + }, [ref, enabled, text]) + + // When text becomes empty there is nothing to truncate. Returning `false &&` + // here keeps the caller's `isInteractive` accurate without triggering an + // extra effect-driven setState. + return text ? truncated : false +} + +// ============================================================================= +// Component +// ============================================================================= + +interface ExpandableActionTextProps { + thoughts?: string + actionResult?: string + /** Extra class names applied to each line (e.g. text color). */ + textClassName?: string +} + +export function ExpandableActionText({ + thoughts, + actionResult, + textClassName, +}: ExpandableActionTextProps) { + const thoughtsRef = useRef(null) + const actionRef = useRef(null) + const [isExpanded, setIsExpanded] = useState(false) + // Only detect truncation while collapsed; once expanded, the block stays + // clickable so the user can collapse it again. + const isThoughtsTruncated = useIsTruncated(thoughtsRef, !isExpanded, thoughts) + const isActionTruncated = useIsTruncated(actionRef, !isExpanded, actionResult) + const isInteractive = isThoughtsTruncated || isActionTruncated || isExpanded + + if (!thoughts && !actionResult) return null + + const toggle = () => setIsExpanded((v) => !v) + + // Single Tooltip wraps the whole block so any hover/click on either line + // shows the same hint and toggles the same state. JSX tree stays stable + // (Tooltip + TooltipTrigger + inner div +

refs all constant) so the + // ResizeObserver-based detection keeps working across re-renders. + return ( + + +

{ + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault() + toggle() + } + } + : undefined + } + className={cn( + isInteractive && + 'focus-visible:ring-ring cursor-pointer rounded-sm focus-visible:ring-2 focus-visible:outline-none' + )} + > + {thoughts && ( +

+ {thoughts} +

+ )} + {actionResult && ( +

+ {actionResult} +

+ )} +
+ + {isInteractive && ( + {isExpanded ? 'Click to collapse' : 'Click to expand'} + )} + + ) +} diff --git a/frontend/src/components/browser/PlaybackButtons.tsx b/frontend/src/components/browser/PlaybackButtons.tsx new file mode 100644 index 0000000..831c3c1 --- /dev/null +++ b/frontend/src/components/browser/PlaybackButtons.tsx @@ -0,0 +1,109 @@ +/** + * PlaybackButtons Component + * + * Step back / Step forward button pair used in history mode. + * The Live toggle is now handled by a Switch in PlaybackControlsRow. + */ + +import type { ReactNode } from 'react' +import { StepBack, StepForward } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { cn } from '@/lib/utils' + +// ============================================================================= +// Types +// ============================================================================= + +interface PlaybackButtonsProps { + /** Use smaller buttons (h-8/size-8) for compact layouts like BrowserEmbed */ + compact?: boolean + /** Whether Prev button can be clicked */ + canStepBack: boolean + /** Whether Next button can be clicked */ + canStepForward: boolean + /** Handler for Prev button */ + onStepBack: () => void + /** Handler for Next button */ + onStepForward: () => void + /** Tooltip text for Next button */ + nextTooltip: string +} + +// ============================================================================= +// Helpers +// ============================================================================= + +/** Icon button with conditional tooltip — tooltip hidden when disabled */ +function StepButton({ + compact, + enabled, + onClick, + icon, + label, + tooltip, +}: { + compact?: boolean + enabled: boolean + onClick: () => void + icon: ReactNode + label: string + tooltip: string +}) { + const btn = ( + + ) + + if (!enabled) return btn + + return ( + + {btn} + {tooltip} + + ) +} + +// ============================================================================= +// Component +// ============================================================================= + +export function PlaybackButtons({ + compact, + canStepBack, + canStepForward, + onStepBack, + onStepForward, + nextTooltip, +}: PlaybackButtonsProps) { + return ( +
+ } + label="Previous action" + tooltip="Previous action" + /> + + } + label="Next action" + tooltip={nextTooltip} + /> +
+ ) +} diff --git a/frontend/src/components/browser/PlaybackControlsRow.tsx b/frontend/src/components/browser/PlaybackControlsRow.tsx new file mode 100644 index 0000000..f01dcaa --- /dev/null +++ b/frontend/src/components/browser/PlaybackControlsRow.tsx @@ -0,0 +1,175 @@ +/** + * PlaybackControlsRow Component + * + * Unified controls row used by both BrowserEmbed and BrowserExpanded. + * + * Layout depends on mode: + * - Live (VNC connected): [LIVE toggle (ON)] ............. [Control button] + * - History: [LIVE toggle (OFF)] | [ProgressBar] [StepBack] [StepForward] + * - VNC only (no history): [LIVE toggle (disabled, ON)] ... [Control button] + * - History only (no VNC): [LIVE toggle (disabled, OFF)] | [ProgressBar] [StepBack] [StepForward] + * + * The LIVE toggle is always visible when this component renders. It is disabled + * when there is nothing to toggle between (no VNC, or no screenshot history). + * Parent components gate rendering on isVncConnected || hasHistory. + */ + +import { MousePointer2 } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Switch } from '@/components/ui/switch' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { ProgressBar } from './ProgressBar' +import { PlaybackButtons } from './PlaybackButtons' +import { cn } from '@/lib/utils' + +// ============================================================================= +// Types +// ============================================================================= + +interface PlaybackControlsRowProps { + /** Additional classes (e.g. padding) */ + className?: string + /** Use smaller buttons (h-8) for compact layouts like BrowserEmbed */ + compact?: boolean + + // -- VNC state -- + /** Whether VNC is connected (determines if LIVE toggle is interactive) */ + isVncConnected: boolean + + // -- Control button (live mode only) -- + /** Called when Control button is clicked; omit to hide the button entirely */ + onControlClick?: () => void + + // -- Playback state -- + /** Whether display is in live mode (pre-computed by usePlaybackControls) */ + displayIsLive: boolean + /** Set live mode on/off (matches Switch onCheckedChange signature) */ + onSetLive: (isLive: boolean) => void + + // -- ProgressBar (history mode only) -- + /** Total number of screenshots */ + total: number + /** Current screenshot index (clamped) */ + validCurrentIndex: number + /** Called when user clicks/drags to a screenshot index */ + onIndexChange: (index: number) => void + + // -- PlaybackButtons (history mode only) -- + /** Whether Prev button can be clicked */ + canStepBack: boolean + /** Whether Next button can be clicked */ + canStepForward: boolean + /** Handler for Prev button */ + onStepBack: () => void + /** Handler for Next button */ + onStepForward: () => void + /** Tooltip text for Next button */ + nextTooltip: string +} + +// ============================================================================= +// Component +// ============================================================================= + +export function PlaybackControlsRow({ + className, + compact, + isVncConnected, + onControlClick, + displayIsLive, + onSetLive, + total, + validCurrentIndex, + onIndexChange, + canStepBack, + canStepForward, + onStepBack, + onStepForward, + nextTooltip, +}: PlaybackControlsRowProps) { + // Toggle is interactive only when VNC is connected and there's history to switch between + const canToggle = isVncConnected && total > 0 + + return ( +
+ {/* LIVE toggle — always visible, disabled when not interactive */} +
+ + LIVE + + + + + + + {!canToggle + ? displayIsLive + ? 'Live view (no history yet)' + : 'No live connection' + : displayIsLive + ? 'Switch to history view' + : 'Switch to live view'} + + +
+ + {/* Live mode: Control button on far right */} + {displayIsLive && ( + <> + {/* Spacer to push Control to the right */} +
+ + {/* Control button */} + {onControlClick && ( + + + + + Take control of the browser + + )} + + )} + + {/* History mode: separator + progress bar + step buttons */} + {!displayIsLive && ( + <> + {/* Vertical separator between toggle and progress bar */} +
+ + + + + + )} +
+ ) +} diff --git a/frontend/src/components/browser/ProgressBar.tsx b/frontend/src/components/browser/ProgressBar.tsx new file mode 100644 index 0000000..b1f5520 --- /dev/null +++ b/frontend/src/components/browser/ProgressBar.tsx @@ -0,0 +1,185 @@ +/** + * ProgressBar Component + * + * Draggable progress bar for screenshot playback. + * + * Progress: 0% = first screenshot, 100% = last screenshot. + * Only rendered in history mode (not live). + */ + +import { useRef, useCallback, useEffect, type KeyboardEvent } from 'react' +import { cn } from '@/lib/utils' + +// Half the playhead diameter (size-4 = 16px → radius = 8px). +// The playhead's center moves within [PLAYHEAD_R, 100% − PLAYHEAD_R] +// so its circle never extends beyond the track edges. +const PLAYHEAD_R = 8 + +// ============================================================================= +// Types +// ============================================================================= + +interface ProgressBarProps { + /** Total number of screenshots */ + total: number + /** Current screenshot index (clamped) */ + currentIndex: number + /** Called when user clicks/drags to a screenshot index */ + onIndexChange: (index: number) => void +} + +// ============================================================================= +// Component +// ============================================================================= + +export function ProgressBar({ total, currentIndex, onIndexChange }: ProgressBarProps) { + const trackRef = useRef(null) + const isDragging = useRef(false) + const cleanupDrag = useRef<(() => void) | null>(null) + + // Clean up document-level drag listeners on unmount + useEffect(() => { + return () => cleanupDrag.current?.() + }, []) + + // Calculate playhead position: + // - Single screenshot → 100% + // - Multiple screenshots: first = 0%, last = 100% + const getProgress = (): number => { + if (total <= 1) return 100 + return (currentIndex / (total - 1)) * 100 + } + + const progress = getProgress() + + // Convert click/drag position to screenshot index. + const getIndexFromPosition = useCallback( + (percentage: number): number => { + if (total <= 1) return 0 + const rawIndex = (percentage / 100) * (total - 1) + return Math.max(0, Math.min(total - 1, Math.round(rawIndex))) + }, + [total] + ) + + const handlePositionChange = useCallback( + (clientX: number) => { + if (!trackRef.current || total === 0) return + const rect = trackRef.current.getBoundingClientRect() + // Map cursor position to the playhead's movable range + const x = clientX - rect.left - PLAYHEAD_R + const effectiveWidth = rect.width - PLAYHEAD_R * 2 + const percentage = Math.max(0, Math.min(100, (x / effectiveWidth) * 100)) + onIndexChange(getIndexFromPosition(percentage)) + }, + [onIndexChange, total, getIndexFromPosition] + ) + + const handleTrackClick = (e: React.MouseEvent) => { + if (isDragging.current || total === 0) return + handlePositionChange(e.clientX) + } + + const handleMouseDown = (e: React.MouseEvent) => { + if (total === 0) return + isDragging.current = true + handlePositionChange(e.clientX) + + const handleMouseMove = (moveEvent: MouseEvent) => { + handlePositionChange(moveEvent.clientX) + } + + const handleMouseUp = () => { + isDragging.current = false + cleanupDrag.current = null + document.removeEventListener('mousemove', handleMouseMove) + document.removeEventListener('mouseup', handleMouseUp) + } + + document.addEventListener('mousemove', handleMouseMove) + document.addEventListener('mouseup', handleMouseUp) + cleanupDrag.current = handleMouseUp + } + + const isEmpty = total === 0 + const isInteractive = !isEmpty + + // Keyboard navigation for ARIA slider role + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (!isInteractive) return + switch (e.key) { + case 'ArrowLeft': + case 'ArrowDown': + e.preventDefault() + onIndexChange(Math.max(0, currentIndex - 1)) + break + case 'ArrowRight': + case 'ArrowUp': + e.preventDefault() + onIndexChange(Math.min(total - 1, currentIndex + 1)) + break + case 'Home': + e.preventDefault() + onIndexChange(0) + break + case 'End': + e.preventDefault() + onIndexChange(total - 1) + break + } + }, + [isInteractive, currentIndex, total, onIndexChange] + ) + + return ( +
+ {/* Track — full width; fill tracks playhead center position */} +
+ {/* Progress fill — width matches playhead center so they stay aligned */} +
+
+ + {/* Playhead — moves within [PLAYHEAD_R, 100% − PLAYHEAD_R] so it never overflows */} + {!isEmpty && ( +
+ )} +
+ ) +} diff --git a/frontend/src/components/browser/TakeoverNotice.tsx b/frontend/src/components/browser/TakeoverNotice.tsx new file mode 100644 index 0000000..430c35f --- /dev/null +++ b/frontend/src/components/browser/TakeoverNotice.tsx @@ -0,0 +1,51 @@ +import { MousePointer2Off } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { cn } from '@/lib/utils' +import type { ControlState } from '@/types' + +/** + * TakeoverNotice Component + * + * Displays a notice when user is in takeover mode (controlling the browser). + * - 'user-pending': Shows animated stripe with "Waiting for agent..." message + * - 'user': Shows solid background with control message + Release Control button + * + * Only used in expanded/maximized view. + */ +interface TakeoverNoticeProps { + /** Current control state (only 'user-pending' or 'user' should be passed) */ + controlState: ControlState + /** Called when Release Control button is clicked */ + onControlClick?: () => void +} + +export function TakeoverNotice({ controlState, onControlClick }: TakeoverNoticeProps) { + const isPending = controlState === 'user-pending' + + return ( +
+

+ {isPending + ? 'Waiting for the agent to stop...' + : "You are in control of the browser. The agent is paused and won't observe these actions."} +

+ {!isPending && onControlClick && ( + + )} +
+ ) +} diff --git a/frontend/src/components/browser/index.ts b/frontend/src/components/browser/index.ts new file mode 100644 index 0000000..367cc08 --- /dev/null +++ b/frontend/src/components/browser/index.ts @@ -0,0 +1,13 @@ +/** + * Browser Components + * + * Components for real-time browser viewing via VNC. + * + * View modes: + * - Embedded: Shows in chat message list (BrowserEmbed) + * - Expanded: Side-by-side with chat (BrowserExpanded) + * - Maximized: Full screen, chat hidden (BrowserExpanded with mode='maximized') + */ + +export { BrowserEmbed } from './BrowserEmbed' +export { BrowserExpanded } from './BrowserExpanded' diff --git a/frontend/src/components/browser/usePlaybackControls.ts b/frontend/src/components/browser/usePlaybackControls.ts new file mode 100644 index 0000000..7998769 --- /dev/null +++ b/frontend/src/components/browser/usePlaybackControls.ts @@ -0,0 +1,159 @@ +/** + * usePlaybackControls Hook + * + * Shared playback logic for browser components (BrowserEmbed, BrowserExpanded). + * Calculates derived state from screenshot actions + playback position. + */ + +import { useMemo, useCallback } from 'react' +import type { LatestCuaAction } from '@/types' +import type { CuaAction } from '@/components/chat/messages' + +// ============================================================================= +// Types +// ============================================================================= + +interface UsePlaybackControlsOptions { + /** CUA actions with screenshots (completed actions only) */ + screenshotActions: CuaAction[] + /** Whether currently showing live VNC stream */ + isLive: boolean + /** Current screenshot index (when in playback mode) */ + currentIndex: number + /** Called when index changes */ + onIndexChange: (index: number) => void + /** Whether VNC is currently connected */ + isVncConnected: boolean + /** Whether auto-advancing to latest screenshot */ + followLatest: boolean + /** Action info for live mode */ + liveActionInfo?: LatestCuaAction +} + +interface UsePlaybackControlsResult { + /** Total number of screenshot actions */ + total: number + /** Whether there are any screenshots to play back */ + hasHistory: boolean + /** Whether display is in live mode (user wants live AND VNC connected) */ + displayIsLive: boolean + /** Current index clamped to valid range */ + validCurrentIndex: number + /** Screenshot URL for current playback position */ + currentScreenshotUrl: string | undefined + /** Whether step-back button is enabled */ + canStepBack: boolean + /** Whether step-forward button is enabled */ + canStepForward: boolean + /** Step back handler */ + handleStepBack: () => void + /** Step forward handler */ + handleStepForward: () => void + /** Tooltip for step-forward button */ + nextTooltip: string + /** Current thoughts text (from playback or live action) */ + thoughts: string | undefined + /** Current action result text (from playback or live action) */ + actionResult: string | undefined + /** Whether there's any content to display in description */ + hasContent: boolean +} + +// ============================================================================= +// Hook +// ============================================================================= + +export function usePlaybackControls({ + screenshotActions, + isLive, + currentIndex, + onIndexChange, + isVncConnected, + followLatest, + liveActionInfo, +}: UsePlaybackControlsOptions): UsePlaybackControlsResult { + const total = screenshotActions.length + const hasHistory = total > 0 + + // Display mode: live only when user wants live AND VNC is actually connected. + // This controls whether VNC stream or screenshot overlay is shown. + const displayIsLive = isLive && isVncConnected + + // Clamp index to valid range. + // When followLatest is true, always show the latest screenshot. + const validCurrentIndex = + hasHistory && followLatest ? total - 1 : Math.max(0, Math.min(currentIndex, total - 1)) + + // Current screenshot action (playback mode) + const currentAction = hasHistory ? screenshotActions[validCurrentIndex] : undefined + + // Screenshot URL for current action + const currentScreenshotUrl = currentAction?.screenshotUrl + + // Step controls — disabled in live mode + const canStepBack = hasHistory && !displayIsLive && validCurrentIndex > 0 + const canStepForward = hasHistory && !displayIsLive && validCurrentIndex < total - 1 + + const handleStepBack = useCallback(() => { + if (canStepBack) { + onIndexChange(validCurrentIndex - 1) + } + }, [canStepBack, validCurrentIndex, onIndexChange]) + + const handleStepForward = useCallback(() => { + if (canStepForward) { + onIndexChange(validCurrentIndex + 1) + } + }, [canStepForward, validCurrentIndex, onIndexChange]) + + // Tooltips + const nextTooltip = displayIsLive + ? 'Already on latest' + : validCurrentIndex >= total - 1 + ? 'Last action' + : 'Next action' + + // Action description content: use live info when live, playback action otherwise + const { thoughts, actionResult, hasContent } = useMemo(() => { + if (displayIsLive) { + const t = liveActionInfo?.thoughts + const a = liveActionInfo?.actionResult + return { thoughts: t, actionResult: a, hasContent: !!(t || a) } + } + const t = currentAction?.toolArgs?.thoughts + const a = currentAction?.actionResult + return { thoughts: t, actionResult: a, hasContent: !!(t || a) } + }, [displayIsLive, liveActionInfo, currentAction]) + + return { + total, + hasHistory, + displayIsLive, + validCurrentIndex, + currentScreenshotUrl, + canStepBack, + canStepForward, + handleStepBack, + handleStepForward, + nextTooltip, + thoughts, + actionResult, + hasContent, + } +} + +/** + * Generate browser title based on connection, playback, and control state. + */ +export function getBrowserTitle( + isVncConnected: boolean, + hasHistory: boolean, + isLive: boolean, + isUserControl?: boolean +): string { + if (isUserControl) return 'Browser (User Control)' + if (!isVncConnected && !hasHistory) return 'Browser' + // VNC disconnected + screenshots = always show history (e.g., completed session) + if (!isVncConnected && hasHistory) return 'Browser (History)' + return isLive ? 'Browser (Live)' : 'Browser (History)' +} diff --git a/frontend/src/components/chat/ChatInput.tsx b/frontend/src/components/chat/ChatInput.tsx new file mode 100644 index 0000000..dd9c7cf --- /dev/null +++ b/frontend/src/components/chat/ChatInput.tsx @@ -0,0 +1,437 @@ +import { useState, useRef, useEffect, useCallback } from 'react' +import { cn } from '@/lib/utils' +import { Button } from '@/components/ui/button' +import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu' +import { FileChip } from '@/components/common' +import { FolderMountDialog } from './FolderMountDialog' +import { FolderBrowserDialog } from './FolderBrowserDialog' +import { Send, CircleStop, CirclePlay, File, FolderClosed, ChevronDown, X } from 'lucide-react' +import type { SessionStatus, FileAttachment } from '@/types' +import { getInputDraft, setInputDraft, clearInputDraft } from '@/lib/inputDrafts' +import { useFileAttachments } from '@/hooks/useFileAttachments' +import { useFolderMount } from '@/hooks/useFolderMount' + +interface ChatInputProps { + /** Session ID — used to persist input drafts per session */ + sessionId?: number + onSend?: (message: string, files?: FileAttachment[]) => void + onStop?: () => void + disabled?: boolean + sessionStatus?: SessionStatus + isStopping?: boolean + isSending?: boolean + /** Show the file upload button. */ + showAttachments?: boolean + /** Show the folder mount button (start-task only). */ + showFolderMount?: boolean + /** Whether user is controlling (or waiting to control) the browser */ + isControlling?: boolean + /** Whether user needs to describe their browser actions (persists after releasing control) */ + pendingTakeoverFeedback?: boolean + /** Pre-loaded file attachments (e.g. from sample tasks) */ + initialAttachments?: FileAttachment[] +} + +// Placeholder text based on session state +const PLACEHOLDER_DEFAULT = 'Type your message here.' +const PLACEHOLDER_ACTIVE = 'Send a message to steer the agent, or use Stop to end the task.' +const PLACEHOLDER_PAUSED = 'Type your message here, or click Continue to resume the task.' +const PLACEHOLDER_TAKEOVER = 'Briefly describe what you changed in the browser.' + +// Shared button styles +const BUTTON_TEXT_CLASS = 'text-sm tracking-wide' +const ICON_CLASS = 'size-3.5' + +/** + * Chat input component with auto-growing textarea and toolbar. + * File attachment logic is extracted to useFileAttachments hook. + */ +export function ChatInput({ + sessionId, + onSend, + onStop, + disabled = false, + sessionStatus, + isStopping = false, + isSending = false, + showAttachments = false, + showFolderMount = false, + isControlling = false, + pendingTakeoverFeedback = false, + initialAttachments, +}: ChatInputProps) { + const [value, setValue] = useState(() => (sessionId != null ? getInputDraft(sessionId) : '')) + const textareaRef = useRef(null) + + // File attachment state & handlers (extracted hook) + const { + attachments, + clearAttachments, + removeAttachment, + isDragOver, + fileInputRef, + handleFileInputChange, + handlePaste, + handleDragOver, + handleDragLeave, + handleDrop, + } = useFileAttachments(initialAttachments) + + // Folder mounting state & handlers (extracted hook using server-side file browser) + const { + mountedFolder, + folderDialogOpen, + setFolderDialogOpen, + pendingFolderName, + folderBrowserOpen, + setFolderBrowserOpen, + handleSelectFolder, + handleBrowserSelect, + handleFolderAllow, + handleFolderAlwaysAllow, + handleFolderCancel, + handleDeselectFolder, + } = useFolderMount(sessionId) + + // ========================================================================= + // Input Draft Persistence + // ========================================================================= + + const prevSessionIdRef = useRef(sessionId) + useEffect(() => { + if (prevSessionIdRef.current === sessionId) return + if (prevSessionIdRef.current != null && value) { + setInputDraft(prevSessionIdRef.current, value) + } + setValue(sessionId != null ? getInputDraft(sessionId) : '') + prevSessionIdRef.current = sessionId + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [sessionId]) + + // Ref for unmount cleanup — updated synchronously in handleValueChange + const valueRef = useRef(value) + + const handleValueChange = useCallback( + (newValue: string) => { + setValue(newValue) + valueRef.current = newValue + if (sessionId != null) { + setInputDraft(sessionId, newValue) + } + }, + [sessionId] + ) + + // Save draft on unmount + useEffect(() => { + return () => { + const current = prevSessionIdRef.current + if (current != null && valueRef.current) { + setInputDraft(current, valueRef.current) + } + } + }, []) + + // ========================================================================= + // Textarea Auto-resize + // ========================================================================= + + const recalculateHeight = useCallback(() => { + const textarea = textareaRef.current + if (textarea) { + textarea.style.height = 'auto' + textarea.style.height = `${Math.min(textarea.scrollHeight, 192)}px` + } + }, []) + + useEffect(() => { + recalculateHeight() + }, [value, recalculateHeight]) + + useEffect(() => { + const textarea = textareaRef.current + if (!textarea) return + const resizeObserver = new ResizeObserver(() => recalculateHeight()) + resizeObserver.observe(textarea) + return () => resizeObserver.disconnect() + }, [recalculateHeight]) + + // ========================================================================= + // Session State Derived Values + // ========================================================================= + + // Suppress Stop/Continue buttons whenever the user is in (or just exited) + // takeover — those buttons would conflict with the takeover/feedback flow. + // Note: this is independent of the placeholder, which is driven by + // `isControlling` alone (the post-release guidance lives in ChatInputBanner). + const suppressTaskControls = isControlling || pendingTakeoverFeedback + const showStopButton = sessionStatus === 'active' && !suppressTaskControls + const showContinueButton = sessionStatus === 'paused' && !suppressTaskControls + + // Use the takeover placeholder only while the user is actively controlling + // the browser. After release, ChatInputBanner above the input already shows + // the same guidance, so fall through to the default placeholder to avoid + // duplicating the message inside the textarea. + const placeholder = isControlling + ? PLACEHOLDER_TAKEOVER + : showStopButton + ? PLACEHOLDER_ACTIVE + : showContinueButton + ? PLACEHOLDER_PAUSED + : PLACEHOLDER_DEFAULT + + // ========================================================================= + // Submit Handlers + // ========================================================================= + + const handleSubmit = () => { + if ((value.trim() || attachments.length > 0) && onSend) { + onSend(value.trim(), attachments.length > 0 ? attachments : undefined) + setValue('') + valueRef.current = '' + clearAttachments() + if (sessionId != null) clearInputDraft(sessionId) + } + } + + const handleContinue = () => { + if (onSend) { + onSend('Continue.', attachments.length > 0 ? attachments : undefined) + setValue('') + valueRef.current = '' + clearAttachments() + if (sessionId != null) clearInputDraft(sessionId) + } + } + + const handleKeyDown = (e: React.KeyboardEvent) => { + const nativeEvent = e.nativeEvent as KeyboardEvent & { isComposing?: boolean } + if (e.key === 'Enter' && !e.shiftKey && !nativeEvent.isComposing) { + e.preventDefault() + handleSubmit() + } + } + + // ========================================================================= + // Render + // ========================================================================= + + return ( +
+ {/* Textarea */} +
+ +