From 3d087cfde0aca9e621d681a114eb3eaf6dd3b4f8 Mon Sep 17 00:00:00 2001 From: Codex Agent Date: Fri, 12 Jun 2026 02:26:01 +0800 Subject: [PATCH 1/3] feat: draft vue cloud web shell --- web/hwlab-cloud-web/bun.lock | 363 ++++++----- web/hwlab-cloud-web/index.html | 4 +- web/hwlab-cloud-web/package.json | 20 +- web/hwlab-cloud-web/postcss.config.js | 6 + web/hwlab-cloud-web/scripts/check.ts | 119 ++-- .../scripts/dist-contract.test.ts | 12 +- web/hwlab-cloud-web/scripts/dist-contract.ts | 6 +- .../scripts/fetchJson-inactivity.test.ts | 116 ---- web/hwlab-cloud-web/scripts/tsc-check.ts | 19 +- web/hwlab-cloud-web/src/App.tsx | 325 --------- web/hwlab-cloud-web/src/App.vue | 18 + .../src/components/access/AccessView.tsx | 181 ----- .../src/components/auth/LoginShell.tsx | 47 -- .../components/command-bar/CommandBar.test.ts | 55 -- .../src/components/command-bar/CommandBar.tsx | 96 --- .../src/components/command-bar/DraftsList.tsx | 72 -- .../components/common/NavigationProgress.vue | 47 ++ .../src/components/common/SkeletonBlock.vue | 14 + .../src/components/common/StatusBadge.vue | 23 + .../conversation/ConversationPanel.test.ts | 74 --- .../conversation/ConversationPanel.tsx | 268 -------- .../conversation/MessageActions.tsx | 40 -- .../conversation/MessageTracePanel.test.tsx | 106 --- .../conversation/MessageTracePanel.tsx | 185 ------ .../src/components/gate/GateView.tsx | 88 --- .../components/hwpod/HwpodNodeOpsSidebar.tsx | 111 ---- .../src/components/layout/ActivityRail.tsx | 34 - .../src/components/layout/AppLayout.vue | 95 +++ .../components/management/ManagementView.tsx | 270 -------- .../performance/PerformanceView.tsx | 176 ----- .../components/sessions/SessionSidebar.tsx | 46 -- .../src/components/settings/SettingsView.tsx | 58 -- .../src/components/shared/DebugDialog.tsx | 123 ---- .../components/shared/MarkdownRenderer.tsx | 18 - .../shared/MessageMarkdown.test.tsx | 47 -- .../src/components/shared/MessageMarkdown.tsx | 20 - .../src/components/shared/StateTag.tsx | 13 - .../src/components/shared/WorkbenchDialog.tsx | 29 - .../src/components/skills/SkillsView.tsx | 119 ---- .../workbench/WorkbenchBuildSummary.tsx | 243 ------- .../components/workbench/WorkbenchProbe.tsx | 110 ---- web/hwlab-cloud-web/src/hooks/useAuth.ts | 145 ----- .../src/hooks/useGateDiagnostics.ts | 153 ----- .../hooks/useProviderProfileOptions.test.ts | 13 - .../src/hooks/useProviderProfileOptions.ts | 95 --- .../src/hooks/useScrollFollow.test.ts | 18 - .../src/hooks/useScrollFollow.ts | 59 -- .../src/hooks/useSidebarResize.ts | 186 ------ web/hwlab-cloud-web/src/i18n/index.ts | 43 ++ web/hwlab-cloud-web/src/main.ts | 17 + web/hwlab-cloud-web/src/main.tsx | 14 - web/hwlab-cloud-web/src/router/index.ts | 81 +++ .../src/services/api/client.ts | 308 ++------- .../src/services/performance/rum.test.ts | 52 -- .../src/services/performance/rum.ts | 312 --------- .../src/state/agent-status-labels.ts | 99 --- .../src/state/code-agent-status.ts | 400 ------------ web/hwlab-cloud-web/src/state/constants.ts | 45 -- .../src/state/conversation.test.ts | 52 -- web/hwlab-cloud-web/src/state/conversation.ts | 184 ------ .../state/issue853-refresh-regression.test.ts | 51 -- .../src/state/runner-actions.ts | 87 --- .../src/state/runner-trace.test.ts | 616 ------------------ web/hwlab-cloud-web/src/state/runner-trace.ts | 516 --------------- .../src/state/trace-reattach.ts | 67 -- .../src/state/trace-renderer.test.ts | 168 ----- .../src/state/workbench-reducer.test.ts | 246 ------- .../src/state/workbench-reducer.ts | 149 ----- .../src/state/workbench-state.ts | 34 - web/hwlab-cloud-web/src/state/workbench.ts | 417 ------------ web/hwlab-cloud-web/src/stores/session.ts | 49 ++ web/hwlab-cloud-web/src/style.css | 67 ++ web/hwlab-cloud-web/src/styles/workbench.css | 484 -------------- web/hwlab-cloud-web/src/types/config.ts | 58 -- web/hwlab-cloud-web/src/types/domain.ts | 561 ---------------- web/hwlab-cloud-web/src/types/live-builds.ts | 48 -- .../src/types/react-input.d.ts | 8 - web/hwlab-cloud-web/src/utils.ts | 125 ---- .../src/views/admin/AdminAccessView.vue | 15 + .../src/views/admin/AdminDashboardView.vue | 34 + .../src/views/admin/AdminProvidersView.vue | 15 + .../src/views/user/DashboardView.vue | 34 + .../src/views/user/SessionsView.vue | 15 + .../src/views/user/SettingsView.vue | 16 + .../src/views/user/SkillsView.vue | 15 + .../src/views/user/WorkbenchView.vue | 69 ++ web/hwlab-cloud-web/src/vite-env.d.ts | 15 + web/hwlab-cloud-web/tailwind.config.js | 44 ++ web/hwlab-cloud-web/tsconfig.json | 12 +- web/hwlab-cloud-web/vite.config.ts | 12 + 90 files changed, 1079 insertions(+), 9060 deletions(-) create mode 100644 web/hwlab-cloud-web/postcss.config.js delete mode 100644 web/hwlab-cloud-web/scripts/fetchJson-inactivity.test.ts delete mode 100644 web/hwlab-cloud-web/src/App.tsx create mode 100644 web/hwlab-cloud-web/src/App.vue delete mode 100644 web/hwlab-cloud-web/src/components/access/AccessView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/auth/LoginShell.tsx delete mode 100644 web/hwlab-cloud-web/src/components/command-bar/CommandBar.test.ts delete mode 100644 web/hwlab-cloud-web/src/components/command-bar/CommandBar.tsx delete mode 100644 web/hwlab-cloud-web/src/components/command-bar/DraftsList.tsx create mode 100644 web/hwlab-cloud-web/src/components/common/NavigationProgress.vue create mode 100644 web/hwlab-cloud-web/src/components/common/SkeletonBlock.vue create mode 100644 web/hwlab-cloud-web/src/components/common/StatusBadge.vue delete mode 100644 web/hwlab-cloud-web/src/components/conversation/ConversationPanel.test.ts delete mode 100644 web/hwlab-cloud-web/src/components/conversation/ConversationPanel.tsx delete mode 100644 web/hwlab-cloud-web/src/components/conversation/MessageActions.tsx delete mode 100644 web/hwlab-cloud-web/src/components/conversation/MessageTracePanel.test.tsx delete mode 100644 web/hwlab-cloud-web/src/components/conversation/MessageTracePanel.tsx delete mode 100644 web/hwlab-cloud-web/src/components/gate/GateView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/hwpod/HwpodNodeOpsSidebar.tsx delete mode 100644 web/hwlab-cloud-web/src/components/layout/ActivityRail.tsx create mode 100644 web/hwlab-cloud-web/src/components/layout/AppLayout.vue delete mode 100644 web/hwlab-cloud-web/src/components/management/ManagementView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/performance/PerformanceView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/sessions/SessionSidebar.tsx delete mode 100644 web/hwlab-cloud-web/src/components/settings/SettingsView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/DebugDialog.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/MarkdownRenderer.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/MessageMarkdown.test.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/MessageMarkdown.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/StateTag.tsx delete mode 100644 web/hwlab-cloud-web/src/components/shared/WorkbenchDialog.tsx delete mode 100644 web/hwlab-cloud-web/src/components/skills/SkillsView.tsx delete mode 100644 web/hwlab-cloud-web/src/components/workbench/WorkbenchBuildSummary.tsx delete mode 100644 web/hwlab-cloud-web/src/components/workbench/WorkbenchProbe.tsx delete mode 100644 web/hwlab-cloud-web/src/hooks/useAuth.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useGateDiagnostics.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useProviderProfileOptions.test.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useProviderProfileOptions.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useScrollFollow.test.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useScrollFollow.ts delete mode 100644 web/hwlab-cloud-web/src/hooks/useSidebarResize.ts create mode 100644 web/hwlab-cloud-web/src/i18n/index.ts create mode 100644 web/hwlab-cloud-web/src/main.ts delete mode 100644 web/hwlab-cloud-web/src/main.tsx create mode 100644 web/hwlab-cloud-web/src/router/index.ts delete mode 100644 web/hwlab-cloud-web/src/services/performance/rum.test.ts delete mode 100644 web/hwlab-cloud-web/src/services/performance/rum.ts delete mode 100644 web/hwlab-cloud-web/src/state/agent-status-labels.ts delete mode 100644 web/hwlab-cloud-web/src/state/code-agent-status.ts delete mode 100644 web/hwlab-cloud-web/src/state/constants.ts delete mode 100644 web/hwlab-cloud-web/src/state/conversation.test.ts delete mode 100644 web/hwlab-cloud-web/src/state/conversation.ts delete mode 100644 web/hwlab-cloud-web/src/state/issue853-refresh-regression.test.ts delete mode 100644 web/hwlab-cloud-web/src/state/runner-actions.ts delete mode 100644 web/hwlab-cloud-web/src/state/runner-trace.test.ts delete mode 100644 web/hwlab-cloud-web/src/state/runner-trace.ts delete mode 100644 web/hwlab-cloud-web/src/state/trace-reattach.ts delete mode 100644 web/hwlab-cloud-web/src/state/trace-renderer.test.ts delete mode 100644 web/hwlab-cloud-web/src/state/workbench-reducer.test.ts delete mode 100644 web/hwlab-cloud-web/src/state/workbench-reducer.ts delete mode 100644 web/hwlab-cloud-web/src/state/workbench-state.ts delete mode 100644 web/hwlab-cloud-web/src/state/workbench.ts create mode 100644 web/hwlab-cloud-web/src/stores/session.ts create mode 100644 web/hwlab-cloud-web/src/style.css delete mode 100644 web/hwlab-cloud-web/src/styles/workbench.css delete mode 100644 web/hwlab-cloud-web/src/types/config.ts delete mode 100644 web/hwlab-cloud-web/src/types/domain.ts delete mode 100644 web/hwlab-cloud-web/src/types/live-builds.ts delete mode 100644 web/hwlab-cloud-web/src/types/react-input.d.ts delete mode 100644 web/hwlab-cloud-web/src/utils.ts create mode 100644 web/hwlab-cloud-web/src/views/admin/AdminAccessView.vue create mode 100644 web/hwlab-cloud-web/src/views/admin/AdminDashboardView.vue create mode 100644 web/hwlab-cloud-web/src/views/admin/AdminProvidersView.vue create mode 100644 web/hwlab-cloud-web/src/views/user/DashboardView.vue create mode 100644 web/hwlab-cloud-web/src/views/user/SessionsView.vue create mode 100644 web/hwlab-cloud-web/src/views/user/SettingsView.vue create mode 100644 web/hwlab-cloud-web/src/views/user/SkillsView.vue create mode 100644 web/hwlab-cloud-web/src/views/user/WorkbenchView.vue create mode 100644 web/hwlab-cloud-web/src/vite-env.d.ts create mode 100644 web/hwlab-cloud-web/tailwind.config.js diff --git a/web/hwlab-cloud-web/bun.lock b/web/hwlab-cloud-web/bun.lock index 3be64a12..4229bc85 100644 --- a/web/hwlab-cloud-web/bun.lock +++ b/web/hwlab-cloud-web/bun.lock @@ -5,22 +5,35 @@ "": { "name": "hwlab-cloud-web", "dependencies": { - "react": "18.3.1", - "react-dom": "18.3.1", - "react-markdown": "10.1.0", - "remark-gfm": "4.0.1", + "pinia": "^2.1.7", + "vue": "^3.4.0", + "vue-i18n": "^9.14.5", + "vue-router": "^4.2.5", }, "devDependencies": { "@types/node": "22.7.5", - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", + "@vitejs/plugin-vue": "^5.2.3", + "autoprefixer": "^10.4.16", "bun-types": "1.1.33", + "postcss": "^8.4.32", + "tailwindcss": "^3.4.0", "typescript": "5.6.3", "vite": "5.4.10", + "vue-tsc": "^2.2.0", }, }, }, "packages": { + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.29.7", "", {}, "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="], + + "@babel/parser": ["@babel/parser@7.29.7", "", { "dependencies": { "@babel/types": "^7.29.7" }, "bin": "./bin/babel-parser.js" }, "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg=="], + + "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], @@ -67,6 +80,26 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + "@intlify/core-base": ["@intlify/core-base@9.14.5", "", { "dependencies": { "@intlify/message-compiler": "9.14.5", "@intlify/shared": "9.14.5" } }, "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA=="], + + "@intlify/message-compiler": ["@intlify/message-compiler@9.14.5", "", { "dependencies": { "@intlify/shared": "9.14.5", "source-map-js": "^1.0.2" } }, "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ=="], + + "@intlify/shared": ["@intlify/shared@9.14.5", "", {}, "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], + + "@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="], + + "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.61.0", "", { "os": "android", "cpu": "arm" }, "sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.61.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg=="], @@ -117,251 +150,245 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.61.0", "", { "os": "win32", "cpu": "x64" }, "sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw=="], - "@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="], - "@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="], - "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], - - "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], - - "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], - - "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], - "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], - - "@types/react": ["@types/react@18.3.12", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw=="], - - "@types/react-dom": ["@types/react-dom@18.3.1", "", { "dependencies": { "@types/react": "*" } }, "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ=="], - - "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], - "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], - "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], - "bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="], + "@volar/language-core": ["@volar/language-core@2.4.15", "", { "dependencies": { "@volar/source-map": "2.4.15" } }, "sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA=="], + + "@volar/source-map": ["@volar/source-map@2.4.15", "", {}, "sha512-CPbMWlUN6hVZJYGcU/GSoHu4EnCHiLaXI9n8c9la6RaI9W5JHX+NqG+GSQcB0JdC2FIBLdZJwGsfKyBB71VlTg=="], + + "@volar/typescript": ["@volar/typescript@2.4.15", "", { "dependencies": { "@volar/language-core": "2.4.15", "path-browserify": "^1.0.1", "vscode-uri": "^3.0.8" } }, "sha512-2aZ8i0cqPGjXb4BhkMsPYDkkuc2ZQ6yOpqwAuNwUoncELqoy5fRgOQtLR9gB0g902iS0NAkvpIzs27geVyVdPg=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.38", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/shared": "3.5.38", "entities": "^7.0.1", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.38", "", { "dependencies": { "@vue/compiler-core": "3.5.38", "@vue/shared": "3.5.38" } }, "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.38", "", { "dependencies": { "@babel/parser": "^7.29.7", "@vue/compiler-core": "3.5.38", "@vue/compiler-dom": "3.5.38", "@vue/compiler-ssr": "3.5.38", "@vue/shared": "3.5.38", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.15", "source-map-js": "^1.2.1" } }, "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.38", "", { "dependencies": { "@vue/compiler-dom": "3.5.38", "@vue/shared": "3.5.38" } }, "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA=="], + + "@vue/compiler-vue2": ["@vue/compiler-vue2@2.7.16", "", { "dependencies": { "de-indent": "^1.0.2", "he": "^1.2.0" } }, "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A=="], + + "@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="], + + "@vue/language-core": ["@vue/language-core@2.2.12", "", { "dependencies": { "@volar/language-core": "2.4.15", "@vue/compiler-dom": "^3.5.0", "@vue/compiler-vue2": "^2.7.16", "@vue/shared": "^3.5.0", "alien-signals": "^1.0.3", "minimatch": "^9.0.3", "muggle-string": "^0.4.1", "path-browserify": "^1.0.1" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-IsGljWbKGU1MZpBPN+BvPAdr55YPkj2nB/TBNGNC32Vy2qLG25DYu/NBN2vNtZqdRbTRjaoYrahLrToim2NanA=="], + + "@vue/reactivity": ["@vue/reactivity@3.5.38", "", { "dependencies": { "@vue/shared": "3.5.38" } }, "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ=="], + + "@vue/runtime-core": ["@vue/runtime-core@3.5.38", "", { "dependencies": { "@vue/reactivity": "3.5.38", "@vue/shared": "3.5.38" } }, "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw=="], + + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.38", "", { "dependencies": { "@vue/reactivity": "3.5.38", "@vue/runtime-core": "3.5.38", "@vue/shared": "3.5.38", "csstype": "^3.2.3" } }, "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A=="], + + "@vue/server-renderer": ["@vue/server-renderer@3.5.38", "", { "dependencies": { "@vue/compiler-ssr": "3.5.38", "@vue/shared": "3.5.38" }, "peerDependencies": { "vue": "3.5.38" } }, "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw=="], + + "@vue/shared": ["@vue/shared@3.5.38", "", {}, "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug=="], + + "alien-signals": ["alien-signals@1.0.13", "", {}, "sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg=="], + + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + + "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + + "arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="], + + "autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "baseline-browser-mapping": ["baseline-browser-mapping@2.10.36", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg=="], + + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], + + "brace-expansion": ["brace-expansion@2.1.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA=="], + + "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], + + "browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="], "bun-types": ["bun-types@1.1.33", "", { "dependencies": { "@types/node": "~20.12.8", "@types/ws": "~8.5.10" } }, "sha512-SOMt3HeI34EyXoAsMxs5qY+e8cjQOn9v1BQl/ZUy1BsOYYuSPBSRVBYE5DjauqY1bOAWGFAjVu9G9pSmCe1bVw=="], - "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + "caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="], - "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], - "character-reference-invalid": ["character-reference-invalid@2.0.1", "", {}, "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw=="], - - "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + "cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="], "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "de-indent": ["de-indent@1.0.2", "", {}, "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="], - "decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="], + "didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="], - "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + "electron-to-chromium": ["electron-to-chromium@1.5.371", "", {}, "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w=="], + + "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], - "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="], + + "fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + + "fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], - "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], - "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], + "hasown": ["hasown@2.0.4", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A=="], - "inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="], + "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], - "is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + "is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="], - "is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="], + "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], - "is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="], + "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], - "is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], - "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="], + "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], - "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], - "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], - "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], + "merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="], - "mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "mdast-util-gfm": ["mdast-util-gfm@3.1.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.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.0.0" } }, "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ=="], + "minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], - "mdast-util-gfm-autolink-literal": ["mdast-util-gfm-autolink-literal@2.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "ccount": "^2.0.0", "devlop": "^1.0.0", "mdast-util-find-and-replace": "^3.0.0", "micromark-util-character": "^2.0.0" } }, "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ=="], + "muggle-string": ["muggle-string@0.4.1", "", {}, "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ=="], - "mdast-util-gfm-footnote": ["mdast-util-gfm-footnote@2.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0" } }, "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ=="], - - "mdast-util-gfm-strikethrough": ["mdast-util-gfm-strikethrough@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg=="], - - "mdast-util-gfm-table": ["mdast-util-gfm-table@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "markdown-table": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg=="], - - "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], - - "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], - - "mdast-util-mdx-jsx": ["mdast-util-mdx-jsx@3.2.0", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "devlop": "^1.1.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0", "parse-entities": "^4.0.0", "stringify-entities": "^4.0.0", "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" } }, "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q=="], - - "mdast-util-mdxjs-esm": ["mdast-util-mdxjs-esm@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg=="], - - "mdast-util-phrasing": ["mdast-util-phrasing@4.1.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" } }, "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w=="], - - "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], - - "mdast-util-to-markdown": ["mdast-util-to-markdown@2.1.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", "mdast-util-phrasing": "^4.0.0", "mdast-util-to-string": "^4.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "unist-util-visit": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA=="], - - "mdast-util-to-string": ["mdast-util-to-string@4.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0" } }, "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg=="], - - "micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="], - - "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], - - "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], - - "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], - - "micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="], - - "micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="], - - "micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="], - - "micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="], - - "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], - - "micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="], - - "micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="], - - "micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="], - - "micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="], - - "micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="], - - "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], - - "micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="], - - "micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="], - - "micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="], - - "micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="], - - "micromark-util-decode-string": ["micromark-util-decode-string@2.0.1", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ=="], - - "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], - - "micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="], - - "micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="], - - "micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="], - - "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], - - "micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="], - - "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], - - "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], - "parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="], + "node-releases": ["node-releases@2.0.47", "", {}, "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og=="], + + "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-hash": ["object-hash@3.0.0", "", {}, "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="], + + "path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + "picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + + "pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="], + + "pinia": ["pinia@2.3.1", "", { "dependencies": { "@vue/devtools-api": "^6.6.3", "vue-demi": "^0.14.10" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug=="], + + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + "postcss": ["postcss@8.5.15", "", { "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A=="], - "property-information": ["property-information@7.2.0", "", {}, "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg=="], + "postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="], - "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="], - "react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], - "react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="], + "postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], - "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "postcss-selector-parser": ["postcss-selector-parser@6.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ=="], - "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], + "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], - "remark-rehype": ["remark-rehype@11.1.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" } }, "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw=="], + "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], - "remark-stringify": ["remark-stringify@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" } }, "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw=="], + "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], + + "readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + + "resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="], + + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], "rollup": ["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.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-T9mWdbWfQtp0B5lv/HX+wrhYsmXRlcWnXXmJbXqKJhlRaoS6KMhq0gpyzW4UJfclcxrEdLnTgjT2NjruLONu0g=="], - "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], - "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="], + "tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="], - "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], - "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], - "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], "typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="], "undici-types": ["undici-types@6.19.8", "", {}, "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw=="], - "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], - "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], - - "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], - - "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], - - "unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="], - - "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], - - "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], - - "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "vite": ["vite@5.4.10", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-1hvaPshuPUtxeQ0hsVH3Mud0ZanOLwVTneA1EgbAM5LhaZEqyPWGRQ7BtaMvUrTDeEaC8pxtj6a6jku3x4z6SQ=="], - "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], + + "vue": ["vue@3.5.38", "", { "dependencies": { "@vue/compiler-dom": "3.5.38", "@vue/compiler-sfc": "3.5.38", "@vue/runtime-dom": "3.5.38", "@vue/server-renderer": "3.5.38", "@vue/shared": "3.5.38" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A=="], + + "vue-demi": ["vue-demi@0.14.10", "", { "peerDependencies": { "@vue/composition-api": "^1.0.0-rc.1", "vue": "^3.0.0-0 || ^2.6.0" }, "optionalPeers": ["@vue/composition-api"], "bin": { "vue-demi-fix": "bin/vue-demi-fix.js", "vue-demi-switch": "bin/vue-demi-switch.js" } }, "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg=="], + + "vue-i18n": ["vue-i18n@9.14.5", "", { "dependencies": { "@intlify/core-base": "9.14.5", "@intlify/shared": "9.14.5", "@vue/devtools-api": "^6.5.0" }, "peerDependencies": { "vue": "^3.0.0" } }, "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g=="], + + "vue-router": ["vue-router@4.6.4", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg=="], + + "vue-tsc": ["vue-tsc@2.2.12", "", { "dependencies": { "@volar/typescript": "2.4.15", "@vue/language-core": "2.2.12" }, "peerDependencies": { "typescript": ">=5.0.0" }, "bin": { "vue-tsc": "./bin/vue-tsc.js" } }, "sha512-P7OP77b2h/Pmk+lZdJ0YWs+5tJ6J2+uOQPo7tlBnY44QqQSPYvS0qVT4wqDJgwrZaLe47etJLLQRFia71GYITw=="], "bun-types/@types/node": ["@types/node@20.12.14", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg=="], - "parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + + "tinyglobby/picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="], "bun-types/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], } diff --git a/web/hwlab-cloud-web/index.html b/web/hwlab-cloud-web/index.html index bf5116f8..8b5f94de 100644 --- a/web/hwlab-cloud-web/index.html +++ b/web/hwlab-cloud-web/index.html @@ -30,7 +30,7 @@ -
- +
+ diff --git a/web/hwlab-cloud-web/package.json b/web/hwlab-cloud-web/package.json index 2b576ef6..b5c259be 100644 --- a/web/hwlab-cloud-web/package.json +++ b/web/hwlab-cloud-web/package.json @@ -4,23 +4,27 @@ "private": true, "type": "module", "scripts": { - "check": "bun run scripts/check.ts && bun run scripts/tsc-check.ts && bun test", + "check": "bun run scripts/check.ts && bun run scripts/tsc-check.ts && bun test scripts/dist-contract.test.ts", "check:tsc": "bun run scripts/tsc-check.ts", "check:tsc-strict": "bun run scripts/tsc-check.ts --strict", + "typecheck": "vue-tsc --noEmit --project tsconfig.json", "build": "bun run scripts/build.ts" }, "dependencies": { - "react": "18.3.1", - "react-dom": "18.3.1", - "react-markdown": "10.1.0", - "remark-gfm": "4.0.1" + "pinia": "^2.1.7", + "vue": "^3.4.0", + "vue-i18n": "^9.14.5", + "vue-router": "^4.2.5" }, "devDependencies": { - "@types/react": "18.3.12", - "@types/react-dom": "18.3.1", + "@vitejs/plugin-vue": "^5.2.3", "@types/node": "22.7.5", + "autoprefixer": "^10.4.16", "bun-types": "1.1.33", + "postcss": "^8.4.32", + "tailwindcss": "^3.4.0", "vite": "5.4.10", - "typescript": "5.6.3" + "typescript": "5.6.3", + "vue-tsc": "^2.2.0" } } diff --git a/web/hwlab-cloud-web/postcss.config.js b/web/hwlab-cloud-web/postcss.config.js new file mode 100644 index 00000000..ba807304 --- /dev/null +++ b/web/hwlab-cloud-web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {} + } +}; diff --git a/web/hwlab-cloud-web/scripts/check.ts b/web/hwlab-cloud-web/scripts/check.ts index 583db03d..b8fe1787 100644 --- a/web/hwlab-cloud-web/scripts/check.ts +++ b/web/hwlab-cloud-web/scripts/check.ts @@ -6,103 +6,74 @@ import { fileURLToPath } from "node:url"; import { assertCloudWebDistFresh, buildCloudWebDist, readCloudWebAppSource } from "./dist-contract.ts"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); -const repoRoot = path.resolve(rootDir, "../.."); const requiredFiles = Object.freeze([ "index.html", - "src/main.tsx", - "src/App.tsx", - "src/components/layout/ActivityRail.tsx", - "src/components/auth/LoginShell.tsx", - "src/components/sessions/SessionSidebar.tsx", - "src/components/conversation/ConversationPanel.tsx", - "src/components/command-bar/CommandBar.tsx", - "src/components/hwpod/HwpodNodeOpsSidebar.tsx", - "src/components/performance/PerformanceView.tsx", - "src/components/settings/SettingsView.tsx", - "src/components/skills/SkillsView.tsx", - "src/hooks/useAuth.ts", + "src/main.ts", + "src/App.vue", + "src/router/index.ts", + "src/components/layout/AppLayout.vue", + "src/components/common/SkeletonBlock.vue", + "src/components/common/StatusBadge.vue", "src/services/api/client.ts", - "src/state/workbench.ts", - "src/types/domain.ts", - "src/styles/workbench.css", + "src/stores/session.ts", + "src/views/user/WorkbenchView.vue", + "src/views/admin/AdminDashboardView.vue", + "src/i18n/index.ts", + "src/style.css", + "tailwind.config.js", + "postcss.config.js", "favicon.svg", "favicon.ico", "help.md" ]); for (const file of requiredFiles) { - if (!fs.existsSync(path.resolve(rootDir, file))) throw new Error(`missing React web asset: ${file}`); + if (!fs.existsSync(path.resolve(rootDir, file))) throw new Error(`missing Vue web asset: ${file}`); } -console.log("hwlab-cloud-web check: React module assets present"); const html = readWeb("index.html"); -const appTsx = readWeb("src/App.tsx"); +const packageJson = readWeb("package.json"); +const routerSource = readWeb("src/router/index.ts"); +const layoutSource = readWeb("src/components/layout/AppLayout.vue"); const apiClientSource = readWeb("src/services/api/client.ts"); -const authHookSource = readWeb("src/hooks/useAuth.ts"); +const styleSource = readWeb("src/style.css"); const appSource = readCloudWebAppSource(rootDir); -const css = readWeb("src/styles/workbench.css"); -assert.match(html, /
<\/div>/u, "index.html must only expose React mount root"); -assert.match(html, /src="\/src\/main\.tsx"/u, "index.html must load React TSX entry"); +assert.match(html, /
<\/div>/u, "index.html must expose the Vue mount root"); +assert.match(html, /src="\/src\/main\.ts"/u, "index.html must load the Vue TS entry"); assert.match(html, /HWLAB_CLOUD_WEB_EARLY_WORKSPACE_BOOTSTRAP/u, "workspace bootstrap must start during HTML parse for the workspace first screen"); -assert.match(html, /new URLSearchParams\(window\.location\.search\)\.get\("projectId"\)/u, "workspace bootstrap must honor the URL projectId before the React bundle loads"); +assert.match(html, /new URLSearchParams\(window\.location\.search\)\.get\("projectId"\)/u, "workspace bootstrap must honor the URL projectId before the Vue bundle loads"); assert.doesNotMatch(html, /prj_device_pod_workbench/u, "workspace bootstrap must not hard-code the retired device-pod project id"); -assert.doesNotMatch(html, /workbench-shell|hwpod-node-sidebar|command-input|conversation-list|session-tabs/u, "index.html must not contain business DOM shell"); -for (const removed of ["app.ts", "app-conversation.ts", "app-helpers.ts", "app-skills.ts", "styles.css", "web-types.d.ts"]) { - assert.equal(fs.existsSync(path.join(rootDir, removed)), false, `${removed} must not remain as a legacy runtime path`); -} -assertIncludes(appSource, "createRoot", "React root must be used"); -assertIncludes(appSource, "React.StrictMode", "React StrictMode must wrap the app"); -assertIncludes(appSource, "useWorkbenchStore", "state hook must own workbench state"); -assertIncludes(appSource, "fetchJson", "API client must own fetch calls"); -assertIncludes(appSource, "HwpodNodeOpsSidebar", "HWPOD node-ops sidebar component must exist"); -assertIncludes(appSource, "CommandBar", "command bar component must exist"); -assertIncludes(appSource, "PerformanceView", "performance view component must exist"); -assertIncludes(appSource, "SkillsView", "skills component must exist"); -assertIncludes(appSource, "SettingsView", "settings component must exist"); -assertIncludes(appTsx, "const workspaceRoute = route === \"workspace\"", "React runtime must explicitly identify workspace route before starting workbench data flows"); -assertIncludes(authHookSource, "consumeEarlyWorkspaceBootstrap", "auth hook must consume the HTML-started workspace bootstrap instead of issuing a second first-screen bootstrap"); -assertIncludes(apiClientSource, "adapter: (): Promise> => fetchJson(\"/v1/rpc/system.health\", { method: \"POST\"", "adapter health must use POST /v1/rpc/system.health"); -assertIncludes(appSource, "api.hwpodNodeOpsHealth()", "Workbench live probe must call HWPOD node-ops through Cloud Web/API"); -assertIncludes(apiClientSource, "\"/v1/hwpod-node-ops\"", "API client must expose HWPOD node-ops route"); -assertIncludes(readRepo("internal/dev-entrypoint/cloud-web-routes.mjs"), "normalizedPath.startsWith(\"/v1/rpc/\")", "Cloud Web must proxy REST RPC bridge routes to cloud-api"); -assertIncludes(appSource, "id=\"command-input\"", "React runtime must render command input selector"); -assertIncludes(appSource, "id=\"conversation-list\"", "React runtime must render conversation list selector"); -assertIncludes(appSource, "id=\"hwpod-node-sidebar\"", "React runtime must render HWPOD node sidebar selector"); -assertIncludes(appSource, "id=\"hwpod-event-scroll\"", "React runtime must render HWPOD node-ops stream selector"); -assertIncludes(appSource, "id=\"logout-button\"", "React runtime must render logout selector"); -assertIncludes(appSource, "route === \"performance\"", "React runtime must route top-level performance panel"); -assertIncludes(readRepo("internal/cloud/server.ts"), "/v1/web-performance/summary", "cloud-api must expose WebUI performance summary API"); -assert.doesNotMatch(appSource, /document\.getElementById\((?!["']root["'])|querySelector\(["']#(?:command-input|conversation-list|hwpod-node-sidebar)/u, "major UI must not be driven by global DOM element lookups"); -assert.doesNotMatch(appSource, /\bconst\s+el\s*=/u, "global el map must not return"); +assert.equal(fs.existsSync(path.join(rootDir, "src/main.tsx")), false, "React TSX entry must not remain"); +assert.equal(fs.existsSync(path.join(rootDir, "src/App.tsx")), false, "React App.tsx entry must not remain"); +assert.doesNotMatch(packageJson, /"react"|"react-dom"|"react-markdown"|"remark-gfm"/u, "React runtime dependencies must not remain in the Vue shell package"); +assert.match(packageJson, /"vue"/u, "Vue dependency must be declared"); +assert.match(packageJson, /"vue-router"/u, "Vue Router dependency must be declared"); +assert.match(packageJson, /"pinia"/u, "Pinia dependency must be declared"); +assert.match(packageJson, /"tailwindcss"/u, "Tailwind dependency must be declared"); +assert.match(routerSource, /createWebHashHistory/u, "Cloud Web must keep hash routing for same-origin static serving"); +assert.match(routerSource, /redirect:\s*"\/workspace"/u, "default first screen must route to the workbench"); +assert.match(routerSource, /path:\s*"\/workspace"/u, "router must expose the workbench route"); +assert.match(routerSource, /path:\s*"\/admin\/access"/u, "router must expose admin access skeleton route"); +assert.match(layoutSource, /userNavigation/u, "layout must keep user navigation separate from admin navigation"); +assert.match(layoutSource, /adminNavigation/u, "layout must keep admin navigation separate from user navigation"); +assert.match(apiClientSource, /credentials:\s*"same-origin"/u, "API client must use same-origin cookie auth"); +assert.match(apiClientSource, /auth\/workspace-bootstrap/u, "API client must consume the Web session workspace bootstrap path"); +assert.match(styleSource, /@tailwind base;/u, "Tailwind base layer must be enabled"); +assert.match(styleSource, /\.hw-card/u, "shared card class must exist for the platform shell"); +assert.doesNotMatch(appSource, /from "react"|react-dom|React\.StrictMode|createRoot/u, "Vue app shell source must not import React runtime"); -for (const directory of ["components/layout", "components/auth", "components/sessions", "components/conversation", "components/command-bar", "components/hwpod", "components/performance", "components/settings", "components/skills", "hooks", "services/api", "state", "types", "styles"]) { - assert.ok(fs.existsSync(path.join(rootDir, "src", directory)), `missing React module boundary src/${directory}`); +for (const directory of ["components/layout", "components/common", "views/user", "views/admin", "router", "stores", "services/api", "i18n"]) { + assert.ok(fs.existsSync(path.join(rootDir, "src", directory)), `missing Vue module boundary src/${directory}`); } -assert.match(css, /@media \(max-width: 720px\)/u, "mobile layout contract must be explicit"); -assert.match(css, /grid-template-columns:\s*56px\s+minmax\(0,\s*1fr\)/u, "390px mobile layout must not keep three squeezed columns"); -assert.match(css, /\.hwpod-event-scroll\s*\{[\s\S]*?overscroll-behavior:\s*contain;/u, "HWPOD node-ops stream scroll must be contained"); -assert.match(css, /\.hwpod-event-panel\s*\{[\s\S]*?max-height:\s*min\(360px,\s*38dvh\);/u, "HWPOD node-ops stream panel must have a bounded own scroll area"); -assert.match(css, /\.session-tabs\s*\{[\s\S]*?overflow-x:\s*hidden;/u, "session sidebar must not horizontally scroll"); -assert.match(css, /\.message-body p\s*\{\s*white-space:\s*pre-wrap;\s*\}/u, "final response markdown paragraphs must preserve soft line breaks"); -assert.match(css, /@keyframes trace-update-pulse/u, "trace updates must have an explicit visual pulse"); -assert.match(css, /@keyframes trace-working-border/u, "running trace frame must show an explicit working animation"); -assert.match(css, /\.message-trace-body\s*\{[\s\S]*?max-height:\s*calc\(4 \* 1\.35em \+ 14px\);/u, "trace/tool output body must be capped to about four lines with internal scroll"); -assert.doesNotMatch(css, /\.message-final-response[^{}]*\{[^}]*max-height\s*:/u, "final response must not be height-capped; only trace output keeps its own height cap"); -assert.doesNotMatch(appSource, /message-trace-storage-key/u, "trace summary must not expose raw trace ids as visible chips"); -assert.doesNotMatch(appSource, /last ·/u, "trace summary must not show raw last-event labels"); - -const cloudApiServer = readRepo("internal/cloud/server.ts"); -assertIncludes(cloudApiServer, "/v1/hwpod-node-ops", "cloud-api must expose HWPOD node-ops forwarder"); -assertIncludes(cloudApiServer, "/v1/skills", "cloud-api must expose Skills REST routes"); await buildCloudWebDist(rootDir); console.log("hwlab-cloud-web check: dist bundle built by Vite"); await assertCloudWebDistFresh(rootDir); console.log("hwlab-cloud-web check: dist freshness verified"); -console.log("hwlab-cloud-web check ok: React + strict TypeScript module boundaries and runtime selectors are present"); +console.log("hwlab-cloud-web check ok: Vue/Tailwind platform shell boundaries are present"); -function readWeb(relativePath: string): string { return fs.readFileSync(path.resolve(rootDir, relativePath), "utf8"); } -function readRepo(relativePath: string): string { return fs.readFileSync(path.resolve(repoRoot, relativePath), "utf8"); } -function assertIncludes(source: string, term: string, message: string): void { assert.ok(source.includes(term), message); } +function readWeb(relativePath: string): string { + return fs.readFileSync(path.resolve(rootDir, relativePath), "utf8"); +} diff --git a/web/hwlab-cloud-web/scripts/dist-contract.test.ts b/web/hwlab-cloud-web/scripts/dist-contract.test.ts index 481a1c54..0833f27e 100644 --- a/web/hwlab-cloud-web/scripts/dist-contract.test.ts +++ b/web/hwlab-cloud-web/scripts/dist-contract.test.ts @@ -19,7 +19,7 @@ test("Cloud Web dist build emits Vite assets and route aliases", async () => { assert.equal(distDir, path.join(rootDir, "dist")); const html = read(path.join(distDir, "index.html")); - assert.match(html, /
<\/div>/u); + assert.match(html, /
<\/div>/u); assert.match(html, /src="\/app\.js"/u); assert.match(html, /href="\/assets\/index\.css"/u); assert.match(html, /href="\/assets\/favicon\.svg"/u); @@ -44,7 +44,7 @@ test("Cloud Web dist freshness reports stale generated app asset", async () => { try { await buildCloudWebDist(rootDir); - fs.writeFileSync(path.join(rootDir, "src/App.tsx"), "export function App() { return 'changed-app'; }\n"); + fs.writeFileSync(path.join(rootDir, "src/main.ts"), "console.log('changed-app');\n"); const freshness = await inspectCloudWebDistFreshness(rootDir); assert.equal(freshness.status, "blocked"); @@ -65,7 +65,7 @@ function makeCloudWebFixture(): string { " ", " fixture", " ", - "
", + "
", "", "" ].join("\n")); @@ -79,9 +79,9 @@ function makeCloudWebFixture(): string { "};", "" ].join("\n")); - write(rootDir, "src/main.tsx", "import './styles/workbench.css';\nimport { App } from './App';\nconsole.log('fixture-app', App());\n"); - write(rootDir, "src/App.tsx", "export function App() { return 'fixture-app'; }\n"); - write(rootDir, "src/styles/workbench.css", ".fixture-shell { color: #0f766e; }\n"); + write(rootDir, "src/main.ts", "import './style.css';\nconsole.log('fixture-app');\n"); + write(rootDir, "src/App.vue", "\n"); + write(rootDir, "src/style.css", ".fixture-shell { color: #0f766e; }\n"); write(rootDir, "favicon.svg", "\n"); write(rootDir, "help.md", "# fixture help\n"); return rootDir; diff --git a/web/hwlab-cloud-web/scripts/dist-contract.ts b/web/hwlab-cloud-web/scripts/dist-contract.ts index 63277644..234908c5 100644 --- a/web/hwlab-cloud-web/scripts/dist-contract.ts +++ b/web/hwlab-cloud-web/scripts/dist-contract.ts @@ -8,7 +8,7 @@ import { fileURLToPath } from "node:url"; export const cloudWebDistPath = "web/hwlab-cloud-web/dist"; export const cloudWebDistBuildCommand = "cd web/hwlab-cloud-web && bun run build"; export const cloudWebDistFreshnessCommand = cloudWebDistBuildCommand; -export const cloudWebAppSourceFiles = Object.freeze(["src/main.tsx", "src/App.tsx"]); +export const cloudWebAppSourceFiles = Object.freeze(["src/main.ts", "src/App.vue"]); export const cloudWebDistRuntimeFiles = Object.freeze(["index.html", "app.js", "assets/index.css", "assets/favicon.svg", "help.md"]); export const cloudWebDistAliasFiles = Object.freeze(["gate/index.html", "diagnostics/gate/index.html", "help/index.html", "skills/index.html", "access/index.html"]); @@ -158,7 +158,7 @@ function collectSourceFiles(dir: string): string[] { const full = path.join(dir, entry); const stat = fs.statSync(full); if (stat.isDirectory()) out.push(...collectSourceFiles(full)); - else if (/\.(ts|tsx|css)$/u.test(full)) out.push(full); + else if (/\.(ts|vue|css)$/u.test(full)) out.push(full); } return out.sort(); } @@ -167,7 +167,7 @@ function ensureWebDeps(rootDir: string): void { const lockPath = path.join(rootDir, "bun.lock"); const nodeModulesPath = path.join(rootDir, "node_modules"); if (!fs.existsSync(lockPath)) return; - if (fs.existsSync(path.join(nodeModulesPath, "vite")) && fs.existsSync(path.join(nodeModulesPath, "react"))) return; + if (fs.existsSync(path.join(nodeModulesPath, "vite")) && fs.existsSync(path.join(nodeModulesPath, "vue")) && fs.existsSync(path.join(nodeModulesPath, "@vitejs/plugin-vue"))) return; const result = spawnSync("bun", ["install", "--frozen-lockfile"], { cwd: rootDir, encoding: "utf8", stdio: ["ignore", "inherit", "inherit"] }); if (result.status !== 0) throw new Error(`bun install exited with code ${result.status ?? "unknown"}`); } diff --git a/web/hwlab-cloud-web/scripts/fetchJson-inactivity.test.ts b/web/hwlab-cloud-web/scripts/fetchJson-inactivity.test.ts deleted file mode 100644 index d8f1a1c4..00000000 --- a/web/hwlab-cloud-web/scripts/fetchJson-inactivity.test.ts +++ /dev/null @@ -1,116 +0,0 @@ -import assert from "node:assert/strict"; -import test from "node:test"; - -import type { ActivityRef } from "../src/services/api/client.ts"; - -const win = globalThis as unknown as { window: { setTimeout: typeof setTimeout; clearTimeout: typeof clearTimeout } }; -if (!win.window) { - win.window = { - setTimeout: (handler, delay) => setTimeout(handler, delay), - clearTimeout: (timer) => clearTimeout(timer as unknown as ReturnType) - }; -} - -function buildMutableActivityRef(initial: number): { ref: ActivityRef; update: (at: number) => void } { - const ref: ActivityRef = { - lastActivityAt: initial, - lastActivityIso: new Date(initial).toISOString(), - waitingFor: "test", - lastEventLabel: null - }; - return { - ref, - update: (at: number) => { - ref.lastActivityAt = at; - ref.lastActivityIso = new Date(at).toISOString(); - } - }; -} - -function bindSignalFetch(): typeof fetch { - return (async (_input, init) => { - return await new Promise((_, reject) => { - const signal = init?.signal; - if (signal) { - if (signal.aborted) reject(new DOMException("aborted", "AbortError")); - else signal.addEventListener("abort", () => reject(new DOMException("aborted", "AbortError")), { once: true }); - } - }); - }) as typeof fetch; -} - -test("fetchJson 总超时:未传 activityRef 时 timeoutMs 走总窗口", { timeout: 5000 }, async () => { - const originalFetch = globalThis.fetch; - globalThis.fetch = bindSignalFetch(); - try { - const { fetchJson } = await import("../src/services/api/client.ts"); - const startedAt = Date.now(); - const result = await fetchJson("/v1/agent/chat", { timeoutMs: 200, timeoutName: "test-total" }); - const elapsed = Date.now() - startedAt; - assert.equal(result.ok, false, "fetch should fail when no activity is supplied"); - assert.match(result.error ?? "", /超时/u, `expected timeout error, got: ${result.error}`); - assert.ok(elapsed < 1500, `fetchJson should abort near 200ms but took ${elapsed}ms`); - } finally { - globalThis.fetch = originalFetch; - } -}); - -test("fetchJson inactivity:activityRef 每 100ms 推一次活动,stop 后 ~2s abort", { timeout: 15000 }, async () => { - const { ref, update } = buildMutableActivityRef(Date.now()); - const originalFetch = globalThis.fetch; - globalThis.fetch = bindSignalFetch(); - - const interval = setInterval(() => { - update(Date.now()); - }, 100); - - try { - const { fetchJson } = await import("../src/services/api/client.ts"); - const startedAt = Date.now(); - const promise = fetchJson("/v1/agent/chat", { timeoutMs: 2000, timeoutName: "test-inactivity", activityRef: () => ref }); - setTimeout(() => clearInterval(interval), 1800); - const result = await promise; - const elapsed = Date.now() - startedAt; - assert.equal(result.ok, false, "fetch should fail once activity stops"); - assert.match(result.error ?? "", /无新活动/u, `expected inactivity-timeout error, got: ${result.error}`); - assert.ok(elapsed >= 1800, `should not abort before 1800ms with continuous activity, got ${elapsed}ms`); - assert.ok(elapsed < 6000, `should abort ~1-2s after activity stops, got ${elapsed}ms`); - } finally { - clearInterval(interval); - globalThis.fetch = originalFetch; - } -}); - -test("fetchJson 无 total timeout:10s 持续活动窗口内不会被总上限打掉", { timeout: 30000 }, async () => { - // HWLAB #795 PR feedback + #802: the per-request inactivity window is the - // SOLE abort signal. There is no client-side total-timeout ceiling on - // waitForAgentResult / pollRunnerTrace. A continuous 200ms-cadence - // activity stream must keep a fetchJson request alive well past the - // 3s inactivity window set on the test, and the test only ends when - // the activity stream stops. - const { ref, update } = buildMutableActivityRef(Date.now()); - const originalFetch = globalThis.fetch; - globalThis.fetch = bindSignalFetch(); - - const interval = setInterval(() => { - update(Date.now()); - }, 200); - - try { - const { fetchJson } = await import("../src/services/api/client.ts"); - const startedAt = Date.now(); - const promise = fetchJson("/v1/agent/chat/result/abc", { timeoutMs: 3000, timeoutName: "trace-poll", activityRef: () => ref }); - setTimeout(() => clearInterval(interval), 10_000); - const result = await promise; - const elapsed = Date.now() - startedAt; - assert.equal(result.ok, false, "fetch should fail once activity stops"); - assert.ok( - elapsed >= 10_000, - `continuous 200ms activity must keep the request alive for the full 10s; a total cap or wall-clock shrink would abort earlier; got ${elapsed}ms` - ); - assert.match(result.error ?? "", /无新活动/u, `expected inactivity-timeout error, got: ${result.error}`); - } finally { - clearInterval(interval); - globalThis.fetch = originalFetch; - } -}); diff --git a/web/hwlab-cloud-web/scripts/tsc-check.ts b/web/hwlab-cloud-web/scripts/tsc-check.ts index 04d56d8f..e77cbb40 100644 --- a/web/hwlab-cloud-web/scripts/tsc-check.ts +++ b/web/hwlab-cloud-web/scripts/tsc-check.ts @@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const sourceFiles = collectSourceFiles(path.join(rootDir, "src")); let explicitAnyCount = 0; + for (const file of sourceFiles) { const text = readFileSync(file, "utf8"); const stripped = stripComments(text); @@ -20,22 +21,24 @@ for (const file of sourceFiles) { console.error(` ${path.relative(rootDir, file)}: ${matches.length} explicit any usage(s)`); } } + if (explicitAnyCount > 0) { - console.error(`hwlab-cloud-web tsc-check: ${explicitAnyCount} explicit any usage(s) in React source`); + console.error(`hwlab-cloud-web tsc-check: ${explicitAnyCount} explicit any usage(s) in Vue shell source`); process.exit(2); } -const tscBin = path.join(rootDir, "node_modules/.bin/tsc"); -const tscArgs = ["--noEmit", "--project", path.join(rootDir, "tsconfig.json"), "--strict"]; -const tscResult = spawnSync(tscBin, tscArgs, { cwd: rootDir, encoding: "utf8" }); +const vueTscBin = path.join(rootDir, "node_modules/.bin/vue-tsc"); +const tscArgs = ["--noEmit", "--project", path.join(rootDir, "tsconfig.json")]; +const tscResult = spawnSync(vueTscBin, tscArgs, { cwd: rootDir, encoding: "utf8" }); if (tscResult.stdout) process.stdout.write(tscResult.stdout); if (tscResult.stderr) process.stderr.write(tscResult.stderr); -if (tscResult.error) console.error(`hwlab-cloud-web tsc-check: failed to start ${tscBin}: ${tscResult.error.message}`); +if (tscResult.error) console.error(`hwlab-cloud-web tsc-check: failed to start ${vueTscBin}: ${tscResult.error.message}`); if (tscResult.status !== 0) { - console.error(`hwlab-cloud-web tsc-check: strict TypeScript failed with exit ${tscResult.status ?? "unknown"}`); + console.error(`hwlab-cloud-web tsc-check: vue-tsc failed with exit ${tscResult.status ?? "unknown"}`); process.exit(tscResult.status ?? 2); } -console.log("hwlab-cloud-web tsc-check: passed (strict React TSX, 0 explicit any)"); + +console.log("hwlab-cloud-web tsc-check: passed (Vue SFC typecheck, 0 explicit any)"); function collectSourceFiles(dir: string): string[] { const out: string[] = []; @@ -44,7 +47,7 @@ function collectSourceFiles(dir: string): string[] { const full = path.join(dir, entry); const stat = statSync(full); if (stat.isDirectory()) out.push(...collectSourceFiles(full)); - else if (/\.(ts|tsx)$/u.test(full)) out.push(full); + else if (/\.(ts|vue)$/u.test(full)) out.push(full); } return out; } diff --git a/web/hwlab-cloud-web/src/App.tsx b/web/hwlab-cloud-web/src/App.tsx deleted file mode 100644 index eca5a396..00000000 --- a/web/hwlab-cloud-web/src/App.tsx +++ /dev/null @@ -1,325 +0,0 @@ -import type { CSSProperties, ReactElement, ReactNode } from "react"; -import { lazy, Suspense, useCallback, useEffect, useMemo, useRef, useState } from "react"; - -import { useAuth } from "./hooks/useAuth"; -import { providerProfileLabel } from "./hooks/useProviderProfileOptions"; -import { useSidebarResize } from "./hooks/useSidebarResize"; -import { useWorkbenchStore } from "./state/workbench"; -import type { RouteId } from "./types/domain"; -import { LoginShell } from "./components/auth/LoginShell"; -import { CommandBar } from "./components/command-bar/CommandBar"; -import { recordDraft } from "./components/command-bar/DraftsList"; -import { ConversationPanel } from "./components/conversation/ConversationPanel"; -import { HwpodNodeOpsSidebar } from "./components/hwpod/HwpodNodeOpsSidebar"; -import { ActivityRail } from "./components/layout/ActivityRail"; -import { SessionSidebar } from "./components/sessions/SessionSidebar"; -import { WorkbenchBuildSummary } from "./components/workbench/WorkbenchBuildSummary"; -import { WorkbenchProbe } from "./components/workbench/WorkbenchProbe"; -import { fetchText } from "./services/api/client"; -import { installWebPerformanceTelemetry } from "./services/performance/rum"; -import { rememberWorkbenchProjectId, resolveInitialWorkbenchProjectId, workspaceProjectId } from "./state/constants"; -import { firstNonEmptyString } from "./utils"; - -const AccessView = lazy(() => import("./components/access/AccessView").then((module) => ({ default: module.AccessView }))); -const GateView = lazy(() => import("./components/gate/GateView").then((module) => ({ default: module.GateView }))); -const ManagementView = lazy(() => import("./components/management/ManagementView").then((module) => ({ default: module.ManagementView }))); -const PerformanceView = lazy(() => import("./components/performance/PerformanceView").then((module) => ({ default: module.PerformanceView }))); -const SettingsView = lazy(() => import("./components/settings/SettingsView").then((module) => ({ default: module.SettingsView }))); -const SkillsView = lazy(() => import("./components/skills/SkillsView").then((module) => ({ default: module.SkillsView }))); - -const SESSION_SIDEBAR_STORAGE_KEY = "hwlab.workbench.sessionSidebarWidth.v1"; -const SESSION_SIDEBAR_DEFAULT_WIDTH = 330; -const SESSION_SIDEBAR_MIN_WIDTH = 180; -const SESSION_SIDEBAR_MAX_WIDTH = 640; -const RIGHT_SIDEBAR_STORAGE_KEY = "hwlab.workbench.rightSidebarWidth.v1"; -const RIGHT_SIDEBAR_DEFAULT_WIDTH = 620; -const RIGHT_SIDEBAR_MIN_WIDTH = 420; -const RIGHT_SIDEBAR_MAX_WIDTH = 680; - -const routeLabels: Record = { - workspace: "工作台", - performance: "性能监控", - skills: "Skills", - management: "管理", - access: "授权管理", - gate: "内部复核", - help: "使用说明", - settings: "设置" -}; - -export function App(): ReactElement { - const [projectId, setProjectId] = useState(() => resolveInitialWorkbenchProjectId()); - const auth = useAuth(projectId); - const [route, setRoute] = useState(() => routeFromLocation()); - const workspaceRoute = route === "workspace"; - const authReady = auth.authState === "authenticated"; - const authChecking = auth.authState === "checking"; - const workspaceShellPendingAuth = authChecking && workspaceRoute; - const store = useWorkbenchStore(authReady && workspaceRoute, projectId); - const [pickedDraft, setPickedDraft] = useState(null); - const [leftCollapsed, setLeftCollapsed] = useState(false); - const [rightCollapsed, setRightCollapsed] = useState(false); - const [help, setHelp] = useState("加载中"); - const shellRef = useRef(null); - const narrow = useMatchMedia("(max-width: 1240px)"); - - useEffect(() => { - installWebPerformanceTelemetry(); - }, []); - - useEffect(() => { - const next = workspaceProjectId(store.state.workspace, projectId); - if (next === projectId) return; - rememberWorkbenchProjectId(next); - setProjectId(next); - }, [projectId, store.state.workspace]); - - const sessionResize = useSidebarResize({ - storageKey: SESSION_SIDEBAR_STORAGE_KEY, - defaultWidth: SESSION_SIDEBAR_DEFAULT_WIDTH, - bounds: { min: SESSION_SIDEBAR_MIN_WIDTH, max: SESSION_SIDEBAR_MAX_WIDTH }, - side: "left", - disabled: narrow || leftCollapsed, - shellElement: shellRef.current, - cssVariable: "--session-sidebar-width" - }); - - const rightResize = useSidebarResize({ - storageKey: RIGHT_SIDEBAR_STORAGE_KEY, - defaultWidth: RIGHT_SIDEBAR_DEFAULT_WIDTH, - bounds: { min: RIGHT_SIDEBAR_MIN_WIDTH, max: RIGHT_SIDEBAR_MAX_WIDTH }, - side: "right", - disabled: narrow || rightCollapsed, - shellElement: shellRef.current, - cssVariable: "--right-sidebar-width" - }); - - useEffect(() => { - const listener = (): void => setRoute(routeFromLocation()); - window.addEventListener("hashchange", listener); - return () => window.removeEventListener("hashchange", listener); - }, []); - - function pickDraft(value: string): void { - setPickedDraft(value); - } - - function clearPickedDraft(): void { - setPickedDraft(null); - } - - // HWLAB #795: wire the command bar's onTyping to the workbench activity - // clock so the Code Agent inactivity-timeout (driven by activityRef in - // state/workbench.ts) does not falsely fire while the user is reading the - // trace / composing a follow-up. The bar only emits onTyping on actual - // input changes; we keep this stable via useCallback so the CommandBar - // does not re-render on every keystroke. - const noteActivity = useCallback(() => { - store.recordActivity(); - }, [store]); - - async function submitWithDraftRecord(value: string): Promise { - recordDraft(value); - await store.submitMessage(value); - } - - useEffect(() => { - if (route !== "help") return; - void fetchText("/help.md", { timeoutMs: 8000 }).then((response) => setHelp(response.ok ? response.data ?? "" : `帮助内容加载失败:${response.error ?? "unknown"}`)); - }, [route]); - - const modelLabel = useMemo(() => providerProfileLabel(store.state.providerProfile), [store.state.providerProfile]); - const sessionSidebarVisible = workspaceRoute && !leftCollapsed; - const hwpodSidebarVisible = workspaceRoute && !rightCollapsed; - const shellStyle = useMemo(() => ({ - "--session-sidebar-width": `${sessionResize.width}px`, - "--right-sidebar-width": `${rightResize.width}px` - }) as CSSProperties, [rightResize.width, sessionResize.width]); - - function navigate(nextRoute: RouteId): void { - window.location.hash = `/${nextRoute}`; - setRoute(nextRoute); - } - - if (authChecking && !workspaceRoute) return ; - if (auth.authState === "login") return ; - - return ( -
- -
-
- -
- {routeLabels[route]} - {store.state.chatPending ? "处理中" : modelLabel} - {workspaceRoute ? ( -
- - -
- ) : null} -
- -
-
- {workspaceRoute ? void store.createSession()} onDelete={() => void store.deleteCurrentSession()} onSelect={(tab) => void store.selectConversation(tab)} /> : null} - {workspaceRoute ? ( -
- {workspaceShellPendingAuth ?
正在检查登录状态
: null} -
- ); -} - -function copyTextToClipboard(value: string): void { - if (!value) return; - const clipboard = typeof navigator !== "undefined" ? navigator.clipboard : null; - if (clipboard?.writeText) { - void clipboard.writeText(value).catch(() => fallbackCopyText(value)); - return; - } - fallbackCopyText(value); -} - -function fallbackCopyText(value: string): void { - if (typeof document === "undefined") return; - const node = document.createElement("textarea"); - node.value = value; - node.setAttribute("readonly", ""); - node.style.position = "fixed"; - node.style.left = "-9999px"; - node.style.top = "0"; - document.body.appendChild(node); - node.select(); - try { document.execCommand("copy"); } catch { /* ignore clipboard fallback failures */ } - node.remove(); -} - -function StatePill({ tone, children }: { tone: "pending" | "source"; children: string }): ReactElement { - return {children}; -} - -function useMatchMedia(query: string): boolean { - const [match, setMatch] = useState(() => typeof window !== "undefined" && window.matchMedia(query).matches); - useEffect(() => { - if (typeof window === "undefined") return; - const media = window.matchMedia(query); - const listener = (event: MediaQueryListEvent): void => setMatch(event.matches); - setMatch(media.matches); - media.addEventListener("change", listener); - return () => media.removeEventListener("change", listener); - }, [query]); - return match; -} - -function AuthLoadingShell(): ReactElement { - return ( -
-
-

HWLAB

-

正在检查登录状态

-

正在读取服务器会话。

-
-
- ); -} - -function DeferredRouteView({ children }: { children: ReactNode }): ReactElement { - return }>{children}; -} - -function RouteLoadingView(): ReactElement { - return ( -
-

HWLAB

正在加载

准备中
-
- ); -} - -function HelpView({ help }: { help: string }): ReactElement { - return ( -
-

内部说明

使用说明

已就绪
{help}
-
- ); -} - -function routeFromLocation(): RouteId { - const hash = window.location.hash.replace(/^#\/?/u, ""); - if (hash === "performance" || hash === "skills" || hash === "management" || hash === "access" || hash === "gate" || hash === "help" || hash === "settings" || hash === "workspace") return hash; - const path = window.location.pathname.replace(/\/+$/u, "") || "/"; - if (path === "/help") return "help"; - if (path === "/performance" || path === "/ops/performance") return "performance"; - if (path === "/skills") return "skills"; - if (path === "/management" || path === "/admin/provider-profiles") return "management"; - if (path === "/access" || path === "/admin/access") return "access"; - if (path === "/gate" || path === "/diagnostics/gate") return "gate"; - return "workspace"; -} diff --git a/web/hwlab-cloud-web/src/App.vue b/web/hwlab-cloud-web/src/App.vue new file mode 100644 index 00000000..3a06005f --- /dev/null +++ b/web/hwlab-cloud-web/src/App.vue @@ -0,0 +1,18 @@ + + + diff --git a/web/hwlab-cloud-web/src/components/access/AccessView.tsx b/web/hwlab-cloud-web/src/components/access/AccessView.tsx deleted file mode 100644 index 27ac7fb2..00000000 --- a/web/hwlab-cloud-web/src/components/access/AccessView.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import type { ReactElement } from "react"; -import { useEffect, useMemo, useState } from "react"; - -import { api } from "../../services/api/client"; -import type { AccessToolId, AccessUserRole, AccessUserStatus, AdminAccessMatrixResponse, AdminAccessSummaryResponse, AdminAccessUserSummary, Tone } from "../../types/domain"; -import { shortToken } from "../../utils"; -import { StateTag } from "../shared/StateTag"; - -interface AccessState { - loading: boolean; - detailLoading: boolean; - savingKey: string | null; - users: AdminAccessUserSummary[]; - selectedUserId: string | null; - summary: AdminAccessSummaryResponse | null; - matrix: AdminAccessMatrixResponse | null; - error: string | null; -} - -const TOOLS: Array<{ id: AccessToolId; label: string; title: string }> = [ - { id: "hwpod", label: "hwpod", title: "HWPOD CLI" }, - { id: "unidesk_ssh", label: "UniDesk SSH", title: "UniDesk SSH passthrough" }, - { id: "trans_cmd", label: "trans cmd", title: "trans passthrough command" }, - { id: "github_pr", label: "GitHub PR", title: "GitHub PR tool" } -]; - -const initialState: AccessState = { loading: false, detailLoading: false, savingKey: null, users: [], selectedUserId: null, summary: null, matrix: null, error: null }; - -export function AccessView(): ReactElement { - const [state, setState] = useState(initialState); - - async function load(): Promise { - setState((current) => ({ ...current, loading: true, error: null })); - const [summary, users] = await Promise.all([api.adminAccessSummary(), api.adminAccessUsers()]); - if (!summary.ok || !users.ok) { - setState((current) => ({ ...current, loading: false, summary: summary.ok ? summary.data : current.summary, users: users.ok ? users.data?.users ?? [] : current.users, error: summary.error ?? users.error ?? "授权管理不可用" })); - return; - } - const nextUsers = users.data?.users ?? []; - const selectedUserId = state.selectedUserId && nextUsers.some((item) => item.user.id === state.selectedUserId) - ? state.selectedUserId - : nextUsers[0]?.user.id ?? null; - setState((current) => ({ ...current, loading: false, summary: summary.data, users: nextUsers, selectedUserId, error: null })); - if (selectedUserId) await loadUser(selectedUserId); - } - - async function loadUser(userId: string): Promise { - if (!userId) return; - setState((current) => ({ ...current, selectedUserId: userId, detailLoading: true, error: null })); - const response = await api.adminAccessUser(userId); - setState((current) => ({ ...current, detailLoading: false, matrix: response.ok ? response.data : current.matrix, error: response.ok ? null : response.error ?? "用户授权矩阵不可用" })); - } - - async function updateUser(patch: { role?: AccessUserRole; status?: AccessUserStatus }): Promise { - const userId = state.matrix?.user.id ?? state.selectedUserId; - if (!userId || state.savingKey) return; - setState((current) => ({ ...current, savingKey: `user:${userId}`, error: null })); - const response = await api.updateAdminAccessUser(userId, patch); - setState((current) => ({ - ...current, - savingKey: null, - matrix: response.ok ? response.data?.access ?? current.matrix : current.matrix, - users: response.ok && response.data?.access ? mergeUserSummaries(current.users, response.data.access) : current.users, - error: response.ok ? null : response.error ?? "用户状态更新失败" - })); - } - - async function toggleTool(toolId: AccessToolId, allowed: boolean): Promise { - const userId = state.matrix?.user.id ?? state.selectedUserId; - if (!userId || state.savingKey) return; - const savingKey = `tool:${userId}:${toolId}`; - setState((current) => ({ ...current, savingKey, error: null })); - const response = await api.setAdminAccessTool(userId, toolId, allowed); - setState((current) => ({ - ...current, - savingKey: null, - matrix: response.ok ? response.data?.access ?? current.matrix : current.matrix, - users: response.ok && response.data?.access ? mergeUserSummaries(current.users, response.data.access) : current.users, - error: response.ok ? null : response.error ?? "工具授权更新失败" - })); - } - - useEffect(() => { void load(); }, []); - - const selectedUser = state.matrix?.user ?? state.users.find((item) => item.user.id === state.selectedUserId)?.user ?? null; - const statusTone = state.error ? "blocked" : state.loading || state.detailLoading ? "pending" : "source"; - const statusLabel = state.error ? "阻塞" : state.loading || state.detailLoading ? "加载中" : `${state.users.length} 个用户`; - const openFga = state.matrix?.openfga ?? state.summary?.openfga ?? null; - const openFgaTone = openFgaStatusTone(openFga?.mode, openFga?.ready, openFga?.degraded); - const tupleCount = state.matrix?.tuples?.length ?? selectedSummary(state.users, state.selectedUserId)?.tupleCount ?? 0; - const activeToolCount = useMemo(() => TOOLS.filter((tool) => state.matrix?.tools?.[tool.id] === true).length, [state.matrix]); - - return ( -
-
-
-

管理员

授权管理

-
{statusLabel}
-
- {state.error ?

{state.error}

: null} -
- - -
-
-

{selectedUser?.displayName || selectedUser?.username || "未选择用户"}

{selectedUser?.id ?? "-"}
-
- - -
-
-
tuples {tupleCount}tools {activeToolCount}
-
-
-
user
{selectedUser?.username ?? selectedUser?.id ?? "-"}
-
role
{selectedUser?.role ?? "user"}
-
status
{selectedUser?.status ?? "active"}
-
updated
{state.detailLoading ? "loading" : "ready"}
-
-
-
- - -
-
-
- ); -} - -function UserButton({ item, active, onSelect }: { item: AdminAccessUserSummary; active: boolean; onSelect(): void }): ReactElement { - return ( - - ); -} - -function PermissionSwitch({ checked, disabled, busy, label, onChange }: { checked: boolean; disabled: boolean; busy: boolean; label: string; onChange(checked: boolean): void }): ReactElement { - return ; -} - -function selectedSummary(users: AdminAccessUserSummary[], userId: string | null): AdminAccessUserSummary | null { - return users.find((item) => item.user.id === userId) ?? null; -} - -function mergeUserSummaries(users: AdminAccessUserSummary[], access: AdminAccessMatrixResponse): AdminAccessUserSummary[] { - return users.map((item) => item.user.id === access.user.id ? { ...item, user: access.user, tupleCount: access.tuples?.length ?? item.tupleCount, tools: access.tools } : item); -} - -function openFgaStatusTone(mode: string | undefined, ready: boolean | undefined, degraded: boolean | undefined): Tone { - if (degraded) return "blocked"; - if (mode === "enforce" && ready) return "ok"; - if (mode === "shadow") return "warn"; - if (mode === "off") return "source"; - return ready ? "ok" : "pending"; -} diff --git a/web/hwlab-cloud-web/src/components/auth/LoginShell.tsx b/web/hwlab-cloud-web/src/components/auth/LoginShell.tsx deleted file mode 100644 index 2d297109..00000000 --- a/web/hwlab-cloud-web/src/components/auth/LoginShell.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { ReactElement } from "react"; -import { FormEvent, useState } from "react"; - -interface LoginShellProps { - error: string | null; - onLogin(username: string, password: string): Promise; -} - -export function LoginShell({ error, onLogin }: LoginShellProps): ReactElement { - const [username, setUsername] = useState("admin"); - const [password, setPassword] = useState("hwlab2026"); - const [pending, setPending] = useState(false); - - async function submit(event: FormEvent): Promise { - event.preventDefault(); - setPending(true); - try { - await onLogin(username, password); - } finally { - setPending(false); - } - } - - return ( -
-
-
-

HWLAB

-

云工作台登录

-

请输入账号和密码。

-
-
- - - - -
-
-
- ); -} diff --git a/web/hwlab-cloud-web/src/components/command-bar/CommandBar.test.ts b/web/hwlab-cloud-web/src/components/command-bar/CommandBar.test.ts deleted file mode 100644 index 69987880..00000000 --- a/web/hwlab-cloud-web/src/components/command-bar/CommandBar.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import assert from "node:assert/strict"; -import fs from "node:fs"; -import path from "node:path"; -import { fileURLToPath } from "node:url"; -import { createElement } from "react"; -import { renderToStaticMarkup } from "react-dom/server"; -import { test } from "bun:test"; - -import { CommandBar, commandInputRows } from "./CommandBar"; - -const sourcePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "CommandBar.tsx"); - -test("CommandBar releases the send lock on the next tick instead of waiting for the full agent turn", () => { - const source = fs.readFileSync(sourcePath, "utf8"); - assert.match(source, /const submitted = props\.onSubmit\(next\);/u); - assert.match(source, /window\.setTimeout\(\(\) => setSubmitting\(false\), 0\);/u); - assert.doesNotMatch(source, /await props\.onSubmit\(next\)/u); - assert.doesNotMatch(source, /\.finally\(\(\) => setSubmitting\(false\)\)/u); -}); - -test("CommandBar textarea grows to five physical lines then becomes scrollable", () => { - assert.deepEqual(commandInputRows(""), { rows: 1, scrollable: false }); - assert.deepEqual(commandInputRows("one\ntwo\nthree"), { rows: 3, scrollable: false }); - assert.deepEqual(commandInputRows("1\n2\n3\n4\n5"), { rows: 5, scrollable: false }); - assert.deepEqual(commandInputRows("1\n2\n3\n4\n5\n6"), { rows: 5, scrollable: true }); -}); - -test("CommandBar only locks editing for shell-level interaction disabled state", () => { - const sessionRequiredHtml = renderToStaticMarkup(createElement(CommandBar, { ...baseCommandBarProps(), disabled: true, disabledReason: "session_required" })); - assert.doesNotMatch(sessionRequiredHtml, /]*id="command-input"[^>]*disabled/u); - assert.match(sessionRequiredHtml, /]*id="command-send"[^>]*disabled=""/u); - - const lockedHtml = renderToStaticMarkup(createElement(CommandBar, { ...baseCommandBarProps(), interactionDisabled: true })); - assert.match(lockedHtml, /]*id="code-agent-provider-profile"[^>]*disabled=""/u); - assert.match(lockedHtml, /]*id="command-input"[^>]*disabled=""/u); - assert.match(lockedHtml, /]*id="command-send"[^>]*disabled=""/u); - assert.match(lockedHtml, /]*id="command-clear"[^>]*disabled=""/u); -}); - -function baseCommandBarProps(): Parameters[0] { - return { - disabled: false, - submitMode: "turn", - targetTraceId: null, - providerProfile: "deepseek", - onProviderProfile: () => {}, - onSubmit: async () => {}, - onClear: () => {}, - pickedDraft: null, - onPickedDraftConsumed: () => {}, - disabledReason: null, - onTyping: () => {}, - onPickDraft: () => {} - }; -} diff --git a/web/hwlab-cloud-web/src/components/command-bar/CommandBar.tsx b/web/hwlab-cloud-web/src/components/command-bar/CommandBar.tsx deleted file mode 100644 index 0509317e..00000000 --- a/web/hwlab-cloud-web/src/components/command-bar/CommandBar.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import type { ReactElement } from "react"; -import { FormEvent, KeyboardEvent, useEffect, useMemo, useState } from "react"; - -import { useProviderProfileOptions } from "../../hooks/useProviderProfileOptions"; -import type { ProviderProfile } from "../../types/domain"; -import { DraftsList } from "./DraftsList"; - -interface CommandBarProps { - disabled: boolean; - interactionDisabled?: boolean; - submitMode: "turn" | "steer"; - targetTraceId: string | null; - providerProfile: ProviderProfile; - onProviderProfile(profile: ProviderProfile): void; - onSubmit(value: string): Promise; - onClear(): void; - pickedDraft: string | null; - onPickedDraftConsumed(): void; - disabledReason: string | null; - /** - * Notify the workbench that the user is actively typing. Wired by App.tsx - * to `store.recordActivity` so the Code Agent inactivity-timeout (driven - * by the activityRef plumbed in workbench.ts) treats a user who is reading - * or composing a follow-up as "active". This avoids HWLAB #795 style - * false-positive timeouts where the user is at the keyboard but the - * backend has not yet emitted the next trace event. - */ - onTyping(): void; - onPickDraft(draft: string): void; -} - -export function CommandBar({ pickedDraft, onPickedDraftConsumed, disabledReason, onTyping, interactionDisabled = false, ...props }: CommandBarProps): ReactElement { - const [value, setValue] = useState(""); - const [submitting, setSubmitting] = useState(false); - const [draftsOpen, setDraftsOpen] = useState(false); - const inputRows = useMemo(() => commandInputRows(value), [value]); - const { options: providerOptions } = useProviderProfileOptions(props.providerProfile); - - useEffect(() => { - if (pickedDraft) { - setValue(pickedDraft); - onPickedDraftConsumed(); - } - }, [pickedDraft, onPickedDraftConsumed]); - - async function submit(event: FormEvent): Promise { - event.preventDefault(); - const next = value.trim(); - if (!next || interactionDisabled || props.disabled || submitting) return; - setValue(""); - setSubmitting(true); - const submitted = props.onSubmit(next); - window.setTimeout(() => setSubmitting(false), 0); - void submitted.catch(() => setSubmitting(false)); - } - - function keyDown(event: KeyboardEvent): void { - if (event.key !== "Enter" || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; - event.preventDefault(); - event.currentTarget.form?.requestSubmit(); - } - - return ( -
- -
- hwlab -