diff --git a/.prettierignore b/.prettierignore index d860985e..280fe92f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,6 +4,7 @@ /.vscode-test/ /.nyc_output/ /coverage/ +/.claude/ *.vsix flake.lock pnpm-debug.log diff --git a/CLAUDE.md b/CLAUDE.md index 7c82571d..443b2de2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,17 +47,30 @@ Comments explain what code does or why it exists: ## Build and Test Commands - Build: `pnpm build` -- Watch mode: `pnpm watch` +- Watch mode: `pnpm watch` (or `pnpm watch:all`) - Package: `pnpm package` - Format: `pnpm fmt` - Format check: `pnpm fmt:check` - Lint: `pnpm lint` - Lint with auto-fix: `pnpm lint:fix` -- Run all tests: `pnpm test` -- Unit tests: `pnpm test:ci` +- All unit tests: `pnpm test` (or `pnpm test:all`) +- Extension tests: `pnpm test:extension` +- Webview tests: `pnpm test:webview` +- CI mode: `pnpm test:ci` - Integration tests: `pnpm test:integration` -- Run specific unit test: `pnpm test:ci ./test/unit/filename.test.ts` -- Run specific integration test: `pnpm test:integration ./test/integration/filename.test.ts` +- Run specific extension test: `pnpm test:extension ./test/unit/filename.test.ts` +- Run specific webview test: `pnpm test:webview ./test/webview/filename.test.ts` + +## Test File Organization + +```text +test/ +├── unit/ # Extension unit tests (mirrors src/ structure) +├── webview/ # Webview unit tests (by package name) +├── integration/ # VS Code integration tests (uses Mocha, not Vitest) +├── utils/ # Test utilities that are also tested +└── mocks/ # Shared test mocks +``` ## Code Style @@ -69,5 +82,7 @@ Comments explain what code does or why it exists: - Prefix unused variables with underscore (e.g., `_unused`) - Error handling: wrap and type errors appropriately - Use async/await for promises, avoid explicit Promise construction where possible -- Unit test files must be named `*.test.ts` and use Vitest, they should be placed in `./test/unit/` +- Unit test files must be named `*.test.ts` and use Vitest +- Extension tests go in `./test/unit/` +- Webview tests go in `./test/webview//` - Never disable ESLint rules without user approval diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 364f0110..1eaca4d3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,18 +121,42 @@ The link format is `vscode://coder.coder-remote/open?${query}`. For example: code --open-url 'vscode://coder.coder-remote/open?url=dev.coder.com&owner=my-username&workspace=my-ws&agent=my-agent' ``` -There are unit tests using `vitest` with mocked VS Code APIs: +### Unit Tests + +The project uses Vitest with separate test configurations for extension and webview code: ```bash -pnpm test:ci +pnpm test:extension # Extension tests (runs in Electron with mocked VS Code APIs) +pnpm test:webview # Webview tests (runs in jsdom) +pnpm test:all # Both extension and webview tests +pnpm test:ci # CI mode (same as test:all with CI=true) +``` + +Test files are organized by type: + +```text +test/ +├── unit/ # Extension unit tests +├── webview/ # Webview unit tests (jsdom environment) +├── integration/ # Integration tests (real VS Code) +└── mocks/ # Shared test mocks ``` -There are also integration tests that run inside a real VS Code instance: +### Integration Tests + +Integration tests run inside a real VS Code instance: ```bash pnpm test:integration ``` +**Limitations:** + +- Must use Mocha (VS Code test runner requirement), not Vitest +- Cannot run while another VS Code instance is open (they share state) +- Requires closing VS Code or running in a clean environment +- Test files in `test/integration/` are compiled to `out/` before running + ## Development > [!IMPORTANT] diff --git a/eslint.config.mjs b/eslint.config.mjs index e48bab4f..6633ebd4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -123,7 +123,7 @@ export default defineConfig( // Test files - use test tsconfig and relax some rules { - files: ["test/**/*.ts", "**/*.test.ts", "**/*.spec.ts"], + files: ["test/**/*.{ts,tsx}", "**/*.test.{ts,tsx}", "**/*.spec.{ts,tsx}"], settings: { "import-x/resolver-next": [ createTypeScriptImportResolver({ project: "test/tsconfig.json" }), diff --git a/package.json b/package.json index 6d6a1fcb..b838d7c1 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,13 @@ "lint:fix": "pnpm lint --fix", "package": "vsce package --no-dependencies", "package:prerelease": "vsce package --pre-release --no-dependencies", - "pretest": "tsc -p test --noEmit && pnpm fmt:check && pnpm lint", - "test": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs", - "test:ci": "CI=true pnpm test", + "test": "CI=true pnpm test:extension && CI=true pnpm test:webview", + "test:ci": "pnpm test", + "test:extension": "ELECTRON_RUN_AS_NODE=1 electron node_modules/vitest/vitest.mjs --project extension", "test:integration": "tsc -p test --outDir out && node esbuild.mjs && vscode-test", + "test:webview": "vitest --project webview", "vscode:prepublish": "pnpm build:production", + "watch": "pnpm watch:all", "watch:all": "concurrently -n extension,webviews \"pnpm watch:extension\" \"pnpm watch:webviews\"", "watch:extension": "node esbuild.mjs --watch", "watch:webviews": "pnpm -r --filter \"./packages/*\" --parallel dev" @@ -463,17 +465,20 @@ "devDependencies": { "@eslint/js": "^9.39.2", "@eslint/markdown": "^7.5.1", + "@testing-library/react": "^16.3.2", "@tsconfig/node20": "^20.1.8", "@types/mocha": "^10.0.10", "@types/node": "^20", "@types/proper-lockfile": "^4.1.4", + "@types/react": "catalog:", + "@types/react-dom": "catalog:", "@types/semver": "^7.7.1", "@types/ua-parser-js": "0.7.39", "@types/vscode": "^1.95.0", "@types/ws": "^8.18.1", "@typescript-eslint/eslint-plugin": "^8.53.1", "@typescript-eslint/parser": "^8.53.1", - "@vitejs/plugin-react-swc": "^3.8.0", + "@vitejs/plugin-react-swc": "catalog:", "@vitest/coverage-v8": "^4.0.16", "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.5.2", @@ -492,13 +497,16 @@ "eslint-plugin-react": "^7.37.0", "eslint-plugin-react-hooks": "^5.0.0", "globals": "^17.0.0", + "jsdom": "^27.4.0", "jsonc-eslint-parser": "^2.4.2", "memfs": "^4.56.10", "prettier": "^3.7.4", - "typescript": "^5.9.3", + "react": "catalog:", + "react-dom": "catalog:", + "typescript": "catalog:", "typescript-eslint": "^8.53.1", "utf-8-validate": "^6.0.6", - "vite": "^6.0.0", + "vite": "catalog:", "vitest": "^4.0.16" }, "extensionPack": [ diff --git a/packages/tasks/package.json b/packages/tasks/package.json index 6e4f3c45..04163928 100644 --- a/packages/tasks/package.json +++ b/packages/tasks/package.json @@ -10,15 +10,15 @@ }, "dependencies": { "@repo/webview-shared": "workspace:*", - "@vscode-elements/react-elements": "^2.4.0", - "react": "^19.0.0", - "react-dom": "^19.0.0" + "@vscode-elements/react-elements": "catalog:", + "react": "catalog:", + "react-dom": "catalog:" }, "devDependencies": { - "@types/react": "^19.0.0", - "@types/react-dom": "^19.0.0", - "@vitejs/plugin-react-swc": "^3.8.0", - "typescript": "^5.7.3", - "vite": "^6.0.0" + "@types/react": "catalog:", + "@types/react-dom": "catalog:", + "@vitejs/plugin-react-swc": "catalog:", + "typescript": "catalog:", + "vite": "catalog:" } } diff --git a/packages/tasks/src/App.tsx b/packages/tasks/src/App.tsx index adb3efff..3e0f70b6 100644 --- a/packages/tasks/src/App.tsx +++ b/packages/tasks/src/App.tsx @@ -1,27 +1,31 @@ -import { postMessage, useMessage } from "@repo/webview-shared/react"; +import { logger } from "@repo/webview-shared/logger"; +import { useMessage } from "@repo/webview-shared/react"; import { VscodeButton, VscodeProgressRing, } from "@vscode-elements/react-elements"; -import { useCallback, useEffect, useState } from "react"; +import { useEffect, useState } from "react"; -import type { WebviewMessage } from "@repo/webview-shared"; +import { sendReady, sendRefresh } from "./messages"; + +import type { TasksExtensionMessage } from "@repo/webview-shared"; export default function App() { const [ready, setReady] = useState(false); - const handleMessage = useCallback((message: WebviewMessage) => { + useMessage((message) => { switch (message.type) { case "init": setReady(true); break; + case "error": + logger.error("Tasks panel error:", message.data); + break; } - }, []); - - useMessage(handleMessage); + }); useEffect(() => { - postMessage({ type: "ready" }); + sendReady(); }, []); if (!ready) { @@ -31,9 +35,7 @@ export default function App() { return (

Coder Tasks

- postMessage({ type: "refresh" })}> - Refresh - + Refresh
); } diff --git a/packages/tasks/src/index.tsx b/packages/tasks/src/index.tsx index 5bb4533c..c6dbba43 100644 --- a/packages/tasks/src/index.tsx +++ b/packages/tasks/src/index.tsx @@ -1,3 +1,4 @@ +import { ErrorBoundary } from "@repo/webview-shared/react"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; @@ -5,10 +6,16 @@ import App from "./App"; import "./index.css"; const root = document.getElementById("root"); -if (root) { - createRoot(root).render( - - - , +if (!root) { + throw new Error( + "Failed to find root element. The webview HTML must contain an element with id='root'.", ); } + +createRoot(root).render( + + + + + , +); diff --git a/packages/tasks/src/messages.ts b/packages/tasks/src/messages.ts new file mode 100644 index 00000000..86948886 --- /dev/null +++ b/packages/tasks/src/messages.ts @@ -0,0 +1,17 @@ +import { postMessage } from "@repo/webview-shared/api"; + +import type { TasksWebviewMessage } from "@repo/webview-shared"; + +function sendMessage(message: TasksWebviewMessage): void { + postMessage(message); +} + +/** Signal to the extension that the webview is ready */ +export function sendReady(): void { + sendMessage({ type: "ready" }); +} + +/** Request task refresh from the extension */ +export function sendRefresh(): void { + sendMessage({ type: "refresh" }); +} diff --git a/packages/webview-shared/extension.d.ts b/packages/webview-shared/extension.d.ts index 09081808..dedd225f 100644 --- a/packages/webview-shared/extension.d.ts +++ b/packages/webview-shared/extension.d.ts @@ -1,2 +1,6 @@ // Types exposed to the extension (react/ subpath is excluded). -export type { WebviewMessage } from "./src/index"; +export type { + TasksExtensionMessage, + TasksWebviewMessage, + WebviewMessage, +} from "./src/index"; diff --git a/packages/webview-shared/package.json b/packages/webview-shared/package.json index 5361e644..472c310d 100644 --- a/packages/webview-shared/package.json +++ b/packages/webview-shared/package.json @@ -9,23 +9,36 @@ "types": "./src/index.ts", "default": "./src/index.ts" }, + "./api": { + "types": "./src/api.ts", + "default": "./src/api.ts" + }, + "./logger": { + "types": "./src/logger.ts", + "default": "./src/logger.ts" + }, "./react": { "types": "./src/react/index.ts", "default": "./src/react/index.ts" } }, "peerDependencies": { - "react": "^19.0.0" + "@vscode-elements/react-elements": "catalog:", + "react": "catalog:" }, "peerDependenciesMeta": { + "@vscode-elements/react-elements": { + "optional": true + }, "react": { "optional": true } }, "devDependencies": { - "@types/react": "^19.0.0", + "@types/react": "catalog:", "@types/vscode-webview": "^1.57.5", - "react": "^19.0.0", - "typescript": "^5.7.3" + "@vscode-elements/react-elements": "catalog:", + "react": "catalog:", + "typescript": "catalog:" } } diff --git a/packages/webview-shared/src/index.ts b/packages/webview-shared/src/index.ts index 32b0cbe4..6a1a1f52 100644 --- a/packages/webview-shared/src/index.ts +++ b/packages/webview-shared/src/index.ts @@ -3,3 +3,11 @@ export interface WebviewMessage { type: string; data?: T; } + +/** Messages sent from the extension to the Tasks webview */ +export type TasksExtensionMessage = + | { type: "init" } + | { type: "error"; data: string }; + +/** Messages sent from the Tasks webview to the extension */ +export type TasksWebviewMessage = { type: "ready" } | { type: "refresh" }; diff --git a/packages/webview-shared/src/logger.ts b/packages/webview-shared/src/logger.ts new file mode 100644 index 00000000..3d2ed4a6 --- /dev/null +++ b/packages/webview-shared/src/logger.ts @@ -0,0 +1,40 @@ +/** + * Logger interface that can be customized to send logs elsewhere (e.g., to the extension). + */ +export interface Logger { + debug: (...args: unknown[]) => void; + info: (...args: unknown[]) => void; + warn: (...args: unknown[]) => void; + error: (...args: unknown[]) => void; +} + +/** + * Default logger that writes to the browser console. + * In webviews, this appears in the DevTools console. + */ +const consoleLogger: Logger = { + /* eslint-disable no-console */ + debug: (...args) => console.debug("[webview]", ...args), + info: (...args) => console.info("[webview]", ...args), + warn: (...args) => console.warn("[webview]", ...args), + error: (...args) => console.error("[webview]", ...args), + /* eslint-enable no-console */ +}; + +let currentLogger: Logger = consoleLogger; + +/** + * Set a custom logger implementation. + * Call this early in your webview's initialization to redirect logs. + */ +export function setLogger(logger: Logger): void { + currentLogger = logger; +} + +// Convenience exports for direct use +export const logger = { + debug: (...args: unknown[]) => currentLogger.debug(...args), + info: (...args: unknown[]) => currentLogger.info(...args), + warn: (...args: unknown[]) => currentLogger.warn(...args), + error: (...args: unknown[]) => currentLogger.error(...args), +}; diff --git a/packages/webview-shared/src/react/ErrorBoundary.tsx b/packages/webview-shared/src/react/ErrorBoundary.tsx new file mode 100644 index 00000000..c1ff8d8e --- /dev/null +++ b/packages/webview-shared/src/react/ErrorBoundary.tsx @@ -0,0 +1,47 @@ +import { Component, type ReactNode } from "react"; + +import { logger } from "../logger"; + +interface ErrorBoundaryProps { + children: ReactNode; + fallback?: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +/** + * Catches errors in child components and displays a fallback UI. + */ +export class ErrorBoundary extends Component< + ErrorBoundaryProps, + ErrorBoundaryState +> { + override state: ErrorBoundaryState = { hasError: false, error: null }; + + static getDerivedStateFromError(error: Error): ErrorBoundaryState { + return { hasError: true, error }; + } + + override componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void { + logger.error("Webview error:", error, errorInfo); + } + + override render(): ReactNode { + if (this.state.hasError) { + return ( + this.props.fallback ?? ( +
+ Something went wrong +
+							{this.state.error?.message}
+						
+
+ ) + ); + } + return this.props.children; + } +} diff --git a/packages/webview-shared/src/react/hooks.ts b/packages/webview-shared/src/react/hooks.ts index daf3e338..4c9cc02b 100644 --- a/packages/webview-shared/src/react/hooks.ts +++ b/packages/webview-shared/src/react/hooks.ts @@ -1,24 +1,21 @@ -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useEffectEvent, useState } from "react"; import { getState, setState } from "../api"; -import type { WebviewMessage } from "../index"; - /** - * Hook to listen for messages from the extension + * Listen for messages from the extension. No need to memoize the handler. */ -export function useMessage( - handler: (message: WebviewMessage) => void, -): void { +export function useMessage(handler: (message: T) => void): void { + const onMessage = useEffectEvent((event: MessageEvent): void => { + handler(event.data); + }); + useEffect((): (() => void) => { - const listener = (event: MessageEvent>): void => { - handler(event.data); - }; - window.addEventListener("message", listener); + window.addEventListener("message", onMessage); return (): void => { - window.removeEventListener("message", listener); + window.removeEventListener("message", onMessage); }; - }, [handler]); + }, []); } /** diff --git a/packages/webview-shared/src/react/index.ts b/packages/webview-shared/src/react/index.ts index 9cbc5af4..cb43953d 100644 --- a/packages/webview-shared/src/react/index.ts +++ b/packages/webview-shared/src/react/index.ts @@ -1,2 +1,3 @@ export { postMessage, getState, setState } from "../api"; +export { ErrorBoundary } from "./ErrorBoundary"; export { useMessage, useVsCodeState } from "./hooks"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7287fff4..992f2dda 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,33 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@types/react': + specifier: ^19.2.10 + version: 19.2.10 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3 + '@vitejs/plugin-react-swc': + specifier: ^4.2.2 + version: 4.2.2 + '@vscode-elements/react-elements': + specifier: ^2.4.0 + version: 2.4.0 + react: + specifier: ^19.2.4 + version: 19.2.4 + react-dom: + specifier: ^19.2.4 + version: 19.2.4 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1 + overrides: semver: 7.7.3 trim: 0.0.3 @@ -62,6 +89,9 @@ importers: '@eslint/markdown': specifier: ^7.5.1 version: 7.5.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@tsconfig/node20': specifier: ^20.1.8 version: 20.1.8 @@ -74,6 +104,12 @@ importers: '@types/proper-lockfile': specifier: ^4.1.4 version: 4.1.4 + '@types/react': + specifier: 'catalog:' + version: 19.2.10 + '@types/react-dom': + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.10) '@types/semver': specifier: ^7.7.1 version: 7.7.1 @@ -93,11 +129,11 @@ importers: specifier: ^8.53.1 version: 8.53.1(eslint@9.39.2)(typescript@5.9.3) '@vitejs/plugin-react-swc': - specifier: ^3.8.0 - version: 3.11.0(vite@6.4.1(@types/node@20.19.30)) + specifier: 'catalog:' + version: 4.2.2(vite@7.3.1(@types/node@20.19.30)) '@vitest/coverage-v8': specifier: ^4.0.16 - version: 4.0.16(vitest@4.0.16(@types/node@20.19.30)) + version: 4.0.16(vitest@4.0.16(@types/node@20.19.30)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6))) '@vscode/test-cli': specifier: ^0.0.12 version: 0.0.12 @@ -112,7 +148,7 @@ importers: version: 4.1.0 coder: specifier: github:coder/coder#main - version: https://codeload.github.com/coder/coder/tar.gz/c3ea5441629008cb5fa93b62d0c1124f6d2d3014 + version: https://codeload.github.com/coder/coder/tar.gz/37aecda165d8697766fd42399416afce6ad41dcb concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -149,6 +185,9 @@ importers: globals: specifier: ^17.0.0 version: 17.0.0 + jsdom: + specifier: ^27.4.0 + version: 27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) jsonc-eslint-parser: specifier: ^2.4.2 version: 2.4.2 @@ -158,8 +197,14 @@ importers: prettier: specifier: ^3.7.4 version: 3.7.4 + react: + specifier: 'catalog:' + version: 19.2.4 + react-dom: + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) typescript: - specifier: ^5.9.3 + specifier: 'catalog:' version: 5.9.3 typescript-eslint: specifier: ^8.53.1 @@ -168,11 +213,11 @@ importers: specifier: ^6.0.6 version: 6.0.6 vite: - specifier: ^6.0.0 - version: 6.4.1(@types/node@20.19.30) + specifier: 'catalog:' + version: 7.3.1(@types/node@20.19.30) vitest: specifier: ^4.0.16 - version: 4.0.16(@types/node@20.19.30) + version: 4.0.16(@types/node@20.19.30)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) packages/tasks: dependencies: @@ -180,44 +225,47 @@ importers: specifier: workspace:* version: link:../webview-shared '@vscode-elements/react-elements': - specifier: ^2.4.0 - version: 2.4.0(@types/react@19.2.9)(@vscode/codicons@0.0.44)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + specifier: 'catalog:' + version: 2.4.0(@types/react@19.2.10)(@vscode/codicons@0.0.44)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: - specifier: ^19.0.0 - version: 19.2.3 + specifier: 'catalog:' + version: 19.2.4 react-dom: - specifier: ^19.0.0 - version: 19.2.3(react@19.2.3) + specifier: 'catalog:' + version: 19.2.4(react@19.2.4) devDependencies: '@types/react': - specifier: ^19.0.0 - version: 19.2.9 + specifier: 'catalog:' + version: 19.2.10 '@types/react-dom': - specifier: ^19.0.0 - version: 19.2.3(@types/react@19.2.9) + specifier: 'catalog:' + version: 19.2.3(@types/react@19.2.10) '@vitejs/plugin-react-swc': - specifier: ^3.8.0 - version: 3.11.0(vite@6.4.1(@types/node@24.10.9)) + specifier: 'catalog:' + version: 4.2.2(vite@7.3.1(@types/node@24.10.9)) typescript: - specifier: ^5.7.3 + specifier: 'catalog:' version: 5.9.3 vite: - specifier: ^6.0.0 - version: 6.4.1(@types/node@24.10.9) + specifier: 'catalog:' + version: 7.3.1(@types/node@24.10.9) packages/webview-shared: devDependencies: '@types/react': - specifier: ^19.0.0 - version: 19.2.9 + specifier: 'catalog:' + version: 19.2.10 '@types/vscode-webview': specifier: ^1.57.5 version: 1.57.5 + '@vscode-elements/react-elements': + specifier: 'catalog:' + version: 2.4.0(@types/react@19.2.10)(@vscode/codicons@0.0.44)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react: - specifier: ^19.0.0 - version: 19.2.3 + specifier: 'catalog:' + version: 19.2.4 typescript: - specifier: ^5.7.3 + specifier: 'catalog:' version: 5.9.3 packages: @@ -226,9 +274,21 @@ packages: resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} engines: {node: '>=0.10.0'} + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + '@altano/repository-tools@2.0.1': resolution: {integrity: sha512-YE/52CkFtb+YtHPgbWPai7oo5N9AKnMuP5LM+i2AG7G1H2jdYBCO1iDnkDE3dZ3C1MIgckaF+d5PNRulgt0bdw==} + '@asamuzakjp/css-color@4.1.1': + resolution: {integrity: sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==} + + '@asamuzakjp/dom-selector@6.7.6': + resolution: {integrity: sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@azu/format-text@1.0.2': resolution: {integrity: sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==} @@ -283,6 +343,10 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} + '@babel/code-frame@7.28.6': + resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -296,6 +360,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + '@babel/types@7.28.5': resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} @@ -304,6 +372,37 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.26': + resolution: {integrity: sha512-6boXK0KkzT5u5xOgF6TKB+CLq9SOpEGmkZw0g5n9/7yg85wab3UzSxB8TxhLJ31L4SGJ6BCFRw/iftTha1CJXA==} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@electron/get@2.0.3': resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} engines: {node: '>=12'} @@ -317,312 +416,156 @@ packages: '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.25.12': - resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.2': resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.12': - resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.27.2': resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.12': - resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.27.2': resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.12': - resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.27.2': resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.12': - resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.27.2': resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.12': - resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.27.2': resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.12': - resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.27.2': resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.12': - resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.12': - resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.27.2': resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.12': - resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.27.2': resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.12': - resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.27.2': resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.12': - resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.27.2': resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.12': - resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.27.2': resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.12': - resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.27.2': resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.12': - resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.27.2': resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.12': - resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.27.2': resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.12': - resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.27.2': resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.12': - resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.27.2': resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.12': - resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.12': - resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.27.2': resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.12': - resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.12': - resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.27.2': resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.12': - resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.27.2': resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.12': - resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.27.2': resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.12': - resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.27.2': resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.12': - resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.27.2': resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} @@ -671,6 +614,15 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@exodus/bytes@1.10.0': + resolution: {integrity: sha512-tf8YdcbirXdPnJ+Nd4UN1EXnz+IP2DI45YVEr3vvzcVTOyrApkmIB4zvOQVd3XPr7RXnfBtAx+PXImXOIU0Ajg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -900,8 +852,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} '@rollup/rollup-android-arm-eabi@4.55.1': resolution: {integrity: sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==} @@ -1163,6 +1115,25 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@textlint/ast-node-types@15.2.1': resolution: {integrity: sha512-20fEcLPsXg81yWpApv4FQxrZmlFF/Ta7/kz1HGIL+pJo5cSTmkc+eCki3GpOPZIoZk0tbJU8hrlwUb91F+3SNQ==} @@ -1187,6 +1158,9 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/cacheable-request@6.0.3': resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} @@ -1240,8 +1214,8 @@ packages: peerDependencies: '@types/react': ^19.2.0 - '@types/react@19.2.9': - resolution: {integrity: sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==} + '@types/react@19.2.10': + resolution: {integrity: sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==} '@types/responselike@1.0.3': resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} @@ -1438,8 +1412,9 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-react-swc@3.11.0': - resolution: {integrity: sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==} + '@vitejs/plugin-react-swc@4.2.2': + resolution: {integrity: sha512-x+rE6tsxq/gxrEJN3Nv3dIV60lFflPj94c90b+NNo6n1QV1QQUTLoL0MpaOVasUZ0zqVBn7ead1B5ecx1JAGfA==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: vite: ^4 || ^5 || ^6 || ^7 @@ -1597,6 +1572,10 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -1615,6 +1594,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} @@ -1685,6 +1667,9 @@ packages: resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1836,8 +1821,8 @@ packages: resolution: {integrity: sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==} engines: {node: '>=16'} - coder@https://codeload.github.com/coder/coder/tar.gz/c3ea5441629008cb5fa93b62d0c1124f6d2d3014: - resolution: {tarball: https://codeload.github.com/coder/coder/tar.gz/c3ea5441629008cb5fa93b62d0c1124f6d2d3014} + coder@https://codeload.github.com/coder/coder/tar.gz/37aecda165d8697766fd42399416afce6ad41dcb: + resolution: {tarball: https://codeload.github.com/coder/coder/tar.gz/37aecda165d8697766fd42399416afce6ad41dcb} version: 0.0.0 color-convert@2.0.1: @@ -1880,10 +1865,18 @@ packages: css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} + cssstyle@5.3.7: + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -1891,6 +1884,10 @@ packages: resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} engines: {node: '>= 14'} + data-urls@6.0.1: + resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} + engines: {node: '>=20'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1922,6 +1919,9 @@ packages: resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} engines: {node: '>=10'} + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-named-character-reference@1.2.0: resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} @@ -1998,6 +1998,9 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -2053,6 +2056,10 @@ packages: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} @@ -2099,11 +2106,6 @@ packages: es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - esbuild@0.25.12: - resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} - engines: {node: '>=18'} - hasBin: true - esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -2524,6 +2526,10 @@ packages: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -2690,6 +2696,9 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2800,6 +2809,15 @@ packages: jsbn@1.1.0: resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + jsdom@27.4.0: + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -2943,6 +2961,10 @@ packages: resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} engines: {node: 20 || >=22} + lru-cache@11.2.5: + resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} + engines: {node: 20 || >=22} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -2951,6 +2973,10 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -3012,6 +3038,9 @@ packages: mdast-util-to-string@4.0.0: resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} @@ -3332,6 +3361,9 @@ packages: parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3405,6 +3437,10 @@ packages: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -3439,6 +3475,10 @@ packages: resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} engines: {node: '>=6'} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + pvtsutils@1.3.6: resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} @@ -3467,16 +3507,19 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@19.2.3: - resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: - react: ^19.2.3 + react: ^19.2.4 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react@19.2.3: - resolution: {integrity: sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==} + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} engines: {node: '>=0.10.0'} read-pkg@9.0.1: @@ -3590,6 +3633,10 @@ packages: sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -3828,6 +3875,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + table@6.9.0: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} @@ -3879,6 +3929,13 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} + + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} + hasBin: true + tmp@0.2.5: resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} engines: {node: '>=14.14'} @@ -3887,6 +3944,14 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + tree-dump@1.1.0: resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} engines: {node: '>=10.0'} @@ -4046,19 +4111,19 @@ packages: resolution: {integrity: sha512-gjb0ARm9qlcBAonU4zPwkl9ecKkas+tC2CGwFfptTCWWIVTWY1YUbT2zZKsOAF1jR/tNxxyLwwG0cb42XlYcTg==} engines: {node: '>=4'} - vite@6.4.1: - resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 jiti: '>=1.21.0' - less: '*' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -4120,6 +4185,26 @@ packages: jsdom: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -4180,6 +4265,10 @@ packages: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + xml2js@0.5.0: resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} engines: {node: '>=4.0.0'} @@ -4188,6 +4277,9 @@ packages: resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} engines: {node: '>=4.0'} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -4235,8 +4327,28 @@ snapshots: '@aashutoshrathi/word-wrap@1.2.6': {} + '@acemir/cssom@0.9.31': {} + '@altano/repository-tools@2.0.1': {} + '@asamuzakjp/css-color@4.1.1': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 11.2.5 + + '@asamuzakjp/dom-selector@6.7.6': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.5 + + '@asamuzakjp/nwsapi@2.3.9': {} + '@azu/format-text@1.0.2': {} '@azu/style-format@1.0.1': @@ -4332,6 +4444,12 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@7.28.6': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} @@ -4340,6 +4458,8 @@ snapshots: dependencies: '@babel/types': 7.28.5 + '@babel/runtime@7.28.6': {} + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -4347,6 +4467,28 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.26': {} + + '@csstools/css-tokenizer@3.0.4': {} + '@electron/get@2.0.3': dependencies: debug: 4.4.3(supports-color@8.1.1) @@ -4377,159 +4519,81 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.25.12': - optional: true - '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/android-arm64@0.25.12': - optional: true - '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/android-arm@0.25.12': - optional: true - '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/android-x64@0.25.12': - optional: true - '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.25.12': - optional: true - '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/darwin-x64@0.25.12': - optional: true - '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.25.12': - optional: true - '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/freebsd-x64@0.25.12': - optional: true - '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/linux-arm64@0.25.12': - optional: true - '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/linux-arm@0.25.12': - optional: true - '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/linux-ia32@0.25.12': - optional: true - '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/linux-loong64@0.25.12': - optional: true - '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/linux-mips64el@0.25.12': - optional: true - '@esbuild/linux-mips64el@0.27.2': optional: true - '@esbuild/linux-ppc64@0.25.12': - optional: true - '@esbuild/linux-ppc64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.25.12': - optional: true - '@esbuild/linux-riscv64@0.27.2': optional: true - '@esbuild/linux-s390x@0.25.12': - optional: true - '@esbuild/linux-s390x@0.27.2': optional: true - '@esbuild/linux-x64@0.25.12': - optional: true - '@esbuild/linux-x64@0.27.2': optional: true - '@esbuild/netbsd-arm64@0.25.12': - optional: true - '@esbuild/netbsd-arm64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.25.12': - optional: true - '@esbuild/netbsd-x64@0.27.2': optional: true - '@esbuild/openbsd-arm64@0.25.12': - optional: true - '@esbuild/openbsd-arm64@0.27.2': optional: true - '@esbuild/openbsd-x64@0.25.12': - optional: true - '@esbuild/openbsd-x64@0.27.2': optional: true - '@esbuild/openharmony-arm64@0.25.12': - optional: true - '@esbuild/openharmony-arm64@0.27.2': optional: true - '@esbuild/sunos-x64@0.25.12': - optional: true - '@esbuild/sunos-x64@0.27.2': optional: true - '@esbuild/win32-arm64@0.25.12': - optional: true - '@esbuild/win32-arm64@0.27.2': optional: true - '@esbuild/win32-ia32@0.25.12': - optional: true - '@esbuild/win32-ia32@0.27.2': optional: true - '@esbuild/win32-x64@0.25.12': - optional: true - '@esbuild/win32-x64@0.27.2': optional: true @@ -4593,6 +4657,8 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@exodus/bytes@1.10.0': {} + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -4763,9 +4829,9 @@ snapshots: dependencies: '@lit/reactive-element': 2.1.2 - '@lit/react@1.0.8(@types/react@19.2.9)': + '@lit/react@1.0.8(@types/react@19.2.10)': dependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.10 '@lit/reactive-element@2.1.2': dependencies: @@ -4883,7 +4949,7 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-beta.47': {} '@rollup/rollup-android-arm-eabi@4.55.1': optional: true @@ -5096,6 +5162,27 @@ snapshots: dependencies: defer-to-connect: 2.0.1 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.28.6 + '@babel/runtime': 7.28.6 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.10))(@types/react@19.2.10)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@testing-library/dom': 10.4.1 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.10 + '@types/react-dom': 19.2.3(@types/react@19.2.10) + '@textlint/ast-node-types@15.2.1': {} '@textlint/linter-formatter@15.2.1': @@ -5134,6 +5221,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 @@ -5185,11 +5274,11 @@ snapshots: dependencies: '@types/retry': 0.12.5 - '@types/react-dom@19.2.3(@types/react@19.2.9)': + '@types/react-dom@19.2.3(@types/react@19.2.10)': dependencies: - '@types/react': 19.2.9 + '@types/react': 19.2.10 - '@types/react@19.2.9': + '@types/react@19.2.10': dependencies: csstype: 3.2.3 @@ -5382,23 +5471,23 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react-swc@3.11.0(vite@6.4.1(@types/node@20.19.30))': + '@vitejs/plugin-react-swc@4.2.2(vite@7.3.1(@types/node@20.19.30))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.27 + '@rolldown/pluginutils': 1.0.0-beta.47 '@swc/core': 1.15.10 - vite: 6.4.1(@types/node@20.19.30) + vite: 7.3.1(@types/node@20.19.30) transitivePeerDependencies: - '@swc/helpers' - '@vitejs/plugin-react-swc@3.11.0(vite@6.4.1(@types/node@24.10.9))': + '@vitejs/plugin-react-swc@4.2.2(vite@7.3.1(@types/node@24.10.9))': dependencies: - '@rolldown/pluginutils': 1.0.0-beta.27 + '@rolldown/pluginutils': 1.0.0-beta.47 '@swc/core': 1.15.10 - vite: 6.4.1(@types/node@24.10.9) + vite: 7.3.1(@types/node@24.10.9) transitivePeerDependencies: - '@swc/helpers' - '@vitest/coverage-v8@4.0.16(vitest@4.0.16(@types/node@20.19.30))': + '@vitest/coverage-v8@4.0.16(vitest@4.0.16(@types/node@20.19.30)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.16 @@ -5411,7 +5500,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.16(@types/node@20.19.30) + vitest: 4.0.16(@types/node@20.19.30)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) transitivePeerDependencies: - supports-color @@ -5424,13 +5513,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.16(vite@6.4.1(@types/node@20.19.30))': + '@vitest/mocker@4.0.16(vite@7.3.1(@types/node@20.19.30))': dependencies: '@vitest/spy': 4.0.16 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@20.19.30) + vite: 7.3.1(@types/node@20.19.30) '@vitest/pretty-format@4.0.16': dependencies: @@ -5460,12 +5549,12 @@ snapshots: '@vscode/codicons': 0.0.44 lit: 3.3.2 - '@vscode-elements/react-elements@2.4.0(@types/react@19.2.9)(@vscode/codicons@0.0.44)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)': + '@vscode-elements/react-elements@2.4.0(@types/react@19.2.10)(@vscode/codicons@0.0.44)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': dependencies: - '@lit/react': 1.0.8(@types/react@19.2.9) + '@lit/react': 1.0.8(@types/react@19.2.10) '@vscode-elements/elements': 2.4.0(@vscode/codicons@0.0.44) - react: 19.2.3 - react-dom: 19.2.3(react@19.2.3) + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) transitivePeerDependencies: - '@types/react' - '@vscode/codicons' @@ -5607,6 +5696,8 @@ snapshots: dependencies: color-convert: 2.0.1 + ansi-styles@5.2.0: {} + ansi-styles@6.2.1: {} ansi-styles@6.2.3: {} @@ -5622,6 +5713,10 @@ snapshots: argparse@2.0.1: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 @@ -5725,6 +5820,10 @@ snapshots: basic-ftp@5.0.5: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + binary-extensions@2.3.0: {} binaryextensions@6.11.0: @@ -5902,7 +6001,7 @@ snapshots: cockatiel@3.2.1: {} - coder@https://codeload.github.com/coder/coder/tar.gz/c3ea5441629008cb5fa93b62d0c1124f6d2d3014: {} + coder@https://codeload.github.com/coder/coder/tar.gz/37aecda165d8697766fd42399416afce6ad41dcb: {} color-convert@2.0.1: dependencies: @@ -5947,12 +6046,29 @@ snapshots: domutils: 3.0.1 nth-check: 2.1.1 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + css-what@6.1.0: {} + cssstyle@5.3.7: + dependencies: + '@asamuzakjp/css-color': 4.1.1 + '@csstools/css-syntax-patches-for-csstree': 1.0.26 + css-tree: 3.1.0 + lru-cache: 11.2.5 + csstype@3.2.3: {} data-uri-to-buffer@6.0.2: {} + data-urls@6.0.1: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 15.1.0 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -5983,6 +6099,8 @@ snapshots: decamelize@4.0.0: {} + decimal.js@10.6.0: {} + decode-named-character-reference@1.2.0: dependencies: character-entities: 2.0.2 @@ -6049,6 +6167,8 @@ snapshots: dependencies: esutils: 2.0.3 + dom-accessibility-api@0.5.16: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -6113,6 +6233,8 @@ snapshots: entities@4.4.0: {} + entities@6.0.1: {} + env-paths@2.2.1: {} environment@1.1.0: {} @@ -6223,35 +6345,6 @@ snapshots: es6-error@4.1.1: optional: true - esbuild@0.25.12: - optionalDependencies: - '@esbuild/aix-ppc64': 0.25.12 - '@esbuild/android-arm': 0.25.12 - '@esbuild/android-arm64': 0.25.12 - '@esbuild/android-x64': 0.25.12 - '@esbuild/darwin-arm64': 0.25.12 - '@esbuild/darwin-x64': 0.25.12 - '@esbuild/freebsd-arm64': 0.25.12 - '@esbuild/freebsd-x64': 0.25.12 - '@esbuild/linux-arm': 0.25.12 - '@esbuild/linux-arm64': 0.25.12 - '@esbuild/linux-ia32': 0.25.12 - '@esbuild/linux-loong64': 0.25.12 - '@esbuild/linux-mips64el': 0.25.12 - '@esbuild/linux-ppc64': 0.25.12 - '@esbuild/linux-riscv64': 0.25.12 - '@esbuild/linux-s390x': 0.25.12 - '@esbuild/linux-x64': 0.25.12 - '@esbuild/netbsd-arm64': 0.25.12 - '@esbuild/netbsd-x64': 0.25.12 - '@esbuild/openbsd-arm64': 0.25.12 - '@esbuild/openbsd-x64': 0.25.12 - '@esbuild/openharmony-arm64': 0.25.12 - '@esbuild/sunos-x64': 0.25.12 - '@esbuild/win32-arm64': 0.25.12 - '@esbuild/win32-ia32': 0.25.12 - '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -6768,6 +6861,12 @@ snapshots: dependencies: lru-cache: 10.4.3 + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.10.0 + transitivePeerDependencies: + - '@noble/hashes' + html-escaper@2.0.2: {} htmlparser2@8.0.1: @@ -6927,6 +7026,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -7043,6 +7144,34 @@ snapshots: jsbn@1.1.0: {} + jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + dependencies: + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.7.6 + '@exodus/bytes': 1.10.0 + cssstyle: 5.3.7 + data-urls: 6.0.1 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + - bufferutil + - supports-color + - utf-8-validate + json-buffer@3.0.1: {} json-schema-traverse@0.4.1: {} @@ -7202,12 +7331,16 @@ snapshots: lru-cache@11.1.0: {} + lru-cache@11.2.5: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 lru-cache@7.18.3: {} + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -7353,6 +7486,8 @@ snapshots: dependencies: '@types/mdast': 4.0.4 + mdn-data@2.12.2: {} + mdurl@2.0.0: {} memfs@4.56.10(tslib@2.8.1): @@ -7825,6 +7960,10 @@ snapshots: dependencies: entities: 4.4.0 + parse5@8.0.0: + dependencies: + entities: 6.0.1 + path-exists@4.0.0: {} path-key@3.1.1: {} @@ -7887,6 +8026,12 @@ snapshots: pretty-bytes@7.1.0: {} + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + process-nextick-args@2.0.1: {} progress@2.0.3: {} @@ -7933,6 +8078,8 @@ snapshots: punycode@2.3.0: {} + punycode@2.3.1: {} + pvtsutils@1.3.6: dependencies: tslib: 2.8.1 @@ -7968,14 +8115,16 @@ snapshots: strip-json-comments: 2.0.1 optional: true - react-dom@19.2.3(react@19.2.3): + react-dom@19.2.4(react@19.2.4): dependencies: - react: 19.2.3 + react: 19.2.4 scheduler: 0.27.0 react-is@16.13.1: {} - react@19.2.3: {} + react-is@17.0.2: {} + + react@19.2.4: {} read-pkg@9.0.1: dependencies: @@ -8139,6 +8288,10 @@ snapshots: sax@1.2.4: {} + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + scheduler@0.27.0: {} secretlint@10.2.2: @@ -8429,6 +8582,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + table@6.9.0: dependencies: ajv: 8.17.1 @@ -8488,12 +8643,26 @@ snapshots: tinyrainbow@3.0.3: {} + tldts-core@7.0.19: {} + + tldts@7.0.19: + dependencies: + tldts-core: 7.0.19 + tmp@0.2.5: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.19 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + tree-dump@1.1.0(tslib@2.8.1): dependencies: tslib: 2.8.1 @@ -8677,9 +8846,9 @@ snapshots: version-range@4.14.0: {} - vite@6.4.1(@types/node@20.19.30): + vite@7.3.1(@types/node@20.19.30): dependencies: - esbuild: 0.25.12 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -8689,9 +8858,9 @@ snapshots: '@types/node': 20.19.30 fsevents: 2.3.3 - vite@6.4.1(@types/node@24.10.9): + vite@7.3.1(@types/node@24.10.9): dependencies: - esbuild: 0.25.12 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -8701,10 +8870,10 @@ snapshots: '@types/node': 24.10.9 fsevents: 2.3.3 - vitest@4.0.16(@types/node@20.19.30): + vitest@4.0.16(@types/node@20.19.30)(jsdom@27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)): dependencies: '@vitest/expect': 4.0.16 - '@vitest/mocker': 4.0.16(vite@6.4.1(@types/node@20.19.30)) + '@vitest/mocker': 4.0.16(vite@7.3.1(@types/node@20.19.30)) '@vitest/pretty-format': 4.0.16 '@vitest/runner': 4.0.16 '@vitest/snapshot': 4.0.16 @@ -8721,10 +8890,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 6.4.1(@types/node@20.19.30) + vite: 7.3.1(@types/node@20.19.30) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.19.30 + jsdom: 27.4.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) transitivePeerDependencies: - jiti - less @@ -8738,6 +8908,21 @@ snapshots: - tsx - yaml + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 @@ -8819,6 +9004,8 @@ snapshots: dependencies: is-wsl: 3.1.0 + xml-name-validator@5.0.0: {} + xml2js@0.5.0: dependencies: sax: 1.2.4 @@ -8826,6 +9013,8 @@ snapshots: xmlbuilder@11.0.1: {} + xmlchars@2.2.0: {} + y18n@5.0.8: {} yallist@4.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0fa2ada2..c209e1e1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,20 @@ packages: - packages/* +# Enforce all shared deps come from catalog to prevent version drift +catalogMode: strict + +# Shared dependency versions (use "catalog:" in package.json to reference) +catalog: + typescript: ^5.9.3 + vite: ^7.3.1 + "@vitejs/plugin-react-swc": ^4.2.2 + react: ^19.2.4 + react-dom: ^19.2.4 + "@types/react": ^19.2.10 + "@types/react-dom": ^19.2.3 + "@vscode-elements/react-elements": ^2.4.0 + # Native modules allowed to run install scripts (pnpm blocks by default for security) onlyBuiltDependencies: - "@swc/core" # vite diff --git a/src/webviews/tasks/TasksPanel.ts b/src/webviews/tasks/TasksPanel.ts index e83217eb..9b071604 100644 --- a/src/webviews/tasks/TasksPanel.ts +++ b/src/webviews/tasks/TasksPanel.ts @@ -2,7 +2,10 @@ import * as vscode from "vscode"; import { getWebviewHtml } from "../util"; -import type { WebviewMessage } from "@repo/webview-shared"; +import type { + TasksExtensionMessage, + TasksWebviewMessage, +} from "@repo/webview-shared"; export class TasksPanel implements vscode.WebviewViewProvider { public static readonly viewType = "coder.tasksPanel"; @@ -32,9 +35,11 @@ export class TasksPanel implements vscode.WebviewViewProvider { }); this.disposables = []; this.disposables.push( - webviewView.webview.onDidReceiveMessage((message: WebviewMessage) => { - this.handleMessage(message); - }), + webviewView.webview.onDidReceiveMessage( + (message: TasksWebviewMessage) => { + this.handleMessage(message); + }, + ), ); webviewView.webview.html = getWebviewHtml( @@ -52,7 +57,7 @@ export class TasksPanel implements vscode.WebviewViewProvider { }); } - private handleMessage(message: WebviewMessage): void { + private handleMessage(message: TasksWebviewMessage): void { switch (message.type) { case "ready": this.sendMessage({ type: "init" }); @@ -63,7 +68,7 @@ export class TasksPanel implements vscode.WebviewViewProvider { } } - private sendMessage(message: WebviewMessage): void { + private sendMessage(message: TasksExtensionMessage): void { void this.view?.webview.postMessage(message); } } diff --git a/test/tsconfig.json b/test/tsconfig.json index 3ce57336..32c1e314 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -2,9 +2,12 @@ "extends": "../tsconfig.json", "compilerOptions": { "baseUrl": "..", + "jsx": "react-jsx", + // Both mappings needed: exact match for root, wildcard for subpaths "paths": { "@/*": ["src/*"], - "@repo/webview-shared": ["packages/webview-shared/extension.d.ts"] + "@repo/webview-shared": ["packages/webview-shared/src/index.ts"], + "@repo/webview-shared/*": ["packages/webview-shared/src/*"] } }, "include": [".", "../src"] diff --git a/test/webview/shared/ErrorBoundary.test.tsx b/test/webview/shared/ErrorBoundary.test.tsx new file mode 100644 index 00000000..0a1733e6 --- /dev/null +++ b/test/webview/shared/ErrorBoundary.test.tsx @@ -0,0 +1,76 @@ +import { render, screen } from "@testing-library/react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +import { logger } from "@repo/webview-shared/logger"; +import { ErrorBoundary } from "@repo/webview-shared/react/ErrorBoundary"; + +vi.mock("@repo/webview-shared/logger", () => ({ + logger: { error: vi.fn() }, +})); + +const TEST_ERROR = "TEST_ERROR_BOUNDARY_ERROR"; + +function ThrowingComponent(): never { + throw new Error(TEST_ERROR); +} + +describe("ErrorBoundary", () => { + beforeEach(() => { + vi.clearAllMocks(); + // Suppress only our test error, fail on unexpected errors + vi.spyOn(globalThis.console, "error").mockImplementation( + (...args: unknown[]) => { + const str = args.map(String).join(" "); + if (str.includes(TEST_ERROR)) { + return; + } + throw new Error(`Unexpected console.error: ${str}`); + }, + ); + }); + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("renders children when no error", () => { + render( + +
Hello World
+
, + ); + expect(screen.getByText("Hello World")).toBeTruthy(); + }); + + it("renders default fallback on error", () => { + render( + + + , + ); + expect(screen.getByText("Something went wrong")).toBeTruthy(); + expect(screen.getByText(TEST_ERROR)).toBeTruthy(); + }); + + it("renders custom fallback on error", () => { + render( + Custom error UI}> + + , + ); + expect(screen.getByText("Custom error UI")).toBeTruthy(); + expect(screen.queryByText("Something went wrong")).toBeNull(); + }); + + it("logs error via componentDidCatch", () => { + render( + + + , + ); + expect(logger.error).toHaveBeenCalledWith( + "Webview error:", + expect.any(Error), + expect.objectContaining({ componentStack: expect.any(String) }), + ); + }); +}); diff --git a/test/webview/shared/api.test.ts b/test/webview/shared/api.test.ts new file mode 100644 index 00000000..fe8c05b8 --- /dev/null +++ b/test/webview/shared/api.test.ts @@ -0,0 +1,59 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { postMessage, getState, setState } from "@repo/webview-shared/api"; + +let stored: unknown; +const sent: unknown[] = []; + +vi.stubGlobal( + "acquireVsCodeApi", + vi.fn(() => ({ + postMessage: (msg: unknown) => sent.push(msg), + getState: () => stored, + setState: (s: unknown) => (stored = s), + })), +); + +beforeEach(() => { + stored = undefined; + sent.length = 0; +}); + +describe("postMessage", () => { + it("sends messages to the extension", () => { + postMessage({ type: "ready" }); + postMessage({ type: "refresh", data: { id: 1 } }); + + expect(sent).toEqual([ + { type: "ready" }, + { type: "refresh", data: { id: 1 } }, + ]); + }); +}); + +describe("state persistence", () => { + it("round-trips state through setState and getState", () => { + const state = { count: 42, items: ["a", "b"] }; + setState(state); + + expect(getState()).toEqual(state); + }); + + it("returns undefined when no state has been set", () => { + expect(getState()).toBeUndefined(); + }); + + it("overwrites previous state", () => { + setState({ version: 1 }); + setState({ version: 2 }); + + expect(getState()).toEqual({ version: 2 }); + }); + + it("handles null state", () => { + setState({ something: true }); + setState(null); + + expect(getState()).toBeNull(); + }); +}); diff --git a/test/webview/shared/hooks.test.ts b/test/webview/shared/hooks.test.ts new file mode 100644 index 00000000..6358dfce --- /dev/null +++ b/test/webview/shared/hooks.test.ts @@ -0,0 +1,127 @@ +import { renderHook, act } from "@testing-library/react"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +import { useMessage, useVsCodeState } from "@repo/webview-shared/react/hooks"; + +let stored: unknown; + +vi.stubGlobal( + "acquireVsCodeApi", + vi.fn(() => ({ + postMessage: vi.fn(), + getState: () => stored, + setState: (s: unknown) => (stored = s), + })), +); + +beforeEach(() => { + stored = undefined; +}); + +function dispatchMessage(data: unknown) { + window.dispatchEvent(new MessageEvent("message", { data })); +} + +describe("useMessage", () => { + it("receives dispatched messages", () => { + const received: unknown[] = []; + + renderHook(() => useMessage((msg) => received.push(msg))); + + act(() => { + dispatchMessage({ type: "first" }); + dispatchMessage({ type: "second", value: 42 }); + }); + + expect(received).toEqual([ + { type: "first" }, + { type: "second", value: 42 }, + ]); + }); + + it("stops receiving messages after unmount", () => { + const received: unknown[] = []; + + const { unmount } = renderHook(() => + useMessage((msg) => received.push(msg)), + ); + + act(() => dispatchMessage({ type: "before" })); + unmount(); + act(() => dispatchMessage({ type: "after" })); + + expect(received).toEqual([{ type: "before" }]); + }); + + it("always calls the current handler, not a stale one", () => { + let currentPrefix = "old"; + const log: string[] = []; + + const { rerender } = renderHook(() => + useMessage(() => log.push(currentPrefix)), + ); + + currentPrefix = "new"; + rerender(); + + act(() => dispatchMessage({ type: "test" })); + + // Should use "new", not the original "old" + expect(log).toEqual(["new"]); + }); +}); + +describe("useVsCodeState", () => { + it("initializes with provided value when no saved state exists", () => { + const { result } = renderHook(() => useVsCodeState({ count: 0 })); + const [state] = result.current; + + expect(state).toEqual({ count: 0 }); + }); + + it("restores saved state on mount", () => { + stored = { count: 42 }; + + const { result } = renderHook(() => useVsCodeState({ count: 0 })); + const [state] = result.current; + + expect(state).toEqual({ count: 42 }); + }); + + it("updates and persists state", () => { + const { result } = renderHook(() => useVsCodeState({ value: "initial" })); + + act(() => { + const [, setState] = result.current; + setState({ value: "updated" }); + }); + + const [state] = result.current; + expect(state).toEqual({ value: "updated" }); + expect(stored).toEqual({ value: "updated" }); + }); + + it("persists state across remounts", () => { + const { result, unmount } = renderHook(() => useVsCodeState({ x: 1 })); + + act(() => { + const [, setState] = result.current; + setState({ x: 99 }); + }); + unmount(); + + const { result: result2 } = renderHook(() => useVsCodeState({ x: 1 })); + const [state] = result2.current; + expect(state).toEqual({ x: 99 }); + }); + + it("returns a stable setter function", () => { + const { result, rerender } = renderHook(() => useVsCodeState({ x: 1 })); + + const [, setterBefore] = result.current; + rerender(); + const [, setterAfter] = result.current; + + expect(setterBefore).toBe(setterAfter); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index a3fcd089..fd34ceb8 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -1,22 +1,53 @@ import path from "node:path"; import { defineConfig } from "vitest/config"; +const webviewSharedAlias = path.resolve( + __dirname, + "packages/webview-shared/src", +); + export default defineConfig({ test: { - globals: true, - environment: "node", - include: ["test/unit/**/*.test.ts", "test/utils/**/*.test.ts"], - exclude: ["**/node_modules/**", "**/out/**", "**/*.d.ts"], - pool: "threads", - fileParallelism: true, + projects: [ + { + extends: true, + test: { + name: "extension", + include: ["test/unit/**/*.test.ts", "test/utils/**/*.test.ts"], + exclude: ["**/node_modules/**", "**/out/**", "**/*.d.ts"], + environment: "node", + globals: true, + pool: "threads", + fileParallelism: true, + }, + resolve: { + alias: { + "@": path.resolve(__dirname, "src"), + "@repo/webview-shared": webviewSharedAlias, + vscode: path.resolve(__dirname, "test/mocks/vscode.runtime.ts"), + }, + }, + }, + { + extends: true, + test: { + name: "webview", + include: ["test/webview/**/*.test.{ts,tsx}"], + exclude: ["**/node_modules/**", "**/out/**", "**/*.d.ts"], + environment: "jsdom", + globals: true, + pool: "threads", + fileParallelism: true, + }, + resolve: { + alias: { + "@repo/webview-shared": webviewSharedAlias, + }, + }, + }, + ], coverage: { provider: "v8", }, }, - resolve: { - alias: { - "@": path.resolve(__dirname, "src"), - vscode: path.resolve(__dirname, "test/mocks/vscode.runtime.ts"), - }, - }, });