From 13c5aa7952096d0c4f4350f9c6fdec3061a4bc53 Mon Sep 17 00:00:00 2001 From: karngyan Date: Sun, 9 Aug 2026 03:34:03 +0530 Subject: [PATCH 01/33] Switch the build from tsup to tsdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tsup's last npm release was 2025-11-12 (nine months ago) with 409 open issues; tsdown ships actively under the rolldown org and targets libraries specifically. Neither is formally deprecated on npm, but the maintenance signal is one-sided. tsdown externalizes dependencies and peerDependencies by default, so the explicit external array is gone — react, react-dom and @base-ui/react are external without configuration. tsdown's own external option is deprecated in favour of deps.neverBundle; neither is needed here. Also documents that clean: true makes build order load-bearing: tsdown must run before build-css.mjs or dist/dowel.css is deleted after it is written. Co-Authored-By: Claude Opus 5 (1M context) --- .../plans/2026-08-09-dowel-phase-1.md | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md index 737329f..ad48ad2 100644 --- a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md +++ b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md @@ -4,9 +4,9 @@ **Goal:** Ship `dowel@0.1.0` to npm with a working token system, a compiled-CSS build pipeline, release automation, a live docs site at dowel.sh, and 8 components proving the pattern end-to-end. -**Architecture:** pnpm workspace with one publishable package (`packages/dowel`) and one docs app (`apps/docs`). Components are React wrappers over `@base-ui/react` primitives, styled by hand-authored plain CSS with `dowel-` prefixed classes. Lightning CSS bundles per-component CSS into a single `dist/dowel.css`; tsup emits ESM + types. Variants are expressed as `data-*` attributes, never className props. +**Architecture:** pnpm workspace with one publishable package (`packages/dowel`) and one docs app (`apps/docs`). Components are React wrappers over `@base-ui/react` primitives, styled by hand-authored plain CSS with `dowel-` prefixed classes. Lightning CSS bundles per-component CSS into a single `dist/dowel.css`; tsdown emits ESM + types. Variants are expressed as `data-*` attributes, never className props. -**Tech Stack:** React 19.2, `@base-ui/react` 1.7, TypeScript 5.9.3, Lightning CSS 1.33, tsup 8.5, vitest 4.1, changesets 2.31, TanStack Start 1.168, Cloudflare Workers. +**Tech Stack:** React 19.2, `@base-ui/react` 1.7, TypeScript 5.9.3, Lightning CSS 1.33, tsdown 0.22, vitest 4.1, changesets 2.31, TanStack Start 1.168, Cloudflare Workers. ## Global Constraints @@ -35,7 +35,7 @@ packages/dowel/ ├── package.json name "dowel", exports . and ./dowel.css ├── tsconfig.json -├── tsup.config.ts ESM + d.ts +├── tsdown.config.ts ESM + d.ts ├── scripts/build-css.mjs Lightning CSS bundle+minify ├── src/ │ ├── index.ts barrel: re-exports every component @@ -217,7 +217,7 @@ and add `"LICENSE"` to the package's `files` array alongside `"dist"`. "./dowel.css": "./dist/dowel.css" }, "scripts": { - "build": "tsup && node scripts/build-css.mjs", + "build": "tsdown && node scripts/build-css.mjs", "test": "vitest run", "test:watch": "vitest", "typecheck": "tsc --noEmit" @@ -241,7 +241,7 @@ and add `"LICENSE"` to the package's `files` array alongside `"dist"`. "lightningcss": "^1.33.0", "react": "^19.2.8", "react-dom": "^19.2.8", - "tsup": "^8.5.1", + "tsdown": "^0.22.14", "vitest": "^4.1.10" } } @@ -663,7 +663,7 @@ git commit -m "Add the dowel token layer with light and dark parity tests" ## Task 3: Build pipeline **Files:** -- Create: `packages/dowel/tsup.config.ts` +- Create: `packages/dowel/tsdown.config.ts` - Create: `packages/dowel/scripts/build-css.mjs` - Create: `packages/dowel/src/index.ts` - Create: `packages/dowel/src/lib/cx.ts` @@ -671,7 +671,7 @@ git commit -m "Add the dowel token layer with light and dark parity tests" **Interfaces:** - Consumes: Task 2's `src/index.css` and token names. -- Produces: `pnpm --filter dowel build` emitting `dist/index.js`, `dist/index.d.ts`, `dist/dowel.css`. Exports `cx(...parts: Array): string` from `src/lib/cx.ts`, used by every component in Tasks 4+. +- Produces: `pnpm --filter dowel build` emitting `dist/index.js`, `dist/index.d.ts`, `dist/dowel.css`. Build order is `tsdown` then `build-css.mjs` — never the reverse. Exports `cx(...parts: Array): string` from `src/lib/cx.ts`, used by every component in Tasks 4+. - [ ] **Step 1: Write the failing build-contract test** @@ -760,24 +760,30 @@ console.log(`built dist/dowel.css (${(code.length / 1024).toFixed(1)} kB)`); pnpm --filter dowel add -D browserslist@^4.26.0 ``` -- [ ] **Step 5: Write tsup.config.ts** +- [ ] **Step 5: Write tsdown.config.ts** -`packages/dowel/tsup.config.ts`: +`packages/dowel/tsdown.config.ts`: ```ts -import { defineConfig } from "tsup"; +import { defineConfig } from "tsdown"; export default defineConfig({ entry: ["src/index.ts"], - format: ["esm"], + format: "esm", + platform: "browser", dts: true, clean: true, treeshake: true, - // CSS is built separately by scripts/build-css.mjs; tsup must not try to - // process the .css imports that components do not make at runtime. - external: ["react", "react-dom", "@base-ui/react"], + // No `external` here on purpose: tsdown never bundles `dependencies` or + // `peerDependencies`, so react, react-dom and @base-ui/react are already + // external. (tsdown's `external` option is deprecated in favour of + // `deps.neverBundle`, and neither is needed for this package.) }); ``` +`clean: true` wipes `dist/` — which is why `package.json` runs `tsdown` first +and `build-css.mjs` second. Reversing that order silently deletes +`dist/dowel.css` and the CSS contract test fails with a confusing "not built". + - [ ] **Step 6: Write cx.ts** `packages/dowel/src/lib/cx.ts`: @@ -817,7 +823,7 @@ Expected: build prints a kB size; all 4 contract tests PASS. ```bash grep -q '^dist/' .gitignore || echo 'dist/' >> .gitignore git add -A -git commit -m "Add the Lightning CSS and tsup build pipeline with a token contract test" +git commit -m "Add the Lightning CSS and tsdown build pipeline with a token contract test" ``` --- From 44992efed244407a1d8539bbfd3a9461d9eebb6b Mon Sep 17 00:00:00 2001 From: karngyan Date: Sun, 9 Aug 2026 03:37:09 +0530 Subject: [PATCH 02/33] Drop the cx class-name helper from the plan Pre-flight scan: every component called cx("dowel-x") with exactly one string literal, so cx was an identity function at all 18 call sites. The design expresses variants as data-* attributes, so no component ever builds a conditional class name and the helper has no use case. Components now write className="dowel-x" directly. src/lib/cx.ts is gone. Co-Authored-By: Claude Opus 5 (1M context) --- .../plans/2026-08-09-dowel-phase-1.md | 73 ++++++++----------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md index ad48ad2..ab2fa11 100644 --- a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md +++ b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md @@ -44,7 +44,6 @@ packages/dowel/ │ │ ├── scale.css non-colour tokens (type, size, motion, shape) │ │ ├── light.css :root colour tokens │ │ └── dark.css dark overrides (class, attr, media) -│ ├── lib/cx.ts tiny class joiner (no clsx dep) │ └── components/ │ ├── button/{index.tsx,button.css,button.test.tsx} │ ├── icon-button/{...} @@ -666,12 +665,11 @@ git commit -m "Add the dowel token layer with light and dark parity tests" - Create: `packages/dowel/tsdown.config.ts` - Create: `packages/dowel/scripts/build-css.mjs` - Create: `packages/dowel/src/index.ts` -- Create: `packages/dowel/src/lib/cx.ts` - Test: `packages/dowel/test/css-contract.test.ts` **Interfaces:** - Consumes: Task 2's `src/index.css` and token names. -- Produces: `pnpm --filter dowel build` emitting `dist/index.js`, `dist/index.d.ts`, `dist/dowel.css`. Build order is `tsdown` then `build-css.mjs` — never the reverse. Exports `cx(...parts: Array): string` from `src/lib/cx.ts`, used by every component in Tasks 4+. +- Produces: `pnpm --filter dowel build` emitting `dist/index.js`, `dist/index.d.ts`, `dist/dowel.css`. Build order is `tsdown` then `build-css.mjs` — never the reverse. Produces no class-name helper: components in Tasks 4+ write `className="dowel-x"` as a plain string literal. - [ ] **Step 1: Write the failing build-contract test** @@ -784,24 +782,19 @@ export default defineConfig({ and `build-css.mjs` second. Reversing that order silently deletes `dist/dowel.css` and the CSS contract test fails with a confusing "not built". -- [ ] **Step 6: Write cx.ts** +- [ ] **Step 6: (intentionally empty — no class-name helper)** -`packages/dowel/src/lib/cx.ts`: -```ts -/** - * Joins class names, dropping falsy entries. - * - * dowel has no clsx/tailwind-merge dependency: class names are authored by us - * and never merged with consumer classes, so conflict resolution is not a - * problem this library has. - */ -export function cx( - ...parts: Array -): string { - return parts.filter(Boolean).join(" "); -} +dowel ships **no `cx`/`clsx`/`tailwind-merge` helper**. Variants are `data-*` +attributes, so no component ever builds a conditional class name — every +`className` in this library is a single string literal: + +```tsx +className="dowel-btn" ``` +If a later phase genuinely needs conditional classes, add the helper then. +Do not add one now, and do not wrap these literals in a function. + - [ ] **Step 7: Write the barrel** `packages/dowel/src/index.ts`: @@ -977,7 +970,6 @@ Expected: FAIL — cannot resolve `./index`. import { Button as BaseButton } from "@base-ui/react/button"; import { forwardRef } from "react"; import type { ComponentPropsWithoutRef, ReactElement } from "react"; -import { cx } from "../../lib/cx"; type NativeButtonProps = Omit< ComponentPropsWithoutRef<"button">, @@ -1003,7 +995,7 @@ export const Button = forwardRef( , @@ -1241,7 +1232,7 @@ export const IconButton = forwardRef( ref={ref} render={render} aria-label={label} - className={cx("dowel-icon-btn")} + className="dowel-icon-btn" data-variant={variant} data-size={size} {...props} @@ -1430,7 +1421,6 @@ Expected: FAIL — modules not found. ```tsx import { forwardRef } from "react"; import type { ComponentPropsWithoutRef } from "react"; -import { cx } from "../../lib/cx"; export interface BadgeProps extends Omit, "className" | "style"> { @@ -1444,7 +1434,7 @@ export const Badge = forwardRef(function Badge( return ( @@ -1499,7 +1489,6 @@ export const Badge = forwardRef(function Badge( ```tsx import { forwardRef } from "react"; import type { ComponentPropsWithoutRef } from "react"; -import { cx } from "../../lib/cx"; export interface KbdProps extends Omit< @@ -1515,7 +1504,7 @@ export const Kbd = forwardRef(function Kbd( ref, ) { return ( - + {keys.map((key, i) => ( {key} ))} @@ -1718,7 +1707,6 @@ import { Field as BaseField } from "@base-ui/react/field"; import { Input as BaseInput } from "@base-ui/react/input"; import { forwardRef } from "react"; import type { ComponentPropsWithoutRef } from "react"; -import { cx } from "../../lib/cx"; export interface InputProps extends Omit, "className" | "style"> { @@ -1734,7 +1722,7 @@ export const Input = forwardRef(function Input( return ( , "className" | "style"> >(function FieldRoot(props, ref) { return ( - + ); }), @@ -1763,7 +1751,7 @@ export const Field = { return ( ); @@ -1776,7 +1764,7 @@ export const Field = { return ( ); @@ -1789,7 +1777,7 @@ export const Field = { return ( ); @@ -1984,7 +1972,6 @@ Expected: FAIL — cannot resolve `./index`. ```tsx import { Dialog as BaseDialog } from "@base-ui/react/dialog"; import type { ComponentProps } from "react"; -import { cx } from "../../lib/cx"; /** Strips the appearance escape hatches from any Base UI component's props. */ type Props unknown> = Omit< @@ -1998,15 +1985,15 @@ export const Dialog = { Portal: BaseDialog.Portal, Backdrop: function DialogBackdrop(props: Props) { - return ; + return ; }, Popup: function DialogPopup(props: Props) { - return ; + return ; }, Title: function DialogTitle(props: Props) { - return ; + return ; }, Description: function DialogDescription( @@ -2014,7 +2001,7 @@ export const Dialog = { ) { return ( ); @@ -2205,7 +2192,6 @@ Expected: FAIL — cannot resolve `./index`. ```tsx import { Menu as BaseMenu } from "@base-ui/react/menu"; import type { ComponentProps } from "react"; -import { cx } from "../../lib/cx"; /** Strips the appearance escape hatches from any Base UI component's props. */ type Props unknown> = Omit< @@ -2225,16 +2211,16 @@ export const Menu = { }, Popup: function MenuPopup(props: Props) { - return ; + return ; }, Item: function MenuItem(props: Props) { - return ; + return ; }, Separator: function MenuSeparator(props: Props) { return ( - + ); }, @@ -2244,7 +2230,7 @@ export const Menu = { props: Props, ) { return ( - + ); }, }; @@ -2431,7 +2417,6 @@ Expected: FAIL — cannot resolve `./index`. ```tsx import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip"; import type { ComponentProps } from "react"; -import { cx } from "../../lib/cx"; /** Strips the appearance escape hatches from any Base UI component's props. */ type Props unknown> = Omit< @@ -2452,7 +2437,7 @@ export const Tooltip = { }, Popup: function TooltipPopup(props: Props) { - return ; + return ; }, }; ``` From 99e7a0336e55f706e1c6c5e7c8c295e1a18313fb Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 03:41:31 +0530 Subject: [PATCH 03/33] Scaffold the pnpm workspace and dowel package --- .npmrc | 2 + .prettierrc | 6 + LICENSE | 21 + package.json | 20 + packages/dowel/LICENSE | 21 + packages/dowel/package.json | 69 + packages/dowel/tsconfig.json | 4 + pnpm-lock.yaml | 2435 ++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 10 + tsconfig.base.json | 15 + 10 files changed, 2603 insertions(+) create mode 100644 .npmrc create mode 100644 .prettierrc create mode 100644 LICENSE create mode 100644 package.json create mode 100644 packages/dowel/LICENSE create mode 100644 packages/dowel/package.json create mode 100644 packages/dowel/tsconfig.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 tsconfig.base.json diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..53b788d --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +# Keep the lockfile honest in CI; mise pins the pnpm version itself. +engine-strict=true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c5d3910 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "semi": true, + "singleQuote": false, + "printWidth": 80, + "trailingComma": "all" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..722270b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Karn Gyan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/package.json b/package.json new file mode 100644 index 0000000..1cd40ba --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "dowel-monorepo", + "private": true, + "type": "module", + "packageManager": "pnpm@11.9.0", + "engines": { + "node": ">=24" + }, + "scripts": { + "build": "pnpm -r build", + "test": "pnpm -r test", + "typecheck": "pnpm -r typecheck", + "format": "prettier --write .", + "format:check": "prettier --check ." + }, + "devDependencies": { + "prettier": "3.6.2", + "typescript": "5.9.3" + } +} diff --git a/packages/dowel/LICENSE b/packages/dowel/LICENSE new file mode 100644 index 0000000..722270b --- /dev/null +++ b/packages/dowel/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Karn Gyan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/dowel/package.json b/packages/dowel/package.json new file mode 100644 index 0000000..50e402a --- /dev/null +++ b/packages/dowel/package.json @@ -0,0 +1,69 @@ +{ + "name": "dowel", + "version": "0.0.0", + "description": "An opinionated React component library. One look, well made.", + "license": "MIT", + "author": "Karn Gyan", + "homepage": "https://dowel.sh", + "repository": { + "type": "git", + "url": "git+https://github.com/karnstack/dowel.git" + }, + "bugs": { + "url": "https://github.com/karnstack/dowel/issues" + }, + "keywords": [ + "react", + "components", + "ui", + "design-system", + "linear" + ], + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "files": [ + "dist", + "LICENSE" + ], + "publishConfig": { + "access": "public", + "provenance": true + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + }, + "./dowel.css": "./dist/dowel.css" + }, + "scripts": { + "build": "tsdown && node scripts/build-css.mjs", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "dependencies": { + "@base-ui/react": "^1.7.0" + }, + "devDependencies": { + "@testing-library/dom": "^10.4.1", + "@testing-library/react": "^16.3.2", + "@testing-library/user-event": "^14.6.1", + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^6.0.5", + "axe-core": "^4.13.0", + "jsdom": "^27.0.0", + "lightningcss": "^1.33.0", + "react": "^19.2.8", + "react-dom": "^19.2.8", + "tsdown": "^0.22.14", + "vitest": "^4.1.10" + } +} diff --git a/packages/dowel/tsconfig.json b/packages/dowel/tsconfig.json new file mode 100644 index 0000000..516027f --- /dev/null +++ b/packages/dowel/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src", "test", "*.config.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..8385023 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2435 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + devDependencies: + prettier: + specifier: 3.6.2 + version: 3.6.2 + typescript: + specifier: 5.9.3 + version: 5.9.3 + + packages/dowel: + dependencies: + "@base-ui/react": + specifier: ^1.7.0 + version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + devDependencies: + "@testing-library/dom": + specifier: ^10.4.1 + version: 10.4.1 + "@testing-library/react": + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + "@testing-library/user-event": + specifier: ^14.6.1 + version: 14.6.3(@testing-library/dom@10.4.1) + "@types/react": + specifier: ^19.2.18 + version: 19.2.18 + "@types/react-dom": + specifier: ^19.2.0 + version: 19.2.4(@types/react@19.2.18) + "@vitejs/plugin-react": + specifier: ^6.0.5 + version: 6.0.5(vite@8.2.1) + axe-core: + specifier: ^4.13.0 + version: 4.13.0 + jsdom: + specifier: ^27.0.0 + version: 27.4.0 + lightningcss: + specifier: ^1.33.0 + version: 1.33.0 + react: + specifier: ^19.2.8 + version: 19.2.8 + react-dom: + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) + tsdown: + specifier: ^0.22.14 + version: 0.22.14(typescript@5.9.3) + vitest: + specifier: ^4.1.10 + version: 4.1.10(jsdom@27.4.0)(vite@8.2.1) + +packages: + "@acemir/cssom@0.9.31": + resolution: + { + integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==, + } + + "@asamuzakjp/css-color@4.1.2": + resolution: + { + integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==, + } + + "@asamuzakjp/dom-selector@6.8.1": + resolution: + { + integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==, + } + + "@asamuzakjp/nwsapi@2.3.9": + resolution: + { + integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, + } + + "@babel/code-frame@7.29.7": + resolution: + { + integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, + } + engines: { node: ">=6.9.0" } + + "@babel/helper-validator-identifier@7.29.7": + resolution: + { + integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, + } + engines: { node: ">=6.9.0" } + + "@babel/runtime@7.29.7": + resolution: + { + integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==, + } + engines: { node: ">=6.9.0" } + + "@base-ui/react@1.7.0": + resolution: + { + integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==, + } + engines: { node: ">=14.0.0" } + peerDependencies: + "@date-fns/tz": ^1.2.0 + "@types/react": ^17 || ^18 || ^19 + date-fns: ^4.0.0 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@date-fns/tz": + optional: true + "@types/react": + optional: true + date-fns: + optional: true + + "@base-ui/utils@0.3.2": + resolution: + { + integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==, + } + peerDependencies: + "@types/react": ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@types/react": + optional: true + + "@csstools/color-helpers@6.1.0": + resolution: + { + integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==, + } + engines: { node: ">=20.19.0" } + + "@csstools/css-calc@3.3.0": + resolution: + { + integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==, + } + engines: { node: ">=20.19.0" } + peerDependencies: + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + + "@csstools/css-color-parser@4.1.10": + resolution: + { + integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==, + } + engines: { node: ">=20.19.0" } + peerDependencies: + "@csstools/css-parser-algorithms": ^4.0.0 + "@csstools/css-tokenizer": ^4.0.0 + + "@csstools/css-parser-algorithms@4.0.0": + resolution: + { + integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, + } + engines: { node: ">=20.19.0" } + peerDependencies: + "@csstools/css-tokenizer": ^4.0.0 + + "@csstools/css-syntax-patches-for-csstree@1.1.7": + resolution: + { + integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==, + } + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + "@csstools/css-tokenizer@4.0.0": + resolution: + { + integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, + } + engines: { node: ">=20.19.0" } + + "@exodus/bytes@1.15.1": + resolution: + { + integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + peerDependencies: + "@noble/hashes": ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + "@noble/hashes": + optional: true + + "@floating-ui/core@1.8.0": + resolution: + { + integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==, + } + + "@floating-ui/dom@1.8.0": + resolution: + { + integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==, + } + + "@floating-ui/react-dom@2.1.9": + resolution: + { + integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==, + } + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + + "@floating-ui/utils@0.2.12": + resolution: + { + integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==, + } + + "@jridgewell/sourcemap-codec@1.5.5": + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } + + "@oxc-project/types@0.143.0": + resolution: + { + integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==, + } + + "@quansync/fs@1.0.0": + resolution: + { + integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==, + } + + "@rolldown/binding-android-arm64@1.2.3": + resolution: + { + integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [android] + + "@rolldown/binding-darwin-arm64@1.2.3": + resolution: + { + integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [darwin] + + "@rolldown/binding-darwin-x64@1.2.3": + resolution: + { + integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [darwin] + + "@rolldown/binding-freebsd-x64@1.2.3": + resolution: + { + integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [freebsd] + + "@rolldown/binding-linux-arm-gnueabihf@1.2.3": + resolution: + { + integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [linux] + + "@rolldown/binding-linux-arm64-gnu@1.2.3": + resolution: + { + integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-arm64-musl@1.2.3": + resolution: + { + integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@rolldown/binding-linux-ppc64-gnu@1.2.3": + resolution: + { + integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-s390x-gnu@1.2.3": + resolution: + { + integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-gnu@1.2.3": + resolution: + { + integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-musl@1.2.3": + resolution: + { + integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [musl] + + "@rolldown/binding-openharmony-arm64@1.2.3": + resolution: + { + integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [openharmony] + + "@rolldown/binding-win32-arm64-msvc@1.2.3": + resolution: + { + integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [win32] + + "@rolldown/binding-win32-x64-msvc@1.2.3": + resolution: + { + integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [win32] + + "@rolldown/pluginutils@1.0.1": + resolution: + { + integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==, + } + + "@standard-schema/spec@1.1.0": + resolution: + { + integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, + } + + "@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 + + "@testing-library/user-event@14.6.3": + resolution: + { + integrity: sha512-6dBq67jT8lE+JTE8Exm02Kt6ze43hz1jdiSpSJwtTZiT1xQQ6b7nZYTTQ9njdArdU8XklOwaDp/AbT/eYSKF4g==, + } + engines: { node: ">=12", npm: ">=6" } + peerDependencies: + "@testing-library/dom": ">=7.21.4" + + "@types/aria-query@5.0.4": + resolution: + { + integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, + } + + "@types/chai@5.2.3": + resolution: + { + integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, + } + + "@types/deep-eql@4.0.2": + resolution: + { + integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, + } + + "@types/estree@1.0.9": + resolution: + { + integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, + } + + "@types/react-dom@19.2.4": + resolution: + { + integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==, + } + peerDependencies: + "@types/react": ^19.2.0 + + "@types/react@19.2.18": + resolution: + { + integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==, + } + + "@vitejs/plugin-react@6.0.5": + resolution: + { + integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + peerDependencies: + "@rolldown/plugin-babel": ^0.1.7 || ^0.2.0 + babel-plugin-react-compiler: ^1.0.0 + vite: ^8.0.0 + peerDependenciesMeta: + "@rolldown/plugin-babel": + optional: true + babel-plugin-react-compiler: + optional: true + + "@vitest/expect@4.1.10": + resolution: + { + integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==, + } + + "@vitest/mocker@4.1.10": + resolution: + { + integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==, + } + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + "@vitest/pretty-format@4.1.10": + resolution: + { + integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==, + } + + "@vitest/runner@4.1.10": + resolution: + { + integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==, + } + + "@vitest/snapshot@4.1.10": + resolution: + { + integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==, + } + + "@vitest/spy@4.1.10": + resolution: + { + integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==, + } + + "@vitest/utils@4.1.10": + resolution: + { + integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==, + } + + "@yuku-codegen/binding-android-arm64@0.8.4": + resolution: + { + integrity: sha512-rsYkGl2kOkDRsh1mxriYnk1qBS78vjlBJ3+T2XwtwKwqOliy2n+2Ae0EDxJ/uX1DZLm3KkBZarGO5isEnmHchA==, + } + cpu: [arm64] + os: [android] + + "@yuku-codegen/binding-darwin-arm64@0.8.4": + resolution: + { + integrity: sha512-tNLKzPF3FYmEcHSYvWp/LEpjHHAtDR13hwo6/gdCkYMi9x59CWn2obczKzWNe7kDor4/1AMZuJDEVRpFkTSefw==, + } + cpu: [arm64] + os: [darwin] + + "@yuku-codegen/binding-darwin-x64@0.8.4": + resolution: + { + integrity: sha512-tK7LWzXNb5JbZpnoCNHB0nEhPFss/LwwehM6m/f0oYDan+iZgFZXzdoy80JdE7dVxjTZDIhUNPx8X8xbIdaoxA==, + } + cpu: [x64] + os: [darwin] + + "@yuku-codegen/binding-freebsd-x64@0.8.4": + resolution: + { + integrity: sha512-5MUV4d7g2p5Hd8GiXW6ynTRgYjm4Dw4eM2gaWRZ4crkGetzNx+HlPxcEfGtVNPqH5Qaa4Z2REtzdsdgvaE6/Ng==, + } + cpu: [x64] + os: [freebsd] + + "@yuku-codegen/binding-linux-arm-gnu@0.8.4": + resolution: + { + integrity: sha512-g6LnHrR0Rfqq5cXs7olwR2+LlVDc876pw7Hh7YXbukVSiBxQkaxqsEO/trO25z2g99zWzgXwoYvQZ1TnwA2wEw==, + } + cpu: [arm] + os: [linux] + libc: [glibc] + + "@yuku-codegen/binding-linux-arm-musl@0.8.4": + resolution: + { + integrity: sha512-7XAPHrROPEFuJWXEGZeLZQN4xR8ENQ65+HSCtCHjSzCwGgnX53GUjM9ExVcHopV0a5g4vu57v2wwtyWIM5iNOQ==, + } + cpu: [arm] + os: [linux] + libc: [musl] + + "@yuku-codegen/binding-linux-arm64-gnu@0.8.4": + resolution: + { + integrity: sha512-fnBm7NLuuwFXy7F1vRIuyOc+RW9ADUjuCKVGY87Dj8jtr9XgESNrwb+B9VLSFY7nZ0rCdK/Sm1fBqJpI7eLdKQ==, + } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@yuku-codegen/binding-linux-arm64-musl@0.8.4": + resolution: + { + integrity: sha512-6vTw4ZHO9nm4SUkw36uG+UE6/qifZ0E8HIef7Mx/U/c2Zxu3JLBfXtU7U/NN2GMkDmcJkgwjXfpQoYw4Ch5Y1w==, + } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@yuku-codegen/binding-linux-x64-gnu@0.8.4": + resolution: + { + integrity: sha512-+vuC3V3Lw+DB4oJgHV9pVDfQZlsZJnxmbdods7HzxgEALU3P5+czwdAcw3wfh7Ebabt0Ny8eLUb1k9RV5OB/+w==, + } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@yuku-codegen/binding-linux-x64-musl@0.8.4": + resolution: + { + integrity: sha512-QH60PE4eZecmgNGa1/T1cKPhrfxt6ANtu4lrQ1FZ50F9b0GS9WjGmIjrfdSUeQa+f2Iqk3oEFSJdgVHBg1KPNg==, + } + cpu: [x64] + os: [linux] + libc: [musl] + + "@yuku-codegen/binding-win32-arm64@0.8.4": + resolution: + { + integrity: sha512-6r68c0nKZPIBRXZIBiD7zjlEukBR+xRxpTOVj4n1Fsjcdh4YbbJfjFfmIzdbo9jXR1xL+dPueDVdsRGOUH5MoQ==, + } + cpu: [arm64] + os: [win32] + + "@yuku-codegen/binding-win32-x64@0.8.4": + resolution: + { + integrity: sha512-i+BW77LPjNqe7Apq50J3OeEaVfga3G+eT2bKjb6bj4yO99fil/jnKb4ZDH4JLvby/Q7hRa6VicL2EZ7iS+ifzA==, + } + cpu: [x64] + os: [win32] + + "@yuku-parser/binding-android-arm64@0.8.4": + resolution: + { + integrity: sha512-+HIMmv08Zrh9ugIAEMnKBMMePOl7CDxrjc8Vui1+GG2TJHM1yI1+3wo1pnXB6Nj2IiHugDkQw8ycUI6SA2EUkQ==, + } + cpu: [arm64] + os: [android] + + "@yuku-parser/binding-darwin-arm64@0.8.4": + resolution: + { + integrity: sha512-Elf/B/2m3OsyvxoQnBk8Dtu+9csHkzBNs5Yv9GbHjT3x0kVKNWjFusyZgm41VwxcPDqdpRi8tWxNX7OqXkmf/A==, + } + cpu: [arm64] + os: [darwin] + + "@yuku-parser/binding-darwin-x64@0.8.4": + resolution: + { + integrity: sha512-CjZuMoXnL5XUkVpDqh4WDPwpAw8CwmtHHnTerGkS45So/sNuwkXdyIAEqqIZfaLopi5W/V9NApAT2md9XizjsQ==, + } + cpu: [x64] + os: [darwin] + + "@yuku-parser/binding-freebsd-x64@0.8.4": + resolution: + { + integrity: sha512-ibLKORdz71iI4Vs+fyFgvwQ51P5XcxJIyQLa8cSEWqwptRdo+BTcZHIQEcZnFDPUuvmJ19RRH9CoiJdfhr7pZw==, + } + cpu: [x64] + os: [freebsd] + + "@yuku-parser/binding-linux-arm-gnu@0.8.4": + resolution: + { + integrity: sha512-Fo3r5fYhGDcFnl+KN+L9PgtiQPS4AIE1n1mG1o5jZ11p7g5yZ/1EjLFmSHAUsoXreun8KjTsFjEL7P1Sb93PZQ==, + } + cpu: [arm] + os: [linux] + libc: [glibc] + + "@yuku-parser/binding-linux-arm-musl@0.8.4": + resolution: + { + integrity: sha512-BEB31vUEgXPWf7WkoMPSzzJhpC/wWCBXyysRCCPsw47BJ/OtbQsvJbxX9fFDuSRLy3kbyIV/WbdUTgbQ9COxiw==, + } + cpu: [arm] + os: [linux] + libc: [musl] + + "@yuku-parser/binding-linux-arm64-gnu@0.8.4": + resolution: + { + integrity: sha512-xGLCRcHn9xVz7JVNyyKtiNJSf503qtUmih9XVSsghgzOmiKUMnHObs69OMMwXN3788tg1jsl11fNjjQlB8idMA==, + } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@yuku-parser/binding-linux-arm64-musl@0.8.4": + resolution: + { + integrity: sha512-3kNRi8NJT2q6FQRVCUFHIQ99+kXdi8cVJEEUi6+xtFqpMgNrZNnbgAj28ILsDC7zmclaU+v47eUCeJoKLSCbww==, + } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@yuku-parser/binding-linux-x64-gnu@0.8.4": + resolution: + { + integrity: sha512-isi62oMy94Z3OXwGs2l2rkqRiRyqLmfHeTRHpA/uWZbsNhnm7IdVvkF7e7wHNKAtd5jwGzOqYSroxKv2fOm7Cw==, + } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@yuku-parser/binding-linux-x64-musl@0.8.4": + resolution: + { + integrity: sha512-9RsEw2xYHqU/pjSRBTOupWN3sF8uz9stjJdARfz6o0llvF+yfrj5QHmTiocGGBeAI80fvKmDtr2RIQClUzAPcA==, + } + cpu: [x64] + os: [linux] + libc: [musl] + + "@yuku-parser/binding-win32-arm64@0.8.4": + resolution: + { + integrity: sha512-VEZHo9rEGOBKR20sA3vCO00aQvwWND5aLu7YxeX+YupMZJh9hd1f17AbClJN37Q2iL1PCHht+wDTOKX6tZYqXg==, + } + cpu: [arm64] + os: [win32] + + "@yuku-parser/binding-win32-x64@0.8.4": + resolution: + { + integrity: sha512-PeH3VzN1feGjPtDpVEAqf000fPT+nxtw/696LKp/5Z9RJi/MaXpB636QC+5QtrAPSoEnIkyEe+c+mq2QLZPWBA==, + } + cpu: [x64] + os: [win32] + + "@yuku-toolchain/types@0.8.4": + resolution: + { + integrity: sha512-p7JE8flrj7ijZ/qLjHi4UwKqMarMD6zumbKXhrjp2I2iLJOuTYiQyci2U36VlXcUlNyzsY7E/mLnKCHotbzJVw==, + } + + agent-base@7.1.4: + resolution: + { + integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, + } + engines: { node: ">= 14" } + + ansi-regex@5.0.1: + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: ">=8" } + + ansi-styles@5.2.0: + resolution: + { + integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, + } + engines: { node: ">=10" } + + ansis@4.3.1: + resolution: + { + integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==, + } + engines: { node: ">=14" } + + aria-query@5.3.0: + resolution: + { + integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, + } + + assertion-error@2.0.1: + resolution: + { + integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, + } + engines: { node: ">=12" } + + axe-core@4.13.0: + resolution: + { + integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==, + } + engines: { node: ">=4" } + + bidi-js@1.0.3: + resolution: + { + integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, + } + + cac@7.0.0: + resolution: + { + integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==, + } + engines: { node: ">=20.19.0" } + + chai@6.2.2: + resolution: + { + integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, + } + engines: { node: ">=18" } + + convert-source-map@2.0.0: + resolution: + { + integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, + } + + css-tree@3.2.1: + resolution: + { + integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, + } + engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + + cssstyle@5.3.7: + resolution: + { + integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==, + } + engines: { node: ">=20" } + + csstype@3.2.3: + resolution: + { + integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, + } + + data-urls@6.0.1: + resolution: + { + integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==, + } + engines: { node: ">=20" } + + debug@4.4.3: + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: + { + integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, + } + + defu@6.1.7: + resolution: + { + integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, + } + + dequal@2.0.3: + resolution: + { + integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, + } + engines: { node: ">=6" } + + detect-libc@2.1.2: + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } + + dom-accessibility-api@0.5.16: + resolution: + { + integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, + } + + dts-resolver@3.0.0: + resolution: + { + integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==, + } + engines: { node: ^22.18.0 || >=24.0.0 } + peerDependencies: + oxc-resolver: ">=11.0.0" + peerDependenciesMeta: + oxc-resolver: + optional: true + + empathic@2.0.1: + resolution: + { + integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==, + } + engines: { node: ">=14" } + + entities@8.0.0: + resolution: + { + integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==, + } + engines: { node: ">=20.19.0" } + + es-module-lexer@2.3.1: + resolution: + { + integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==, + } + + estree-walker@3.0.3: + resolution: + { + integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, + } + + expect-type@1.4.0: + resolution: + { + integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==, + } + engines: { node: ">=12.0.0" } + + fdir@6.5.0: + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + get-tsconfig@5.0.0-beta.5: + resolution: + { + integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==, + } + engines: { node: ">=20.20.0" } + + hookable@6.1.1: + resolution: + { + integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==, + } + + html-encoding-sniffer@6.0.0: + resolution: + { + integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + + http-proxy-agent@7.0.2: + resolution: + { + integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, + } + engines: { node: ">= 14" } + + https-proxy-agent@7.0.6: + resolution: + { + integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, + } + engines: { node: ">= 14" } + + import-without-cache@0.4.0: + resolution: + { + integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==, + } + engines: { node: ^22.18.0 || >=24.0.0 } + + is-potential-custom-element-name@1.0.1: + resolution: + { + integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, + } + + js-tokens@4.0.0: + resolution: + { + integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, + } + + 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 + + lightningcss-android-arm64@1.33.0: + resolution: + { + integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: + { + integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: + { + integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: + { + integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: + { + integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: + { + integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: + { + integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: + { + integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: + { + integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: + { + integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: + { + integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: + { + integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==, + } + engines: { node: ">= 12.0.0" } + + lru-cache@11.5.2: + resolution: + { + integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==, + } + engines: { node: 20 || >=22 } + + 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==, + } + + mdn-data@2.27.1: + resolution: + { + integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, + } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + nanoid@3.3.18: + resolution: + { + integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + obug@2.1.4: + resolution: + { + integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==, + } + engines: { node: ">=12.20.0" } + + parse5@8.0.1: + resolution: + { + integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, + } + + pathe@2.0.3: + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + + picomatch@4.0.5: + resolution: + { + integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==, + } + engines: { node: ">=12" } + + postcss@8.5.26: + resolution: + { + integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==, + } + engines: { node: ^10 || ^12 || >=14 } + + prettier@3.6.2: + resolution: + { + integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, + } + engines: { node: ">=14" } + hasBin: true + + pretty-format@27.5.1: + resolution: + { + integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, + } + engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: ">=6" } + + quansync@1.0.0: + resolution: + { + integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==, + } + + react-dom@19.2.8: + resolution: + { + integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==, + } + peerDependencies: + react: ^19.2.8 + + react-is@17.0.2: + resolution: + { + integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, + } + + react@19.2.8: + resolution: + { + integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==, + } + engines: { node: ">=0.10.0" } + + require-from-string@2.0.2: + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: ">=0.10.0" } + + reselect@5.2.0: + resolution: + { + integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==, + } + + resolve-pkg-maps@1.0.0: + resolution: + { + integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, + } + + rolldown-plugin-dts@0.27.14: + resolution: + { + integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==, + } + engines: { node: ^22.18.0 || >=24.11.0 } + peerDependencies: + "@typescript/native-preview": "*" + "@volar/typescript": ~2.4.0 + rolldown: ^1.0.0 + typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 + vue-tsc: ~3.2.0 || ~3.3.0 + peerDependenciesMeta: + "@typescript/native-preview": + optional: true + "@volar/typescript": + optional: true + typescript: + optional: true + vue-tsc: + optional: true + + rolldown@1.2.3: + resolution: + { + integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + + 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==, + } + + siginfo@2.0.0: + resolution: + { + integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, + } + + source-map-js@1.2.1: + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: ">=0.10.0" } + + stackback@0.0.2: + resolution: + { + integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, + } + + std-env@4.2.0: + resolution: + { + integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==, + } + + symbol-tree@3.2.4: + resolution: + { + integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, + } + + tinybench@2.9.0: + resolution: + { + integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, + } + + tinyexec@1.3.0: + resolution: + { + integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==, + } + engines: { node: ">=18" } + + tinyglobby@0.2.17: + resolution: + { + integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, + } + engines: { node: ">=12.0.0" } + + tinyrainbow@3.1.1: + resolution: + { + integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==, + } + engines: { node: ">=14.0.0" } + + tldts-core@7.4.10: + resolution: + { + integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==, + } + + tldts@7.4.10: + resolution: + { + integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==, + } + hasBin: true + + tough-cookie@6.0.2: + resolution: + { + integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==, + } + engines: { node: ">=16" } + + tr46@6.0.0: + resolution: + { + integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, + } + engines: { node: ">=20" } + + tree-kill@1.2.2: + resolution: + { + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, + } + hasBin: true + + tsdown@0.22.14: + resolution: + { + integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==, + } + engines: { node: ^22.18.0 || >=24.11.0 } + hasBin: true + peerDependencies: + "@arethetypeswrong/core": ^0.18.1 + "@tsdown/css": 0.22.14 + "@tsdown/exe": 0.22.14 + "@vitejs/devtools": "*" + publint: ^0.3.8 + tsx: "*" + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: ^0.5.0 + unrun: "*" + peerDependenciesMeta: + "@arethetypeswrong/core": + optional: true + "@tsdown/css": + optional: true + "@tsdown/exe": + optional: true + "@vitejs/devtools": + optional: true + publint: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + + typescript@5.9.3: + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: ">=14.17" } + hasBin: true + + unconfig-core@7.5.0: + resolution: + { + integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==, + } + + use-sync-external-store@1.6.0: + resolution: + { + integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, + } + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + verkit@0.3.2: + resolution: + { + integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==, + } + engines: { node: ">=18.12.0" } + + vite@8.2.1: + resolution: + { + integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + "@vitejs/devtools": ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: ">=1.21.0" + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + "@types/node": + optional: true + "@vitejs/devtools": + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.10: + resolution: + { + integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==, + } + engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } + hasBin: true + peerDependencies: + "@edge-runtime/vm": "*" + "@opentelemetry/api": ^1.9.0 + "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 + "@vitest/browser-playwright": 4.1.10 + "@vitest/browser-preview": 4.1.10 + "@vitest/browser-webdriverio": 4.1.10 + "@vitest/coverage-istanbul": 4.1.10 + "@vitest/coverage-v8": 4.1.10 + "@vitest/ui": 4.1.10 + happy-dom: "*" + jsdom: "*" + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + "@edge-runtime/vm": + optional: true + "@opentelemetry/api": + optional: true + "@types/node": + optional: true + "@vitest/browser-playwright": + optional: true + "@vitest/browser-preview": + optional: true + "@vitest/browser-webdriverio": + optional: true + "@vitest/coverage-istanbul": + optional: true + "@vitest/coverage-v8": + optional: true + "@vitest/ui": + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: + { + integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, + } + engines: { node: ">=18" } + + webidl-conversions@8.0.1: + resolution: + { + integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==, + } + engines: { node: ">=20" } + + whatwg-mimetype@4.0.0: + resolution: + { + integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==, + } + engines: { node: ">=18" } + + whatwg-mimetype@5.0.0: + resolution: + { + integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==, + } + engines: { node: ">=20" } + + whatwg-url@15.1.0: + resolution: + { + integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==, + } + engines: { node: ">=20" } + + why-is-node-running@2.3.0: + resolution: + { + integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, + } + engines: { node: ">=8" } + hasBin: true + + ws@8.21.3: + resolution: + { + integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==, + } + engines: { node: ">=10.0.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@5.0.0: + resolution: + { + integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, + } + engines: { node: ">=18" } + + xmlchars@2.2.0: + resolution: + { + integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, + } + + yuku-ast@0.8.4: + resolution: + { + integrity: sha512-s7EWfWIQkaGmsGnyr/BU0jli9YTN5TvrKIsSmALyRD9elumDQInuhv0BrVObENKVCxr9W3Ikmnx5u02KvfuUmw==, + } + + yuku-codegen@0.8.4: + resolution: + { + integrity: sha512-1Rw+NYcmB1xkHAWlsIpbwIv/Fr50idtEbLf7OjDA+90dny6PM4Krz7Fs0TT+w2PBdjaldZpN4ye5wR4Dhlm8vA==, + } + + yuku-parser@0.8.4: + resolution: + { + integrity: sha512-sw41wouvT5rUmLIp87hmvm5vtF+MRSI3x6yjq6xqpYmtkQj+Ht6N7xRQ8lMhLv8N7JAzughGj0Rfi0jQRSu9HQ==, + } + +snapshots: + "@acemir/cssom@0.9.31": {} + + "@asamuzakjp/css-color@4.1.2": + dependencies: + "@csstools/css-calc": 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-color-parser": 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 + lru-cache: 11.5.2 + + "@asamuzakjp/dom-selector@6.8.1": + dependencies: + "@asamuzakjp/nwsapi": 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.5.2 + + "@asamuzakjp/nwsapi@2.3.9": {} + + "@babel/code-frame@7.29.7": + dependencies: + "@babel/helper-validator-identifier": 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + "@babel/helper-validator-identifier@7.29.7": {} + + "@babel/runtime@7.29.7": {} + + "@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + dependencies: + "@babel/runtime": 7.29.7 + "@base-ui/utils": 0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + "@floating-ui/react-dom": 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + "@floating-ui/utils": 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + "@types/react": 19.2.18 + + "@base-ui/utils@0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + dependencies: + "@babel/runtime": 7.29.7 + "@floating-ui/utils": 0.2.12 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + reselect: 5.2.0 + use-sync-external-store: 1.6.0(react@19.2.8) + optionalDependencies: + "@types/react": 19.2.18 + + "@csstools/color-helpers@6.1.0": {} + + "@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + dependencies: + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 + + "@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + dependencies: + "@csstools/color-helpers": 6.1.0 + "@csstools/css-calc": 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) + "@csstools/css-tokenizer": 4.0.0 + + "@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)": + dependencies: + "@csstools/css-tokenizer": 4.0.0 + + "@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)": + optionalDependencies: + css-tree: 3.2.1 + + "@csstools/css-tokenizer@4.0.0": {} + + "@exodus/bytes@1.15.1": {} + + "@floating-ui/core@1.8.0": + dependencies: + "@floating-ui/utils": 0.2.12 + + "@floating-ui/dom@1.8.0": + dependencies: + "@floating-ui/core": 1.8.0 + "@floating-ui/utils": 0.2.12 + + "@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + dependencies: + "@floating-ui/dom": 1.8.0 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + "@floating-ui/utils@0.2.12": {} + + "@jridgewell/sourcemap-codec@1.5.5": {} + + "@oxc-project/types@0.143.0": {} + + "@quansync/fs@1.0.0": + dependencies: + quansync: 1.0.0 + + "@rolldown/binding-android-arm64@1.2.3": + optional: true + + "@rolldown/binding-darwin-arm64@1.2.3": + optional: true + + "@rolldown/binding-darwin-x64@1.2.3": + optional: true + + "@rolldown/binding-freebsd-x64@1.2.3": + optional: true + + "@rolldown/binding-linux-arm-gnueabihf@1.2.3": + optional: true + + "@rolldown/binding-linux-arm64-gnu@1.2.3": + optional: true + + "@rolldown/binding-linux-arm64-musl@1.2.3": + optional: true + + "@rolldown/binding-linux-ppc64-gnu@1.2.3": + optional: true + + "@rolldown/binding-linux-s390x-gnu@1.2.3": + optional: true + + "@rolldown/binding-linux-x64-gnu@1.2.3": + optional: true + + "@rolldown/binding-linux-x64-musl@1.2.3": + optional: true + + "@rolldown/binding-openharmony-arm64@1.2.3": + optional: true + + "@rolldown/binding-win32-arm64-msvc@1.2.3": + optional: true + + "@rolldown/binding-win32-x64-msvc@1.2.3": + optional: true + + "@rolldown/pluginutils@1.0.1": {} + + "@standard-schema/spec@1.1.0": {} + + "@testing-library/dom@10.4.1": + dependencies: + "@babel/code-frame": 7.29.7 + "@babel/runtime": 7.29.7 + "@types/aria-query": 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + dependencies: + "@babel/runtime": 7.29.7 + "@testing-library/dom": 10.4.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + "@types/react": 19.2.18 + "@types/react-dom": 19.2.4(@types/react@19.2.18) + + "@testing-library/user-event@14.6.3(@testing-library/dom@10.4.1)": + dependencies: + "@testing-library/dom": 10.4.1 + + "@types/aria-query@5.0.4": {} + + "@types/chai@5.2.3": + dependencies: + "@types/deep-eql": 4.0.2 + assertion-error: 2.0.1 + + "@types/deep-eql@4.0.2": {} + + "@types/estree@1.0.9": {} + + "@types/react-dom@19.2.4(@types/react@19.2.18)": + dependencies: + "@types/react": 19.2.18 + + "@types/react@19.2.18": + dependencies: + csstype: 3.2.3 + + "@vitejs/plugin-react@6.0.5(vite@8.2.1)": + dependencies: + "@rolldown/pluginutils": 1.0.1 + vite: 8.2.1 + + "@vitest/expect@4.1.10": + dependencies: + "@standard-schema/spec": 1.1.0 + "@types/chai": 5.2.3 + "@vitest/spy": 4.1.10 + "@vitest/utils": 4.1.10 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + "@vitest/mocker@4.1.10(vite@8.2.1)": + dependencies: + "@vitest/spy": 4.1.10 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.1 + + "@vitest/pretty-format@4.1.10": + dependencies: + tinyrainbow: 3.1.1 + + "@vitest/runner@4.1.10": + dependencies: + "@vitest/utils": 4.1.10 + pathe: 2.0.3 + + "@vitest/snapshot@4.1.10": + dependencies: + "@vitest/pretty-format": 4.1.10 + "@vitest/utils": 4.1.10 + magic-string: 0.30.21 + pathe: 2.0.3 + + "@vitest/spy@4.1.10": {} + + "@vitest/utils@4.1.10": + dependencies: + "@vitest/pretty-format": 4.1.10 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + "@yuku-codegen/binding-android-arm64@0.8.4": + optional: true + + "@yuku-codegen/binding-darwin-arm64@0.8.4": + optional: true + + "@yuku-codegen/binding-darwin-x64@0.8.4": + optional: true + + "@yuku-codegen/binding-freebsd-x64@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-arm-gnu@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-arm-musl@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-arm64-gnu@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-arm64-musl@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-x64-gnu@0.8.4": + optional: true + + "@yuku-codegen/binding-linux-x64-musl@0.8.4": + optional: true + + "@yuku-codegen/binding-win32-arm64@0.8.4": + optional: true + + "@yuku-codegen/binding-win32-x64@0.8.4": + optional: true + + "@yuku-parser/binding-android-arm64@0.8.4": + optional: true + + "@yuku-parser/binding-darwin-arm64@0.8.4": + optional: true + + "@yuku-parser/binding-darwin-x64@0.8.4": + optional: true + + "@yuku-parser/binding-freebsd-x64@0.8.4": + optional: true + + "@yuku-parser/binding-linux-arm-gnu@0.8.4": + optional: true + + "@yuku-parser/binding-linux-arm-musl@0.8.4": + optional: true + + "@yuku-parser/binding-linux-arm64-gnu@0.8.4": + optional: true + + "@yuku-parser/binding-linux-arm64-musl@0.8.4": + optional: true + + "@yuku-parser/binding-linux-x64-gnu@0.8.4": + optional: true + + "@yuku-parser/binding-linux-x64-musl@0.8.4": + optional: true + + "@yuku-parser/binding-win32-arm64@0.8.4": + optional: true + + "@yuku-parser/binding-win32-x64@0.8.4": + optional: true + + "@yuku-toolchain/types@0.8.4": {} + + agent-base@7.1.4: {} + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + + ansis@4.3.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + assertion-error@2.0.1: {} + + axe-core@4.13.0: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + cac@7.0.0: {} + + chai@6.2.2: {} + + convert-source-map@2.0.0: {} + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + cssstyle@5.3.7: + dependencies: + "@asamuzakjp/css-color": 4.1.2 + "@csstools/css-syntax-patches-for-csstree": 1.1.7(css-tree@3.2.1) + css-tree: 3.2.1 + lru-cache: 11.5.2 + + csstype@3.2.3: {} + + data-urls@6.0.1: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 15.1.0 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + defu@6.1.7: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + dom-accessibility-api@0.5.16: {} + + dts-resolver@3.0.0: {} + + empathic@2.0.1: {} + + entities@8.0.0: {} + + es-module-lexer@2.3.1: {} + + estree-walker@3.0.3: + dependencies: + "@types/estree": 1.0.9 + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fsevents@2.3.3: + optional: true + + get-tsconfig@5.0.0-beta.5: + dependencies: + resolve-pkg-maps: 1.0.0 + + hookable@6.1.1: {} + + html-encoding-sniffer@6.0.0: + dependencies: + "@exodus/bytes": 1.15.1 + transitivePeerDependencies: + - "@noble/hashes" + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + import-without-cache@0.4.0: {} + + is-potential-custom-element-name@1.0.1: {} + + js-tokens@4.0.0: {} + + jsdom@27.4.0: + dependencies: + "@acemir/cssom": 0.9.31 + "@asamuzakjp/dom-selector": 6.8.1 + "@exodus/bytes": 1.15.1 + cssstyle: 5.3.7 + data-urls: 6.0.1 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.21.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - "@noble/hashes" + - bufferutil + - supports-color + - utf-8-validate + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lru-cache@11.5.2: {} + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + "@jridgewell/sourcemap-codec": 1.5.5 + + mdn-data@2.27.1: {} + + ms@2.1.3: {} + + nanoid@3.3.18: {} + + obug@2.1.4: {} + + parse5@8.0.1: + dependencies: + entities: 8.0.0 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prettier@3.6.2: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + punycode@2.3.1: {} + + quansync@1.0.0: {} + + react-dom@19.2.8(react@19.2.8): + dependencies: + react: 19.2.8 + scheduler: 0.27.0 + + react-is@17.0.2: {} + + react@19.2.8: {} + + require-from-string@2.0.2: {} + + reselect@5.2.0: {} + + resolve-pkg-maps@1.0.0: {} + + rolldown-plugin-dts@0.27.14(rolldown@1.2.3)(typescript@5.9.3): + dependencies: + dts-resolver: 3.0.0 + get-tsconfig: 5.0.0-beta.5 + obug: 2.1.4 + rolldown: 1.2.3 + yuku-ast: 0.8.4 + yuku-codegen: 0.8.4 + yuku-parser: 0.8.4 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - oxc-resolver + + rolldown@1.2.3: + dependencies: + "@oxc-project/types": 0.143.0 + "@rolldown/pluginutils": 1.0.1 + optionalDependencies: + "@rolldown/binding-android-arm64": 1.2.3 + "@rolldown/binding-darwin-arm64": 1.2.3 + "@rolldown/binding-darwin-x64": 1.2.3 + "@rolldown/binding-freebsd-x64": 1.2.3 + "@rolldown/binding-linux-arm-gnueabihf": 1.2.3 + "@rolldown/binding-linux-arm64-gnu": 1.2.3 + "@rolldown/binding-linux-arm64-musl": 1.2.3 + "@rolldown/binding-linux-ppc64-gnu": 1.2.3 + "@rolldown/binding-linux-s390x-gnu": 1.2.3 + "@rolldown/binding-linux-x64-gnu": 1.2.3 + "@rolldown/binding-linux-x64-musl": 1.2.3 + "@rolldown/binding-openharmony-arm64": 1.2.3 + "@rolldown/binding-win32-arm64-msvc": 1.2.3 + "@rolldown/binding-win32-x64-msvc": 1.2.3 + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + symbol-tree@3.2.4: {} + + tinybench@2.9.0: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.1: {} + + tldts-core@7.4.10: {} + + tldts@7.4.10: + dependencies: + tldts-core: 7.4.10 + + tough-cookie@6.0.2: + dependencies: + tldts: 7.4.10 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + tsdown@0.22.14(typescript@5.9.3): + dependencies: + ansis: 4.3.1 + cac: 7.0.0 + defu: 6.1.7 + empathic: 2.0.1 + hookable: 6.1.1 + import-without-cache: 0.4.0 + obug: 2.1.4 + picomatch: 4.0.5 + rolldown: 1.2.3 + rolldown-plugin-dts: 0.27.14(rolldown@1.2.3)(typescript@5.9.3) + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + unconfig-core: 7.5.0 + verkit: 0.3.2 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - "@typescript/native-preview" + - "@volar/typescript" + - oxc-resolver + - vue-tsc + + typescript@5.9.3: {} + + unconfig-core@7.5.0: + dependencies: + "@quansync/fs": 1.0.0 + quansync: 1.0.0 + + use-sync-external-store@1.6.0(react@19.2.8): + dependencies: + react: 19.2.8 + + verkit@0.3.2: {} + + vite@8.2.1: + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.3 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 + + vitest@4.1.10(jsdom@27.4.0)(vite@8.2.1): + dependencies: + "@vitest/expect": 4.1.10 + "@vitest/mocker": 4.1.10(vite@8.2.1) + "@vitest/pretty-format": 4.1.10 + "@vitest/runner": 4.1.10 + "@vitest/snapshot": 4.1.10 + "@vitest/spy": 4.1.10 + "@vitest/utils": 4.1.10 + es-module-lexer: 2.3.1 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.1 + why-is-node-running: 2.3.0 + optionalDependencies: + jsdom: 27.4.0 + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@8.21.3: {} + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + yuku-ast@0.8.4: + dependencies: + "@yuku-toolchain/types": 0.8.4 + + yuku-codegen@0.8.4: + dependencies: + "@yuku-toolchain/types": 0.8.4 + optionalDependencies: + "@yuku-codegen/binding-android-arm64": 0.8.4 + "@yuku-codegen/binding-darwin-arm64": 0.8.4 + "@yuku-codegen/binding-darwin-x64": 0.8.4 + "@yuku-codegen/binding-freebsd-x64": 0.8.4 + "@yuku-codegen/binding-linux-arm-gnu": 0.8.4 + "@yuku-codegen/binding-linux-arm-musl": 0.8.4 + "@yuku-codegen/binding-linux-arm64-gnu": 0.8.4 + "@yuku-codegen/binding-linux-arm64-musl": 0.8.4 + "@yuku-codegen/binding-linux-x64-gnu": 0.8.4 + "@yuku-codegen/binding-linux-x64-musl": 0.8.4 + "@yuku-codegen/binding-win32-arm64": 0.8.4 + "@yuku-codegen/binding-win32-x64": 0.8.4 + + yuku-parser@0.8.4: + dependencies: + "@yuku-toolchain/types": 0.8.4 + yuku-ast: 0.8.4 + optionalDependencies: + "@yuku-parser/binding-android-arm64": 0.8.4 + "@yuku-parser/binding-darwin-arm64": 0.8.4 + "@yuku-parser/binding-darwin-x64": 0.8.4 + "@yuku-parser/binding-freebsd-x64": 0.8.4 + "@yuku-parser/binding-linux-arm-gnu": 0.8.4 + "@yuku-parser/binding-linux-arm-musl": 0.8.4 + "@yuku-parser/binding-linux-arm64-gnu": 0.8.4 + "@yuku-parser/binding-linux-arm64-musl": 0.8.4 + "@yuku-parser/binding-linux-x64-gnu": 0.8.4 + "@yuku-parser/binding-linux-x64-musl": 0.8.4 + "@yuku-parser/binding-win32-arm64": 0.8.4 + "@yuku-parser/binding-win32-x64": 0.8.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..e5ba928 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,10 @@ +# pnpm 11 no longer reads the "pnpm" key in package.json, and it refuses to +# finish an install that silently skipped a package's build script. Declaring +# allowBuilds here is what keeps a clean checkout from exiting 1. +packages: + - packages/* + - apps/* + +allowBuilds: + esbuild: true + lightningcss: true diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..864768f --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "react-jsx", + "strict": true, + "noUncheckedIndexedAccess": true, + "noEmit": true, + "skipLibCheck": true, + "isolatedModules": true, + "verbatimModuleSyntax": true + } +} From 3dd48b54b25ad16449958f47bd8428a56ec100af Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 03:41:34 +0530 Subject: [PATCH 04/33] Format pre-existing docs with prettier so format:check passes --- docs/linear-audit-glossary.md | 106 +++++++----- .../plans/2026-08-09-dowel-phase-1.md | 163 +++++++++++++----- .../specs/2026-08-09-dowel-design.md | 132 +++++++------- 3 files changed, 256 insertions(+), 145 deletions(-) diff --git a/docs/linear-audit-glossary.md b/docs/linear-audit-glossary.md index c9e394f..a5f0530 100644 --- a/docs/linear-audit-glossary.md +++ b/docs/linear-audit-glossary.md @@ -24,39 +24,44 @@ Three rules reproduce ~80% of the look: ## 2. Colour system (dark, measured) ### Surfaces -| token | value | -|---|---| -| `bg-primary` | `lch(5.52% 0.4 272)` | -| `bg-secondary` | `lch(7.32% 0.85 272)` | -| `bg-tertiary` | `lch(8.22% 1.3 272)` | -| `bg-quaternary` | `lch(9.345% 0.85 272)` | -| app canvas / sidebar | `lch(2.595% 0.4 272)` | + +| token | value | +| ---------------------- | ---------------------- | +| `bg-primary` | `lch(5.52% 0.4 272)` | +| `bg-secondary` | `lch(7.32% 0.85 272)` | +| `bg-tertiary` | `lch(8.22% 1.3 272)` | +| `bg-quaternary` | `lch(9.345% 0.85 272)` | +| app canvas / sidebar | `lch(2.595% 0.4 272)` | | elevated (menu, modal) | `lch(12.72% 0.85 272)` | ### Borders -| token | value | -|---|---| + +| token | value | +| ------------------ | ---------------------- | | `border-primary` | `lch(9.84% 1.48 272)` | | `border-secondary` | `lch(14.16% 1.48 272)` | | `border-tertiary` | `lch(16.32% 1.48 272)` | | popover border | `lch(25.68% 1.93 272)` | ### Text -| token | value | use | -|---|---|---| -| `text-primary` | `lch(100% 0 272)` | titles, selected | -| `text-secondary` | `lch(90.451% 1.2 272)` | body, control labels | + +| token | value | use | +| ----------------- | ---------------------- | --------------------------- | +| `text-primary` | `lch(100% 0 272)` | titles, selected | +| `text-secondary` | `lch(90.451% 1.2 272)` | body, control labels | | `text-tertiary` | `lch(61.803% 1.2 272)` | metadata, idle tabs — **φ** | -| `text-quaternary` | `lch(36.975% 1.2 272)` | disabled | +| `text-quaternary` | `lch(36.975% 1.2 272)` | disabled | `61.803` is the golden ratio. The ramp is φ-derived, not hand-picked. ### Accent + - brand / primary action: `#5e6ad2` - focus ring: `#5e69d1`, `1px solid`, also seen as `0 0 0 1px` - accent ramp: `#636fd7 #6974e1 #6b75df #727ce6 #6f7ffe` ### Light-theme anchors (exposed, not fully readable) + `bg base #f9f9fa` · `sidebar #efeff0` · `border #e2e2e2` · `text #23252a` · `elevated #fefeff` · `muted #b0b5c0` @@ -71,16 +76,16 @@ theme. dowel derives its own light ramp from the same hue-272 rule. Font: **Inter Variable** (`InterVariable.woff2`), mono: Berkeley Mono (licensed — dowel must substitute). -| step | size | px | -|---|---|---| -| micro | .6875rem | 11 | -| mini | .75rem | 12 | -| small | .8125rem | 13 | -| regular | .9375rem | 15 | -| large | 1.125rem | 18 | -| title3 | 1.25rem | 20 | -| title2 | 1.5rem | 24 | -| title1 | 2.25rem | 36 | +| step | size | px | +| ------- | -------- | --- | +| micro | .6875rem | 11 | +| mini | .75rem | 12 | +| small | .8125rem | 13 | +| regular | .9375rem | 15 | +| large | 1.125rem | 18 | +| title3 | 1.25rem | 20 | +| title2 | 1.5rem | 24 | +| title1 | 2.25rem | 36 | Weights: `300 / 450 / 500 / 600 / 700` — **normal is 450**. @@ -95,33 +100,33 @@ body/editor copy. ## 4. Shape & elevation -| thing | value | -|---|---| -| control radius | `8px` | -| pill / tab / property button | `9999px` | -| popover, modal, input | `12px` | -| kbd, small chip | `4px` | -| editor block | `6px` | -| settings row | `10px` | -| avatar (20px) | `8px` — **not a circle** | -| hairline border | `0.5px` | +| thing | value | +| ---------------------------- | ------------------------ | +| control radius | `8px` | +| pill / tab / property button | `9999px` | +| popover, modal, input | `12px` | +| kbd, small chip | `4px` | +| editor block | `6px` | +| settings row | `10px` | +| avatar (20px) | `8px` — **not a circle** | +| hairline border | `0.5px` | Two elevation tiers only: ```css /* popover, dropdown, select, context menu */ box-shadow: - 0 3px 8px lch(0 0 0/.125), - 0 2px 5px lch(0 0 0/.125), - 0 1px 1px lch(0 0 0/.125); + 0 3px 8px lch(0 0 0/0.125), + 0 2px 5px lch(0 0 0/0.125), + 0 1px 1px lch(0 0 0/0.125); /* modal, command palette */ box-shadow: - 0 4px 40px lch(0 0 0/.10), - 0 3px 20px lch(0 0 0/.125), - 0 3px 12px lch(0 0 0/.125), - 0 2px 8px lch(0 0 0/.125), - 0 1px 1px lch(0 0 0/.125); + 0 4px 40px lch(0 0 0/0.1), + 0 3px 20px lch(0 0 0/0.125), + 0 3px 12px lch(0 0 0/0.125), + 0 2px 8px lch(0 0 0/0.125), + 0 1px 1px lch(0 0 0/0.125); ``` Both pair with `border: 0.5px solid `. The 0.5px hairline plus @@ -137,7 +142,11 @@ easing set (`ease-out-quad` = `cubic-bezier(.25,.46,.45,.94)` is the workhorse). **Measured on real controls: `0.15s`** — and never `all`: ```css -transition: border .15s, background-color .15s, color .15s, opacity .15s; +transition: + border 0.15s, + background-color 0.15s, + color 0.15s, + opacity 0.15s; ``` Only these four properties animate. Nothing transitions transform or size on @@ -148,10 +157,12 @@ hover. This is a deliberate restraint worth copying. ## 6. Component measurements ### Control heights + `24px` small icon button · `28px` **everything else** · `32px` menu option · `36px` filter input · `40px` command-palette input · `46px` palette row ### Segmented tabs (Assigned / Created / …) + ``` height 28 · padding 0 10 · radius 9999 · font 12/500 idle bg lch(10.149 0.593 272) text lch(61.803 1.2 272) @@ -160,6 +171,7 @@ border 0.5px solid transparent ``` ### Sidebar item + ``` height 28 · padding 0 9 0 8 · radius 8 · font 13/500 idle text lch(60.621 1.2 272) @@ -170,15 +182,18 @@ sidebar width 244px ``` ### Property button (status, assignee, label — the pill) + ``` height 28 · padding 0 10 0 6 · radius 9999 · font 13/500 text lch(90.451 1.2 272) open bg lch(14.006 0.593 272) transition border, background-color, color, opacity ``` + Asymmetric padding: 6px left (icon side), 10px right. ### Icon button + ``` 28×28 · padding 0 2 · radius 9999 · border 0.5px solid transparent 24×24 · padding 0 4 · radius 9999 (compact) @@ -186,6 +201,7 @@ hover bg lch(10.149 0.689 272) ``` ### Popover / Select + ``` container role=dialog · radius 12 · bg lch(12.72 0.85 272) border 0.5px solid lch(25.68 1.93 272) · 3-layer shadow @@ -197,6 +213,7 @@ filter input height 36 · padding 10 0 9 · transition color .1s ease-in-out ``` ### Command palette + ``` dialog 720×450 · radius 12 · bg lch(12.72 0.85 272) border 0.5px solid lch(25.68 1.93 272) · 5-layer shadow @@ -207,6 +224,7 @@ row height 46 · padding 0 12 · gap 12 · font 15/400 ``` ### Kbd + ``` 24×17 · padding 2 · radius 4 · font 11/400 border 0.5px solid lch(20.28 1.93 272) @@ -214,6 +232,7 @@ text lch(64.714 1.425 272) · gap 3px between keys ``` ### List row (issue) + ``` id 13/450 · tracking -0.26px · text tertiary title 13/500 · text primary @@ -223,6 +242,7 @@ avatar 20×20 radius 8 ``` ### Editor / composer + ``` title 24/600 · tracking -0.1px body 15/450 · line-height 1.6 · tracking -0.00667em diff --git a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md index ab2fa11..2deb9d5 100644 --- a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md +++ b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md @@ -24,6 +24,7 @@ - **Never ship** Linear's logo, icons, or Berkeley Mono. Mono stack is JetBrains Mono / `ui-monospace`. **Reference documents (read before starting):** + - Spec: `docs/superpowers/specs/2026-08-09-dowel-design.md` - Measured values: `docs/linear-audit-glossary.md` @@ -63,24 +64,27 @@ apps/docs/ TanStack Start, prerendered → dowel.sh .changeset/config.json ``` -**Responsibility boundaries:** one directory per component holding its markup, its styles and its tests together — they change together. Tokens are split by *what changes per theme* (colour) versus *what never does* (scale), because dark mode overrides exactly one of those files. +**Responsibility boundaries:** one directory per component holding its markup, its styles and its tests together — they change together. Tokens are split by _what changes per theme_ (colour) versus _what never does_ (scale), because dark mode overrides exactly one of those files. --- ## Task 1: Workspace scaffold **Files:** + - Create: `pnpm-workspace.yaml`, `package.json`, `.npmrc`, `.prettierrc`, `tsconfig.base.json` - Create: `packages/dowel/package.json`, `packages/dowel/tsconfig.json` - Modify: `mise.toml` (already exists, verify contents) **Interfaces:** + - Consumes: nothing (first task) - Produces: workspace where `pnpm install`, `pnpm -r typecheck`, `pnpm format:check` all run clean. Package name `dowel`. Every later task runs commands from repo root. - [ ] **Step 1: Create the workspace manifest** `pnpm-workspace.yaml`: + ```yaml # pnpm 11 no longer reads the "pnpm" key in package.json, and it refuses to # finish an install that silently skipped a package's build script. Declaring @@ -120,12 +124,14 @@ allowBuilds: - [ ] **Step 3: Create .npmrc, .prettierrc and LICENSE** `.npmrc`: + ``` # Keep the lockfile honest in CI; mise pins the pnpm version itself. engine-strict=true ``` `.prettierrc`: + ```json { "semi": true, @@ -163,9 +169,11 @@ SOFTWARE. ``` Also copy it into the package so it ships with the tarball: + ```bash cp LICENSE packages/dowel/LICENSE ``` + and add `"LICENSE"` to the package's `files` array alongside `"dist"`. - [ ] **Step 4: Create tsconfig.base.json** @@ -259,6 +267,7 @@ and add `"LICENSE"` to the package's `files` array alongside `"dist"`. Run: `cat mise.toml` Expected to contain exactly: + ```toml [tools] node = "24.18.0" @@ -273,6 +282,7 @@ node = "24.18.0" pnpm install pnpm format:check ``` + Expected: install completes, format check passes. `pnpm typecheck` will fail — there is no `src` yet. That is expected; Task 2 creates it. - [ ] **Step 9: Commit** @@ -287,6 +297,7 @@ git commit -m "Scaffold the pnpm workspace and dowel package" ## Task 2: Token layer **Files:** + - Create: `packages/dowel/src/tokens/scale.css` - Create: `packages/dowel/src/tokens/light.css` - Create: `packages/dowel/src/tokens/dark.css` @@ -294,12 +305,14 @@ git commit -m "Scaffold the pnpm workspace and dowel package" - Test: `packages/dowel/test/tokens.test.ts` **Interfaces:** + - Consumes: Task 1's workspace. - Produces: the complete `--dowel-*` custom property set. Every component CSS file from Task 4 onward references ONLY these names. Layer order is declared as `@layer dowel.tokens, dowel.base, dowel.components;` — component CSS must live in `dowel.components`. - [ ] **Step 1: Write the failing test** `packages/dowel/test/tokens.test.ts`: + ```ts import { readFileSync } from "node:fs"; import { resolve } from "node:path"; @@ -349,6 +362,7 @@ Expected: FAIL — `ENOENT` on `src/index.css`. - [ ] **Step 3: Write scale.css** `packages/dowel/src/tokens/scale.css`: + ```css /* Non-colour tokens. Identical in light and dark — dark.css overrides colour only, so this file must never contain a colour value. */ @@ -424,6 +438,7 @@ Expected: FAIL — `ENOENT` on `src/index.css`. - [ ] **Step 4: Write light.css** `packages/dowel/src/tokens/light.css`: + ```css /* Light is the default theme. Every neutral is one hue at low chroma — greys that are not grey. Changing --dowel-hue retints the entire library. */ @@ -477,6 +492,7 @@ Expected: FAIL — `ENOENT` on `src/index.css`. - [ ] **Step 5: Write dark.css** `packages/dowel/src/tokens/dark.css`: + ```css /* Dark overrides colour only. Three activation paths, in precedence order: explicit class, explicit attribute, then system preference — and the media @@ -568,6 +584,7 @@ spec. The `tokens.test.ts` parity test is what keeps the two copies honest. - [ ] **Step 6: Write index.css** `packages/dowel/src/index.css`: + ```css /* Layer order is declared once, first, before any @import. Consumers' own unlayered styles beat every layer here, so overriding dowel never becomes a @@ -624,6 +641,7 @@ self-hosted or served from a CDN. - [ ] **Step 8: Create the vitest config so tests can run** `packages/dowel/vitest.config.ts`: + ```ts import react from "@vitejs/plugin-react"; import { defineConfig } from "vitest/config"; @@ -639,6 +657,7 @@ export default defineConfig({ ``` `packages/dowel/test/setup.ts`: + ```ts // Placeholder until Task 4 adds the axe matcher. Kept as a file so the // vitest config resolves from the very first test run. @@ -662,18 +681,21 @@ git commit -m "Add the dowel token layer with light and dark parity tests" ## Task 3: Build pipeline **Files:** + - Create: `packages/dowel/tsdown.config.ts` - Create: `packages/dowel/scripts/build-css.mjs` - Create: `packages/dowel/src/index.ts` - Test: `packages/dowel/test/css-contract.test.ts` **Interfaces:** + - Consumes: Task 2's `src/index.css` and token names. - Produces: `pnpm --filter dowel build` emitting `dist/index.js`, `dist/index.d.ts`, `dist/dowel.css`. Build order is `tsdown` then `build-css.mjs` — never the reverse. Produces no class-name helper: components in Tasks 4+ write `className="dowel-x"` as a plain string literal. - [ ] **Step 1: Write the failing build-contract test** `packages/dowel/test/css-contract.test.ts`: + ```ts import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; @@ -718,6 +740,7 @@ Expected: FAIL — `dist/dowel.css` does not exist. - [ ] **Step 3: Write the CSS build script** `packages/dowel/scripts/build-css.mjs`: + ```js // Bundles src/index.css (following @import) into one minified dist/dowel.css. // Lightning CSS is used as a library rather than the CLI so the targets and @@ -761,6 +784,7 @@ pnpm --filter dowel add -D browserslist@^4.26.0 - [ ] **Step 5: Write tsdown.config.ts** `packages/dowel/tsdown.config.ts`: + ```ts import { defineConfig } from "tsdown"; @@ -789,7 +813,7 @@ attributes, so no component ever builds a conditional class name — every `className` in this library is a single string literal: ```tsx -className="dowel-btn" +className = "dowel-btn"; ``` If a later phase genuinely needs conditional classes, add the helper then. @@ -798,6 +822,7 @@ Do not add one now, and do not wrap these literals in a function. - [ ] **Step 7: Write the barrel** `packages/dowel/src/index.ts`: + ```ts // Components are appended here by each component task. export {}; @@ -809,6 +834,7 @@ export {}; pnpm --filter dowel build pnpm --filter dowel test css-contract ``` + Expected: build prints a kB size; all 4 contract tests PASS. - [ ] **Step 9: Add dist to .gitignore and commit** @@ -827,12 +853,14 @@ This task establishes the pattern every later component copies: Base UI primitive, `data-*` variants, colocated CSS, a11y test, both-theme render. **Files:** + - Create: `packages/dowel/src/components/button/{index.tsx,button.css,button.test.tsx}` - Create: `packages/dowel/test/render.tsx` - Modify: `packages/dowel/test/setup.ts` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx` from Task 3, tokens from Task 2. - Produces: - `Button` — `React.forwardRef` @@ -843,6 +871,7 @@ primitive, `data-*` variants, colocated CSS, a11y test, both-theme render. - [ ] **Step 1: Write the shared test helpers** `packages/dowel/test/setup.ts`: + ```ts import axe from "axe-core"; import { expect } from "vitest"; @@ -861,6 +890,7 @@ export async function expectNoA11yViolations(el: HTMLElement): Promise { ``` `packages/dowel/test/render.tsx`: + ```tsx import { render } from "@testing-library/react"; import type { ReactElement } from "react"; @@ -883,6 +913,7 @@ export function renderBoth(ui: ReactElement) { - [ ] **Step 2: Write the failing Button test** `packages/dowel/src/components/button/button.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -966,6 +997,7 @@ Expected: FAIL — cannot resolve `./index`. - [ ] **Step 4: Write the Button component** `packages/dowel/src/components/button/index.tsx`: + ```tsx import { Button as BaseButton } from "@base-ui/react/button"; import { forwardRef } from "react"; @@ -1008,6 +1040,7 @@ export const Button = forwardRef( - [ ] **Step 5: Write button.css** `packages/dowel/src/components/button/button.css`: + ```css @layer dowel.components { .dowel-btn { @@ -1091,12 +1124,14 @@ export const Button = forwardRef( - [ ] **Step 6: Wire it into the barrel and the stylesheet** Append to `packages/dowel/src/index.ts`: + ```ts export { Button } from "./components/button"; export type { ButtonProps } from "./components/button"; ``` Append to `packages/dowel/src/index.css` (after the `dowel.base` block): + ```css @import "./components/button/button.css"; ``` @@ -1112,6 +1147,7 @@ Expected: PASS, 9 tests. pnpm --filter dowel build pnpm --filter dowel test ``` + Expected: all suites PASS. If `css-contract` reports a missing token, the button CSS referenced a name absent from Task 2 — fix the reference, do not add an ad-hoc token. - [ ] **Step 9: Commit** @@ -1126,16 +1162,19 @@ git commit -m "Add Button and the component authoring pattern" ## Task 5: IconButton **Files:** + - Create: `packages/dowel/src/components/icon-button/{index.tsx,icon-button.css,icon-button.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, `renderBoth`, `expectNoA11yViolations`. - Produces: `IconButton`, `IconButtonProps = { variant?: "secondary" | "ghost"; size?: "sm" | "md"; label: string; render?: ReactElement }`. `label` is required and becomes `aria-label` — an icon-only button with no name is the single most common a11y defect in component libraries, so the type system forbids it. - [ ] **Step 1: Write the failing test** `packages/dowel/src/components/icon-button/icon-button.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; @@ -1204,6 +1243,7 @@ Expected: FAIL — cannot resolve `./index`. - [ ] **Step 3: Write the component** `packages/dowel/src/components/icon-button/index.tsx`: + ```tsx import { Button as BaseButton } from "@base-ui/react/button"; import { forwardRef } from "react"; @@ -1245,6 +1285,7 @@ export const IconButton = forwardRef( - [ ] **Step 4: Write the CSS** `packages/dowel/src/components/icon-button/icon-button.css`: + ```css @layer dowel.components { .dowel-icon-btn { @@ -1293,12 +1334,14 @@ export const IconButton = forwardRef( - [ ] **Step 5: Wire it up** Append to `src/index.ts`: + ```ts export { IconButton } from "./components/icon-button"; export type { IconButtonProps } from "./components/icon-button"; ``` Append to `src/index.css`: + ```css @import "./components/icon-button/icon-button.css"; ``` @@ -1309,6 +1352,7 @@ Append to `src/index.css`: pnpm --filter dowel test icon-button pnpm --filter dowel build && pnpm --filter dowel test ``` + Expected: PASS. - [ ] **Step 7: Commit** @@ -1326,11 +1370,13 @@ Two plain-element components with no Base UI dependency. Paired because each is small and they share the same "static display element" shape. **Files:** + - Create: `packages/dowel/src/components/badge/{index.tsx,badge.css,badge.test.tsx}` - Create: `packages/dowel/src/components/kbd/{index.tsx,kbd.css,kbd.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, `renderBoth`, `expectNoA11yViolations`. - Produces: - `Badge`, `BadgeProps = { tone?: "neutral" | "accent" | "success" | "warning" | "danger" }` + span props minus className/style. @@ -1339,6 +1385,7 @@ small and they share the same "static display element" shape. - [ ] **Step 1: Write both failing tests** `packages/dowel/src/components/badge/badge.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; @@ -1376,6 +1423,7 @@ describe("Badge", () => { ``` `packages/dowel/src/components/kbd/kbd.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import { describe, expect, it } from "vitest"; @@ -1418,6 +1466,7 @@ Expected: FAIL — modules not found. - [ ] **Step 3: Write Badge** `packages/dowel/src/components/badge/index.tsx`: + ```tsx import { forwardRef } from "react"; import type { ComponentPropsWithoutRef } from "react"; @@ -1431,18 +1480,12 @@ export const Badge = forwardRef(function Badge( { tone = "neutral", ...props }, ref, ) { - return ( - - ); + return ; }); ``` `packages/dowel/src/components/badge/badge.css`: + ```css @layer dowel.components { .dowel-badge { @@ -1486,6 +1529,7 @@ export const Badge = forwardRef(function Badge( - [ ] **Step 4: Write Kbd** `packages/dowel/src/components/kbd/index.tsx`: + ```tsx import { forwardRef } from "react"; import type { ComponentPropsWithoutRef } from "react"; @@ -1514,6 +1558,7 @@ export const Kbd = forwardRef(function Kbd( ``` `packages/dowel/src/components/kbd/kbd.css`: + ```css @layer dowel.components { .dowel-kbd { @@ -1547,6 +1592,7 @@ export const Kbd = forwardRef(function Kbd( - [ ] **Step 5: Wire both up** Append to `src/index.ts`: + ```ts export { Badge } from "./components/badge"; export type { BadgeProps } from "./components/badge"; @@ -1555,6 +1601,7 @@ export type { KbdProps } from "./components/kbd"; ``` Append to `src/index.css`: + ```css @import "./components/badge/badge.css"; @import "./components/kbd/kbd.css"; @@ -1566,6 +1613,7 @@ Append to `src/index.css`: pnpm --filter dowel test badge kbd pnpm --filter dowel build && pnpm --filter dowel test ``` + Expected: PASS. - [ ] **Step 7: Commit** @@ -1580,10 +1628,12 @@ git commit -m "Add Badge and Kbd" ## Task 7: Input and Field **Files:** + - Create: `packages/dowel/src/components/input/{index.tsx,input.css,input.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, test helpers. - Produces: - `Input`, `InputProps = { size?: "md" | "lg"; invalid?: boolean }` + native input props minus className/style. @@ -1592,6 +1642,7 @@ git commit -m "Add Badge and Kbd" - [ ] **Step 1: Write the failing test** `packages/dowel/src/components/input/input.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -1702,6 +1753,7 @@ matching `id`/`htmlFor` strings — that is the bug Field exists to remove. - [ ] **Step 3: Write the component** `packages/dowel/src/components/input/index.tsx`: + ```tsx import { Field as BaseField } from "@base-ui/react/field"; import { Input as BaseInput } from "@base-ui/react/input"; @@ -1739,9 +1791,7 @@ export const Field = { HTMLDivElement, Omit, "className" | "style"> >(function FieldRoot(props, ref) { - return ( - - ); + return ; }), Label: forwardRef< @@ -1749,11 +1799,7 @@ export const Field = { Omit, "className" | "style"> >(function FieldLabel(props, ref) { return ( - + ); }), @@ -1775,11 +1821,7 @@ export const Field = { Omit, "className" | "style"> >(function FieldError(props, ref) { return ( - + ); }), }; @@ -1788,6 +1830,7 @@ export const Field = { - [ ] **Step 4: Write the CSS** `packages/dowel/src/components/input/input.css`: + ```css @layer dowel.components { .dowel-input { @@ -1863,12 +1906,14 @@ export const Field = { - [ ] **Step 5: Wire it up** Append to `src/index.ts`: + ```ts export { Input, Field } from "./components/input"; export type { InputProps } from "./components/input"; ``` Append to `src/index.css`: + ```css @import "./components/input/input.css"; ``` @@ -1879,6 +1924,7 @@ Append to `src/index.css`: pnpm --filter dowel test input pnpm --filter dowel build && pnpm --filter dowel test ``` + Expected: PASS. - [ ] **Step 7: Commit** @@ -1893,16 +1939,19 @@ git commit -m "Add Input and Field with automatic label association" ## Task 8: Dialog **Files:** + - Create: `packages/dowel/src/components/dialog/{index.tsx,dialog.css,dialog.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, test helpers. - Produces: `Dialog` compound — `{ Root, Trigger, Portal, Backdrop, Popup, Title, Description, Close }`. Uses the **modal** shadow tier (`--dowel-shadow-modal`). - [ ] **Step 1: Write the failing test** `packages/dowel/src/components/dialog/dialog.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -1969,6 +2018,7 @@ Expected: FAIL — cannot resolve `./index`. - [ ] **Step 3: Write the component** `packages/dowel/src/components/dialog/index.tsx`: + ```tsx import { Dialog as BaseDialog } from "@base-ui/react/dialog"; import type { ComponentProps } from "react"; @@ -2000,10 +2050,7 @@ export const Dialog = { props: Props, ) { return ( - + ); }, @@ -2014,6 +2061,7 @@ export const Dialog = { - [ ] **Step 4: Write the CSS** `packages/dowel/src/components/dialog/dialog.css`: + ```css @layer dowel.components { .dowel-backdrop { @@ -2074,11 +2122,13 @@ export const Dialog = { - [ ] **Step 5: Wire it up** Append to `src/index.ts`: + ```ts export { Dialog } from "./components/dialog"; ``` Append to `src/index.css`: + ```css @import "./components/dialog/dialog.css"; ``` @@ -2089,6 +2139,7 @@ Append to `src/index.css`: pnpm --filter dowel test dialog pnpm --filter dowel build && pnpm --filter dowel test ``` + Expected: PASS. - [ ] **Step 7: Commit** @@ -2103,16 +2154,19 @@ git commit -m "Add Dialog on the modal elevation tier" ## Task 9: Menu **Files:** + - Create: `packages/dowel/src/components/menu/{index.tsx,menu.css,menu.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, `Button` (for the trigger in tests). - Produces: `Menu` compound — `{ Root, Trigger, Portal, Positioner, Popup, Item, Separator, Group, GroupLabel }`. Uses the **popover** shadow tier. Item height 32px per the audit. - [ ] **Step 1: Write the failing test** `packages/dowel/src/components/menu/menu.test.tsx`: + ```tsx import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -2189,6 +2243,7 @@ Expected: FAIL — cannot resolve `./index`. - [ ] **Step 3: Write the component** `packages/dowel/src/components/menu/index.tsx`: + ```tsx import { Menu as BaseMenu } from "@base-ui/react/menu"; import type { ComponentProps } from "react"; @@ -2219,9 +2274,7 @@ export const Menu = { }, Separator: function MenuSeparator(props: Props) { - return ( - - ); + return ; }, Group: BaseMenu.Group, @@ -2229,9 +2282,7 @@ export const Menu = { GroupLabel: function MenuGroupLabel( props: Props, ) { - return ( - - ); + return ; }, }; ``` @@ -2239,6 +2290,7 @@ export const Menu = { - [ ] **Step 4: Write the CSS** `packages/dowel/src/components/menu/menu.css`: + ```css @layer dowel.components { .dowel-menu { @@ -2310,11 +2362,13 @@ export const Menu = { - [ ] **Step 5: Wire it up** Append to `src/index.ts`: + ```ts export { Menu } from "./components/menu"; ``` Append to `src/index.css`: + ```css @import "./components/menu/menu.css"; ``` @@ -2325,6 +2379,7 @@ Append to `src/index.css`: pnpm --filter dowel test menu pnpm --filter dowel build && pnpm --filter dowel test ``` + Expected: PASS. - [ ] **Step 7: Commit** @@ -2339,16 +2394,19 @@ git commit -m "Add Menu with keyboard navigation" ## Task 10: Tooltip **Files:** + - Create: `packages/dowel/src/components/tooltip/{index.tsx,tooltip.css,tooltip.test.tsx}` - Modify: `packages/dowel/src/index.ts`, `packages/dowel/src/index.css` **Interfaces:** + - Consumes: `cx`, tokens, `IconButton`. - Produces: `Tooltip` compound — `{ Provider, Root, Trigger, Portal, Positioner, Popup }`. Consumers must wrap their app in `Tooltip.Provider` once. - [ ] **Step 1: Write the failing test** `packages/dowel/src/components/tooltip/tooltip.test.tsx`: + ```tsx import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; @@ -2414,6 +2472,7 @@ Expected: FAIL — cannot resolve `./index`. - [ ] **Step 3: Write the component** `packages/dowel/src/components/tooltip/index.tsx`: + ```tsx import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip"; import type { ComponentProps } from "react"; @@ -2445,6 +2504,7 @@ export const Tooltip = { - [ ] **Step 4: Write the CSS** `packages/dowel/src/components/tooltip/tooltip.css`: + ```css @layer dowel.components { .dowel-tooltip { @@ -2473,11 +2533,13 @@ export const Tooltip = { - [ ] **Step 5: Wire it up** Append to `src/index.ts`: + ```ts export { Tooltip } from "./components/tooltip"; ``` Append to `src/index.css`: + ```css @import "./components/tooltip/tooltip.css"; ``` @@ -2489,6 +2551,7 @@ pnpm --filter dowel build pnpm --filter dowel test pnpm typecheck ``` + Expected: all PASS. This is the complete 8-component slice. - [ ] **Step 7: Commit** @@ -2503,15 +2566,18 @@ git commit -m "Add Tooltip, completing the phase 1 component slice" ## Task 11: CI workflow **Files:** + - Create: `.github/workflows/ci.yml` **Interfaces:** + - Consumes: root scripts `format:check`, `typecheck`, `build`, `test` from Task 1. - Produces: a workflow named exactly **`CI`** — Task 12's release workflow keys off that name in its `workflow_run` trigger. Renaming it breaks releases. - [ ] **Step 1: Write the workflow** `.github/workflows/ci.yml`: + ```yaml name: CI @@ -2578,6 +2644,7 @@ jobs: pnpm install --frozen-lockfile pnpm format:check && pnpm typecheck && pnpm build && pnpm test ``` + Expected: all four succeed. Fix anything red before pushing — a red first CI run is noise. - [ ] **Step 3: Commit and open a PR** @@ -2599,11 +2666,13 @@ Expected: the `CI / build` check passes. ## Task 12: Release automation **Files:** + - Create: `.changeset/config.json` - Create: `.github/workflows/release.yml` - Modify: root `package.json` (add `@changesets/cli`) **Interfaces:** + - Consumes: the workflow named `CI` from Task 11; the org secret `NPM_TOKEN`. - Produces: merging a PR that contains a changeset opens a "Version Packages" PR; merging that publishes `dowel` to npm. @@ -2617,13 +2686,11 @@ pnpm exec changeset init - [ ] **Step 2: Configure it** `.changeset/config.json`: + ```json { "$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json", - "changelog": [ - "@changesets/changelog-github", - { "repo": "karnstack/dowel" } - ], + "changelog": ["@changesets/changelog-github", { "repo": "karnstack/dowel" }], "commit": false, "fixed": [], "linked": [], @@ -2641,6 +2708,7 @@ pnpm add -Dw @changesets/changelog-github@^0.5.1 - [ ] **Step 3: Write the release workflow** `.github/workflows/release.yml`: + ```yaml name: Release @@ -2707,6 +2775,7 @@ Set `packages/dowel/package.json` `"version"` to `"0.0.0"` (already done in Task ```bash pnpm exec changeset ``` + Choose `dowel`, select **minor**, and use the summary: `First release: token system, build pipeline, and eight components.` @@ -2728,9 +2797,11 @@ Run: `gh pr list` Expected: a `chore: version packages` PR exists. Merging it publishes `dowel@0.1.0`. **Verification after that merge:** + ```bash sleep 60 && curl -s https://registry.npmjs.org/dowel | python3 -c "import sys,json;print(json.load(sys.stdin)['dist-tags'])" ``` + Expected: `{"latest": "0.1.0"}`. --- @@ -2738,17 +2809,20 @@ Expected: `{"latest": "0.1.0"}`. ## Task 13: Docs site **Files:** + - Create: `apps/docs/` — TanStack Start app - Create: `apps/docs/package.json`, `apps/docs/app.config.ts`, `apps/docs/wrangler.jsonc` - Create: `apps/docs/src/routes/{__root.tsx,index.tsx,components/button.tsx}` **Interfaces:** + - Consumes: the built `dowel` package via workspace protocol. - Produces: a prerendered static site in `apps/docs/dist` ready for Task 14's deploy. - [ ] **Step 1: Scaffold the app package** `apps/docs/package.json`: + ```json { "name": "@dowel/docs", @@ -2780,6 +2854,7 @@ Expected: `{"latest": "0.1.0"}`. - [ ] **Step 2: Configure prerendering** `apps/docs/app.config.ts`: + ```ts import { defineConfig } from "@tanstack/react-start/config"; @@ -2799,6 +2874,7 @@ export default defineConfig({ - [ ] **Step 3: Write the root route importing dowel's stylesheet** `apps/docs/src/routes/__root.tsx`: + ```tsx import { Outlet, createRootRoute } from "@tanstack/react-router"; import { Tooltip } from "dowel"; @@ -2821,6 +2897,7 @@ export const Route = createRootRoute({ - [ ] **Step 4: Write the landing route** `apps/docs/src/routes/index.tsx`: + ```tsx import { createFileRoute } from "@tanstack/react-router"; import { Badge, Button, Kbd } from "dowel"; @@ -2848,6 +2925,7 @@ function Home() { - [ ] **Step 5: Write the Button docs route** `apps/docs/src/routes/components/button.tsx`: + ```tsx import { createFileRoute } from "@tanstack/react-router"; import { Button } from "dowel"; @@ -2880,6 +2958,7 @@ pnpm --filter dowel build pnpm --filter @dowel/docs build test -f apps/docs/dist/index.html && echo "prerender OK" ``` + Expected: `prerender OK`. If the file is missing, the preset did not run static generation — check `app.config.ts`. - [ ] **Step 7: Commit and PR** @@ -2897,16 +2976,19 @@ gh pr checks --watch ## Task 14: Cloudflare deploy **Files:** + - Create: `apps/docs/wrangler.jsonc` - Create: `.github/workflows/deploy-docs.yml` **Interfaces:** + - Consumes: `apps/docs/dist` from Task 13; the org secret `CLOUDFLARE_API_TOKEN`. - Produces: dowel.sh serving the docs. - [ ] **Step 1: Write the Worker config** `apps/docs/wrangler.jsonc`: + ```jsonc // dowel.sh — the docs site, served as Cloudflare Worker static assets. // Assets-only: no worker script, because the site is fully prerendered. @@ -2917,16 +2999,17 @@ gh pr checks --watch "name": "dowel-sh", "compatibility_date": "2026-08-09", "assets": { - "directory": "./dist" + "directory": "./dist", }, "workers_dev": false, - "routes": [{ "pattern": "dowel.sh", "custom_domain": true }] + "routes": [{ "pattern": "dowel.sh", "custom_domain": true }], } ``` - [ ] **Step 2: Write the deploy workflow** `.github/workflows/deploy-docs.yml`: + ```yaml # Deploys dowel.sh when the docs or the library change on main. # @@ -3008,6 +3091,7 @@ gh pr checks --watch gh run list --workflow=deploy-docs --limit 1 curl -sS -o /dev/null -w "%{http_code}\n" https://dowel.sh ``` + Expected: workflow `completed success`, and HTTP `200` from dowel.sh. If the custom domain 522s or 404s on the first deploy, the route is still @@ -3021,6 +3105,7 @@ curl -s https://registry.npmjs.org/dowel | python3 -c "import sys,json;print(jso curl -sS -o /dev/null -w "dowel.sh %{http_code}\n" https://dowel.sh gh run list --limit 5 ``` + Expected: `dowel@0.1.0` on npm, `dowel.sh 200`, recent runs green. --- diff --git a/docs/superpowers/specs/2026-08-09-dowel-design.md b/docs/superpowers/specs/2026-08-09-dowel-design.md index 2b18173..5488849 100644 --- a/docs/superpowers/specs/2026-08-09-dowel-design.md +++ b/docs/superpowers/specs/2026-08-09-dowel-design.md @@ -24,16 +24,16 @@ identical, unglamorous, load-bearing. That is what a component is. dowel is an homage to Linear's craft. The README credits them and states clearly that dowel is unaffiliated with and unendorsed by Linear. -We reimplement the *visual language* — colour relationships, spacing, density, +We reimplement the _visual language_ — colour relationships, spacing, density, radii, motion curves — which is not protectable and is fair to learn from. We ship none of their assets: -| never ship | reason | dowel does instead | -|---|---|---| -| Linear logo / wordmark | trademark | own mark | -| Linear's icon set | their original artwork | Lucide, or redraw | -| Berkeley Mono | commercial licence | JetBrains Mono / `ui-monospace` | -| any affiliation claim | false endorsement | explicit disclaimer | +| never ship | reason | dowel does instead | +| ---------------------- | ---------------------- | ------------------------------- | +| Linear logo / wordmark | trademark | own mark | +| Linear's icon set | their original artwork | Lucide, or redraw | +| Berkeley Mono | commercial licence | JetBrains Mono / `ui-monospace` | +| any affiliation claim | false endorsement | explicit disclaimer | Inter Variable is OFL-licensed and safe to self-host. @@ -76,42 +76,42 @@ All public tokens are prefixed `--dowel-`. --dowel-hue: 272; /* surfaces — 4 steps */ - --dowel-bg-1: lch(99% 0.4 var(--dowel-hue)); - --dowel-bg-2: lch(97% 0.85 var(--dowel-hue)); - --dowel-bg-3: lch(94.5% 1.3 var(--dowel-hue)); - --dowel-bg-4: lch(92% 0.85 var(--dowel-hue)); + --dowel-bg-1: lch(99% 0.4 var(--dowel-hue)); + --dowel-bg-2: lch(97% 0.85 var(--dowel-hue)); + --dowel-bg-3: lch(94.5% 1.3 var(--dowel-hue)); + --dowel-bg-4: lch(92% 0.85 var(--dowel-hue)); --dowel-bg-elevated: lch(100% 0 var(--dowel-hue)); /* borders — 3 steps */ - --dowel-border-1: lch(91% 1.48 var(--dowel-hue)); - --dowel-border-2: lch(87% 1.48 var(--dowel-hue)); - --dowel-border-3: lch(82% 1.93 var(--dowel-hue)); + --dowel-border-1: lch(91% 1.48 var(--dowel-hue)); + --dowel-border-2: lch(87% 1.48 var(--dowel-hue)); + --dowel-border-3: lch(82% 1.93 var(--dowel-hue)); /* text — 4 steps, phi-derived */ - --dowel-text-1: lch(14% 0 var(--dowel-hue)); - --dowel-text-2: lch(28% 1.2 var(--dowel-hue)); - --dowel-text-3: lch(48% 1.2 var(--dowel-hue)); + --dowel-text-1: lch(14% 0 var(--dowel-hue)); + --dowel-text-2: lch(28% 1.2 var(--dowel-hue)); + --dowel-text-3: lch(48% 1.2 var(--dowel-hue)); --dowel-text-4: lch(61.803% 1.2 var(--dowel-hue)); /* accent — ours, not Linear's. Same energy (L~49, high chroma, indigo), deliberately a different hue angle: their #5e6ad2 is lch(48.7% 60.8 295) and that exact value is their brand mark, not a design pattern. */ - --dowel-accent: lch(49% 62 285); + --dowel-accent: lch(49% 62 285); --dowel-accent-fg: lch(100% 0 0); - --dowel-focus: var(--dowel-accent); + --dowel-focus: var(--dowel-accent); /* type */ --dowel-font: "Inter Variable", system-ui, sans-serif; --dowel-mono: "JetBrains Mono", ui-monospace, monospace; - --dowel-fs-micro: .6875rem; /* 11 */ - --dowel-fs-mini: .75rem; /* 12 */ - --dowel-fs-small: .8125rem; /* 13 — workhorse */ - --dowel-fs-base: .9375rem; /* 15 */ - --dowel-fs-lg: 1.125rem; /* 18 */ + --dowel-fs-micro: 0.6875rem; /* 11 */ + --dowel-fs-mini: 0.75rem; /* 12 */ + --dowel-fs-small: 0.8125rem; /* 13 — workhorse */ + --dowel-fs-base: 0.9375rem; /* 15 */ + --dowel-fs-lg: 1.125rem; /* 18 */ --dowel-fw-normal: 450; --dowel-fw-medium: 500; --dowel-fw-semibold: 600; - --dowel-tracking: -.02em; + --dowel-tracking: -0.02em; /* shape */ --dowel-radius: 8px; @@ -122,21 +122,21 @@ All public tokens are prefixed `--dowel-`. /* size */ --dowel-h-sm: 24px; - --dowel-h: 28px; + --dowel-h: 28px; --dowel-h-lg: 32px; /* motion */ - --dowel-dur: .15s; - --dowel-ease: cubic-bezier(.25, .46, .45, .94); + --dowel-dur: 0.15s; + --dowel-ease: cubic-bezier(0.25, 0.46, 0.45, 0.94); /* elevation — two tiers, no more */ --dowel-shadow-popover: - 0 3px 8px lch(0 0 0/.125), 0 2px 5px lch(0 0 0/.125), - 0 1px 1px lch(0 0 0/.125); + 0 3px 8px lch(0 0 0/0.125), 0 2px 5px lch(0 0 0/0.125), + 0 1px 1px lch(0 0 0/0.125); --dowel-shadow-modal: - 0 4px 40px lch(0 0 0/.10), 0 3px 20px lch(0 0 0/.125), - 0 3px 12px lch(0 0 0/.125), 0 2px 8px lch(0 0 0/.125), - 0 1px 1px lch(0 0 0/.125); + 0 4px 40px lch(0 0 0/0.1), 0 3px 20px lch(0 0 0/0.125), + 0 3px 12px lch(0 0 0/0.125), 0 2px 8px lch(0 0 0/0.125), + 0 1px 1px lch(0 0 0/0.125); } ``` @@ -147,9 +147,14 @@ was not extractable, and copying it was not the goal. ### Theming contract ```css -.dowel-dark, [data-dowel-theme="dark"] { /* colour overrides */ } +.dowel-dark, +[data-dowel-theme="dark"] { + /* colour overrides */ +} @media (prefers-color-scheme: dark) { - :root:not([data-dowel-theme="light"]) { /* same overrides */ } + :root:not([data-dowel-theme="light"]) { + /* same overrides */ + } } ``` @@ -202,38 +207,38 @@ styles. `sideEffects: ["*.css"]` so JS tree-shakes. ## 5. Components -49 total: 39 Base UI-backed, 10 plain elements *(marked)*. +49 total: 39 Base UI-backed, 10 plain elements _(marked)_. **Actions** Button · IconButton · Toggle · ToggleGroup · Toolbar -**Form** Input · Textarea *(plain)* · NumberField · OTPField · Checkbox · +**Form** Input · Textarea _(plain)_ · NumberField · OTPField · Checkbox · CheckboxGroup · Radio · RadioGroup · Switch · Slider · Field · Fieldset · Form **Selection** Select · Combobox · Autocomplete **Overlay** Dialog · AlertDialog · Drawer · Popover · PreviewCard · Tooltip · Toast **Navigation** Menu · ContextMenu · Menubar · NavigationMenu · Tabs · -Breadcrumb *(plain)* · Pagination *(plain)* +Breadcrumb _(plain)_ · Pagination _(plain)_ **Disclosure** Accordion · Collapsible -**Display** Avatar · Badge *(plain)* · Card *(plain)* · Kbd *(plain)* · -Callout *(plain)* · Code *(plain)* · Table *(plain)* · Separator · Progress · -Meter · Skeleton *(plain)* · ScrollArea +**Display** Avatar · Badge _(plain)_ · Card _(plain)_ · Kbd _(plain)_ · +Callout _(plain)_ · Code _(plain)_ · Table _(plain)_ · Separator · Progress · +Meter · Skeleton _(plain)_ · ScrollArea ### Measured component specs -| component | spec | -|---|---| -| Button / control | `h 28 · radius 8 · 13px/500 · pad 0 10` | -| Button (pill variant) | `h 28 · radius 9999 · pad 0 10 0 6` (asymmetric, icon side) | -| IconButton | `28×28` or `24×24 · radius 9999 · pad 0 2` | -| Tab (segmented) | `h 28 · radius 9999 · 12px/500` | -| Menu / Select option | `h 32 · pad 0 18 0 14 · 13px/400` | -| Popover container | `radius 12 · hairline border · shadow-popover` | -| Dialog | `radius 12 · hairline border · shadow-modal · 13vh from top` | -| Command palette | `720w · input h 40 · group h 30 · row h 46` | -| Kbd | `radius 4 · 11px/400 · hairline border · gap 3` | -| Avatar | `20×20 · radius 8` (**not** a circle) | -| Input | `h 28 · radius 8`; large `h 40 · radius 12` | -| Hover surface | `bg-3` | -| Sidebar item | `h 28 · radius 8 · pad 0 9 0 8 · 13px/500` | +| component | spec | +| --------------------- | ------------------------------------------------------------ | +| Button / control | `h 28 · radius 8 · 13px/500 · pad 0 10` | +| Button (pill variant) | `h 28 · radius 9999 · pad 0 10 0 6` (asymmetric, icon side) | +| IconButton | `28×28` or `24×24 · radius 9999 · pad 0 2` | +| Tab (segmented) | `h 28 · radius 9999 · 12px/500` | +| Menu / Select option | `h 32 · pad 0 18 0 14 · 13px/400` | +| Popover container | `radius 12 · hairline border · shadow-popover` | +| Dialog | `radius 12 · hairline border · shadow-modal · 13vh from top` | +| Command palette | `720w · input h 40 · group h 30 · row h 46` | +| Kbd | `radius 4 · 11px/400 · hairline border · gap 3` | +| Avatar | `20×20 · radius 8` (**not** a circle) | +| Input | `h 28 · radius 8`; large `h 40 · radius 12` | +| Hover surface | `bg-3` | +| Sidebar item | `h 28 · radius 8 · pad 0 9 0 8 · 13px/500` | --- @@ -300,7 +305,7 @@ same account as `karnstack.com`). "compatibility_date": "2026-08-09", "assets": { "directory": "./dist" }, "workers_dev": false, - "routes": [{ "pattern": "dowel.sh", "custom_domain": true }] + "routes": [{ "pattern": "dowel.sh", "custom_domain": true }], } ``` @@ -316,20 +321,21 @@ dashboard Redirect Rule, same manual follow-up flue needed. Today the same credentials are duplicated per repo: -| secret | currently in | needed by | -|---|---|---| -| `NPM_TOKEN` | kino, reins | + dowel | -| `CLOUDFLARE_API_TOKEN` | flue | + dowel | +| secret | currently in | needed by | +| ---------------------- | ------------ | --------- | +| `NPM_TOKEN` | kino, reins | + dowel | +| `CLOUDFLARE_API_TOKEN` | flue | + dowel | Both move to **karnstack org secrets**, so a rotation is one update instead of N. New repos inherit them. -**Precedence caveat:** a repo secret *shadows* an org secret of the same name. +**Precedence caveat:** a repo secret _shadows_ an org secret of the same name. So after setting the org secrets, the per-repo duplicates in kino, reins and flue must be deleted — otherwise those repos keep silently using their old copies and a rotation appears to work while doing nothing. Token scopes: + - `NPM_TOKEN` — npm **granular automation** token, write access to the `dowel`, `@karnstack/*` packages - `CLOUDFLARE_API_TOKEN` — **Workers Scripts: Edit** on the karnstack account @@ -338,6 +344,6 @@ Setting org secrets requires `admin:org` on the `gh` token; the default `repo, read:org, gist` set returns 403. **Plan constraint:** karnstack is on GitHub Free, where org secrets only reach -*public* repositories. This is why dowel is public from the start rather than +_public_ repositories. This is why dowel is public from the start rather than private until launch — it is also the honest posture for a project whose premise is an open homage. From 909cb473f251329740e94c4e72097132d8d40bf2 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 03:47:36 +0530 Subject: [PATCH 05/33] Restore corrupted className literal in plan; add .prettierignore --- .prettierignore | 3 +++ docs/superpowers/plans/2026-08-09-dowel-phase-1.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..1b2972f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +pnpm-lock.yaml +docs/ +.superpowers/ diff --git a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md index 2deb9d5..78a49f4 100644 --- a/docs/superpowers/plans/2026-08-09-dowel-phase-1.md +++ b/docs/superpowers/plans/2026-08-09-dowel-phase-1.md @@ -812,8 +812,8 @@ dowel ships **no `cx`/`clsx`/`tailwind-merge` helper**. Variants are `data-*` attributes, so no component ever builds a conditional class name — every `className` in this library is a single string literal: -```tsx -className = "dowel-btn"; +```text +className="dowel-btn" ``` If a later phase genuinely needs conditional classes, add the helper then. From 375ce2e0c10e1b44b7cf52d87c5d35bec1e3424f Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 03:55:23 +0530 Subject: [PATCH 06/33] Add the dowel token layer with light and dark parity tests Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 17 + packages/dowel/package.json | 1 + packages/dowel/src/index.css | 20 + packages/dowel/src/tokens/dark.css | 79 ++ packages/dowel/src/tokens/light.css | 45 + packages/dowel/src/tokens/scale.css | 73 + packages/dowel/test/setup.ts | 3 + packages/dowel/test/tokens.test.ts | 38 + packages/dowel/vitest.config.ts | 11 + pnpm-lock.yaml | 1938 ++++++++++----------------- 10 files changed, 981 insertions(+), 1244 deletions(-) create mode 100644 packages/dowel/src/index.css create mode 100644 packages/dowel/src/tokens/dark.css create mode 100644 packages/dowel/src/tokens/light.css create mode 100644 packages/dowel/src/tokens/scale.css create mode 100644 packages/dowel/test/setup.ts create mode 100644 packages/dowel/test/tokens.test.ts create mode 100644 packages/dowel/vitest.config.ts diff --git a/README.md b/README.md index b26a8c6..232084b 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,23 @@ Not headless, not framework-agnostic, not a Tailwind plugin, not customisable per component. If you need a different button, dowel is the wrong library — that is the point. +## Typeface + +dowel is designed for Inter. It falls back to `system-ui`, which works but +looks different. To match the docs: + +```bash +pnpm add @fontsource-variable/inter +``` + +```ts +import "@fontsource-variable/inter"; +import "dowel/dowel.css"; +``` + +Inter is OFL-licensed. dowel does not bundle it, so you control whether it is +self-hosted or served from a CDN. + ## Credit dowel is an homage to the craft of [Linear](https://linear.app). Their diff --git a/packages/dowel/package.json b/packages/dowel/package.json index 50e402a..8c40da1 100644 --- a/packages/dowel/package.json +++ b/packages/dowel/package.json @@ -55,6 +55,7 @@ "@testing-library/dom": "^10.4.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.1", + "@types/node": "^26.2.0", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/packages/dowel/src/index.css b/packages/dowel/src/index.css new file mode 100644 index 0000000..6f6d4f5 --- /dev/null +++ b/packages/dowel/src/index.css @@ -0,0 +1,20 @@ +/* Layer order is declared once, first, before any @import. Consumers' own + unlayered styles beat every layer here, so overriding dowel never becomes a + specificity fight. */ +@layer dowel.tokens, dowel.base, dowel.components; + +@import "./tokens/scale.css"; +@import "./tokens/light.css"; +@import "./tokens/dark.css"; + +@layer dowel.base { + .dowel-root, + [data-dowel-theme] { + font-family: var(--dowel-font); + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-normal); + letter-spacing: var(--dowel-tracking); + color: var(--dowel-text-2); + background-color: var(--dowel-bg-1); + } +} diff --git a/packages/dowel/src/tokens/dark.css b/packages/dowel/src/tokens/dark.css new file mode 100644 index 0000000..cdf8f66 --- /dev/null +++ b/packages/dowel/src/tokens/dark.css @@ -0,0 +1,79 @@ +/* Dark overrides colour only. Three activation paths, in precedence order: + explicit class, explicit attribute, then system preference — and the media + query is guarded so an explicit light choice always wins over the OS. */ +@layer dowel.tokens { + .dowel-dark, + [data-dowel-theme="dark"] { + --dowel-bg-1: lch(5.52% 0.4 var(--dowel-hue)); + --dowel-bg-2: lch(7.32% 0.85 var(--dowel-hue)); + --dowel-bg-3: lch(8.22% 1.3 var(--dowel-hue)); + --dowel-bg-4: lch(9.345% 0.85 var(--dowel-hue)); + --dowel-bg-elevated: lch(12.72% 0.85 var(--dowel-hue)); + + --dowel-border-1: lch(9.84% 1.48 var(--dowel-hue)); + --dowel-border-2: lch(14.16% 1.48 var(--dowel-hue)); + --dowel-border-3: lch(25.68% 1.93 var(--dowel-hue)); + + --dowel-text-1: lch(100% 0 var(--dowel-hue)); + --dowel-text-2: lch(90.451% 1.2 var(--dowel-hue)); + --dowel-text-3: lch(61.803% 1.2 var(--dowel-hue)); + --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); + + --dowel-accent: lch(58% 62 285); + --dowel-accent-hover: lch(64% 62 285); + --dowel-accent-fg: lch(100% 0 0); + --dowel-focus: var(--dowel-accent); + + --dowel-danger: lch(58% 68 28); + --dowel-danger-fg: lch(100% 0 0); + --dowel-success: lch(64% 55 145); + --dowel-warning: lch(80% 78 82); + + --dowel-shadow-popover: + 0 3px 8px lch(0 0 0 / 0.125), 0 2px 5px lch(0 0 0 / 0.125), + 0 1px 1px lch(0 0 0 / 0.125); + --dowel-shadow-modal: + 0 4px 40px lch(0 0 0 / 0.1), 0 3px 20px lch(0 0 0 / 0.125), + 0 3px 12px lch(0 0 0 / 0.125), 0 2px 8px lch(0 0 0 / 0.125), + 0 1px 1px lch(0 0 0 / 0.125); + --dowel-overlay: lch(0 0 0 / 0.6); + } + + @media (prefers-color-scheme: dark) { + :root:not(.dowel-light):not([data-dowel-theme="light"]) { + --dowel-bg-1: lch(5.52% 0.4 var(--dowel-hue)); + --dowel-bg-2: lch(7.32% 0.85 var(--dowel-hue)); + --dowel-bg-3: lch(8.22% 1.3 var(--dowel-hue)); + --dowel-bg-4: lch(9.345% 0.85 var(--dowel-hue)); + --dowel-bg-elevated: lch(12.72% 0.85 var(--dowel-hue)); + + --dowel-border-1: lch(9.84% 1.48 var(--dowel-hue)); + --dowel-border-2: lch(14.16% 1.48 var(--dowel-hue)); + --dowel-border-3: lch(25.68% 1.93 var(--dowel-hue)); + + --dowel-text-1: lch(100% 0 var(--dowel-hue)); + --dowel-text-2: lch(90.451% 1.2 var(--dowel-hue)); + --dowel-text-3: lch(61.803% 1.2 var(--dowel-hue)); + --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); + + --dowel-accent: lch(58% 62 285); + --dowel-accent-hover: lch(64% 62 285); + --dowel-accent-fg: lch(100% 0 0); + --dowel-focus: var(--dowel-accent); + + --dowel-danger: lch(58% 68 28); + --dowel-danger-fg: lch(100% 0 0); + --dowel-success: lch(64% 55 145); + --dowel-warning: lch(80% 78 82); + + --dowel-shadow-popover: + 0 3px 8px lch(0 0 0 / 0.125), 0 2px 5px lch(0 0 0 / 0.125), + 0 1px 1px lch(0 0 0 / 0.125); + --dowel-shadow-modal: + 0 4px 40px lch(0 0 0 / 0.1), 0 3px 20px lch(0 0 0 / 0.125), + 0 3px 12px lch(0 0 0 / 0.125), 0 2px 8px lch(0 0 0 / 0.125), + 0 1px 1px lch(0 0 0 / 0.125); + --dowel-overlay: lch(0 0 0 / 0.6); + } + } +} diff --git a/packages/dowel/src/tokens/light.css b/packages/dowel/src/tokens/light.css new file mode 100644 index 0000000..0c7ab95 --- /dev/null +++ b/packages/dowel/src/tokens/light.css @@ -0,0 +1,45 @@ +/* Light is the default theme. Every neutral is one hue at low chroma — + greys that are not grey. Changing --dowel-hue retints the entire library. */ +@layer dowel.tokens { + :root { + /* surfaces */ + --dowel-bg-1: lch(99% 0.4 var(--dowel-hue)); + --dowel-bg-2: lch(97% 0.85 var(--dowel-hue)); + --dowel-bg-3: lch(94.5% 1.3 var(--dowel-hue)); + --dowel-bg-4: lch(92% 0.85 var(--dowel-hue)); + --dowel-bg-elevated: lch(100% 0 var(--dowel-hue)); + + /* borders */ + --dowel-border-1: lch(91% 1.48 var(--dowel-hue)); + --dowel-border-2: lch(87% 1.48 var(--dowel-hue)); + --dowel-border-3: lch(82% 1.93 var(--dowel-hue)); + + /* text */ + --dowel-text-1: lch(14% 0 var(--dowel-hue)); + --dowel-text-2: lch(28% 1.2 var(--dowel-hue)); + --dowel-text-3: lch(48% 1.2 var(--dowel-hue)); + --dowel-text-4: lch(61.803% 1.2 var(--dowel-hue)); + + /* accent — ours, deliberately not Linear's 295 brand hue */ + --dowel-accent: lch(49% 62 285); + --dowel-accent-hover: lch(44% 62 285); + --dowel-accent-fg: lch(100% 0 0); + --dowel-focus: var(--dowel-accent); + + /* status */ + --dowel-danger: lch(52% 68 28); + --dowel-danger-fg: lch(100% 0 0); + --dowel-success: lch(58% 55 145); + --dowel-warning: lch(76% 78 82); + + /* elevation — exactly two tiers */ + --dowel-shadow-popover: + 0 3px 8px lch(0 0 0 / 0.08), 0 2px 5px lch(0 0 0 / 0.08), + 0 1px 1px lch(0 0 0 / 0.08); + --dowel-shadow-modal: + 0 4px 40px lch(0 0 0 / 0.06), 0 3px 20px lch(0 0 0 / 0.08), + 0 3px 12px lch(0 0 0 / 0.08), 0 2px 8px lch(0 0 0 / 0.08), + 0 1px 1px lch(0 0 0 / 0.08); + --dowel-overlay: lch(0 0 0 / 0.4); + } +} diff --git a/packages/dowel/src/tokens/scale.css b/packages/dowel/src/tokens/scale.css new file mode 100644 index 0000000..4a46575 --- /dev/null +++ b/packages/dowel/src/tokens/scale.css @@ -0,0 +1,73 @@ +/* Non-colour tokens. Identical in light and dark — dark.css overrides colour + only, so this file must never contain a colour value. */ +@layer dowel.tokens { + :root { + /* hue — a number, not a colour. Both themes read it, so it lives with + the shared tokens; changing it retints the entire library. */ + --dowel-hue: 272; + + /* type */ + --dowel-font: + "Inter Variable", system-ui, -apple-system, "Segoe UI", sans-serif; + --dowel-mono: "JetBrains Mono", ui-monospace, "SF Mono", monospace; + + --dowel-fs-micro: 0.6875rem; /* 11 */ + --dowel-fs-mini: 0.75rem; /* 12 */ + --dowel-fs-small: 0.8125rem; /* 13 — the workhorse */ + --dowel-fs-base: 0.9375rem; /* 15 */ + --dowel-fs-lg: 1.125rem; /* 18 */ + --dowel-fs-title3: 1.25rem; /* 20 */ + --dowel-fs-title2: 1.5rem; /* 24 */ + --dowel-fs-title1: 2.25rem; /* 36 */ + + --dowel-fw-light: 300; + --dowel-fw-normal: 450; /* not 400 — this is the Linear signature */ + --dowel-fw-medium: 500; + --dowel-fw-semibold: 600; + --dowel-fw-bold: 700; + + --dowel-tracking: -0.02em; + --dowel-tracking-title: -0.004em; + --dowel-leading: 1.6; + + /* shape */ + --dowel-radius-sm: 4px; + --dowel-radius: 8px; + --dowel-radius-lg: 12px; + --dowel-radius-pill: 9999px; + --dowel-hairline: 0.5px; + + /* size — every interactive control is 28px unless explicitly compact */ + --dowel-h-sm: 24px; + --dowel-h: 28px; + --dowel-h-lg: 32px; + --dowel-h-field: 36px; + + /* space */ + --dowel-space-1: 2px; + --dowel-space-2: 4px; + --dowel-space-3: 6px; + --dowel-space-4: 8px; + --dowel-space-5: 10px; + --dowel-space-6: 12px; + --dowel-space-7: 14px; + --dowel-space-8: 18px; + + /* motion — only border, background-color, color, opacity may transition */ + --dowel-dur: 0.15s; + --dowel-dur-fast: 0.1s; + --dowel-ease: cubic-bezier(0.25, 0.46, 0.45, 0.94); + --dowel-transition: + border var(--dowel-dur) var(--dowel-ease), + background-color var(--dowel-dur) var(--dowel-ease), + color var(--dowel-dur) var(--dowel-ease), + opacity var(--dowel-dur) var(--dowel-ease); + } + + @media (prefers-reduced-motion: reduce) { + :root { + --dowel-dur: 0.01ms; + --dowel-dur-fast: 0.01ms; + } + } +} diff --git a/packages/dowel/test/setup.ts b/packages/dowel/test/setup.ts new file mode 100644 index 0000000..8da7ce0 --- /dev/null +++ b/packages/dowel/test/setup.ts @@ -0,0 +1,3 @@ +// Placeholder until Task 4 adds the axe matcher. Kept as a file so the +// vitest config resolves from the very first test run. +export {}; diff --git a/packages/dowel/test/tokens.test.ts b/packages/dowel/test/tokens.test.ts new file mode 100644 index 0000000..bbc56a6 --- /dev/null +++ b/packages/dowel/test/tokens.test.ts @@ -0,0 +1,38 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +const read = (p: string) => + readFileSync(resolve(import.meta.dirname, "..", "src", p), "utf8"); + +describe("token layer", () => { + it("declares the layer order before anything else", () => { + const index = read("index.css"); + const decl = index.match(/@layer\s+([^;]+);/); + expect(decl?.[1]?.split(",").map((s) => s.trim())).toEqual([ + "dowel.tokens", + "dowel.base", + "dowel.components", + ]); + }); + + it("defines every colour token in BOTH light and dark", () => { + const names = (css: string) => [ + ...new Set([...css.matchAll(/(--dowel-[\w-]+)\s*:/g)].map((m) => m[1])), + ]; + const light = names(read("tokens/light.css")); + const dark = names(read("tokens/dark.css")); + expect(light.length).toBeGreaterThan(0); + // A token defined in light but not dark renders unstyled in dark mode. + expect([...light].sort()).toEqual([...dark].sort()); + }); + + it("uses 450 as the normal font weight, not 400", () => { + expect(read("tokens/scale.css")).toContain("--dowel-fw-normal: 450"); + }); + + it("keeps every scale token free of colour", () => { + // Colour belongs in light.css/dark.css so dark mode overrides one file. + expect(read("tokens/scale.css")).not.toMatch(/lch\(|#[0-9a-f]{3,8}\b/i); + }); +}); diff --git a/packages/dowel/vitest.config.ts b/packages/dowel/vitest.config.ts new file mode 100644 index 0000000..f1e1b75 --- /dev/null +++ b/packages/dowel/vitest.config.ts @@ -0,0 +1,11 @@ +import react from "@vitejs/plugin-react"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + plugins: [react()], + test: { + environment: "jsdom", + globals: false, + setupFiles: ["./test/setup.ts"], + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8385023..bfdaf11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,10 +1,11 @@ -lockfileVersion: "9.0" +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false importers: + .: devDependencies: prettier: @@ -16,28 +17,31 @@ importers: packages/dowel: dependencies: - "@base-ui/react": + '@base-ui/react': specifier: ^1.7.0 version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) devDependencies: - "@testing-library/dom": + '@testing-library/dom': specifier: ^10.4.1 version: 10.4.1 - "@testing-library/react": + '@testing-library/react': specifier: ^16.3.2 version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - "@testing-library/user-event": + '@testing-library/user-event': specifier: ^14.6.1 version: 14.6.3(@testing-library/dom@10.4.1) - "@types/react": + '@types/node': + specifier: ^26.2.0 + version: 26.2.0 + '@types/react': specifier: ^19.2.18 version: 19.2.18 - "@types/react-dom": + '@types/react-dom': specifier: ^19.2.0 version: 19.2.4(@types/react@19.2.18) - "@vitejs/plugin-react": + '@vitejs/plugin-react': specifier: ^6.0.5 - version: 6.0.5(vite@8.2.1) + version: 6.0.5(vite@8.2.1(@types/node@26.2.0)) axe-core: specifier: ^4.13.0 version: 4.13.0 @@ -58,441 +62,292 @@ importers: version: 0.22.14(typescript@5.9.3) vitest: specifier: ^4.1.10 - version: 4.1.10(jsdom@27.4.0)(vite@8.2.1) + version: 4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)) packages: - "@acemir/cssom@0.9.31": - resolution: - { - integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==, - } - - "@asamuzakjp/css-color@4.1.2": - resolution: - { - integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==, - } - - "@asamuzakjp/dom-selector@6.8.1": - resolution: - { - integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==, - } - - "@asamuzakjp/nwsapi@2.3.9": - resolution: - { - integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==, - } - - "@babel/code-frame@7.29.7": - resolution: - { - integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==, - } - engines: { node: ">=6.9.0" } - - "@babel/helper-validator-identifier@7.29.7": - resolution: - { - integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==, - } - engines: { node: ">=6.9.0" } - - "@babel/runtime@7.29.7": - resolution: - { - integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==, - } - engines: { node: ">=6.9.0" } - - "@base-ui/react@1.7.0": - resolution: - { - integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==, - } - engines: { node: ">=14.0.0" } + + '@acemir/cssom@0.9.31': + resolution: {integrity: sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==} + + '@asamuzakjp/css-color@4.1.2': + resolution: {integrity: sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==} + + '@asamuzakjp/dom-selector@6.8.1': + resolution: {integrity: sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@base-ui/react@1.7.0': + resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==} + engines: {node: '>=14.0.0'} peerDependencies: - "@date-fns/tz": ^1.2.0 - "@types/react": ^17 || ^18 || ^19 + '@date-fns/tz': ^1.2.0 + '@types/react': ^17 || ^18 || ^19 date-fns: ^4.0.0 react: ^17 || ^18 || ^19 react-dom: ^17 || ^18 || ^19 peerDependenciesMeta: - "@date-fns/tz": + '@date-fns/tz': optional: true - "@types/react": + '@types/react': optional: true date-fns: optional: true - "@base-ui/utils@0.3.2": - resolution: - { - integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==, - } + '@base-ui/utils@0.3.2': + resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==} peerDependencies: - "@types/react": ^17 || ^18 || ^19 + '@types/react': ^17 || ^18 || ^19 react: ^17 || ^18 || ^19 react-dom: ^17 || ^18 || ^19 peerDependenciesMeta: - "@types/react": + '@types/react': optional: true - "@csstools/color-helpers@6.1.0": - resolution: - { - integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==, - } - engines: { node: ">=20.19.0" } - - "@csstools/css-calc@3.3.0": - resolution: - { - integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==, - } - engines: { node: ">=20.19.0" } + '@csstools/color-helpers@6.1.0': + resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.3.0': + resolution: {integrity: sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-parser-algorithms": ^4.0.0 - "@csstools/css-tokenizer": ^4.0.0 - - "@csstools/css-color-parser@4.1.10": - resolution: - { - integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==, - } - engines: { node: ">=20.19.0" } + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.10': + resolution: {integrity: sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-parser-algorithms": ^4.0.0 - "@csstools/css-tokenizer": ^4.0.0 - - "@csstools/css-parser-algorithms@4.0.0": - resolution: - { - integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==, - } - engines: { node: ">=20.19.0" } + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} peerDependencies: - "@csstools/css-tokenizer": ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 - "@csstools/css-syntax-patches-for-csstree@1.1.7": - resolution: - { - integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==, - } + '@csstools/css-syntax-patches-for-csstree@1.1.7': + resolution: {integrity: sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==} peerDependencies: css-tree: ^3.2.1 peerDependenciesMeta: css-tree: optional: true - "@csstools/css-tokenizer@4.0.0": - resolution: - { - integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==, - } - engines: { node: ">=20.19.0" } - - "@exodus/bytes@1.15.1": - resolution: - { - integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@exodus/bytes@1.15.1': + resolution: {integrity: sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - "@noble/hashes": ^1.8.0 || ^2.0.0 + '@noble/hashes': ^1.8.0 || ^2.0.0 peerDependenciesMeta: - "@noble/hashes": + '@noble/hashes': optional: true - "@floating-ui/core@1.8.0": - resolution: - { - integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==, - } - - "@floating-ui/dom@1.8.0": - resolution: - { - integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==, - } - - "@floating-ui/react-dom@2.1.9": - resolution: - { - integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==, - } + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/react-dom@2.1.9': + resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==} peerDependencies: - react: ">=16.8.0" - react-dom: ">=16.8.0" - - "@floating-ui/utils@0.2.12": - resolution: - { - integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==, - } - - "@jridgewell/sourcemap-codec@1.5.5": - resolution: - { - integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, - } - - "@oxc-project/types@0.143.0": - resolution: - { - integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==, - } - - "@quansync/fs@1.0.0": - resolution: - { - integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==, - } - - "@rolldown/binding-android-arm64@1.2.3": - resolution: - { - integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@oxc-project/types@0.143.0': + resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + + '@rolldown/binding-android-arm64@1.2.3': + resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - "@rolldown/binding-darwin-arm64@1.2.3": - resolution: - { - integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-darwin-arm64@1.2.3': + resolution: {integrity: sha512-ieIiibVCp0tX7TLu2cafoNPv8wJyYi01ekXpbf8q2j7F4rGAhhXb/eQh7ge9DRBY78GwmRQtvjZDux7EDbA8kA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - "@rolldown/binding-darwin-x64@1.2.3": - resolution: - { - integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-darwin-x64@1.2.3': + resolution: {integrity: sha512-Zh9tCon19eDXJoihx0rqKhMUlMYqzwj3aPsSuHmI4RWZh62dWUL+DJN4C5YQya5TcQBJU/Fe8+rY0jhXTQITqA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - "@rolldown/binding-freebsd-x64@1.2.3": - resolution: - { - integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-freebsd-x64@1.2.3': + resolution: {integrity: sha512-nGbJWewA1wrXXZiQhjAT5rhibGfns5ZNkDVqxsO6zJ3f3YvpoDNNmGMSbbhLuXKjNScaBJVOAboztAWVespQMg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - "@rolldown/binding-linux-arm-gnueabihf@1.2.3": - resolution: - { - integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': + resolution: {integrity: sha512-QNniJr5Kml0kDEB98jiDOJjXNroxIIi0IXIbdYzY26Xt1pVbeP62+KnoIZLwirOymX/0jDk/2gI/bNUv7A7OIw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - "@rolldown/binding-linux-arm64-gnu@1.2.3": - resolution: - { - integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-arm64-gnu@1.2.3': + resolution: {integrity: sha512-TkqEAcmmvH3I/q4114NB4RVt6241Dao48pF45uLcFGrwAaIn0iITgTAKP/dLjbN0R4buJjGb91+UHSoFmpgIWw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - "@rolldown/binding-linux-arm64-musl@1.2.3": - resolution: - { - integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-arm64-musl@1.2.3': + resolution: {integrity: sha512-NHqjnxpsndf4MPymxteFAWHHfkTL8HjWh1KB7z23ofZ6QO2euONuxDXjat69dKZRALnGypg8k8SsK8vZJoXv1Q==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - "@rolldown/binding-linux-ppc64-gnu@1.2.3": - resolution: - { - integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-ppc64-gnu@1.2.3': + resolution: {integrity: sha512-6tbrbwfz5GB9DQ4Jwo6hy9v+vR31xZlvzZ6n5Xut6Hhx5PvrA9q/HsK8KMaYQp063iqZGXwNvZtYNLD7EM/x0w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - "@rolldown/binding-linux-s390x-gnu@1.2.3": - resolution: - { - integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-s390x-gnu@1.2.3': + resolution: {integrity: sha512-oyuXxXmoZHjXC917IAPFAAv4wWAa0cM9afk8nx1+9/jNNOX1uPf8yDA6p7G0RypOfw/X0PQt5IfoquY1um+zSg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - "@rolldown/binding-linux-x64-gnu@1.2.3": - resolution: - { - integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-x64-gnu@1.2.3': + resolution: {integrity: sha512-TytMwF2KVGqP2tgd0I1OY0PAv78dZRAYcF5ssDzjM34SUXCED3uXvSd5+lHoC0bTD6eEdFz7LdQNCO1y0oVk9w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - "@rolldown/binding-linux-x64-musl@1.2.3": - resolution: - { - integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-linux-x64-musl@1.2.3': + resolution: {integrity: sha512-/E9m3qstrJFVPoULV25mVQblSNExY2+kBsYe4sy0Tn0yOOgJ8wZbZt3KnRbF/XeU2Gl1STKUQnDNTqhIE5MD4A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - "@rolldown/binding-openharmony-arm64@1.2.3": - resolution: - { - integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-openharmony-arm64@1.2.3': + resolution: {integrity: sha512-Kr0OcsoQI816i6HOl3vFHpd1K0eZyh76zgfj4c1nTyaTsd5r2Mj1lwM4R90y/qaCfmTn9eHy0SKwi98eitRxug==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - "@rolldown/binding-win32-arm64-msvc@1.2.3": - resolution: - { - integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-win32-arm64-msvc@1.2.3': + resolution: {integrity: sha512-hOtMwTqnME+/gJcH/PCZ0wn0zPUjiWOgkHpxbSJpfGKMezHltx1S7/k1SitzVa7Ww2cqrDDaFbZEhcJZO8o+Jw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - "@rolldown/binding-win32-x64-msvc@1.2.3": - resolution: - { - integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@rolldown/binding-win32-x64-msvc@1.2.3': + resolution: {integrity: sha512-ekcqMMkI2PlhYnfzQnB/cEdYUVVJViWvoUyLrbzgDoi3Snfc1mVBwdnc306ufA5ejy8JSPjT2RlW1nQSjW7efg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - "@rolldown/pluginutils@1.0.1": - resolution: - { - integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==, - } - - "@standard-schema/spec@1.1.0": - resolution: - { - integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==, - } - - "@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" } + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@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 + '@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": + '@types/react': optional: true - "@types/react-dom": + '@types/react-dom': optional: true - "@testing-library/user-event@14.6.3": - resolution: - { - integrity: sha512-6dBq67jT8lE+JTE8Exm02Kt6ze43hz1jdiSpSJwtTZiT1xQQ6b7nZYTTQ9njdArdU8XklOwaDp/AbT/eYSKF4g==, - } - engines: { node: ">=12", npm: ">=6" } + '@testing-library/user-event@14.6.3': + resolution: {integrity: sha512-6dBq67jT8lE+JTE8Exm02Kt6ze43hz1jdiSpSJwtTZiT1xQQ6b7nZYTTQ9njdArdU8XklOwaDp/AbT/eYSKF4g==} + engines: {node: '>=12', npm: '>=6'} peerDependencies: - "@testing-library/dom": ">=7.21.4" - - "@types/aria-query@5.0.4": - resolution: - { - integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==, - } - - "@types/chai@5.2.3": - resolution: - { - integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==, - } - - "@types/deep-eql@4.0.2": - resolution: - { - integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==, - } - - "@types/estree@1.0.9": - resolution: - { - integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, - } - - "@types/react-dom@19.2.4": - resolution: - { - integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==, - } + '@testing-library/dom': '>=7.21.4' + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@26.2.0': + resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} + + '@types/react-dom@19.2.4': + resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==} peerDependencies: - "@types/react": ^19.2.0 - - "@types/react@19.2.18": - resolution: - { - integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==, - } - - "@vitejs/plugin-react@6.0.5": - resolution: - { - integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + '@types/react': ^19.2.0 + + '@types/react@19.2.18': + resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==} + + '@vitejs/plugin-react@6.0.5': + resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==} + engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - "@rolldown/plugin-babel": ^0.1.7 || ^0.2.0 + '@rolldown/plugin-babel': ^0.1.7 || ^0.2.0 babel-plugin-react-compiler: ^1.0.0 vite: ^8.0.0 peerDependenciesMeta: - "@rolldown/plugin-babel": + '@rolldown/plugin-babel': optional: true babel-plugin-react-compiler: optional: true - "@vitest/expect@4.1.10": - resolution: - { - integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==, - } - - "@vitest/mocker@4.1.10": - resolution: - { - integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==, - } + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} + + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -502,442 +357,268 @@ packages: vite: optional: true - "@vitest/pretty-format@4.1.10": - resolution: - { - integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==, - } - - "@vitest/runner@4.1.10": - resolution: - { - integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==, - } - - "@vitest/snapshot@4.1.10": - resolution: - { - integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==, - } - - "@vitest/spy@4.1.10": - resolution: - { - integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==, - } - - "@vitest/utils@4.1.10": - resolution: - { - integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==, - } - - "@yuku-codegen/binding-android-arm64@0.8.4": - resolution: - { - integrity: sha512-rsYkGl2kOkDRsh1mxriYnk1qBS78vjlBJ3+T2XwtwKwqOliy2n+2Ae0EDxJ/uX1DZLm3KkBZarGO5isEnmHchA==, - } + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + + '@yuku-codegen/binding-android-arm64@0.8.4': + resolution: {integrity: sha512-rsYkGl2kOkDRsh1mxriYnk1qBS78vjlBJ3+T2XwtwKwqOliy2n+2Ae0EDxJ/uX1DZLm3KkBZarGO5isEnmHchA==} cpu: [arm64] os: [android] - "@yuku-codegen/binding-darwin-arm64@0.8.4": - resolution: - { - integrity: sha512-tNLKzPF3FYmEcHSYvWp/LEpjHHAtDR13hwo6/gdCkYMi9x59CWn2obczKzWNe7kDor4/1AMZuJDEVRpFkTSefw==, - } + '@yuku-codegen/binding-darwin-arm64@0.8.4': + resolution: {integrity: sha512-tNLKzPF3FYmEcHSYvWp/LEpjHHAtDR13hwo6/gdCkYMi9x59CWn2obczKzWNe7kDor4/1AMZuJDEVRpFkTSefw==} cpu: [arm64] os: [darwin] - "@yuku-codegen/binding-darwin-x64@0.8.4": - resolution: - { - integrity: sha512-tK7LWzXNb5JbZpnoCNHB0nEhPFss/LwwehM6m/f0oYDan+iZgFZXzdoy80JdE7dVxjTZDIhUNPx8X8xbIdaoxA==, - } + '@yuku-codegen/binding-darwin-x64@0.8.4': + resolution: {integrity: sha512-tK7LWzXNb5JbZpnoCNHB0nEhPFss/LwwehM6m/f0oYDan+iZgFZXzdoy80JdE7dVxjTZDIhUNPx8X8xbIdaoxA==} cpu: [x64] os: [darwin] - "@yuku-codegen/binding-freebsd-x64@0.8.4": - resolution: - { - integrity: sha512-5MUV4d7g2p5Hd8GiXW6ynTRgYjm4Dw4eM2gaWRZ4crkGetzNx+HlPxcEfGtVNPqH5Qaa4Z2REtzdsdgvaE6/Ng==, - } + '@yuku-codegen/binding-freebsd-x64@0.8.4': + resolution: {integrity: sha512-5MUV4d7g2p5Hd8GiXW6ynTRgYjm4Dw4eM2gaWRZ4crkGetzNx+HlPxcEfGtVNPqH5Qaa4Z2REtzdsdgvaE6/Ng==} cpu: [x64] os: [freebsd] - "@yuku-codegen/binding-linux-arm-gnu@0.8.4": - resolution: - { - integrity: sha512-g6LnHrR0Rfqq5cXs7olwR2+LlVDc876pw7Hh7YXbukVSiBxQkaxqsEO/trO25z2g99zWzgXwoYvQZ1TnwA2wEw==, - } + '@yuku-codegen/binding-linux-arm-gnu@0.8.4': + resolution: {integrity: sha512-g6LnHrR0Rfqq5cXs7olwR2+LlVDc876pw7Hh7YXbukVSiBxQkaxqsEO/trO25z2g99zWzgXwoYvQZ1TnwA2wEw==} cpu: [arm] os: [linux] libc: [glibc] - "@yuku-codegen/binding-linux-arm-musl@0.8.4": - resolution: - { - integrity: sha512-7XAPHrROPEFuJWXEGZeLZQN4xR8ENQ65+HSCtCHjSzCwGgnX53GUjM9ExVcHopV0a5g4vu57v2wwtyWIM5iNOQ==, - } + '@yuku-codegen/binding-linux-arm-musl@0.8.4': + resolution: {integrity: sha512-7XAPHrROPEFuJWXEGZeLZQN4xR8ENQ65+HSCtCHjSzCwGgnX53GUjM9ExVcHopV0a5g4vu57v2wwtyWIM5iNOQ==} cpu: [arm] os: [linux] libc: [musl] - "@yuku-codegen/binding-linux-arm64-gnu@0.8.4": - resolution: - { - integrity: sha512-fnBm7NLuuwFXy7F1vRIuyOc+RW9ADUjuCKVGY87Dj8jtr9XgESNrwb+B9VLSFY7nZ0rCdK/Sm1fBqJpI7eLdKQ==, - } + '@yuku-codegen/binding-linux-arm64-gnu@0.8.4': + resolution: {integrity: sha512-fnBm7NLuuwFXy7F1vRIuyOc+RW9ADUjuCKVGY87Dj8jtr9XgESNrwb+B9VLSFY7nZ0rCdK/Sm1fBqJpI7eLdKQ==} cpu: [arm64] os: [linux] libc: [glibc] - "@yuku-codegen/binding-linux-arm64-musl@0.8.4": - resolution: - { - integrity: sha512-6vTw4ZHO9nm4SUkw36uG+UE6/qifZ0E8HIef7Mx/U/c2Zxu3JLBfXtU7U/NN2GMkDmcJkgwjXfpQoYw4Ch5Y1w==, - } + '@yuku-codegen/binding-linux-arm64-musl@0.8.4': + resolution: {integrity: sha512-6vTw4ZHO9nm4SUkw36uG+UE6/qifZ0E8HIef7Mx/U/c2Zxu3JLBfXtU7U/NN2GMkDmcJkgwjXfpQoYw4Ch5Y1w==} cpu: [arm64] os: [linux] libc: [musl] - "@yuku-codegen/binding-linux-x64-gnu@0.8.4": - resolution: - { - integrity: sha512-+vuC3V3Lw+DB4oJgHV9pVDfQZlsZJnxmbdods7HzxgEALU3P5+czwdAcw3wfh7Ebabt0Ny8eLUb1k9RV5OB/+w==, - } + '@yuku-codegen/binding-linux-x64-gnu@0.8.4': + resolution: {integrity: sha512-+vuC3V3Lw+DB4oJgHV9pVDfQZlsZJnxmbdods7HzxgEALU3P5+czwdAcw3wfh7Ebabt0Ny8eLUb1k9RV5OB/+w==} cpu: [x64] os: [linux] libc: [glibc] - "@yuku-codegen/binding-linux-x64-musl@0.8.4": - resolution: - { - integrity: sha512-QH60PE4eZecmgNGa1/T1cKPhrfxt6ANtu4lrQ1FZ50F9b0GS9WjGmIjrfdSUeQa+f2Iqk3oEFSJdgVHBg1KPNg==, - } + '@yuku-codegen/binding-linux-x64-musl@0.8.4': + resolution: {integrity: sha512-QH60PE4eZecmgNGa1/T1cKPhrfxt6ANtu4lrQ1FZ50F9b0GS9WjGmIjrfdSUeQa+f2Iqk3oEFSJdgVHBg1KPNg==} cpu: [x64] os: [linux] libc: [musl] - "@yuku-codegen/binding-win32-arm64@0.8.4": - resolution: - { - integrity: sha512-6r68c0nKZPIBRXZIBiD7zjlEukBR+xRxpTOVj4n1Fsjcdh4YbbJfjFfmIzdbo9jXR1xL+dPueDVdsRGOUH5MoQ==, - } + '@yuku-codegen/binding-win32-arm64@0.8.4': + resolution: {integrity: sha512-6r68c0nKZPIBRXZIBiD7zjlEukBR+xRxpTOVj4n1Fsjcdh4YbbJfjFfmIzdbo9jXR1xL+dPueDVdsRGOUH5MoQ==} cpu: [arm64] os: [win32] - "@yuku-codegen/binding-win32-x64@0.8.4": - resolution: - { - integrity: sha512-i+BW77LPjNqe7Apq50J3OeEaVfga3G+eT2bKjb6bj4yO99fil/jnKb4ZDH4JLvby/Q7hRa6VicL2EZ7iS+ifzA==, - } + '@yuku-codegen/binding-win32-x64@0.8.4': + resolution: {integrity: sha512-i+BW77LPjNqe7Apq50J3OeEaVfga3G+eT2bKjb6bj4yO99fil/jnKb4ZDH4JLvby/Q7hRa6VicL2EZ7iS+ifzA==} cpu: [x64] os: [win32] - "@yuku-parser/binding-android-arm64@0.8.4": - resolution: - { - integrity: sha512-+HIMmv08Zrh9ugIAEMnKBMMePOl7CDxrjc8Vui1+GG2TJHM1yI1+3wo1pnXB6Nj2IiHugDkQw8ycUI6SA2EUkQ==, - } + '@yuku-parser/binding-android-arm64@0.8.4': + resolution: {integrity: sha512-+HIMmv08Zrh9ugIAEMnKBMMePOl7CDxrjc8Vui1+GG2TJHM1yI1+3wo1pnXB6Nj2IiHugDkQw8ycUI6SA2EUkQ==} cpu: [arm64] os: [android] - "@yuku-parser/binding-darwin-arm64@0.8.4": - resolution: - { - integrity: sha512-Elf/B/2m3OsyvxoQnBk8Dtu+9csHkzBNs5Yv9GbHjT3x0kVKNWjFusyZgm41VwxcPDqdpRi8tWxNX7OqXkmf/A==, - } + '@yuku-parser/binding-darwin-arm64@0.8.4': + resolution: {integrity: sha512-Elf/B/2m3OsyvxoQnBk8Dtu+9csHkzBNs5Yv9GbHjT3x0kVKNWjFusyZgm41VwxcPDqdpRi8tWxNX7OqXkmf/A==} cpu: [arm64] os: [darwin] - "@yuku-parser/binding-darwin-x64@0.8.4": - resolution: - { - integrity: sha512-CjZuMoXnL5XUkVpDqh4WDPwpAw8CwmtHHnTerGkS45So/sNuwkXdyIAEqqIZfaLopi5W/V9NApAT2md9XizjsQ==, - } + '@yuku-parser/binding-darwin-x64@0.8.4': + resolution: {integrity: sha512-CjZuMoXnL5XUkVpDqh4WDPwpAw8CwmtHHnTerGkS45So/sNuwkXdyIAEqqIZfaLopi5W/V9NApAT2md9XizjsQ==} cpu: [x64] os: [darwin] - "@yuku-parser/binding-freebsd-x64@0.8.4": - resolution: - { - integrity: sha512-ibLKORdz71iI4Vs+fyFgvwQ51P5XcxJIyQLa8cSEWqwptRdo+BTcZHIQEcZnFDPUuvmJ19RRH9CoiJdfhr7pZw==, - } + '@yuku-parser/binding-freebsd-x64@0.8.4': + resolution: {integrity: sha512-ibLKORdz71iI4Vs+fyFgvwQ51P5XcxJIyQLa8cSEWqwptRdo+BTcZHIQEcZnFDPUuvmJ19RRH9CoiJdfhr7pZw==} cpu: [x64] os: [freebsd] - "@yuku-parser/binding-linux-arm-gnu@0.8.4": - resolution: - { - integrity: sha512-Fo3r5fYhGDcFnl+KN+L9PgtiQPS4AIE1n1mG1o5jZ11p7g5yZ/1EjLFmSHAUsoXreun8KjTsFjEL7P1Sb93PZQ==, - } + '@yuku-parser/binding-linux-arm-gnu@0.8.4': + resolution: {integrity: sha512-Fo3r5fYhGDcFnl+KN+L9PgtiQPS4AIE1n1mG1o5jZ11p7g5yZ/1EjLFmSHAUsoXreun8KjTsFjEL7P1Sb93PZQ==} cpu: [arm] os: [linux] libc: [glibc] - "@yuku-parser/binding-linux-arm-musl@0.8.4": - resolution: - { - integrity: sha512-BEB31vUEgXPWf7WkoMPSzzJhpC/wWCBXyysRCCPsw47BJ/OtbQsvJbxX9fFDuSRLy3kbyIV/WbdUTgbQ9COxiw==, - } + '@yuku-parser/binding-linux-arm-musl@0.8.4': + resolution: {integrity: sha512-BEB31vUEgXPWf7WkoMPSzzJhpC/wWCBXyysRCCPsw47BJ/OtbQsvJbxX9fFDuSRLy3kbyIV/WbdUTgbQ9COxiw==} cpu: [arm] os: [linux] libc: [musl] - "@yuku-parser/binding-linux-arm64-gnu@0.8.4": - resolution: - { - integrity: sha512-xGLCRcHn9xVz7JVNyyKtiNJSf503qtUmih9XVSsghgzOmiKUMnHObs69OMMwXN3788tg1jsl11fNjjQlB8idMA==, - } + '@yuku-parser/binding-linux-arm64-gnu@0.8.4': + resolution: {integrity: sha512-xGLCRcHn9xVz7JVNyyKtiNJSf503qtUmih9XVSsghgzOmiKUMnHObs69OMMwXN3788tg1jsl11fNjjQlB8idMA==} cpu: [arm64] os: [linux] libc: [glibc] - "@yuku-parser/binding-linux-arm64-musl@0.8.4": - resolution: - { - integrity: sha512-3kNRi8NJT2q6FQRVCUFHIQ99+kXdi8cVJEEUi6+xtFqpMgNrZNnbgAj28ILsDC7zmclaU+v47eUCeJoKLSCbww==, - } + '@yuku-parser/binding-linux-arm64-musl@0.8.4': + resolution: {integrity: sha512-3kNRi8NJT2q6FQRVCUFHIQ99+kXdi8cVJEEUi6+xtFqpMgNrZNnbgAj28ILsDC7zmclaU+v47eUCeJoKLSCbww==} cpu: [arm64] os: [linux] libc: [musl] - "@yuku-parser/binding-linux-x64-gnu@0.8.4": - resolution: - { - integrity: sha512-isi62oMy94Z3OXwGs2l2rkqRiRyqLmfHeTRHpA/uWZbsNhnm7IdVvkF7e7wHNKAtd5jwGzOqYSroxKv2fOm7Cw==, - } + '@yuku-parser/binding-linux-x64-gnu@0.8.4': + resolution: {integrity: sha512-isi62oMy94Z3OXwGs2l2rkqRiRyqLmfHeTRHpA/uWZbsNhnm7IdVvkF7e7wHNKAtd5jwGzOqYSroxKv2fOm7Cw==} cpu: [x64] os: [linux] libc: [glibc] - "@yuku-parser/binding-linux-x64-musl@0.8.4": - resolution: - { - integrity: sha512-9RsEw2xYHqU/pjSRBTOupWN3sF8uz9stjJdARfz6o0llvF+yfrj5QHmTiocGGBeAI80fvKmDtr2RIQClUzAPcA==, - } + '@yuku-parser/binding-linux-x64-musl@0.8.4': + resolution: {integrity: sha512-9RsEw2xYHqU/pjSRBTOupWN3sF8uz9stjJdARfz6o0llvF+yfrj5QHmTiocGGBeAI80fvKmDtr2RIQClUzAPcA==} cpu: [x64] os: [linux] libc: [musl] - "@yuku-parser/binding-win32-arm64@0.8.4": - resolution: - { - integrity: sha512-VEZHo9rEGOBKR20sA3vCO00aQvwWND5aLu7YxeX+YupMZJh9hd1f17AbClJN37Q2iL1PCHht+wDTOKX6tZYqXg==, - } + '@yuku-parser/binding-win32-arm64@0.8.4': + resolution: {integrity: sha512-VEZHo9rEGOBKR20sA3vCO00aQvwWND5aLu7YxeX+YupMZJh9hd1f17AbClJN37Q2iL1PCHht+wDTOKX6tZYqXg==} cpu: [arm64] os: [win32] - "@yuku-parser/binding-win32-x64@0.8.4": - resolution: - { - integrity: sha512-PeH3VzN1feGjPtDpVEAqf000fPT+nxtw/696LKp/5Z9RJi/MaXpB636QC+5QtrAPSoEnIkyEe+c+mq2QLZPWBA==, - } + '@yuku-parser/binding-win32-x64@0.8.4': + resolution: {integrity: sha512-PeH3VzN1feGjPtDpVEAqf000fPT+nxtw/696LKp/5Z9RJi/MaXpB636QC+5QtrAPSoEnIkyEe+c+mq2QLZPWBA==} cpu: [x64] os: [win32] - "@yuku-toolchain/types@0.8.4": - resolution: - { - integrity: sha512-p7JE8flrj7ijZ/qLjHi4UwKqMarMD6zumbKXhrjp2I2iLJOuTYiQyci2U36VlXcUlNyzsY7E/mLnKCHotbzJVw==, - } + '@yuku-toolchain/types@0.8.4': + resolution: {integrity: sha512-p7JE8flrj7ijZ/qLjHi4UwKqMarMD6zumbKXhrjp2I2iLJOuTYiQyci2U36VlXcUlNyzsY7E/mLnKCHotbzJVw==} agent-base@7.1.4: - resolution: - { - integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} ansi-styles@5.2.0: - resolution: - { - integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, - } - engines: { node: ">=10" } + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} ansis@4.3.1: - resolution: - { - integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} + engines: {node: '>=14'} aria-query@5.3.0: - resolution: - { - integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==, - } + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} assertion-error@2.0.1: - resolution: - { - integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} axe-core@4.13.0: - resolution: - { - integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==, - } - engines: { node: ">=4" } + resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} + engines: {node: '>=4'} bidi-js@1.0.3: - resolution: - { - integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==, - } + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} cac@7.0.0: - resolution: - { - integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==, - } - engines: { node: ">=20.19.0" } + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} chai@6.2.2: - resolution: - { - integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} css-tree@3.2.1: - resolution: - { - integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} cssstyle@5.3.7: - resolution: - { - integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==} + engines: {node: '>=20'} csstype@3.2.3: - resolution: - { - integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==, - } + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} data-urls@6.0.1: - resolution: - { - integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} + engines: {node: '>=20'} debug@4.4.3: - resolution: - { - integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, - } - engines: { node: ">=6.0" } + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} peerDependencies: - supports-color: "*" + supports-color: '*' peerDependenciesMeta: supports-color: optional: true decimal.js@10.6.0: - resolution: - { - integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==, - } + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} defu@6.1.7: - resolution: - { - integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==, - } + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} dequal@2.0.3: - resolution: - { - integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} detect-libc@2.1.2: - resolution: - { - integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} dom-accessibility-api@0.5.16: - resolution: - { - integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==, - } + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dts-resolver@3.0.0: - resolution: - { - integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==, - } - engines: { node: ^22.18.0 || >=24.0.0 } + resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} + engines: {node: ^22.18.0 || >=24.0.0} peerDependencies: - oxc-resolver: ">=11.0.0" + oxc-resolver: '>=11.0.0' peerDependenciesMeta: oxc-resolver: optional: true empathic@2.0.1: - resolution: - { - integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} + engines: {node: '>=14'} entities@8.0.0: - resolution: - { - integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==, - } - engines: { node: ">=20.19.0" } + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} es-module-lexer@2.3.1: - resolution: - { - integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==, - } + resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} expect-type@1.4.0: - resolution: - { - integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} fdir@6.5.0: - resolution: - { - integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -945,72 +626,42 @@ packages: optional: true fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] get-tsconfig@5.0.0-beta.5: - resolution: - { - integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==, - } - engines: { node: ">=20.20.0" } + resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} + engines: {node: '>=20.20.0'} hookable@6.1.1: - resolution: - { - integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==, - } + resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} html-encoding-sniffer@6.0.0: - resolution: - { - integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} http-proxy-agent@7.0.2: - resolution: - { - integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} https-proxy-agent@7.0.6: - resolution: - { - integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==, - } - engines: { node: ">= 14" } + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} import-without-cache@0.4.0: - resolution: - { - integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==, - } - engines: { node: ^22.18.0 || >=24.0.0 } + resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} + engines: {node: ^22.18.0 || >=24.0.0} is-potential-custom-element-name@1.0.1: - resolution: - { - integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, - } + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} jsdom@27.4.0: - resolution: - { - integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==, - } - engines: { node: ^20.19.0 || ^22.12.0 || >=24.0.0 } + resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -1018,278 +669,173 @@ packages: optional: true lightningcss-android-arm64@1.33.0: - resolution: - { - integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.33.0: - resolution: - { - integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.33.0: - resolution: - { - integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.33.0: - resolution: - { - integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.33.0: - resolution: - { - integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.33.0: - resolution: - { - integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.33.0: - resolution: - { - integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.33.0: - resolution: - { - integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.33.0: - resolution: - { - integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.33.0: - resolution: - { - integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.33.0: - resolution: - { - integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.33.0: - resolution: - { - integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==, - } - engines: { node: ">= 12.0.0" } + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} lru-cache@11.5.2: - resolution: - { - integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==, - } - engines: { node: 20 || >=22 } + resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} + engines: {node: 20 || >=22} lz-string@1.5.0: - resolution: - { - integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==, - } + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true magic-string@0.30.21: - resolution: - { - integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, - } + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} mdn-data@2.27.1: - resolution: - { - integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==, - } + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} nanoid@3.3.18: - resolution: - { - integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true obug@2.1.4: - resolution: - { - integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==, - } - engines: { node: ">=12.20.0" } + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} parse5@8.0.1: - resolution: - { - integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==, - } + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} pathe@2.0.3: - resolution: - { - integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, - } + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: - resolution: - { - integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, - } + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@4.0.5: - resolution: - { - integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==, - } - engines: { node: ">=12" } + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} postcss@8.5.26: - resolution: - { - integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==, - } - engines: { node: ^10 || ^12 || >=14 } + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} prettier@3.6.2: - resolution: - { - integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==, - } - engines: { node: ">=14" } + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} hasBin: true pretty-format@27.5.1: - resolution: - { - integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} punycode@2.3.1: - resolution: - { - integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, - } - engines: { node: ">=6" } + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} quansync@1.0.0: - resolution: - { - integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==, - } + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} react-dom@19.2.8: - resolution: - { - integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==, - } + resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: react: ^19.2.8 react-is@17.0.2: - resolution: - { - integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, - } + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} react@19.2.8: - resolution: - { - integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} + engines: {node: '>=0.10.0'} require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} reselect@5.2.0: - resolution: - { - integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==, - } + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} rolldown-plugin-dts@0.27.14: - resolution: - { - integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==, - } - engines: { node: ^22.18.0 || >=24.11.0 } + resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} + engines: {node: ^22.18.0 || >=24.11.0} peerDependencies: - "@typescript/native-preview": "*" - "@volar/typescript": ~2.4.0 + '@typescript/native-preview': '*' + '@volar/typescript': ~2.4.0 rolldown: ^1.0.0 typescript: ^5.0.0 || ^6.0.0 || ~7.0.0 vue-tsc: ~3.2.0 || ~3.3.0 peerDependenciesMeta: - "@typescript/native-preview": + '@typescript/native-preview': optional: true - "@volar/typescript": + '@volar/typescript': optional: true typescript: optional: true @@ -1297,143 +843,89 @@ packages: optional: true rolldown@1.2.3: - resolution: - { - integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true saxes@6.0.0: - resolution: - { - integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==, - } - engines: { node: ">=v12.22.7" } + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} scheduler@0.27.0: - resolution: - { - integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==, - } + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} siginfo@2.0.0: - resolution: - { - integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==, - } + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} source-map-js@1.2.1: - resolution: - { - integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, - } - engines: { node: ">=0.10.0" } + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} stackback@0.0.2: - resolution: - { - integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==, - } + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@4.2.0: - resolution: - { - integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==, - } + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} symbol-tree@3.2.4: - resolution: - { - integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, - } + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} tinybench@2.9.0: - resolution: - { - integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==, - } + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@1.3.0: - resolution: - { - integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} tinyglobby@0.2.17: - resolution: - { - integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, - } - engines: { node: ">=12.0.0" } + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} tinyrainbow@3.1.1: - resolution: - { - integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==, - } - engines: { node: ">=14.0.0" } + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} tldts-core@7.4.10: - resolution: - { - integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==, - } + resolution: {integrity: sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==} tldts@7.4.10: - resolution: - { - integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==, - } + resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==} hasBin: true tough-cookie@6.0.2: - resolution: - { - integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==, - } - engines: { node: ">=16" } + resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} + engines: {node: '>=16'} tr46@6.0.0: - resolution: - { - integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==, - } - engines: { node: ">=20" } + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} tree-kill@1.2.2: - resolution: - { - integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, - } + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true tsdown@0.22.14: - resolution: - { - integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==, - } - engines: { node: ^22.18.0 || >=24.11.0 } + resolution: {integrity: sha512-ule7Y+fsAN2iZbLDoo7C4KYljFJNJJ+fLshyn+9gozeTspVersWHxwdGB+Dm2hzA38s6muFnUTl0jK3vJm9ifQ==} + engines: {node: ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: - "@arethetypeswrong/core": ^0.18.1 - "@tsdown/css": 0.22.14 - "@tsdown/exe": 0.22.14 - "@vitejs/devtools": "*" + '@arethetypeswrong/core': ^0.18.1 + '@tsdown/css': 0.22.14 + '@tsdown/exe': 0.22.14 + '@vitejs/devtools': '*' publint: ^0.3.8 - tsx: "*" + tsx: '*' typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 unplugin-unused: ^0.5.0 - unrun: "*" + unrun: '*' peerDependenciesMeta: - "@arethetypeswrong/core": + '@arethetypeswrong/core': optional: true - "@tsdown/css": + '@tsdown/css': optional: true - "@tsdown/exe": + '@tsdown/exe': optional: true - "@vitejs/devtools": + '@vitejs/devtools': optional: true publint: optional: true @@ -1447,58 +939,46 @@ packages: optional: true typescript@5.9.3: - resolution: - { - integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, - } - engines: { node: ">=14.17" } + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true unconfig-core@7.5.0: - resolution: - { - integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==, - } + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} use-sync-external-store@1.6.0: - resolution: - { - integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==, - } + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 verkit@0.3.2: - resolution: - { - integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==, - } - engines: { node: ">=18.12.0" } + resolution: {integrity: sha512-zj/ob3UsvJGN0whEAKFp53REA5X66hvffVqoCtVQAakJKnKlH+/PcOfMoFwIG/o4rElqLv/ycAFlx8ZlXUorCg==} + engines: {node: '>=18.12.0'} vite@8.2.1: - resolution: - { - integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==, - } - engines: { node: ^20.19.0 || >=22.12.0 } + resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - "@vitejs/devtools": ^0.4.0 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 esbuild: ^0.27.0 || ^0.28.0 - jiti: ">=1.21.0" + jiti: '>=1.21.0' less: ^4.0.0 sass: ^1.70.0 sass-embedded: ^1.70.0 - stylus: ">=0.54.8" + stylus: '>=0.54.8' sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: - "@types/node": + '@types/node': optional: true - "@vitejs/devtools": + '@vitejs/devtools': optional: true esbuild: optional: true @@ -1522,43 +1002,40 @@ packages: optional: true vitest@4.1.10: - resolution: - { - integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==, - } - engines: { node: ^20.0.0 || ^22.0.0 || >=24.0.0 } + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: - "@edge-runtime/vm": "*" - "@opentelemetry/api": ^1.9.0 - "@types/node": ^20.0.0 || ^22.0.0 || >=24.0.0 - "@vitest/browser-playwright": 4.1.10 - "@vitest/browser-preview": 4.1.10 - "@vitest/browser-webdriverio": 4.1.10 - "@vitest/coverage-istanbul": 4.1.10 - "@vitest/coverage-v8": 4.1.10 - "@vitest/ui": 4.1.10 - happy-dom: "*" - jsdom: "*" + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 + happy-dom: '*' + jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - "@edge-runtime/vm": + '@edge-runtime/vm': optional: true - "@opentelemetry/api": + '@opentelemetry/api': optional: true - "@types/node": + '@types/node': optional: true - "@vitest/browser-playwright": + '@vitest/browser-playwright': optional: true - "@vitest/browser-preview": + '@vitest/browser-preview': optional: true - "@vitest/browser-webdriverio": + '@vitest/browser-webdriverio': optional: true - "@vitest/coverage-istanbul": + '@vitest/coverage-istanbul': optional: true - "@vitest/coverage-v8": + '@vitest/coverage-v8': optional: true - "@vitest/ui": + '@vitest/ui': optional: true happy-dom: optional: true @@ -1566,57 +1043,36 @@ packages: optional: true w3c-xmlserializer@5.0.0: - resolution: - { - integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==, - } - engines: { node: ">=18" } + 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" } + 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" } + 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" } + 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" } + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} why-is-node-running@2.3.0: - resolution: - { - integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==, - } - engines: { node: ">=8" } + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} hasBin: true ws@8.21.3: - resolution: - { - integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==, - } - engines: { node: ">=10.0.0" } + resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} + engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" + utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true @@ -1624,350 +1080,340 @@ packages: optional: true xml-name-validator@5.0.0: - resolution: - { - integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==, - } - engines: { node: ">=18" } + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} xmlchars@2.2.0: - resolution: - { - integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, - } + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} yuku-ast@0.8.4: - resolution: - { - integrity: sha512-s7EWfWIQkaGmsGnyr/BU0jli9YTN5TvrKIsSmALyRD9elumDQInuhv0BrVObENKVCxr9W3Ikmnx5u02KvfuUmw==, - } + resolution: {integrity: sha512-s7EWfWIQkaGmsGnyr/BU0jli9YTN5TvrKIsSmALyRD9elumDQInuhv0BrVObENKVCxr9W3Ikmnx5u02KvfuUmw==} yuku-codegen@0.8.4: - resolution: - { - integrity: sha512-1Rw+NYcmB1xkHAWlsIpbwIv/Fr50idtEbLf7OjDA+90dny6PM4Krz7Fs0TT+w2PBdjaldZpN4ye5wR4Dhlm8vA==, - } + resolution: {integrity: sha512-1Rw+NYcmB1xkHAWlsIpbwIv/Fr50idtEbLf7OjDA+90dny6PM4Krz7Fs0TT+w2PBdjaldZpN4ye5wR4Dhlm8vA==} yuku-parser@0.8.4: - resolution: - { - integrity: sha512-sw41wouvT5rUmLIp87hmvm5vtF+MRSI3x6yjq6xqpYmtkQj+Ht6N7xRQ8lMhLv8N7JAzughGj0Rfi0jQRSu9HQ==, - } + resolution: {integrity: sha512-sw41wouvT5rUmLIp87hmvm5vtF+MRSI3x6yjq6xqpYmtkQj+Ht6N7xRQ8lMhLv8N7JAzughGj0Rfi0jQRSu9HQ==} snapshots: - "@acemir/cssom@0.9.31": {} - "@asamuzakjp/css-color@4.1.2": + '@acemir/cssom@0.9.31': {} + + '@asamuzakjp/css-color@4.1.2': dependencies: - "@csstools/css-calc": 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-color-parser": 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 lru-cache: 11.5.2 - "@asamuzakjp/dom-selector@6.8.1": + '@asamuzakjp/dom-selector@6.8.1': dependencies: - "@asamuzakjp/nwsapi": 2.3.9 + '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 css-tree: 3.2.1 is-potential-custom-element-name: 1.0.1 lru-cache: 11.5.2 - "@asamuzakjp/nwsapi@2.3.9": {} + '@asamuzakjp/nwsapi@2.3.9': {} - "@babel/code-frame@7.29.7": + '@babel/code-frame@7.29.7': dependencies: - "@babel/helper-validator-identifier": 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - "@babel/helper-validator-identifier@7.29.7": {} + '@babel/helper-validator-identifier@7.29.7': {} - "@babel/runtime@7.29.7": {} + '@babel/runtime@7.29.7': {} - "@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + '@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - "@babel/runtime": 7.29.7 - "@base-ui/utils": 0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - "@floating-ui/react-dom": 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) - "@floating-ui/utils": 0.2.12 + '@babel/runtime': 7.29.7 + '@base-ui/utils': 0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/react-dom': 2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@floating-ui/utils': 0.2.12 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - "@types/react": 19.2.18 + '@types/react': 19.2.18 - "@base-ui/utils@0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + '@base-ui/utils@0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - "@babel/runtime": 7.29.7 - "@floating-ui/utils": 0.2.12 + '@babel/runtime': 7.29.7 + '@floating-ui/utils': 0.2.12 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) reselect: 5.2.0 use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - "@types/react": 19.2.18 + '@types/react': 19.2.18 - "@csstools/color-helpers@6.1.0": {} + '@csstools/color-helpers@6.1.0': {} - "@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-color-parser@4.1.10(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/color-helpers": 6.1.0 - "@csstools/css-calc": 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) - "@csstools/css-parser-algorithms": 4.0.0(@csstools/css-tokenizer@4.0.0) - "@csstools/css-tokenizer": 4.0.0 + '@csstools/color-helpers': 6.1.0 + '@csstools/css-calc': 3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)": + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': dependencies: - "@csstools/css-tokenizer": 4.0.0 + '@csstools/css-tokenizer': 4.0.0 - "@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)": + '@csstools/css-syntax-patches-for-csstree@1.1.7(css-tree@3.2.1)': optionalDependencies: css-tree: 3.2.1 - "@csstools/css-tokenizer@4.0.0": {} + '@csstools/css-tokenizer@4.0.0': {} - "@exodus/bytes@1.15.1": {} + '@exodus/bytes@1.15.1': {} - "@floating-ui/core@1.8.0": + '@floating-ui/core@1.8.0': dependencies: - "@floating-ui/utils": 0.2.12 + '@floating-ui/utils': 0.2.12 - "@floating-ui/dom@1.8.0": + '@floating-ui/dom@1.8.0': dependencies: - "@floating-ui/core": 1.8.0 - "@floating-ui/utils": 0.2.12 + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 - "@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + '@floating-ui/react-dom@2.1.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - "@floating-ui/dom": 1.8.0 + '@floating-ui/dom': 1.8.0 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - "@floating-ui/utils@0.2.12": {} + '@floating-ui/utils@0.2.12': {} - "@jridgewell/sourcemap-codec@1.5.5": {} + '@jridgewell/sourcemap-codec@1.5.5': {} - "@oxc-project/types@0.143.0": {} + '@oxc-project/types@0.143.0': {} - "@quansync/fs@1.0.0": + '@quansync/fs@1.0.0': dependencies: quansync: 1.0.0 - "@rolldown/binding-android-arm64@1.2.3": + '@rolldown/binding-android-arm64@1.2.3': optional: true - "@rolldown/binding-darwin-arm64@1.2.3": + '@rolldown/binding-darwin-arm64@1.2.3': optional: true - "@rolldown/binding-darwin-x64@1.2.3": + '@rolldown/binding-darwin-x64@1.2.3': optional: true - "@rolldown/binding-freebsd-x64@1.2.3": + '@rolldown/binding-freebsd-x64@1.2.3': optional: true - "@rolldown/binding-linux-arm-gnueabihf@1.2.3": + '@rolldown/binding-linux-arm-gnueabihf@1.2.3': optional: true - "@rolldown/binding-linux-arm64-gnu@1.2.3": + '@rolldown/binding-linux-arm64-gnu@1.2.3': optional: true - "@rolldown/binding-linux-arm64-musl@1.2.3": + '@rolldown/binding-linux-arm64-musl@1.2.3': optional: true - "@rolldown/binding-linux-ppc64-gnu@1.2.3": + '@rolldown/binding-linux-ppc64-gnu@1.2.3': optional: true - "@rolldown/binding-linux-s390x-gnu@1.2.3": + '@rolldown/binding-linux-s390x-gnu@1.2.3': optional: true - "@rolldown/binding-linux-x64-gnu@1.2.3": + '@rolldown/binding-linux-x64-gnu@1.2.3': optional: true - "@rolldown/binding-linux-x64-musl@1.2.3": + '@rolldown/binding-linux-x64-musl@1.2.3': optional: true - "@rolldown/binding-openharmony-arm64@1.2.3": + '@rolldown/binding-openharmony-arm64@1.2.3': optional: true - "@rolldown/binding-win32-arm64-msvc@1.2.3": + '@rolldown/binding-win32-arm64-msvc@1.2.3': optional: true - "@rolldown/binding-win32-x64-msvc@1.2.3": + '@rolldown/binding-win32-x64-msvc@1.2.3': optional: true - "@rolldown/pluginutils@1.0.1": {} + '@rolldown/pluginutils@1.0.1': {} - "@standard-schema/spec@1.1.0": {} + '@standard-schema/spec@1.1.0': {} - "@testing-library/dom@10.4.1": + '@testing-library/dom@10.4.1': dependencies: - "@babel/code-frame": 7.29.7 - "@babel/runtime": 7.29.7 - "@types/aria-query": 5.0.4 + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 - "@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)": + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: - "@babel/runtime": 7.29.7 - "@testing-library/dom": 10.4.1 + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) optionalDependencies: - "@types/react": 19.2.18 - "@types/react-dom": 19.2.4(@types/react@19.2.18) + '@types/react': 19.2.18 + '@types/react-dom': 19.2.4(@types/react@19.2.18) - "@testing-library/user-event@14.6.3(@testing-library/dom@10.4.1)": + '@testing-library/user-event@14.6.3(@testing-library/dom@10.4.1)': dependencies: - "@testing-library/dom": 10.4.1 + '@testing-library/dom': 10.4.1 - "@types/aria-query@5.0.4": {} + '@types/aria-query@5.0.4': {} - "@types/chai@5.2.3": + '@types/chai@5.2.3': dependencies: - "@types/deep-eql": 4.0.2 + '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - "@types/deep-eql@4.0.2": {} + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} - "@types/estree@1.0.9": {} + '@types/node@26.2.0': + dependencies: + undici-types: 8.3.0 - "@types/react-dom@19.2.4(@types/react@19.2.18)": + '@types/react-dom@19.2.4(@types/react@19.2.18)': dependencies: - "@types/react": 19.2.18 + '@types/react': 19.2.18 - "@types/react@19.2.18": + '@types/react@19.2.18': dependencies: csstype: 3.2.3 - "@vitejs/plugin-react@6.0.5(vite@8.2.1)": + '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0))': dependencies: - "@rolldown/pluginutils": 1.0.1 - vite: 8.2.1 + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.1(@types/node@26.2.0) - "@vitest/expect@4.1.10": + '@vitest/expect@4.1.10': dependencies: - "@standard-schema/spec": 1.1.0 - "@types/chai": 5.2.3 - "@vitest/spy": 4.1.10 - "@vitest/utils": 4.1.10 + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 tinyrainbow: 3.1.1 - "@vitest/mocker@4.1.10(vite@8.2.1)": + '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@26.2.0))': dependencies: - "@vitest/spy": 4.1.10 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.2.1 + vite: 8.2.1(@types/node@26.2.0) - "@vitest/pretty-format@4.1.10": + '@vitest/pretty-format@4.1.10': dependencies: tinyrainbow: 3.1.1 - "@vitest/runner@4.1.10": + '@vitest/runner@4.1.10': dependencies: - "@vitest/utils": 4.1.10 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - "@vitest/snapshot@4.1.10": + '@vitest/snapshot@4.1.10': dependencies: - "@vitest/pretty-format": 4.1.10 - "@vitest/utils": 4.1.10 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 - "@vitest/spy@4.1.10": {} + '@vitest/spy@4.1.10': {} - "@vitest/utils@4.1.10": + '@vitest/utils@4.1.10': dependencies: - "@vitest/pretty-format": 4.1.10 + '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 tinyrainbow: 3.1.1 - "@yuku-codegen/binding-android-arm64@0.8.4": + '@yuku-codegen/binding-android-arm64@0.8.4': optional: true - "@yuku-codegen/binding-darwin-arm64@0.8.4": + '@yuku-codegen/binding-darwin-arm64@0.8.4': optional: true - "@yuku-codegen/binding-darwin-x64@0.8.4": + '@yuku-codegen/binding-darwin-x64@0.8.4': optional: true - "@yuku-codegen/binding-freebsd-x64@0.8.4": + '@yuku-codegen/binding-freebsd-x64@0.8.4': optional: true - "@yuku-codegen/binding-linux-arm-gnu@0.8.4": + '@yuku-codegen/binding-linux-arm-gnu@0.8.4': optional: true - "@yuku-codegen/binding-linux-arm-musl@0.8.4": + '@yuku-codegen/binding-linux-arm-musl@0.8.4': optional: true - "@yuku-codegen/binding-linux-arm64-gnu@0.8.4": + '@yuku-codegen/binding-linux-arm64-gnu@0.8.4': optional: true - "@yuku-codegen/binding-linux-arm64-musl@0.8.4": + '@yuku-codegen/binding-linux-arm64-musl@0.8.4': optional: true - "@yuku-codegen/binding-linux-x64-gnu@0.8.4": + '@yuku-codegen/binding-linux-x64-gnu@0.8.4': optional: true - "@yuku-codegen/binding-linux-x64-musl@0.8.4": + '@yuku-codegen/binding-linux-x64-musl@0.8.4': optional: true - "@yuku-codegen/binding-win32-arm64@0.8.4": + '@yuku-codegen/binding-win32-arm64@0.8.4': optional: true - "@yuku-codegen/binding-win32-x64@0.8.4": + '@yuku-codegen/binding-win32-x64@0.8.4': optional: true - "@yuku-parser/binding-android-arm64@0.8.4": + '@yuku-parser/binding-android-arm64@0.8.4': optional: true - "@yuku-parser/binding-darwin-arm64@0.8.4": + '@yuku-parser/binding-darwin-arm64@0.8.4': optional: true - "@yuku-parser/binding-darwin-x64@0.8.4": + '@yuku-parser/binding-darwin-x64@0.8.4': optional: true - "@yuku-parser/binding-freebsd-x64@0.8.4": + '@yuku-parser/binding-freebsd-x64@0.8.4': optional: true - "@yuku-parser/binding-linux-arm-gnu@0.8.4": + '@yuku-parser/binding-linux-arm-gnu@0.8.4': optional: true - "@yuku-parser/binding-linux-arm-musl@0.8.4": + '@yuku-parser/binding-linux-arm-musl@0.8.4': optional: true - "@yuku-parser/binding-linux-arm64-gnu@0.8.4": + '@yuku-parser/binding-linux-arm64-gnu@0.8.4': optional: true - "@yuku-parser/binding-linux-arm64-musl@0.8.4": + '@yuku-parser/binding-linux-arm64-musl@0.8.4': optional: true - "@yuku-parser/binding-linux-x64-gnu@0.8.4": + '@yuku-parser/binding-linux-x64-gnu@0.8.4': optional: true - "@yuku-parser/binding-linux-x64-musl@0.8.4": + '@yuku-parser/binding-linux-x64-musl@0.8.4': optional: true - "@yuku-parser/binding-win32-arm64@0.8.4": + '@yuku-parser/binding-win32-arm64@0.8.4': optional: true - "@yuku-parser/binding-win32-x64@0.8.4": + '@yuku-parser/binding-win32-x64@0.8.4': optional: true - "@yuku-toolchain/types@0.8.4": {} + '@yuku-toolchain/types@0.8.4': {} agent-base@7.1.4: {} @@ -2002,8 +1448,8 @@ snapshots: cssstyle@5.3.7: dependencies: - "@asamuzakjp/css-color": 4.1.2 - "@csstools/css-syntax-patches-for-csstree": 1.1.7(css-tree@3.2.1) + '@asamuzakjp/css-color': 4.1.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.7(css-tree@3.2.1) css-tree: 3.2.1 lru-cache: 11.5.2 @@ -2038,7 +1484,7 @@ snapshots: estree-walker@3.0.3: dependencies: - "@types/estree": 1.0.9 + '@types/estree': 1.0.9 expect-type@1.4.0: {} @@ -2057,9 +1503,9 @@ snapshots: html-encoding-sniffer@6.0.0: dependencies: - "@exodus/bytes": 1.15.1 + '@exodus/bytes': 1.15.1 transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' http-proxy-agent@7.0.2: dependencies: @@ -2083,9 +1529,9 @@ snapshots: jsdom@27.4.0: dependencies: - "@acemir/cssom": 0.9.31 - "@asamuzakjp/dom-selector": 6.8.1 - "@exodus/bytes": 1.15.1 + '@acemir/cssom': 0.9.31 + '@asamuzakjp/dom-selector': 6.8.1 + '@exodus/bytes': 1.15.1 cssstyle: 5.3.7 data-urls: 6.0.1 decimal.js: 10.6.0 @@ -2104,7 +1550,7 @@ snapshots: ws: 8.21.3 xml-name-validator: 5.0.0 transitivePeerDependencies: - - "@noble/hashes" + - '@noble/hashes' - bufferutil - supports-color - utf-8-validate @@ -2164,7 +1610,7 @@ snapshots: magic-string@0.30.21: dependencies: - "@jridgewell/sourcemap-codec": 1.5.5 + '@jridgewell/sourcemap-codec': 1.5.5 mdn-data@2.27.1: {} @@ -2233,23 +1679,23 @@ snapshots: rolldown@1.2.3: dependencies: - "@oxc-project/types": 0.143.0 - "@rolldown/pluginutils": 1.0.1 + '@oxc-project/types': 0.143.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - "@rolldown/binding-android-arm64": 1.2.3 - "@rolldown/binding-darwin-arm64": 1.2.3 - "@rolldown/binding-darwin-x64": 1.2.3 - "@rolldown/binding-freebsd-x64": 1.2.3 - "@rolldown/binding-linux-arm-gnueabihf": 1.2.3 - "@rolldown/binding-linux-arm64-gnu": 1.2.3 - "@rolldown/binding-linux-arm64-musl": 1.2.3 - "@rolldown/binding-linux-ppc64-gnu": 1.2.3 - "@rolldown/binding-linux-s390x-gnu": 1.2.3 - "@rolldown/binding-linux-x64-gnu": 1.2.3 - "@rolldown/binding-linux-x64-musl": 1.2.3 - "@rolldown/binding-openharmony-arm64": 1.2.3 - "@rolldown/binding-win32-arm64-msvc": 1.2.3 - "@rolldown/binding-win32-x64-msvc": 1.2.3 + '@rolldown/binding-android-arm64': 1.2.3 + '@rolldown/binding-darwin-arm64': 1.2.3 + '@rolldown/binding-darwin-x64': 1.2.3 + '@rolldown/binding-freebsd-x64': 1.2.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.3 + '@rolldown/binding-linux-arm64-gnu': 1.2.3 + '@rolldown/binding-linux-arm64-musl': 1.2.3 + '@rolldown/binding-linux-ppc64-gnu': 1.2.3 + '@rolldown/binding-linux-s390x-gnu': 1.2.3 + '@rolldown/binding-linux-x64-gnu': 1.2.3 + '@rolldown/binding-linux-x64-musl': 1.2.3 + '@rolldown/binding-openharmony-arm64': 1.2.3 + '@rolldown/binding-win32-arm64-msvc': 1.2.3 + '@rolldown/binding-win32-x64-msvc': 1.2.3 saxes@6.0.0: dependencies: @@ -2314,8 +1760,8 @@ snapshots: optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - - "@typescript/native-preview" - - "@volar/typescript" + - '@typescript/native-preview' + - '@volar/typescript' - oxc-resolver - vue-tsc @@ -2323,16 +1769,18 @@ snapshots: unconfig-core@7.5.0: dependencies: - "@quansync/fs": 1.0.0 + '@quansync/fs': 1.0.0 quansync: 1.0.0 + undici-types@8.3.0: {} + use-sync-external-store@1.6.0(react@19.2.8): dependencies: react: 19.2.8 verkit@0.3.2: {} - vite@8.2.1: + vite@8.2.1(@types/node@26.2.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -2340,17 +1788,18 @@ snapshots: rolldown: 1.2.3 tinyglobby: 0.2.17 optionalDependencies: + '@types/node': 26.2.0 fsevents: 2.3.3 - vitest@4.1.10(jsdom@27.4.0)(vite@8.2.1): + vitest@4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)): dependencies: - "@vitest/expect": 4.1.10 - "@vitest/mocker": 4.1.10(vite@8.2.1) - "@vitest/pretty-format": 4.1.10 - "@vitest/runner": 4.1.10 - "@vitest/snapshot": 4.1.10 - "@vitest/spy": 4.1.10 - "@vitest/utils": 4.1.10 + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 es-module-lexer: 2.3.1 expect-type: 1.4.0 magic-string: 0.30.21 @@ -2362,9 +1811,10 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 8.2.1 + vite: 8.2.1(@types/node@26.2.0) why-is-node-running: 2.3.0 optionalDependencies: + '@types/node': 26.2.0 jsdom: 27.4.0 transitivePeerDependencies: - msw @@ -2397,39 +1847,39 @@ snapshots: yuku-ast@0.8.4: dependencies: - "@yuku-toolchain/types": 0.8.4 + '@yuku-toolchain/types': 0.8.4 yuku-codegen@0.8.4: dependencies: - "@yuku-toolchain/types": 0.8.4 + '@yuku-toolchain/types': 0.8.4 optionalDependencies: - "@yuku-codegen/binding-android-arm64": 0.8.4 - "@yuku-codegen/binding-darwin-arm64": 0.8.4 - "@yuku-codegen/binding-darwin-x64": 0.8.4 - "@yuku-codegen/binding-freebsd-x64": 0.8.4 - "@yuku-codegen/binding-linux-arm-gnu": 0.8.4 - "@yuku-codegen/binding-linux-arm-musl": 0.8.4 - "@yuku-codegen/binding-linux-arm64-gnu": 0.8.4 - "@yuku-codegen/binding-linux-arm64-musl": 0.8.4 - "@yuku-codegen/binding-linux-x64-gnu": 0.8.4 - "@yuku-codegen/binding-linux-x64-musl": 0.8.4 - "@yuku-codegen/binding-win32-arm64": 0.8.4 - "@yuku-codegen/binding-win32-x64": 0.8.4 + '@yuku-codegen/binding-android-arm64': 0.8.4 + '@yuku-codegen/binding-darwin-arm64': 0.8.4 + '@yuku-codegen/binding-darwin-x64': 0.8.4 + '@yuku-codegen/binding-freebsd-x64': 0.8.4 + '@yuku-codegen/binding-linux-arm-gnu': 0.8.4 + '@yuku-codegen/binding-linux-arm-musl': 0.8.4 + '@yuku-codegen/binding-linux-arm64-gnu': 0.8.4 + '@yuku-codegen/binding-linux-arm64-musl': 0.8.4 + '@yuku-codegen/binding-linux-x64-gnu': 0.8.4 + '@yuku-codegen/binding-linux-x64-musl': 0.8.4 + '@yuku-codegen/binding-win32-arm64': 0.8.4 + '@yuku-codegen/binding-win32-x64': 0.8.4 yuku-parser@0.8.4: dependencies: - "@yuku-toolchain/types": 0.8.4 + '@yuku-toolchain/types': 0.8.4 yuku-ast: 0.8.4 optionalDependencies: - "@yuku-parser/binding-android-arm64": 0.8.4 - "@yuku-parser/binding-darwin-arm64": 0.8.4 - "@yuku-parser/binding-darwin-x64": 0.8.4 - "@yuku-parser/binding-freebsd-x64": 0.8.4 - "@yuku-parser/binding-linux-arm-gnu": 0.8.4 - "@yuku-parser/binding-linux-arm-musl": 0.8.4 - "@yuku-parser/binding-linux-arm64-gnu": 0.8.4 - "@yuku-parser/binding-linux-arm64-musl": 0.8.4 - "@yuku-parser/binding-linux-x64-gnu": 0.8.4 - "@yuku-parser/binding-linux-x64-musl": 0.8.4 - "@yuku-parser/binding-win32-arm64": 0.8.4 - "@yuku-parser/binding-win32-x64": 0.8.4 + '@yuku-parser/binding-android-arm64': 0.8.4 + '@yuku-parser/binding-darwin-arm64': 0.8.4 + '@yuku-parser/binding-darwin-x64': 0.8.4 + '@yuku-parser/binding-freebsd-x64': 0.8.4 + '@yuku-parser/binding-linux-arm-gnu': 0.8.4 + '@yuku-parser/binding-linux-arm-musl': 0.8.4 + '@yuku-parser/binding-linux-arm64-gnu': 0.8.4 + '@yuku-parser/binding-linux-arm64-musl': 0.8.4 + '@yuku-parser/binding-linux-x64-gnu': 0.8.4 + '@yuku-parser/binding-linux-x64-musl': 0.8.4 + '@yuku-parser/binding-win32-arm64': 0.8.4 + '@yuku-parser/binding-win32-x64': 0.8.4 From b69145b6961538dfc131baa09cc711ba5f835f56 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 04:01:39 +0530 Subject: [PATCH 07/33] Guard the duplicated dark.css copies with a value-level parity test The name-set parity test pools both dark copies into one Set, so a value edited in one copy but not the other -- or a token dropped from a single copy -- shipped with a green suite. Split dark.css on the media-query boundary and assert the two copies' name->value maps are deeply equal. Also point light.css's header at scale.css, where --dowel-hue now lives. Co-Authored-By: Claude Opus 5 (1M context) --- packages/dowel/src/tokens/light.css | 2 +- packages/dowel/test/tokens.test.ts | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/dowel/src/tokens/light.css b/packages/dowel/src/tokens/light.css index 0c7ab95..37ba3e9 100644 --- a/packages/dowel/src/tokens/light.css +++ b/packages/dowel/src/tokens/light.css @@ -1,5 +1,5 @@ /* Light is the default theme. Every neutral is one hue at low chroma — - greys that are not grey. Changing --dowel-hue retints the entire library. */ + greys that are not grey. The hue itself lives in scale.css. */ @layer dowel.tokens { :root { /* surfaces */ diff --git a/packages/dowel/test/tokens.test.ts b/packages/dowel/test/tokens.test.ts index bbc56a6..b3253a4 100644 --- a/packages/dowel/test/tokens.test.ts +++ b/packages/dowel/test/tokens.test.ts @@ -27,6 +27,33 @@ describe("token layer", () => { expect([...light].sort()).toEqual([...dark].sort()); }); + it("keeps the two dark copies identical, value for value", () => { + // dark.css intentionally repeats its declarations: once under the class / + // attribute selectors, once inside the prefers-color-scheme media query. + // CSS cannot share one block across that boundary without a preprocessor, + // so this test is what keeps the two copies from drifting apart — a value + // edited in one copy but not the other must fail here, not ship. + const decls = (css: string) => { + const map: Record = {}; + for (const [, name, value] of css.matchAll( + /(--dowel-[\w-]+)\s*:\s*([^;]+);/g, + )) { + // Collapse whitespace only — never the values themselves, so real + // drift like 0.15s vs .15s still fails. + if (name && value) map[name] = value.replace(/\s+/g, " ").trim(); + } + return map; + }; + const parts = read("tokens/dark.css").split( + /@media\s*\(prefers-color-scheme:\s*dark\)\s*\{/, + ); + expect(parts).toHaveLength(2); + const classCopy = decls(parts[0] ?? ""); + const mediaCopy = decls(parts[1] ?? ""); + expect(Object.keys(classCopy).length).toBeGreaterThan(0); + expect(mediaCopy).toEqual(classCopy); + }); + it("uses 450 as the normal font weight, not 400", () => { expect(read("tokens/scale.css")).toContain("--dowel-fw-normal: 450"); }); From 5899ef9d10000f733cc506a4da08f2eeb11bb21c Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 04:06:14 +0530 Subject: [PATCH 08/33] Add the Lightning CSS and tsdown build pipeline with a token contract test tsdown emits ESM + d.ts from an empty barrel; build-css.mjs bundles the token CSS through Lightning CSS into one minified dist/dowel.css. The css-contract suite asserts the bundle exists, resolves every --dowel- var() it references, inlines all @imports, and keeps the cascade layers. tsdown's clean:true wipes dist/, so the build script runs tsdown first. Co-Authored-By: Claude Opus 5 (1M context) --- packages/dowel/package.json | 1 + packages/dowel/scripts/build-css.mjs | 31 +++++++++++++ packages/dowel/src/index.ts | 2 + packages/dowel/test/css-contract.test.ts | 33 ++++++++++++++ packages/dowel/tsdown.config.ts | 14 ++++++ pnpm-lock.yaml | 57 ++++++++++++++++++++++++ 6 files changed, 138 insertions(+) create mode 100644 packages/dowel/scripts/build-css.mjs create mode 100644 packages/dowel/src/index.ts create mode 100644 packages/dowel/test/css-contract.test.ts create mode 100644 packages/dowel/tsdown.config.ts diff --git a/packages/dowel/package.json b/packages/dowel/package.json index 8c40da1..0752b04 100644 --- a/packages/dowel/package.json +++ b/packages/dowel/package.json @@ -60,6 +60,7 @@ "@types/react-dom": "^19.2.0", "@vitejs/plugin-react": "^6.0.5", "axe-core": "^4.13.0", + "browserslist": "^4.28.7", "jsdom": "^27.0.0", "lightningcss": "^1.33.0", "react": "^19.2.8", diff --git a/packages/dowel/scripts/build-css.mjs b/packages/dowel/scripts/build-css.mjs new file mode 100644 index 0000000..2f6516f --- /dev/null +++ b/packages/dowel/scripts/build-css.mjs @@ -0,0 +1,31 @@ +// Bundles src/index.css (following @import) into one minified dist/dowel.css. +// Lightning CSS is used as a library rather than the CLI so the targets and +// the drafts flag stay in version control instead of a shell string. +import { mkdirSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { bundle, browserslistToTargets } from "lightningcss"; +import browserslist from "browserslist"; + +const here = dirname(fileURLToPath(import.meta.url)); +const entry = resolve(here, "..", "src", "index.css"); +const out = resolve(here, "..", "dist", "dowel.css"); + +// lch() and cascade layers both need reasonably current browsers; this is the +// floor dowel supports and it is asserted in the README. +const targets = browserslistToTargets( + browserslist(["chrome >= 111", "firefox >= 113", "safari >= 16.4"]), +); + +const { code, warnings } = bundle({ + filename: entry, + minify: true, + targets, + drafts: { customMedia: false }, +}); + +for (const w of warnings) console.warn(`lightningcss: ${w.message}`); + +mkdirSync(dirname(out), { recursive: true }); +writeFileSync(out, code); +console.log(`built dist/dowel.css (${(code.length / 1024).toFixed(1)} kB)`); diff --git a/packages/dowel/src/index.ts b/packages/dowel/src/index.ts new file mode 100644 index 0000000..aea33ce --- /dev/null +++ b/packages/dowel/src/index.ts @@ -0,0 +1,2 @@ +// Components are appended here by each component task. +export {}; diff --git a/packages/dowel/test/css-contract.test.ts b/packages/dowel/test/css-contract.test.ts new file mode 100644 index 0000000..e1a07f8 --- /dev/null +++ b/packages/dowel/test/css-contract.test.ts @@ -0,0 +1,33 @@ +import { existsSync, readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { describe, expect, it } from "vitest"; + +const DIST = resolve(import.meta.dirname, "..", "dist", "dowel.css"); + +describe("dowel.css build contract", () => { + it("has been built", () => { + // Run `pnpm --filter dowel build` before this suite. + expect(existsSync(DIST)).toBe(true); + }); + + it("resolves every var() it references", () => { + const css = readFileSync(DIST, "utf8"); + const defined = new Set( + [...css.matchAll(/(--dowel-[\w-]+)\s*:/g)].map((m) => m[1]), + ); + const used = new Set( + [...css.matchAll(/var\(\s*(--dowel-[\w-]+)/g)].map((m) => m[1]), + ); + // A typo'd token silently renders as nothing. This is the guard. + const missing = [...used].filter((t) => !defined.has(t)); + expect(missing).toEqual([]); + }); + + it("inlines every @import", () => { + expect(readFileSync(DIST, "utf8")).not.toContain("@import"); + }); + + it("keeps the cascade layer names", () => { + expect(readFileSync(DIST, "utf8")).toContain("@layer"); + }); +}); diff --git a/packages/dowel/tsdown.config.ts b/packages/dowel/tsdown.config.ts new file mode 100644 index 0000000..a85900d --- /dev/null +++ b/packages/dowel/tsdown.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from "tsdown"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: "esm", + platform: "browser", + dts: true, + clean: true, + treeshake: true, + // No `external` here on purpose: tsdown never bundles `dependencies` or + // `peerDependencies`, so react, react-dom and @base-ui/react are already + // external. (tsdown's `external` option is deprecated in favour of + // `deps.neverBundle`, and neither is needed for this package.) +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfdaf11..b616c29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,9 @@ importers: axe-core: specifier: ^4.13.0 version: 4.13.0 + browserslist: + specifier: ^4.28.7 + version: 4.28.7 jsdom: specifier: ^27.0.0 version: 27.4.0 @@ -534,13 +537,26 @@ packages: resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} engines: {node: '>=4'} + baseline-browser-mapping@2.11.12: + resolution: {integrity: sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==} + engines: {node: '>=6.0.0'} + hasBin: true + bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + browserslist@4.28.7: + resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + cac@7.0.0: resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} engines: {node: '>=20.19.0'} + caniuse-lite@1.0.30001809: + resolution: {integrity: sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -598,6 +614,9 @@ packages: oxc-resolver: optional: true + electron-to-chromium@1.5.402: + resolution: {integrity: sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==} + empathic@2.0.1: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} @@ -609,6 +628,10 @@ packages: es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -764,6 +787,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-releases@2.0.53: + resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} + engines: {node: '>=18'} + obug@2.1.4: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} @@ -949,6 +976,12 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + update-browserslist-db@1.3.0: + resolution: {integrity: sha512-x/M6q3w4Ybp91CNaS4S69UnliqR3BzRpOT6LWbksjth0S/+jhfaPJsWjt/TewpT8j9eLIojUf5jr29WextHroA==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + use-sync-external-store@1.6.0: resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: @@ -1431,12 +1464,24 @@ snapshots: axe-core@4.13.0: {} + baseline-browser-mapping@2.11.12: {} + bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 + browserslist@4.28.7: + dependencies: + baseline-browser-mapping: 2.11.12 + caniuse-lite: 1.0.30001809 + electron-to-chromium: 1.5.402 + node-releases: 2.0.53 + update-browserslist-db: 1.3.0(browserslist@4.28.7) + cac@7.0.0: {} + caniuse-lite@1.0.30001809: {} + chai@6.2.2: {} convert-source-map@2.0.0: {} @@ -1476,12 +1521,16 @@ snapshots: dts-resolver@3.0.0: {} + electron-to-chromium@1.5.402: {} + empathic@2.0.1: {} entities@8.0.0: {} es-module-lexer@2.3.1: {} + escalade@3.2.0: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 @@ -1618,6 +1667,8 @@ snapshots: nanoid@3.3.18: {} + node-releases@2.0.53: {} + obug@2.1.4: {} parse5@8.0.1: @@ -1774,6 +1825,12 @@ snapshots: undici-types@8.3.0: {} + update-browserslist-db@1.3.0(browserslist@4.28.7): + dependencies: + browserslist: 4.28.7 + escalade: 3.2.0 + picocolors: 1.1.1 + use-sync-external-store@1.6.0(react@19.2.8): dependencies: react: 19.2.8 From e0eb435111257f7a5298b04ce569d48076450eef Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 04:15:52 +0530 Subject: [PATCH 09/33] Add Button and the component authoring pattern Co-Authored-By: Claude Opus 5 (1M context) --- .../dowel/src/components/button/button.css | 77 +++++++++++++++++++ .../src/components/button/button.test.tsx | 72 +++++++++++++++++ .../dowel/src/components/button/index.tsx | 36 +++++++++ packages/dowel/src/index.css | 5 ++ packages/dowel/src/index.ts | 3 +- packages/dowel/test/render.tsx | 16 ++++ packages/dowel/test/setup.ts | 23 +++++- 7 files changed, 228 insertions(+), 4 deletions(-) create mode 100644 packages/dowel/src/components/button/button.css create mode 100644 packages/dowel/src/components/button/button.test.tsx create mode 100644 packages/dowel/src/components/button/index.tsx create mode 100644 packages/dowel/test/render.tsx diff --git a/packages/dowel/src/components/button/button.css b/packages/dowel/src/components/button/button.css new file mode 100644 index 0000000..5e6452f --- /dev/null +++ b/packages/dowel/src/components/button/button.css @@ -0,0 +1,77 @@ +@layer dowel.components { + .dowel-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--dowel-space-3); + + height: var(--dowel-h); + padding-inline: var(--dowel-space-5); + + font-family: var(--dowel-font); + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-medium); + letter-spacing: var(--dowel-tracking); + white-space: nowrap; + + border: var(--dowel-hairline) solid transparent; + border-radius: var(--dowel-radius); + transition: var(--dowel-transition); + cursor: default; + user-select: none; + } + + .dowel-btn[data-size="sm"] { + height: var(--dowel-h-sm); + padding-inline: var(--dowel-space-4); + font-size: var(--dowel-fs-mini); + } + + .dowel-btn:focus-visible { + outline: 1px solid var(--dowel-focus); + outline-offset: 1px; + } + + .dowel-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + /* primary */ + .dowel-btn[data-variant="primary"] { + background-color: var(--dowel-accent); + color: var(--dowel-accent-fg); + } + .dowel-btn[data-variant="primary"]:hover:not(:disabled) { + background-color: var(--dowel-accent-hover); + } + + /* secondary */ + .dowel-btn[data-variant="secondary"] { + background-color: var(--dowel-bg-2); + border-color: var(--dowel-border-2); + color: var(--dowel-text-2); + } + .dowel-btn[data-variant="secondary"]:hover:not(:disabled) { + background-color: var(--dowel-bg-3); + } + + /* ghost */ + .dowel-btn[data-variant="ghost"] { + background-color: transparent; + color: var(--dowel-text-3); + } + .dowel-btn[data-variant="ghost"]:hover:not(:disabled) { + background-color: var(--dowel-bg-3); + color: var(--dowel-text-2); + } + + /* danger */ + .dowel-btn[data-variant="danger"] { + background-color: var(--dowel-danger); + color: var(--dowel-danger-fg); + } + .dowel-btn[data-variant="danger"]:hover:not(:disabled) { + opacity: 0.9; + } +} diff --git a/packages/dowel/src/components/button/button.test.tsx b/packages/dowel/src/components/button/button.test.tsx new file mode 100644 index 0000000..b59fa70 --- /dev/null +++ b/packages/dowel/src/components/button/button.test.tsx @@ -0,0 +1,72 @@ +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; +import { expectNoA11yViolations } from "../../../test/setup"; +import { renderBoth } from "../../../test/render"; +import { Button } from "./index"; + +describe("Button", () => { + it("renders its label in a real button element", () => { + render(); + expect(screen.getByRole("button", { name: "Ship it" })).toBeDefined(); + }); + + it("defaults to the secondary variant at md size", () => { + render(); + const btn = screen.getByRole("button"); + expect(btn.dataset.variant).toBe("secondary"); + expect(btn.dataset.size).toBe("md"); + }); + + it("exposes the variant and size as data attributes", () => { + render( + , + ); + const btn = screen.getByRole("button"); + expect(btn.dataset.variant).toBe("danger"); + expect(btn.dataset.size).toBe("sm"); + }); + + it("carries the dowel-btn class", () => { + render(); + expect(screen.getByRole("button").className).toContain("dowel-btn"); + }); + + it("fires onClick", async () => { + const onClick = vi.fn(); + render(); + await userEvent.click(screen.getByRole("button")); + expect(onClick).toHaveBeenCalledOnce(); + }); + + it("does not fire onClick when disabled", async () => { + const onClick = vi.fn(); + render( + , + ); + await userEvent.click(screen.getByRole("button")); + expect(onClick).not.toHaveBeenCalled(); + }); + + it("renders as another element via the render prop", () => { + render(); + const link = screen.getByRole("link", { name: "Docs" }); + expect(link.tagName).toBe("A"); + expect(link.className).toContain("dowel-btn"); + }); + + it("renders in both themes", () => { + const { light, dark } = renderBoth(); + expect(light.querySelector(".dowel-btn")).not.toBeNull(); + expect(dark.querySelector(".dowel-btn")).not.toBeNull(); + }); + + it("has no accessibility violations", async () => { + const { container } = render(); + await expectNoA11yViolations(container); + }); +}); diff --git a/packages/dowel/src/components/button/index.tsx b/packages/dowel/src/components/button/index.tsx new file mode 100644 index 0000000..775f8fe --- /dev/null +++ b/packages/dowel/src/components/button/index.tsx @@ -0,0 +1,36 @@ +import { Button as BaseButton } from "@base-ui/react/button"; +import { forwardRef } from "react"; +import type { ComponentPropsWithoutRef, ReactElement } from "react"; + +type NativeButtonProps = Omit< + ComponentPropsWithoutRef<"button">, + // dowel is opinionated: appearance is not a consumer concern. + "className" | "style" +>; + +export interface ButtonProps extends NativeButtonProps { + /** Visual weight. Defaults to `secondary`. */ + variant?: "primary" | "secondary" | "ghost" | "danger"; + /** Control height. `sm` is 24px, `md` is 28px. Defaults to `md`. */ + size?: "sm" | "md"; + /** Render as a different element, e.g. `render={}`. */ + render?: ReactElement; +} + +export const Button = forwardRef( + function Button( + { variant = "secondary", size = "md", render, ...props }, + ref, + ) { + return ( + + ); + }, +); diff --git a/packages/dowel/src/index.css b/packages/dowel/src/index.css index 6f6d4f5..244ea2f 100644 --- a/packages/dowel/src/index.css +++ b/packages/dowel/src/index.css @@ -7,6 +7,11 @@ @import "./tokens/light.css"; @import "./tokens/dark.css"; +/* Component sheets. CSS requires every @import before the first style rule, + and each sheet declares its own @layer, so listing them here cannot change + the cascade — the @layer statement above pins the order. */ +@import "./components/button/button.css"; + @layer dowel.base { .dowel-root, [data-dowel-theme] { diff --git a/packages/dowel/src/index.ts b/packages/dowel/src/index.ts index aea33ce..c597a4c 100644 --- a/packages/dowel/src/index.ts +++ b/packages/dowel/src/index.ts @@ -1,2 +1,3 @@ // Components are appended here by each component task. -export {}; +export { Button } from "./components/button"; +export type { ButtonProps } from "./components/button"; diff --git a/packages/dowel/test/render.tsx b/packages/dowel/test/render.tsx new file mode 100644 index 0000000..16f2452 --- /dev/null +++ b/packages/dowel/test/render.tsx @@ -0,0 +1,16 @@ +import { render } from "@testing-library/react"; +import type { ReactElement } from "react"; + +/** + * Renders `ui` in both themes. Every component test asserts against both so a + * token missing from dark.css fails at the component that uses it. + */ +export function renderBoth(ui: ReactElement) { + const light = render(
{ui}
); + const lightEl = light.container.firstElementChild as HTMLElement; + + const dark = render(
{ui}
); + const darkEl = dark.container.firstElementChild as HTMLElement; + + return { light: lightEl, dark: darkEl }; +} diff --git a/packages/dowel/test/setup.ts b/packages/dowel/test/setup.ts index 8da7ce0..706b5af 100644 --- a/packages/dowel/test/setup.ts +++ b/packages/dowel/test/setup.ts @@ -1,3 +1,20 @@ -// Placeholder until Task 4 adds the axe matcher. Kept as a file so the -// vitest config resolves from the very first test run. -export {}; +import { cleanup } from "@testing-library/react"; +import axe from "axe-core"; +import { afterEach, expect } from "vitest"; + +// The vitest config sets `globals: false`, so Testing Library's automatic +// cleanup (which needs a global `afterEach`) never registers. Register it +// here or every `render` leaks into the next test's document. +afterEach(cleanup); + +/** Fails the test with a readable list if axe finds any violation. */ +export async function expectNoA11yViolations(el: HTMLElement): Promise { + const results = await axe.run(el, { + // colour-contrast cannot be computed in jsdom (no layout/paint). + rules: { "color-contrast": { enabled: false } }, + }); + const summary = results.violations.map( + (v) => `${v.id}: ${v.help} (${v.nodes.length} node(s))`, + ); + expect(summary).toEqual([]); +} From da79295af651fec5ade2cdd303bb89f37b6861e8 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 04:37:31 +0530 Subject: [PATCH 10/33] Harden Button: spread order, nativeButton forwarding, aria-disabled styling Review fixes for Task 4, all three replicate into Tasks 5-10: - Spread props before className/style/data-* so consumers cannot smuggle overrides through a wider object; guarded by a new test. - Forward Base UI's nativeButton so render={
} produces valid DOM (no type attr, no dev warning); two tests updated/added. - Style aria-disabled like :disabled for non-form-control renders. - renderBoth docstring now states what jsdom can actually check. Co-Authored-By: Claude Opus 5 (1M context) --- .../dowel/src/components/button/button.css | 5 ++- .../src/components/button/button.test.tsx | 35 ++++++++++++++++--- .../dowel/src/components/button/index.tsx | 12 ++++++- packages/dowel/test/render.tsx | 8 +++-- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/packages/dowel/src/components/button/button.css b/packages/dowel/src/components/button/button.css index 5e6452f..1fd89cf 100644 --- a/packages/dowel/src/components/button/button.css +++ b/packages/dowel/src/components/button/button.css @@ -32,7 +32,10 @@ outline-offset: 1px; } - .dowel-btn:disabled { + /* :disabled only matches form controls; a Button rendered as an anchor + with nativeButton={false} is disabled via aria-disabled instead. */ + .dowel-btn:disabled, + .dowel-btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; } diff --git a/packages/dowel/src/components/button/button.test.tsx b/packages/dowel/src/components/button/button.test.tsx index b59fa70..9a3d12e 100644 --- a/packages/dowel/src/components/button/button.test.tsx +++ b/packages/dowel/src/components/button/button.test.tsx @@ -34,6 +34,17 @@ describe("Button", () => { expect(screen.getByRole("button").className).toContain("dowel-btn"); }); + it("ignores className and style smuggled through a spread", () => { + // ButtonProps Omits className/style, but JSX spreads skip excess-property + // checks, so a wider object typechecks. The runtime must hold the line. + const smuggled = { className: "evil", style: { color: "red" } }; + render(); + const btn = screen.getByRole("button"); + expect(btn.className).toContain("dowel-btn"); + expect(btn.className).not.toContain("evil"); + expect(btn.getAttribute("style")).toBeNull(); + }); + it("fires onClick", async () => { const onClick = vi.fn(); render(); @@ -52,11 +63,25 @@ describe("Button", () => { expect(onClick).not.toHaveBeenCalled(); }); - it("renders as another element via the render prop", () => { - render(); - const link = screen.getByRole("link", { name: "Docs" }); - expect(link.tagName).toBe("A"); - expect(link.className).toContain("dowel-btn"); + it("renders as another element via render plus nativeButton={false}", () => { + // Base UI swaps native , + ); + const el = screen.getByRole("button", { name: "Docs" }); + expect(el.tagName).toBe("A"); + expect(el.getAttribute("href")).toBe("/docs"); + expect(el.className).toContain("dowel-btn"); + // `type` is a MIME hint on anchors — it must not leak from button mode. + expect(el.hasAttribute("type")).toBe(false); + }); + + it("renders a native ); + expect(screen.getByRole("button").tagName).toBe("BUTTON"); }); it("renders in both themes", () => { diff --git a/packages/dowel/src/components/button/index.tsx b/packages/dowel/src/components/button/index.tsx index 775f8fe..0384d00 100644 --- a/packages/dowel/src/components/button/index.tsx +++ b/packages/dowel/src/components/button/index.tsx @@ -15,6 +15,13 @@ export interface ButtonProps extends NativeButtonProps { size?: "sm" | "md"; /** Render as a different element, e.g. `render={}`. */ render?: ReactElement; + /** + * Whether the rendered element is a native `} /> + + + + Delete issue + This cannot be undone. + Cancel} /> + + + + ); +} + +describe("Dialog", () => { + it("is closed until the trigger is activated", () => { + render(); + expect(screen.queryByRole("dialog")).toBeNull(); + }); + + it("opens on trigger click and is labelled by its title", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + expect(screen.getByRole("dialog", { name: "Delete issue" })).toBeDefined(); + }); + + it("closes on Escape", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + await userEvent.keyboard("{Escape}"); + expect(screen.queryByRole("dialog")).toBeNull(); + }); + + it("closes via the Close control", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + await userEvent.click(screen.getByRole("button", { name: "Cancel" })); + expect(screen.queryByRole("dialog")).toBeNull(); + }); + + it("carries the dowel classes on every styled part", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + const dialog = screen.getByRole("dialog"); + expect(dialog.className).toContain("dowel-dialog"); + expect(document.querySelector(".dowel-backdrop")).not.toBeNull(); + expect(dialog.querySelector(".dowel-dialog-title")).not.toBeNull(); + expect(dialog.querySelector(".dowel-dialog-description")).not.toBeNull(); + }); + + it("ignores className and style smuggled through a spread", async () => { + // Part props Omit className/style, but JSX spreads skip excess-property + // checks, so a wider object typechecks. The runtime must hold the line. + // `id` rides along for two reasons: it gives the otherwise-empty Backdrop + // spread a property in common with the part's all-optional props (TS2559 + // rejects a spread with none), and it proves functional props survive the + // spread while appearance is stripped. Base UI sets its own internal + // inline styles on Backdrop (user-select) and Popup (--nested-dialogs), + // so assert the smuggled declaration is absent rather than that the style + // attribute is empty. + const smuggle = (id: string) => ({ + id, + className: "evil", + style: { color: "red" }, + }); + render( + + + + + Delete issue + + This cannot be undone. + + + + , + ); + for (const [selector, id] of [ + [".dowel-backdrop", "s-backdrop"], + [".dowel-dialog", "s-popup"], + [".dowel-dialog-title", "s-title"], + [".dowel-dialog-description", "s-description"], + ] as const) { + const el = document.querySelector(selector); + expect(el, selector).not.toBeNull(); + expect(el!.id, selector).toBe(id); + expect(el!.className, selector).not.toContain("evil"); + expect(el!.style.color, selector).toBe(""); + } + }); + + it("opens without console errors or warnings", async () => { + // Vitest 4 intercepts console output, so a visually clean run proves + // nothing — spy and assert. + const error = vi.spyOn(console, "error").mockImplementation(() => {}); + const warn = vi.spyOn(console, "warn").mockImplementation(() => {}); + try { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + expect(error.mock.calls).toEqual([]); + expect(warn.mock.calls).toEqual([]); + } finally { + error.mockRestore(); + warn.mockRestore(); + } + }); + + it("has no accessibility violations when open", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Open" })); + await expectNoA11yViolations(screen.getByRole("dialog")); + }); +}); diff --git a/packages/dowel/src/components/dialog/index.tsx b/packages/dowel/src/components/dialog/index.tsx new file mode 100644 index 0000000..e8a0bbc --- /dev/null +++ b/packages/dowel/src/components/dialog/index.tsx @@ -0,0 +1,71 @@ +import { Dialog as BaseDialog } from "@base-ui/react/dialog"; + +/** + * Public props for a Dialog part: the corresponding Base UI component's own + * props (so `initialFocus`, `finalFocus`, `keepMounted`, … stay reachable) + * minus appearance, which is not a consumer concern. Props are inferred from + * the component's call signature — `ComponentProps` rejects this loose + * constraint (its own requires a `ReactNode` return), and the result is + * identical. + */ +type Props unknown> = T extends ( + props: infer P, +) => unknown + ? Omit + : never; + +/** + * A modal dialog on the modal elevation tier. Compound component: compose + * `Root`, `Trigger`, `Portal`, `Backdrop`, `Popup`, `Title`, `Description` + * and `Close`. `Title` labels the dialog for assistive tech automatically — + * no hand-rolled `aria-labelledby`. + */ +export const Dialog = { + Root: BaseDialog.Root, + Trigger: BaseDialog.Trigger, + Portal: BaseDialog.Portal, + + Backdrop: function DialogBackdrop(props: Props) { + return ( + + ); + }, + + Popup: function DialogPopup(props: Props) { + return ( + + ); + }, + + Title: function DialogTitle(props: Props) { + return ( + + ); + }, + + Description: function DialogDescription( + props: Props, + ) { + return ( + + ); + }, + + Close: BaseDialog.Close, +}; diff --git a/packages/dowel/src/index.css b/packages/dowel/src/index.css index aa93621..4797fde 100644 --- a/packages/dowel/src/index.css +++ b/packages/dowel/src/index.css @@ -15,6 +15,7 @@ @import "./components/badge/badge.css"; @import "./components/kbd/kbd.css"; @import "./components/input/input.css"; +@import "./components/dialog/dialog.css"; @layer dowel.base { .dowel-root, diff --git a/packages/dowel/src/index.ts b/packages/dowel/src/index.ts index 20583cc..1e6008b 100644 --- a/packages/dowel/src/index.ts +++ b/packages/dowel/src/index.ts @@ -9,3 +9,4 @@ export { Kbd } from "./components/kbd"; export type { KbdProps } from "./components/kbd"; export { Input, Field } from "./components/input"; export type { InputProps } from "./components/input"; +export { Dialog } from "./components/dialog"; From bb981a31d453ebc458da01309298ab0e9cf7aa70 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 05:46:25 +0530 Subject: [PATCH 18/33] Guard Dialog's structural parts: Portal/Trigger/Close channels, focus return Co-Authored-By: Claude Opus 5 (1M context) --- .../src/components/dialog/dialog.test.tsx | 65 +++++++++++++++---- .../dowel/src/components/dialog/index.tsx | 28 +++++++- 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/packages/dowel/src/components/dialog/dialog.test.tsx b/packages/dowel/src/components/dialog/dialog.test.tsx index d1e1ef6..db0b1f0 100644 --- a/packages/dowel/src/components/dialog/dialog.test.tsx +++ b/packages/dowel/src/components/dialog/dialog.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from "@testing-library/react"; +import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi } from "vitest"; import { expectNoA11yViolations } from "../../../test/setup"; @@ -72,33 +72,74 @@ describe("Dialog", () => { className: "evil", style: { color: "red" }, }); + // Trigger and Close are used bare (no `render`): that path renders Base + // UI's own native } /> + + + + Duplicate + + Delete + + + + + ); +} + +describe("Menu", () => { + it("is closed until triggered", () => { + render(); + expect(screen.queryByRole("menu")).toBeNull(); + }); + + // Base UI opens the menu one animation frame after mousedown (useClick + // defers setOpen to a rAF), so the popup is NOT in the DOM when + // userEvent.click resolves. findByRole polls; its timeout is a failure, so + // none of these can pass without the menu actually opening. + it("opens on trigger click", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Actions" })); + expect(await screen.findByRole("menu")).toBeDefined(); + expect(screen.getAllByRole("menuitem")).toHaveLength(2); + }); + + it("invokes the item handler on click", async () => { + const onSelect = vi.fn(); + render(); + await userEvent.click(screen.getByRole("button", { name: "Actions" })); + await userEvent.click( + await screen.findByRole("menuitem", { name: "Duplicate" }), + ); + expect(onSelect).toHaveBeenCalledOnce(); + }); + + it("closes on Escape", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Actions" })); + // Wait for the open to land first — otherwise this passes vacuously. + await screen.findByRole("menu"); + await userEvent.keyboard("{Escape}"); + expect(screen.queryByRole("menu")).toBeNull(); + }); + + it("moves focus with the arrow keys", async () => { + render(); + await userEvent.click(screen.getByRole("button", { name: "Actions" })); + await screen.findByRole("menu"); + await userEvent.keyboard("{ArrowDown}"); + // Base UI moves item focus asynchronously, so poll. A timeout is a + // failure, so the assertion cannot pass vacuously. + await waitFor(() => + expect(document.activeElement?.textContent).toBe("Duplicate"), + ); + await userEvent.keyboard("{ArrowDown}"); + await waitFor(() => + expect(document.activeElement?.textContent).toBe("Delete"), + ); + }); + + it("returns focus to the trigger on Escape", async () => { + render(); + const trigger = screen.getByRole("button", { name: "Actions" }); + await userEvent.click(trigger); + await screen.findByRole("menu"); + await userEvent.keyboard("{ArrowDown}"); + // Focus must actually enter the menu first, or the final assertion could + // pass without focus ever having left the trigger. + await waitFor(() => + expect(document.activeElement?.textContent).toBe("Duplicate"), + ); + await userEvent.keyboard("{Escape}"); + await waitFor(() => expect(document.activeElement).toBe(trigger)); + }); + + it("carries the dowel classes on every styled part", async () => { + render( + + Actions} /> + + + + + Edit + Duplicate + + + Delete + + + + , + ); + const menu = screen.getByRole("menu"); + expect(menu.className).toContain("dowel-menu"); + expect(menu.querySelectorAll(".dowel-menu-item")).toHaveLength(2); + expect(menu.querySelector(".dowel-menu-separator")).not.toBeNull(); + expect(menu.querySelector(".dowel-menu-label")).not.toBeNull(); + }); + + it("ignores className and style smuggled through a spread", async () => { + // Part props Omit className/style, but JSX spreads skip excess-property + // checks, so a wider object typechecks. The runtime must hold the line. + // `id` rides along for two reasons: it gives the otherwise-empty spreads + // a property in common with a part's all-optional props (TS2559 rejects a + // spread with none), and it proves functional props survive the spread + // while appearance is stripped. Base UI sets its own inline styles on + // Positioner (floating-ui placement) and Popup, so assert the smuggled + // DECLARATION is absent rather than that the style attribute is empty. + const smuggle = (id: string) => ({ + id, + className: "evil", + style: { color: "red" }, + }); + // Trigger is used bare (no `render`): that path renders Base UI's own + // native ; +``` + +That is the whole setup. Light and dark ship in the one stylesheet, switched +by class, data attribute or system preference. + +## Components + +`Button` · `IconButton` · `Badge` · `Kbd` · `Input` (with `Field`) · +`Dialog` · `Menu` · `Tooltip` + +Behaviour comes from [Base UI](https://base-ui.com); every component is +keyboard-tested and axe-checked. + +## Theming + +Two CSS variables — `--dowel-hue` and `--dowel-accent` — are the only +supported knobs. There is no per-component `className` or `style` override +API, by design. If you need a different button, dowel is the wrong library. + +## Typeface + +dowel is designed for Inter but ships no font; it falls back to `system-ui`. +To match the docs: + +```bash +pnpm add @fontsource-variable/inter +``` + +```ts +import "@fontsource-variable/inter"; +import "dowel/dowel.css"; +``` + +## Status + +Pre-1.0. The API will change between minor versions. + +## Licence + +MIT diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b616c29..8730f3e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,12 @@ importers: .: devDependencies: + '@changesets/changelog-github': + specifier: ^0.5.2 + version: 0.5.2 + '@changesets/cli': + specifier: ^2.31.1 + version: 2.31.1(@types/node@26.2.0) prettier: specifier: 3.6.2 version: 3.6.2 @@ -120,6 +126,67 @@ packages: '@types/react': optional: true + '@changesets/apply-release-plan@7.1.1': + resolution: {integrity: sha512-9qPCm/rLx/xoOFXIHGB229+4GOL76S4MC+7tyOuTsR6+1jYlfFDQORdvwR5hDA6y4FL2BPt3qpbcQIS+dW85LA==} + + '@changesets/assemble-release-plan@6.0.10': + resolution: {integrity: sha512-rSDcqdJ9KbVyjpBIuCidhvZNIiVt1XaIYp73ycVQRIA5n/j6wQaEk0ChRLMUQ1vkxZe51PTQ9OIhbg6HQMW45A==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/changelog-github@0.5.2': + resolution: {integrity: sha512-HeGeDl8HaIGj9fQHo/tv5XKQ2SNEi9+9yl1Bss1jttPqeiASRXhfi0A2wv8yFKCp07kR1gpOI5ge6+CWNm1jPw==} + + '@changesets/cli@2.31.1': + resolution: {integrity: sha512-uO05WTcRBwuVOJVSW8Cmpqw6q0WDL53ajGCMyszutvOe5toOnunbpM4jZzf+qxBOz7i0AzopZ8diBuewjmF40w==} + hasBin: true + + '@changesets/config@3.1.4': + resolution: {integrity: sha512-pf0bvD/v6WI2cRlZ6hzpjtZdSlXDXMAJ+Iz7xfFzV4ZxJ8OGGAON+1qYc99ZPrijnt4xp3VGG7eNvAOGS24V1Q==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.4': + resolution: {integrity: sha512-ZsS00x6WvmHq3sQv8oCMwL0f/z3wbXCVuSVTJwCnnmbC/iBdNJGFx1EcbMG4PC6sXRyH69liM4A2WKXzn/kRPg==} + + '@changesets/get-github-info@0.7.0': + resolution: {integrity: sha512-+i67Bmhfj9V4KfDeS1+Tz3iF32btKZB2AAx+cYMqDSRFP7r3/ZdGbjCo+c6qkyViN9ygDuBjzageuPGJtKGe5A==} + + '@changesets/get-release-plan@4.0.16': + resolution: {integrity: sha512-2K5Om6CrMPm45rtvckfzWo7e9jOVCKLCnXia5eUPaURH7/LWzri7pK1TycdzAuAtehLkW7VPbWLCSExTHmiI6g==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@csstools/color-helpers@6.1.0': resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} @@ -180,9 +247,36 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + '@oxc-project/types@0.143.0': resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} @@ -322,6 +416,9 @@ packages: '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@26.2.0': resolution: {integrity: sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==} @@ -514,6 +611,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -526,9 +627,19 @@ packages: resolution: {integrity: sha512-BJ8/l4R5LRE7hW9WdSuGYrLSHi2ynxeFpDFbH0K/CgNeY/tyhk+vO6TYxXC5r5CpUhNVX310xzPsN/H9lCdfOA==} engines: {node: '>=14'} + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + 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-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -542,9 +653,17 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + browserslist@4.28.7: resolution: {integrity: sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -561,9 +680,16 @@ packages: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} + chardet@2.2.0: + resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + css-tree@3.2.1: resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} @@ -579,6 +705,9 @@ packages: resolution: {integrity: sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==} engines: {node: '>=20'} + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -598,13 +727,25 @@ packages: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + dts-resolver@3.0.0: resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} engines: {node: ^22.18.0 || >=24.0.0} @@ -621,6 +762,10 @@ packages: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} engines: {node: '>=14'} + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + entities@8.0.0: resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} engines: {node: '>=20.19.0'} @@ -632,6 +777,11 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} @@ -639,6 +789,16 @@ packages: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} @@ -648,6 +808,22 @@ packages: picomatch: optional: true + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -657,6 +833,17 @@ packages: resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} engines: {node: '>=20.20.0'} + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} @@ -672,16 +859,59 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + human-id@4.2.0: + resolution: {integrity: sha512-K3GbkIWqyvvlpfhBPlbEvD97TtqBpAYA4kt+cn2lD2x2HuohzZCibcA2nOlnJT6exqvJLggoB5nv2dNf192nEA==} + hasBin: true + + iconv-lite@0.7.3: + resolution: {integrity: sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + import-without-cache@0.4.0: resolution: {integrity: sha512-NkJQA7oZ4YHQhd2+H3BoRFKF3d/XNsiKpHZCQEMH9pDX27hQQLsTyOocyRgaIVtf8gHX3Nt3LPkR4e5EdtPAGQ==} engines: {node: ^22.18.0 || >=24.0.0} + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-yaml@3.15.1: + resolution: {integrity: sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==} + hasBin: true + + js-yaml@4.3.1: + resolution: {integrity: sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==} + hasBin: true + jsdom@27.4.0: resolution: {integrity: sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -691,6 +921,9 @@ packages: canvas: optional: true + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + lightningcss-android-arm64@1.33.0: resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} engines: {node: '>= 12.0.0'} @@ -765,6 +998,13 @@ packages: resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} engines: {node: '>= 12.0.0'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} @@ -779,6 +1019,18 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -787,6 +1039,15 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-releases@2.0.53: resolution: {integrity: sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==} engines: {node: '>=18'} @@ -795,23 +1056,74 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + parse5@8.0.1: resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + postcss@8.5.26: resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} engines: {node: ^10 || ^12 || >=14} + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} @@ -825,9 +1137,15 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + quansync@1.0.0: resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + react-dom@19.2.8: resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==} peerDependencies: @@ -840,6 +1158,10 @@ packages: resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==} engines: {node: '>=0.10.0'} + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -847,9 +1169,17 @@ packages: reselect@5.2.0: resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rolldown-plugin-dts@0.27.14: resolution: {integrity: sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw==} engines: {node: ^22.18.0 || >=24.11.0} @@ -874,6 +1204,12 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} engines: {node: '>=v12.22.7'} @@ -881,22 +1217,61 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -919,10 +1294,17 @@ packages: resolution: {integrity: sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==} hasBin: true + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + tough-cookie@6.0.2: resolution: {integrity: sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==} engines: {node: '>=16'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@6.0.0: resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} engines: {node: '>=20'} @@ -976,6 +1358,10 @@ packages: undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + update-browserslist-db@1.3.0: resolution: {integrity: sha512-x/M6q3w4Ybp91CNaS4S69UnliqR3BzRpOT6LWbksjth0S/+jhfaPJsWjt/TewpT8j9eLIojUf5jr29WextHroA==} hasBin: true @@ -1079,6 +1465,9 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@8.0.1: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} @@ -1095,6 +1484,14 @@ packages: resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} engines: {node: '>=20'} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -1183,6 +1580,164 @@ snapshots: optionalDependencies: '@types/react': 19.2.18 + '@changesets/apply-release-plan@7.1.1': + dependencies: + '@changesets/config': 3.1.4 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.8.5 + + '@changesets/assemble-release-plan@6.0.10': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.8.5 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/changelog-github@0.5.2': + dependencies: + '@changesets/get-github-info': 0.7.0 + '@changesets/types': 6.1.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding + + '@changesets/cli@2.31.1(@types/node@26.2.0)': + dependencies: + '@changesets/apply-release-plan': 7.1.1 + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.4 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/get-release-plan': 4.0.16 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@26.2.0) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.8.5 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.4 + '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.4': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.8.5 + + '@changesets/get-github-info@0.7.0': + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@changesets/get-release-plan@4.0.16': + dependencies: + '@changesets/assemble-release-plan': 6.0.10 + '@changesets/config': 3.1.4 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.7 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.3': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 4.3.1 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.7': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.3 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.2.0 + prettier: 2.8.8 + '@csstools/color-helpers@6.1.0': {} '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -1226,8 +1781,43 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@inquirer/external-editor@1.0.3(@types/node@26.2.0)': + dependencies: + chardet: 2.2.0 + iconv-lite: 0.7.3 + optionalDependencies: + '@types/node': 26.2.0 + '@jridgewell/sourcemap-codec@1.5.5': {} + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.29.7 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.29.7 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + '@oxc-project/types@0.143.0': {} '@quansync/fs@1.0.0': @@ -1316,6 +1906,8 @@ snapshots: '@types/estree@1.0.9': {} + '@types/node@12.20.55': {} + '@types/node@26.2.0': dependencies: undici-types: 8.3.0 @@ -1450,26 +2042,44 @@ snapshots: agent-base@7.1.4: {} + ansi-colors@4.1.3: {} + ansi-regex@5.0.1: {} ansi-styles@5.2.0: {} ansis@4.3.1: {} + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + aria-query@5.3.0: dependencies: dequal: 2.0.3 + array-union@2.1.0: {} + assertion-error@2.0.1: {} axe-core@4.13.0: {} baseline-browser-mapping@2.11.12: {} + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + bidi-js@1.0.3: dependencies: require-from-string: 2.0.2 + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + browserslist@4.28.7: dependencies: baseline-browser-mapping: 2.11.12 @@ -1484,8 +2094,16 @@ snapshots: chai@6.2.2: {} + chardet@2.2.0: {} + convert-source-map@2.0.0: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + css-tree@3.2.1: dependencies: mdn-data: 2.27.1 @@ -1505,6 +2123,8 @@ snapshots: whatwg-mimetype: 5.0.0 whatwg-url: 15.1.0 + dataloader@1.4.0: {} + debug@4.4.3: dependencies: ms: 2.1.3 @@ -1515,32 +2135,82 @@ snapshots: dequal@2.0.3: {} + detect-indent@6.1.0: {} + detect-libc@2.1.2: {} + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + dom-accessibility-api@0.5.16: {} + dotenv@8.6.0: {} + dts-resolver@3.0.0: {} electron-to-chromium@1.5.402: {} empathic@2.0.1: {} + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + entities@8.0.0: {} es-module-lexer@2.3.1: {} escalade@3.2.0: {} + esprima@4.0.1: {} + estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 expect-type@1.4.0: {} + extendable-error@0.1.7: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fsevents@2.3.3: optional: true @@ -1548,6 +2218,21 @@ snapshots: dependencies: resolve-pkg-maps: 1.0.0 + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + graceful-fs@4.2.11: {} + hookable@6.1.1: {} html-encoding-sniffer@6.0.0: @@ -1570,12 +2255,45 @@ snapshots: transitivePeerDependencies: - supports-color + human-id@4.2.0: {} + + iconv-lite@0.7.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + import-without-cache@0.4.0: {} + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-windows@1.0.2: {} + + isexe@2.0.0: {} + js-tokens@4.0.0: {} + js-yaml@3.15.1: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.3.1: + dependencies: + argparse: 2.0.1 + jsdom@27.4.0: dependencies: '@acemir/cssom': 0.9.31 @@ -1604,6 +2322,10 @@ snapshots: - supports-color - utf-8-validate + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + lightningcss-android-arm64@1.33.0: optional: true @@ -1653,6 +2375,12 @@ snapshots: lightningcss-win32-arm64-msvc: 1.33.0 lightningcss-win32-x64-msvc: 1.33.0 + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + lodash.startcase@4.4.0: {} + lru-cache@11.5.2: {} lz-string@1.5.0: {} @@ -1663,30 +2391,77 @@ snapshots: mdn-data@2.27.1: {} + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + mri@1.2.0: {} + ms@2.1.3: {} nanoid@3.3.18: {} + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-releases@2.0.53: {} obug@2.1.4: {} + outdent@0.5.0: {} + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-map@2.1.0: {} + + p-try@2.2.0: {} + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + parse5@8.0.1: dependencies: entities: 8.0.0 + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-type@4.0.0: {} + pathe@2.0.3: {} picocolors@1.1.1: {} + picomatch@2.3.2: {} + picomatch@4.0.5: {} + pify@4.0.1: {} + postcss@8.5.26: dependencies: nanoid: 3.3.18 picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@2.8.8: {} + prettier@3.6.2: {} pretty-format@27.5.1: @@ -1697,8 +2472,12 @@ snapshots: punycode@2.3.1: {} + quansync@0.2.11: {} + quansync@1.0.0: {} + queue-microtask@1.2.3: {} + react-dom@19.2.8(react@19.2.8): dependencies: react: 19.2.8 @@ -1708,12 +2487,23 @@ snapshots: react@19.2.8: {} + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.15.1 + pify: 4.0.1 + strip-bom: 3.0.0 + require-from-string@2.0.2: {} reselect@5.2.0: {} + resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + reusify@1.1.0: {} + rolldown-plugin-dts@0.27.14(rolldown@1.2.3)(typescript@5.9.3): dependencies: dts-resolver: 3.0.0 @@ -1748,22 +2538,55 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.3 '@rolldown/binding-win32-x64-msvc': 1.2.3 + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safer-buffer@2.1.2: {} + saxes@6.0.0: dependencies: xmlchars: 2.2.0 scheduler@0.27.0: {} + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + siginfo@2.0.0: {} + signal-exit@4.1.0: {} + + slash@3.0.0: {} + source-map-js@1.2.1: {} + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + sprintf-js@1.0.3: {} + stackback@0.0.2: {} std-env@4.2.0: {} + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-bom@3.0.0: {} + symbol-tree@3.2.4: {} + term-size@2.2.1: {} + tinybench@2.9.0: {} tinyexec@1.3.0: {} @@ -1781,10 +2604,16 @@ snapshots: dependencies: tldts-core: 7.4.10 + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + tough-cookie@6.0.2: dependencies: tldts: 7.4.10 + tr46@0.0.3: {} + tr46@6.0.0: dependencies: punycode: 2.3.1 @@ -1825,6 +2654,8 @@ snapshots: undici-types@8.3.0: {} + universalify@0.1.2: {} + update-browserslist-db@1.3.0(browserslist@4.28.7): dependencies: browserslist: 4.28.7 @@ -1880,6 +2711,8 @@ snapshots: dependencies: xml-name-validator: 5.0.0 + webidl-conversions@3.0.1: {} + webidl-conversions@8.0.1: {} whatwg-mimetype@4.0.0: {} @@ -1891,6 +2724,15 @@ snapshots: tr46: 6.0.0 webidl-conversions: 8.0.1 + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 From 140e73676038e8dba72ca8cb761b35c18b21c1d9 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 13:17:29 +0530 Subject: [PATCH 24/33] Address Task 12 review: empty changeset ignore, derive accent hover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .changeset/config.json ships ignore: []; Task 13 adds @dowel/docs in the same commit that creates the package (changesets hard-errors on ignore entries that match no package, and with the docs app present but unignored it writes a phantom patch bump — the entry must move, not vanish). - --dowel-accent-hover is now color-mix-derived from --dowel-accent (92% toward black in light, 85% toward white in dark) so a retheme keeps its hover. Percentages chosen by resolving candidates with Lightning CSS against the old hardcoded hovers. - Both READMEs now name the real theming surface: --dowel-hue, --dowel-accent, --dowel-accent-fg. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/config.json | 2 +- README.md | 3 ++- packages/dowel/README.md | 7 ++++--- packages/dowel/src/tokens/dark.css | 8 ++++++-- packages/dowel/src/tokens/light.css | 4 +++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.changeset/config.json b/.changeset/config.json index edca366..24db6ee 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["@dowel/docs"] + "ignore": [] } diff --git a/README.md b/README.md index 232084b..be0f12f 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,8 @@ That is the whole setup. No Tailwind, no PostCSS config, no preset, no - **A real package.** Import components, bump a version, get the fixes. Your UI does not drift across apps. - **Opinionated on purpose.** There is no per-component override API. Retheming - is two CSS variables: `--dowel-hue` and `--dowel-accent`. + is three CSS variables: `--dowel-hue`, `--dowel-accent` and + `--dowel-accent-fg`. Hover states derive from the accent automatically. - **Light and dark from day one**, in one stylesheet, by class, data attribute or system preference. - **Accessible by construction.** Behaviour comes from diff --git a/packages/dowel/README.md b/packages/dowel/README.md index eec96de..0239048 100644 --- a/packages/dowel/README.md +++ b/packages/dowel/README.md @@ -33,9 +33,10 @@ keyboard-tested and axe-checked. ## Theming -Two CSS variables — `--dowel-hue` and `--dowel-accent` — are the only -supported knobs. There is no per-component `className` or `style` override -API, by design. If you need a different button, dowel is the wrong library. +Three CSS variables — `--dowel-hue`, `--dowel-accent` and `--dowel-accent-fg` +— are the only supported knobs. Hover states derive from `--dowel-accent` +automatically. There is no per-component `className` or `style` override API, +by design. If you need a different button, dowel is the wrong library. ## Typeface diff --git a/packages/dowel/src/tokens/dark.css b/packages/dowel/src/tokens/dark.css index cdf8f66..124d864 100644 --- a/packages/dowel/src/tokens/dark.css +++ b/packages/dowel/src/tokens/dark.css @@ -20,7 +20,9 @@ --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); --dowel-accent: lch(58% 62 285); - --dowel-accent-hover: lch(64% 62 285); + /* Dark hovers LIGHTEN. Derived from the accent so overrides follow; + 85% toward white matches the old hardcoded lch(64% 62 285). */ + --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 85%, white); --dowel-accent-fg: lch(100% 0 0); --dowel-focus: var(--dowel-accent); @@ -57,7 +59,9 @@ --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); --dowel-accent: lch(58% 62 285); - --dowel-accent-hover: lch(64% 62 285); + /* Dark hovers LIGHTEN. Derived from the accent so overrides follow; + 85% toward white matches the old hardcoded lch(64% 62 285). */ + --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 85%, white); --dowel-accent-fg: lch(100% 0 0); --dowel-focus: var(--dowel-accent); diff --git a/packages/dowel/src/tokens/light.css b/packages/dowel/src/tokens/light.css index 37ba3e9..3f37965 100644 --- a/packages/dowel/src/tokens/light.css +++ b/packages/dowel/src/tokens/light.css @@ -22,7 +22,9 @@ /* accent — ours, deliberately not Linear's 295 brand hue */ --dowel-accent: lch(49% 62 285); - --dowel-accent-hover: lch(44% 62 285); + /* Derived from the accent so a --dowel-accent override retheming stays + coherent on hover. 92% matches the old hardcoded lch(44% 62 285). */ + --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 92%, black); --dowel-accent-fg: lch(100% 0 0); --dowel-focus: var(--dowel-accent); From c91b2a0a9cf4e65cc31303aa5ffd7dd54c4c6888 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 13:32:29 +0530 Subject: [PATCH 25/33] Add the docs site on TanStack Start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs dogfood dowel: every component on the page comes from the workspace package through its published exports, so a broken export map breaks the docs build rather than a consumer's. Prerendered to static HTML. Docs discovery is search-driven and nothing here needs a request, so the deploy target is an assets-only Worker with no runtime. The client environment writes straight to dist/ instead of the default dist/client, which makes the deploy "upload dist/" with no server bundle sitting next to the HTML; the SSR build exists only to render those pages, so it goes to .tanstack/ and is gitignored. The brief's app.config.ts shape does not exist in Start 1.168 — there is no @tanstack/react-start/config export at all. Configuration is a Vite plugin now (@tanstack/react-start/plugin/vite), and `server.preset: "static"` is replaced by `pages` + `prerender`. Seeding the crawler with "/" and letting crawlLinks follow the nav means a new route linked from the shell prerenders without touching the config. Two constraints the docs have to state, because both are invisible from the type signatures: dowel ships no typeface — it names "Inter Variable" first in --dowel-font and expects the app to supply it, which the docs do via @fontsource-variable/inter — and a Tooltip is a visual label only. Base UI deliberately writes no aria-describedby, so the trigger has to carry its own accessible name and hover content that must reach assistive tech belongs in a Popover. Changesets ignores @dowel/docs. The package is private, and with an empty ignore list changesets writes a phantom 0.0.1 bump and a CHANGELOG for a package that is never published. The .prettierignore fix is not cosmetic: `docs/` is a gitignore-style pattern that matches a directory of that name at any depth, so adding apps/docs silently excluded the entire new app from format:check. Anchoring it to /docs/ puts the app back under the gate. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/config.json | 2 +- .gitignore | 3 + .prettierignore | 9 +- apps/docs/package.json | 27 + apps/docs/src/docs.css | 76 ++ apps/docs/src/routeTree.gen.ts | 104 +++ apps/docs/src/router.tsx | 25 + apps/docs/src/routes/__root.tsx | 59 ++ apps/docs/src/routes/components/button.tsx | 47 ++ apps/docs/src/routes/components/tooltip.tsx | 46 ++ apps/docs/src/routes/index.tsx | 42 + apps/docs/tsconfig.json | 15 + apps/docs/vite.config.ts | 33 + pnpm-lock.yaml | 861 +++++++++++++++++++- 14 files changed, 1337 insertions(+), 12 deletions(-) create mode 100644 apps/docs/package.json create mode 100644 apps/docs/src/docs.css create mode 100644 apps/docs/src/routeTree.gen.ts create mode 100644 apps/docs/src/router.tsx create mode 100644 apps/docs/src/routes/__root.tsx create mode 100644 apps/docs/src/routes/components/button.tsx create mode 100644 apps/docs/src/routes/components/tooltip.tsx create mode 100644 apps/docs/src/routes/index.tsx create mode 100644 apps/docs/tsconfig.json create mode 100644 apps/docs/vite.config.ts diff --git a/.changeset/config.json b/.changeset/config.json index 24db6ee..edca366 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": ["@dowel/docs"] } diff --git a/.gitignore b/.gitignore index 7841de3..033aa3d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ dist/ .DS_Store *.log .turbo/ +# TanStack Start's scratch directory: the SSR bundle it builds only to render +# the prerendered HTML, plus the route generator's temp files. +.tanstack/ diff --git a/.prettierignore b/.prettierignore index 1b2972f..22579fc 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,10 @@ pnpm-lock.yaml -docs/ +# Anchored. An unanchored `docs/` is a gitignore-style pattern that matches a +# directory of that name at ANY depth, which silently swallowed the whole +# apps/docs app — the docs site was never formatted or format-checked. +/docs/ .superpowers/ +# Generated by TanStack Router on every build; its own header says to exclude +# it from the formatter. It is committed because CI typechecks before it +# builds, so the file has to exist in a fresh checkout. +apps/docs/src/routeTree.gen.ts diff --git a/apps/docs/package.json b/apps/docs/package.json new file mode 100644 index 0000000..a8f9989 --- /dev/null +++ b/apps/docs/package.json @@ -0,0 +1,27 @@ +{ + "name": "@dowel/docs", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "typecheck": "tsc --noEmit", + "test": "echo \"no tests in docs\" && exit 0" + }, + "dependencies": { + "@fontsource-variable/inter": "^5.3.0", + "@tanstack/react-router": "^1.170.23", + "@tanstack/react-start": "^1.168.40", + "dowel": "workspace:*", + "react": "^19.2.8", + "react-dom": "^19.2.8" + }, + "devDependencies": { + "@types/react": "^19.2.18", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^6.0.5", + "typescript": "5.9.3", + "vite": "^8.2.1" + } +} diff --git a/apps/docs/src/docs.css b/apps/docs/src/docs.css new file mode 100644 index 0000000..f3e92fc --- /dev/null +++ b/apps/docs/src/docs.css @@ -0,0 +1,76 @@ +/* The docs site's own layout. Deliberately not part of dowel: page chrome is + an application concern, and dowel ships components, not a shell. Colour and + type come from dowel's public tokens so the docs stay in step with the + library; page-scale spacing does not, because dowel's space scale tops out + at 18px — it is sized for the inside of a control, not for a page. */ + +body { + margin: 0; + min-height: 100vh; +} + +.docs-nav { + display: flex; + align-items: center; + gap: var(--dowel-space-8); + padding: var(--dowel-space-6) 1.5rem; + border-bottom: 1px solid var(--dowel-border-1); +} + +.docs-nav a { + color: var(--dowel-text-3); + text-decoration: none; + transition: var(--dowel-transition); +} + +.docs-nav a:hover, +.docs-nav a[data-status="active"] { + color: var(--dowel-text-1); +} + +.docs-main { + max-width: 42rem; + margin: 0 auto; + padding: 3rem 1.5rem 6rem; +} + +.docs-main h1 { + font-size: var(--dowel-fs-title1); + font-weight: var(--dowel-fw-semibold); + letter-spacing: var(--dowel-tracking-title); + color: var(--dowel-text-1); + margin: 0 0 var(--dowel-space-6); +} + +.docs-main h2 { + font-size: var(--dowel-fs-title3); + font-weight: var(--dowel-fw-semibold); + color: var(--dowel-text-1); + margin: 3rem 0 var(--dowel-space-6); +} + +.docs-main p { + margin: 0 0 1rem; + line-height: 1.6; +} + +.docs-main code { + font-family: var(--dowel-mono); + font-size: var(--dowel-fs-mini); + color: var(--dowel-text-1); +} + +/* A row of live components. */ +.docs-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--dowel-space-4); +} + +/* A constraint a consumer has to know about, not decoration. */ +.docs-note { + border-left: 2px solid var(--dowel-border-3); + padding-left: 1rem; + color: var(--dowel-text-3); +} diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts new file mode 100644 index 0000000..79c9704 --- /dev/null +++ b/apps/docs/src/routeTree.gen.ts @@ -0,0 +1,104 @@ +/* eslint-disable */ + +// @ts-nocheck + +// noinspection JSUnusedGlobalSymbols + +// This file was automatically generated by TanStack Router. +// You should NOT make any changes in this file as it will be overwritten. +// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. + +import { Route as rootRouteImport } from './routes/__root' +import { Route as IndexRouteImport } from './routes/index' +import { Route as ComponentsButtonRouteImport } from './routes/components/button' +import { Route as ComponentsTooltipRouteImport } from './routes/components/tooltip' + +const IndexRoute = IndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => rootRouteImport, +} as any) +const ComponentsButtonRoute = ComponentsButtonRouteImport.update({ + id: '/components/button', + path: '/components/button', + getParentRoute: () => rootRouteImport, +} as any) +const ComponentsTooltipRoute = ComponentsTooltipRouteImport.update({ + id: '/components/tooltip', + path: '/components/tooltip', + getParentRoute: () => rootRouteImport, +} as any) + +export interface FileRoutesByFullPath { + '/': typeof IndexRoute + '/components/button': typeof ComponentsButtonRoute + '/components/tooltip': typeof ComponentsTooltipRoute +} +export interface FileRoutesByTo { + '/': typeof IndexRoute + '/components/button': typeof ComponentsButtonRoute + '/components/tooltip': typeof ComponentsTooltipRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/': typeof IndexRoute + '/components/button': typeof ComponentsButtonRoute + '/components/tooltip': typeof ComponentsTooltipRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/' | '/components/button' | '/components/tooltip' + fileRoutesByTo: FileRoutesByTo + to: '/' | '/components/button' | '/components/tooltip' + id: '__root__' | '/' | '/components/button' | '/components/tooltip' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + IndexRoute: typeof IndexRoute + ComponentsButtonRoute: typeof ComponentsButtonRoute + ComponentsTooltipRoute: typeof ComponentsTooltipRoute +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/': { + id: '/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof IndexRouteImport + parentRoute: typeof rootRouteImport + } + '/components/button': { + id: '/components/button' + path: '/components/button' + fullPath: '/components/button' + preLoaderRoute: typeof ComponentsButtonRouteImport + parentRoute: typeof rootRouteImport + } + '/components/tooltip': { + id: '/components/tooltip' + path: '/components/tooltip' + fullPath: '/components/tooltip' + preLoaderRoute: typeof ComponentsTooltipRouteImport + parentRoute: typeof rootRouteImport + } + } +} + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + ComponentsButtonRoute: ComponentsButtonRoute, + ComponentsTooltipRoute: ComponentsTooltipRoute, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() + +import type { getRouter } from './router.tsx' +import type { createStart } from '@tanstack/react-start' +declare module '@tanstack/react-start' { + interface Register { + ssr: true + router: Awaited> + } +} diff --git a/apps/docs/src/router.tsx b/apps/docs/src/router.tsx new file mode 100644 index 0000000..5fa2dba --- /dev/null +++ b/apps/docs/src/router.tsx @@ -0,0 +1,25 @@ +import { createRouter } from "@tanstack/react-router"; + +import { routeTree } from "./routeTree.gen"; + +/** + * TanStack Start's entry points import `getRouter` from this module by + * convention (`src/router` inside the app's `srcDirectory`). It runs once per + * request on the server and once on the client, so it must build a fresh + * router each time rather than share one instance. + */ +export function getRouter() { + return createRouter({ + routeTree, + // The docs are prerendered to static HTML and hydrate on load; scrolling + // to the top on navigation matches how a docs site is read. + scrollRestoration: true, + defaultPreload: "intent", + }); +} + +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } +} diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx new file mode 100644 index 0000000..90930e5 --- /dev/null +++ b/apps/docs/src/routes/__root.tsx @@ -0,0 +1,59 @@ +import { + HeadContent, + Link, + Outlet, + Scripts, + createRootRoute, +} from "@tanstack/react-router"; +import { Tooltip } from "dowel"; + +// The docs self-host Inter; dowel itself ships no typeface, it only names +// "Inter Variable" first in --dowel-font. +import "@fontsource-variable/inter"; +import "dowel/dowel.css"; +import "../docs.css"; + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { charSet: "utf-8" }, + { name: "viewport", content: "width=device-width, initial-scale=1" }, + { title: "dowel — an opinionated React component library" }, + { + name: "description", + content: + "dowel is an opinionated React component library. One look, well made.", + }, + ], + }), + component: RootDocument, +}); + +function RootDocument() { + return ( + // The theme hooks onto rather than a wrapper div so the page + // background reaches the viewport edges instead of stopping at the + // content box. + + + + + + {/* + Tooltip.Provider belongs once at the app root: it is what lets + adjacent tooltips share a single delay, so the second one a pointer + reaches opens instantly instead of waiting again. + */} + + + + + + + + ); +} diff --git a/apps/docs/src/routes/components/button.tsx b/apps/docs/src/routes/components/button.tsx new file mode 100644 index 0000000..36d0c88 --- /dev/null +++ b/apps/docs/src/routes/components/button.tsx @@ -0,0 +1,47 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Button, IconButton } from "dowel"; + +export const Route = createFileRoute("/components/button")({ + component: ButtonDocs, +}); + +function ButtonDocs() { + return ( +
+

Button

+

+ The default control. Four variants, two sizes, and no appearance props —{" "} + className and style are omitted from the type + and neutralised at runtime. +

+ +

Variants

+
+ + + + +
+ +

Sizes and states

+
+ + + +
+ +

IconButton

+

+ For a control whose content is an icon. label is required — + an icon alone never names a control, so the accessible name is part of + the API rather than something a caller can forget. +

+
+ × + + + + +
+
+ ); +} diff --git a/apps/docs/src/routes/components/tooltip.tsx b/apps/docs/src/routes/components/tooltip.tsx new file mode 100644 index 0000000..10b1aed --- /dev/null +++ b/apps/docs/src/routes/components/tooltip.tsx @@ -0,0 +1,46 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { IconButton, Tooltip } from "dowel"; + +export const Route = createFileRoute("/components/tooltip")({ + component: TooltipDocs, +}); + +function TooltipDocs() { + return ( +
+

Tooltip

+

+ A hover and focus label on the popover elevation tier. Compose{" "} + Root, Trigger, Portal,{" "} + Positioner and Popup, with a single{" "} + Tooltip.Provider wrapping the app so adjacent tooltips + share one delay and the second one opens instantly. +

+ +
+ + 🔗} + /> + + + Copy link + + + +
+ +

A tooltip is a visual label only

+

+ Base UI, which dowel builds on, deliberately does not associate the + popup with its trigger — there is no aria-describedby and + no touch affordance. So the tooltip text does not reach a screen reader, + and the trigger has to carry its own accessible name.{" "} + IconButton enforces exactly that with its required{" "} + label prop, which is why the example above wraps one. If + the hover content is information a user cannot do without, it does not + belong in a Tooltip — reach for a Popover, which is announced. +

+
+ ); +} diff --git a/apps/docs/src/routes/index.tsx b/apps/docs/src/routes/index.tsx new file mode 100644 index 0000000..a763d92 --- /dev/null +++ b/apps/docs/src/routes/index.tsx @@ -0,0 +1,42 @@ +import { Link, createFileRoute } from "@tanstack/react-router"; +import { Badge, Button, Kbd } from "dowel"; + +export const Route = createFileRoute("/")({ + component: Home, +}); + +function Home() { + return ( +
+

dowel

+

An opinionated React component library. One look, well made.

+

+ Every component ships its own appearance and refuses{" "} + className and style. What you compose is + behaviour and content; what you get back is one coherent surface. +

+ +
+ + + v0.1.0 + +
+ +

Install

+

+ pnpm add dowel, then import the single stylesheet once:{" "} + import "dowel/dowel.css". dowel ships no typeface + — its --dowel-font token names Inter Variable{" "} + first, and the app supplies it. This site self-hosts it via{" "} + @fontsource-variable/inter. +

+ +

Components

+

+ Button ·{" "} + Tooltip +

+
+ ); +} diff --git a/apps/docs/tsconfig.json b/apps/docs/tsconfig.json new file mode 100644 index 0000000..e5780ff --- /dev/null +++ b/apps/docs/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "types": ["vite/client"], + // Root `pnpm typecheck` runs before `pnpm build` in CI, so dowel's + // dist/index.d.ts does not exist yet. Point the docs at dowel's source + // instead: the docs then typecheck against what the library actually is, + // and the gate stops depending on build order. Vite still resolves the + // workspace link through the package's exports map at build time. + "paths": { + "dowel": ["../../packages/dowel/src/index.ts"] + } + }, + "include": ["src", "vite.config.ts"] +} diff --git a/apps/docs/vite.config.ts b/apps/docs/vite.config.ts new file mode 100644 index 0000000..8c7bf9b --- /dev/null +++ b/apps/docs/vite.config.ts @@ -0,0 +1,33 @@ +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import viteReact from "@vitejs/plugin-react"; +import { defineConfig } from "vite"; + +export default defineConfig({ + // TanStack Start builds two Vite environments. The defaults would put them + // at dist/client and dist/server; the docs flatten the client one to dist/ + // so the deploy target is exactly "upload dist/" with no server bundle + // sitting next to the HTML. The SSR build is a prerender intermediate, not + // an artifact, so it goes to a scratch directory outside dist/. + environments: { + client: { build: { outDir: "dist" } }, + ssr: { build: { outDir: ".tanstack/ssr" } }, + }, + plugins: [ + tanstackStart({ + // Prerender to static HTML: docs discovery is search-driven, and a + // static build means the deploy target is an assets-only Worker with + // no runtime. `pages` seeds the crawler; `crawlLinks` follows the nav + // from there, so a new route linked from the shell is picked up + // without editing this file. + pages: [{ path: "/" }], + prerender: { + enabled: true, + crawlLinks: true, + // A page that throws during prerender must fail the build rather + // than silently ship a missing route. + failOnError: true, + }, + }), + viteReact(), + ], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8730f3e..14a8a38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,6 +21,43 @@ importers: specifier: 5.9.3 version: 5.9.3 + apps/docs: + dependencies: + '@fontsource-variable/inter': + specifier: ^5.3.0 + version: 5.3.0 + '@tanstack/react-router': + specifier: ^1.170.23 + version: 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start': + specifier: ^1.168.40 + version: 1.168.40(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + dowel: + specifier: workspace:* + version: link:../../packages/dowel + react: + specifier: ^19.2.8 + version: 19.2.8 + react-dom: + specifier: ^19.2.8 + version: 19.2.8(react@19.2.8) + devDependencies: + '@types/react': + specifier: ^19.2.18 + version: 19.2.18 + '@types/react-dom': + specifier: ^19.2.0 + version: 19.2.4(@types/react@19.2.18) + '@vitejs/plugin-react': + specifier: ^6.0.5 + version: 6.0.5(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + typescript: + specifier: 5.9.3 + version: 5.9.3 + vite: + specifier: ^8.2.1 + version: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + packages/dowel: dependencies: '@base-ui/react': @@ -47,7 +84,7 @@ importers: version: 19.2.4(@types/react@19.2.18) '@vitejs/plugin-react': specifier: ^6.0.5 - version: 6.0.5(vite@8.2.1(@types/node@26.2.0)) + version: 6.0.5(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) axe-core: specifier: ^4.13.0 version: 4.13.0 @@ -71,7 +108,7 @@ importers: version: 0.22.14(typescript@5.9.3) vitest: specifier: ^4.1.10 - version: 4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)) + version: 4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) packages: @@ -87,18 +124,81 @@ packages: '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.8': + resolution: {integrity: sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.8': + resolution: {integrity: sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + '@base-ui/react@1.7.0': resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==} engines: {node: '>=14.0.0'} @@ -247,6 +347,9 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + '@fontsource-variable/inter@5.3.0': + resolution: {integrity: sha512-OupL48va4JNofb97w6NYeF9S7W/kHNKM0Er8Dem5nqi4jeOLrVJDoE8tZEpnMJmtkvNbB1EIPPwHcdkF6b1oUA==} + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -256,9 +359,22 @@ packages: '@types/node': optional: true + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -277,6 +393,22 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@oozcitak/dom@2.0.2': + resolution: {integrity: sha512-GjpKhkSYC3Mj4+lfwEyI1dqnsKTgwGy48ytZEhm4A/xnH/8z9M3ZVXKr/YGQi3uCLs1AEBS+x5T2JPiueEDW8w==} + engines: {node: '>=20.0'} + + '@oozcitak/infra@2.0.2': + resolution: {integrity: sha512-2g+E7hoE2dgCz/APPOEK5s3rMhJvNxSMBrP+U+j1OWsIbtSpWxxlUjq1lU8RIsFJNYv7NMlnVsCuHcUzJW+8vA==} + engines: {node: '>=20.0'} + + '@oozcitak/url@3.0.0': + resolution: {integrity: sha512-ZKfET8Ak1wsLAiLWNfFkZc/BraDccuTJKR6svTYc7sVjbR+Iu0vtXdiDMY4o6jaFl5TW2TlS7jbLl4VovtAJWQ==} + engines: {node: '>=20.0'} + + '@oozcitak/util@10.0.0': + resolution: {integrity: sha512-hAX0pT/73190NLqBPPWSdBVGtbY6VOhWYK3qqHqtXQ1gK7kS2yz4+ivsN07hpJ6I3aeMtKP6J6npsEKOAzuTLA==} + engines: {node: '>=20.0'} + '@oxc-project/types@0.143.0': resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} @@ -379,6 +511,139 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tanstack/history@1.162.1': + resolution: {integrity: sha512-DR9t6lfLVdrjgCwpglrR9DR7Ok8/HlXjcOE+goWXF3zyuLUO/ug7vMbSFxTqrQTtbRghJfyhmIZ0S6LhPIy44w==} + engines: {node: '>=20.19'} + + '@tanstack/react-router@1.170.23': + resolution: {integrity: sha512-iKyHk7vGVaTdk7wukFZLjzlOs4TQbQJiRMkvFsphpysOlxzLaqWSlWHKU4gVPW3WjJ19k7EjVUCD4q3DJqZpZg==} + engines: {node: '>=20.19'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-client@1.168.21': + resolution: {integrity: sha512-H5LyBYZs8qBG0qlnbA213F4CaL3OKnEC6T3lmycR2UR1kWWKE43ERbcKDKz+ZjXlP2qTCYqoGlzeh58t7nfwPQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start-rsc@0.1.39': + resolution: {integrity: sha512-/19+PnxBMoseBoydD1LMB4V31/uma/r6oQCr8ZCMXNm6lAw3bHfUHNQfc5CdrwZ9TGHkGvmecQ65RjiLhpdalQ==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rspack/core': '>=2.0.0-0' + '@vitejs/plugin-rsc': '>=0.5.30' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + react-server-dom-rspack: '>=0.0.2' + peerDependenciesMeta: + '@rspack/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + react-server-dom-rspack: + optional: true + + '@tanstack/react-start-server@1.167.28': + resolution: {integrity: sha512-ouZmlPdEwaI3wzd+FfLAuo16UFqEusN7Rcu0QXFT020ledvtE5wAbkNQNE2dFiivtmV0BGg/Z37HDSM19aJm7w==} + engines: {node: '>=22.12.0'} + peerDependencies: + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + + '@tanstack/react-start@1.168.40': + resolution: {integrity: sha512-MFwtKSdNos3sF8NzzI64qbupopi8/eR5NCsC+1TZ0LXHqUaBKZmbHOd6PTXq2fDRIofpVSPpZYXZYuYbYUTSog==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + '@vitejs/plugin-rsc': '*' + react: '>=18.0.0 || >=19.0.0' + react-dom: '>=18.0.0 || >=19.0.0' + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@vitejs/plugin-rsc': + optional: true + vite: + optional: true + + '@tanstack/react-store@0.9.3': + resolution: {integrity: sha512-y2iHd/N9OkoQbFJLUX1T9vbc2O9tjH0pQRgTcx1/Nz4IlwLvkgpuglXUx+mXt0g5ZDFrEeDnONPqkbfxXJKwRg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@tanstack/router-core@1.171.19': + resolution: {integrity: sha512-uCZhgnfmuBA3PoRLIVSjUhQpJQd/HA7p0XxG1IFKnvXU9lIMZ7gIqx45hyuf9JopxXZI8k7qaz9/6PC7mZLdfQ==} + engines: {node: '>=20.19'} + + '@tanstack/router-generator@1.167.25': + resolution: {integrity: sha512-M+S+QvGicfiH7kMkjrm6q+UMWXTdl7v9+pvMVnzA+lFm7whe8qmVOY72h/O8GGnejNn14jxdPrnLXv97NJjlWQ==} + engines: {node: '>=20.19'} + + '@tanstack/router-plugin@1.168.27': + resolution: {integrity: sha512-M/3XG6RIxid8+f5VKXILtPpapQPjIgKRKV1JxuvYJwQM811iSUzK9onrZnezUktZcHaWJN+wD7FR5HedH/WY7Q==} + engines: {node: '>=20.19'} + peerDependencies: + '@rsbuild/core': '>=1.0.2 || ^2.0.0' + '@tanstack/react-router': ^1.170.22 + vite: '>=5.0.0 || >=6.0.0 || >=7.0.0 || >=8.0.0' + vite-plugin-solid: ^2.11.10 || ^3.0.0-0 + webpack: '>=5.92.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + '@tanstack/react-router': + optional: true + vite: + optional: true + vite-plugin-solid: + optional: true + webpack: + optional: true + + '@tanstack/router-utils@1.162.2': + resolution: {integrity: sha512-hTWqJtqIFFdvuCl8WXNyrodp2L9zo2G37xKRrcVmVRWpAB2h+U1LuRAfS4tsFTiWOIoE/B+WDVFB8JpoEdw6jQ==} + engines: {node: '>=20.19'} + + '@tanstack/start-client-core@1.170.19': + resolution: {integrity: sha512-Tdr3djfTnIn5VcFjgtYI6DWcaD7aGP9rySe7TH1QPVcb29pyS3eGUy5sSt3tykD8Zbn9+lYsdiY7bzZadxJM+A==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-fn-stubs@1.162.0': + resolution: {integrity: sha512-QWfUZ3Yo923tdQn38LyKMU8rcTw69zc+T4dAvgTWV4O56SqFRsGfS0lSWIMhJRwXIx/bvdi7nTUBDdZtTHtpTQ==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-plugin-core@1.171.31': + resolution: {integrity: sha512-7vHNDktNU+sh2fZLENd9vCkP73aWTv23IGo1cjn/4P5Y/OP7FuEruX28/Rqb6pzNKL9pLjcO/q+RdN8GsTdJ9g==} + engines: {node: '>=22.12.0'} + peerDependencies: + '@rsbuild/core': ^2.0.0 + vite: '>=7.0.0' + peerDependenciesMeta: + '@rsbuild/core': + optional: true + vite: + optional: true + + '@tanstack/start-server-core@1.169.23': + resolution: {integrity: sha512-cQmGZmvFnX3aAwPqVASwqbq0jwYzQrr4bi+rv8t6DAt2G/UyRjKEHsE/r7HuxUR46jIa/DVaSPuizmNEgzBscw==} + engines: {node: '>=22.12.0'} + + '@tanstack/start-storage-context@1.167.21': + resolution: {integrity: sha512-+S3ZkueTlNqpopkjQDJGAJeZ9MXAqjEsbfvJmsfMnf2Lr+Qm+8Y3B9r5khJuYfzcfF889tP0VXQ45JdzyJJwBw==} + engines: {node: '>=22.12.0'} + + '@tanstack/store@0.9.3': + resolution: {integrity: sha512-8reSzl/qGWGGVKhBoxXPMWzATSbZLZFWhwBAFO9NAyp0TxzfBP0mIrGb8CP8KrQTmvzXlR/vFPPUrHTLBGyFyw==} + + '@tanstack/virtual-file-routes@1.162.0': + resolution: {integrity: sha512-uhOeFyxLcU41HzvrxsGpiWdcMbScY1EDgbZ5K7DVRMYInbLYWAC0EA/kx9wXAoSM8q82bUG2hRl8+EAjE6XAbA==} + engines: {node: '>=20.19'} + '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -648,6 +913,9 @@ packages: resolution: {integrity: sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==} engines: {node: '>=4'} + babel-dead-code-elimination@1.0.12: + resolution: {integrity: sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig==} + baseline-browser-mapping@2.11.12: resolution: {integrity: sha512-r7WnVImvVCeFpf2DOXfy41aPWzeNg3H/A2X4dKmy1QL0MSyyk/e7z8ihJ3N6Nn2PsdhkVlqnEfnUE4a05P2aTA==} engines: {node: '>=6.0.0'} @@ -683,9 +951,16 @@ packages: chardet@2.2.0: resolution: {integrity: sha512-rddelWYNPRrXq6PtNEN2S3f6t9ILzvqaN5pVgi4kqt9jHQaXIial9PznB5iSPVlQSLNaaH22ItWz3EJtQ10+OA==} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie-es@3.1.1: + resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -735,6 +1010,10 @@ packages: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -789,6 +1068,9 @@ packages: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + extendable-error@0.1.7: resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} @@ -808,6 +1090,9 @@ packages: picomatch: optional: true + fetchdts@0.1.7: + resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -829,6 +1114,10 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + get-tsconfig@5.0.0-beta.5: resolution: {integrity: sha512-/6gFNr0N04nob252sTQxyFLi3eKFRqIg1I87YcqAMT1i6SQrSF6KujUEQrtrjMV0H/eejTCltLdDSTEMzHbnsQ==} engines: {node: '>=20.20.0'} @@ -844,6 +1133,16 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + h3@2.0.1-rc.20: + resolution: {integrity: sha512-28ljodXuUp0fZovdiSRq4G9OgrxCztrJe5VdYzXAB7ueRvI7pIUqLU14Xi3XqdYJ/khXjfpUOOD2EQa6CmBgsg==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.1 + peerDependenciesMeta: + crossws: + optional: true + hookable@6.1.1: resolution: {integrity: sha512-U9LYDy1CwhMCnprUfeAZWZGByVbhd54hwepegYTK7Pi5NvqEj63ifz5z+xukznehT7i6NIZRu89Ay1AZmRsLEQ==} @@ -898,9 +1197,17 @@ packages: resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} engines: {node: '>=0.10.0'} + isbot@5.2.1: + resolution: {integrity: sha512-dJ+LpKyClQZ7NG+j3OensC/mAZkGpukE9YUrgPYvAZj2doVL0edfDgywTUh5CXa0o+nW9a1V9e5+CJTX8+SxRw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -921,6 +1228,16 @@ packages: canvas: optional: true + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -1009,6 +1326,9 @@ packages: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -1162,6 +1482,10 @@ packages: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + require-from-string@2.0.2: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} @@ -1204,6 +1528,9 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rou3@0.8.1: + resolution: {integrity: sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -1217,11 +1544,25 @@ packages: scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true + seroval-plugins@1.6.2: + resolution: {integrity: sha512-TfxuUjlbBESzUOWdTkTKqvSmav0ABym+itetDXLK6mDz8SmrpdI30aF8RTXE8Bvq+tH/1yIDkvy3W0lfQb1ipQ==} + engines: {node: '>=10'} + peerDependencies: + seroval: ^1.0 + + seroval@1.6.2: + resolution: {integrity: sha512-mPT+SD2TrlB6wvte1KkYOYUkubaTbd6pZ/6Kk3C9nxzrHmCZyhxOO7XGAeL7f+yLKZglzGtM9odUVvg/EhO+vQ==} + engines: {node: '>=10'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1245,12 +1586,21 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + spawndamnit@3.0.1: resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + srvx@0.11.22: + resolution: {integrity: sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ==} + engines: {node: '>=20.16.0'} + hasBin: true + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -1352,6 +1702,9 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + unconfig-core@7.5.0: resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} @@ -1362,6 +1715,39 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: '*' + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + update-browserslist-db@1.3.0: resolution: {integrity: sha512-x/M6q3w4Ybp91CNaS4S69UnliqR3BzRpOT6LWbksjth0S/+jhfaPJsWjt/TewpT8j9eLIojUf5jr29WextHroA==} hasBin: true @@ -1420,6 +1806,14 @@ packages: yaml: optional: true + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest@4.1.10: resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1472,6 +1866,9 @@ packages: resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} engines: {node: '>=20'} + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-mimetype@4.0.0: resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} engines: {node: '>=18'} @@ -1513,9 +1910,16 @@ packages: resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} engines: {node: '>=18'} + xmlbuilder2@4.0.3: + resolution: {integrity: sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA==} + engines: {node: '>=20.0'} + xmlchars@2.2.0: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yuku-ast@0.8.4: resolution: {integrity: sha512-s7EWfWIQkaGmsGnyr/BU0jli9YTN5TvrKIsSmALyRD9elumDQInuhv0BrVObENKVCxr9W3Ikmnx5u02KvfuUmw==} @@ -1525,6 +1929,9 @@ packages: yuku-parser@0.8.4: resolution: {integrity: sha512-sw41wouvT5rUmLIp87hmvm5vtF+MRSI3x6yjq6xqpYmtkQj+Ht6N7xRQ8lMhLv8N7JAzughGj0Rfi0jQRSu9HQ==} + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + snapshots: '@acemir/cssom@0.9.31': {} @@ -1547,16 +1954,114 @@ snapshots: '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.8': + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.7 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.8 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.29.7': {} + '@babel/helper-validator-identifier@7.29.7': {} + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + '@babel/runtime@7.29.7': {} + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + + '@babel/traverse@7.29.8': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.8 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@babel/runtime': 7.29.7 @@ -1781,6 +2286,8 @@ snapshots: '@floating-ui/utils@0.2.12': {} + '@fontsource-variable/inter@5.3.0': {} + '@inquirer/external-editor@1.0.3(@types/node@26.2.0)': dependencies: chardet: 2.2.0 @@ -1788,8 +2295,25 @@ snapshots: optionalDependencies: '@types/node': 26.2.0 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -1818,6 +2342,23 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 + '@oozcitak/dom@2.0.2': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/url': 3.0.0 + '@oozcitak/util': 10.0.0 + + '@oozcitak/infra@2.0.2': + dependencies: + '@oozcitak/util': 10.0.0 + + '@oozcitak/url@3.0.0': + dependencies: + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + + '@oozcitak/util@10.0.0': {} + '@oxc-project/types@0.143.0': {} '@quansync/fs@1.0.0': @@ -1870,6 +2411,221 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tanstack/history@1.162.1': {} + + '@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/history': 1.162.1 + '@tanstack/react-store': 0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.19 + isbot: 5.2.1 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/react-start-client@1.168.21(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/react-router': 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.19 + '@tanstack/start-client-core': 1.170.19 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + + '@tanstack/react-start-rsc@0.1.39(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': + dependencies: + '@tanstack/react-router': 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.19 + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.19 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-plugin-core': 1.171.31(@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + '@tanstack/start-storage-context': 1.167.21 + pathe: 2.0.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + transitivePeerDependencies: + - '@farmfe/core' + - '@rsbuild/core' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite + - vite-plugin-solid + - webpack + + '@tanstack/react-start-server@1.167.28(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/react-router': 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-core': 1.171.19 + '@tanstack/start-server-core': 1.169.23 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + transitivePeerDependencies: + - crossws + + '@tanstack/react-start@1.168.40(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': + dependencies: + '@tanstack/react-router': 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start-client': 1.168.21(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/react-start-rsc': 0.1.39(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + '@tanstack/react-start-server': 1.167.28(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-client-core': 1.170.19 + '@tanstack/start-plugin-core': 1.171.31(@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + '@tanstack/start-server-core': 1.169.23 + pathe: 2.0.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - crossws + - esbuild + - react-server-dom-rspack + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/react-store@0.9.3(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + dependencies: + '@tanstack/store': 0.9.3 + react: 19.2.8 + react-dom: 19.2.8(react@19.2.8) + use-sync-external-store: 1.6.0(react@19.2.8) + + '@tanstack/router-core@1.171.19': + dependencies: + '@tanstack/history': 1.162.1 + cookie-es: 3.1.1 + seroval: 1.6.2 + seroval-plugins: 1.6.2(seroval@1.6.2) + + '@tanstack/router-generator@1.167.25': + dependencies: + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.19 + '@tanstack/router-utils': 1.162.2 + '@tanstack/virtual-file-routes': 1.162.0 + jiti: 2.7.0 + magic-string: 0.30.21 + prettier: 3.6.2 + zod: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@tanstack/router-plugin@1.168.27(@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.19 + '@tanstack/router-generator': 1.167.25 + '@tanstack/router-utils': 1.162.2 + chokidar: 5.0.0 + unplugin: 3.3.0(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + zod: 4.4.3 + optionalDependencies: + '@tanstack/react-router': 1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - supports-color + - unloader + + '@tanstack/router-utils@1.162.2': + dependencies: + '@babel/generator': 7.29.8 + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + ansis: 4.3.1 + babel-dead-code-elimination: 1.0.12 + diff: 8.0.4 + pathe: 2.0.3 + tinyglobby: 0.2.17 + transitivePeerDependencies: + - supports-color + + '@tanstack/start-client-core@1.170.19': + dependencies: + '@tanstack/router-core': 1.171.19 + '@tanstack/start-fn-stubs': 1.162.0 + '@tanstack/start-storage-context': 1.167.21 + seroval: 1.6.2 + + '@tanstack/start-fn-stubs@1.162.0': {} + + '@tanstack/start-plugin-core@1.171.31(@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.29.7 + '@babel/types': 7.29.8 + '@tanstack/router-core': 1.171.19 + '@tanstack/router-generator': 1.167.25 + '@tanstack/router-plugin': 1.168.27(@tanstack/react-router@1.170.23(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + '@tanstack/router-utils': 1.162.2 + '@tanstack/start-server-core': 1.169.23 + exsolve: 1.1.1 + lightningcss: 1.33.0 + pathe: 2.0.3 + picomatch: 4.0.5 + seroval: 1.6.2 + source-map: 0.7.6 + srvx: 0.11.22 + tinyglobby: 0.2.17 + ufo: 1.6.4 + vitefu: 1.1.3(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) + xmlbuilder2: 4.0.3 + zod: 4.4.3 + optionalDependencies: + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - '@tanstack/react-router' + - bun-types-no-globals + - crossws + - esbuild + - rolldown + - rollup + - supports-color + - unloader + - vite-plugin-solid + - webpack + + '@tanstack/start-server-core@1.169.23': + dependencies: + '@tanstack/history': 1.162.1 + '@tanstack/router-core': 1.171.19 + '@tanstack/start-client-core': 1.170.19 + '@tanstack/start-storage-context': 1.167.21 + fetchdts: 0.1.7 + h3-v2: h3@2.0.1-rc.20 + seroval: 1.6.2 + transitivePeerDependencies: + - crossws + + '@tanstack/start-storage-context@1.167.21': + dependencies: + '@tanstack/router-core': 1.171.19 + + '@tanstack/store@0.9.3': {} + + '@tanstack/virtual-file-routes@1.162.0': {} + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.7 @@ -1920,10 +2676,10 @@ snapshots: dependencies: csstype: 3.2.3 - '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0))': + '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.2.1(@types/node@26.2.0) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) '@vitest/expect@4.1.10': dependencies: @@ -1934,13 +2690,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@26.2.0))': + '@vitest/mocker@4.1.10(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.2.1(@types/node@26.2.0) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -2066,6 +2822,15 @@ snapshots: axe-core@4.13.0: {} + babel-dead-code-elimination@1.0.12: + dependencies: + '@babel/core': 7.29.7 + '@babel/parser': 7.29.8 + '@babel/traverse': 7.29.8 + '@babel/types': 7.29.8 + transitivePeerDependencies: + - supports-color + baseline-browser-mapping@2.11.12: {} better-path-resolve@1.0.0: @@ -2096,8 +2861,14 @@ snapshots: chardet@2.2.0: {} + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + convert-source-map@2.0.0: {} + cookie-es@3.1.1: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2139,6 +2910,8 @@ snapshots: detect-libc@2.1.2: {} + diff@8.0.4: {} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 @@ -2172,6 +2945,8 @@ snapshots: expect-type@1.4.0: {} + exsolve@1.1.1: {} + extendable-error@0.1.7: {} fast-glob@3.3.3: @@ -2190,6 +2965,8 @@ snapshots: optionalDependencies: picomatch: 4.0.5 + fetchdts@0.1.7: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -2214,6 +2991,8 @@ snapshots: fsevents@2.3.3: optional: true + gensync@1.0.0-beta.2: {} + get-tsconfig@5.0.0-beta.5: dependencies: resolve-pkg-maps: 1.0.0 @@ -2233,6 +3012,11 @@ snapshots: graceful-fs@4.2.11: {} + h3@2.0.1-rc.20: + dependencies: + rou3: 0.8.1 + srvx: 0.11.22 + hookable@6.1.1: {} html-encoding-sniffer@6.0.0: @@ -2281,8 +3065,12 @@ snapshots: is-windows@1.0.2: {} + isbot@5.2.1: {} + isexe@2.0.0: {} + jiti@2.7.0: {} + js-tokens@4.0.0: {} js-yaml@3.15.1: @@ -2322,6 +3110,10 @@ snapshots: - supports-color - utf-8-validate + jsesc@3.1.0: {} + + json5@2.2.3: {} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 @@ -2383,6 +3175,10 @@ snapshots: lru-cache@11.5.2: {} + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + lz-string@1.5.0: {} magic-string@0.30.21: @@ -2494,6 +3290,8 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 + readdirp@5.1.1: {} + require-from-string@2.0.2: {} reselect@5.2.0: {} @@ -2538,6 +3336,8 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.2.3 '@rolldown/binding-win32-x64-msvc': 1.2.3 + rou3@0.8.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -2550,8 +3350,16 @@ snapshots: scheduler@0.27.0: {} + semver@6.3.1: {} + semver@7.8.5: {} + seroval-plugins@1.6.2(seroval@1.6.2): + dependencies: + seroval: 1.6.2 + + seroval@1.6.2: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -2566,6 +3374,8 @@ snapshots: source-map-js@1.2.1: {} + source-map@0.7.6: {} + spawndamnit@3.0.1: dependencies: cross-spawn: 7.0.6 @@ -2573,6 +3383,8 @@ snapshots: sprintf-js@1.0.3: {} + srvx@0.11.22: {} + stackback@0.0.2: {} std-env@4.2.0: {} @@ -2647,6 +3459,8 @@ snapshots: typescript@5.9.3: {} + ufo@1.6.4: {} + unconfig-core@7.5.0: dependencies: '@quansync/fs': 1.0.0 @@ -2656,6 +3470,15 @@ snapshots: universalify@0.1.2: {} + unplugin@3.3.0(rolldown@1.2.3)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.2.3 + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + update-browserslist-db@1.3.0(browserslist@4.28.7): dependencies: browserslist: 4.28.7 @@ -2668,7 +3491,7 @@ snapshots: verkit@0.3.2: {} - vite@8.2.1(@types/node@26.2.0): + vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -2678,11 +3501,16 @@ snapshots: optionalDependencies: '@types/node': 26.2.0 fsevents: 2.3.3 + jiti: 2.7.0 - vitest@4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)): + vitefu@1.1.3(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)): + optionalDependencies: + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) + + vitest@4.1.10(@types/node@26.2.0)(jsdom@27.4.0)(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)) + '@vitest/mocker': 4.1.10(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -2699,7 +3527,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 8.2.1(@types/node@26.2.0) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.2.0 @@ -2715,6 +3543,8 @@ snapshots: webidl-conversions@8.0.1: {} + webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@4.0.0: {} whatwg-mimetype@5.0.0: {} @@ -2742,8 +3572,17 @@ snapshots: xml-name-validator@5.0.0: {} + xmlbuilder2@4.0.3: + dependencies: + '@oozcitak/dom': 2.0.2 + '@oozcitak/infra': 2.0.2 + '@oozcitak/util': 10.0.0 + js-yaml: 4.3.1 + xmlchars@2.2.0: {} + yallist@3.1.1: {} + yuku-ast@0.8.4: dependencies: '@yuku-toolchain/types': 0.8.4 @@ -2782,3 +3621,5 @@ snapshots: '@yuku-parser/binding-linux-x64-musl': 0.8.4 '@yuku-parser/binding-win32-arm64': 0.8.4 '@yuku-parser/binding-win32-x64': 0.8.4 + + zod@4.4.3: {} From df4912ccbff56db6731870a03d70c73fcb462229 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 13:44:57 +0530 Subject: [PATCH 26/33] Deploy the docs to dowel.sh via Cloudflare Workers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Assets-only Worker config (html_handling auto-trailing-slash so directory sub-pages resolve; not_found_handling none — no 404.html is emitted) plus a main-branch deploy workflow that skips cleanly when the org CLOUDFLARE_API_TOKEN is absent and pins wrangler-action to wrangler v4. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/deploy-docs.yml | 62 +++++++++++++++++++++++++++++++ apps/docs/wrangler.jsonc | 20 ++++++++++ 2 files changed, 82 insertions(+) create mode 100644 .github/workflows/deploy-docs.yml create mode 100644 apps/docs/wrangler.jsonc diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..370273d --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,62 @@ +# Deploys dowel.sh when the docs or the library change on main. +# +# Uses the karnstack org secret CLOUDFLARE_API_TOKEN. The secret is treated as +# optional: without it the job skips rather than fails, so a fork's CI is not +# a wall of red. +name: deploy-docs + +on: + workflow_dispatch: + push: + branches: [main] + paths: + - "apps/docs/**" + - "packages/dowel/**" + - ".github/workflows/deploy-docs.yml" + +permissions: + contents: read + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - name: Setup toolchain (mise) + uses: jdx/mise-action@v4 + with: + cache: true + + - name: Check for the deploy token + id: token + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + run: | + if [ -n "$CLOUDFLARE_API_TOKEN" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + echo "CLOUDFLARE_API_TOKEN is not set; skipping the deploy." + fi + + - name: Install dependencies + if: steps.token.outputs.present == 'true' + run: pnpm install --frozen-lockfile + + - name: Build the library then the docs + if: steps.token.outputs.present == 'true' + run: | + pnpm --filter dowel build + pnpm --filter @dowel/docs build + + - name: Deploy + if: steps.token.outputs.present == 'true' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + workingDirectory: apps/docs + # The action's bundled default predates wrangler v4 and cannot read + # an assets-only config ("Missing entry-point"). Pin the major that + # wrangler.jsonc is written for. + wranglerVersion: "4" diff --git a/apps/docs/wrangler.jsonc b/apps/docs/wrangler.jsonc new file mode 100644 index 0000000..901b59a --- /dev/null +++ b/apps/docs/wrangler.jsonc @@ -0,0 +1,20 @@ +// dowel.sh — the docs site, served as Cloudflare Worker static assets. +// Assets-only: no worker script, because the site is fully prerendered. +// +// www.dowel.sh cannot be redirected from an assets-only worker; that is a +// Cloudflare dashboard Redirect Rule (manual follow-up). +{ + "name": "dowel-sh", + "compatibility_date": "2026-08-09", + "assets": { + "directory": "./dist", + // The build emits sub-pages as directories (components/button/index.html), + // so /components/button must resolve to that file. auto-trailing-slash + // serves /components/button/ directly and 307s /components/button to it. + "html_handling": "auto-trailing-slash", + // No 404.html is emitted, so keep the plain null-body 404 for misses. + "not_found_handling": "none", + }, + "workers_dev": false, + "routes": [{ "pattern": "dowel.sh", "custom_domain": true }], +} From 2be396f37d4ec93843d46e38f4b2953e00fcfea7 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 14:11:20 +0530 Subject: [PATCH 27/33] Address the final whole-branch review Document theming, the one thing the READMEs never explained, and clear the four minors left open across the branch. - Theming section in packages/dowel/README.md, verified against source: the three dark paths (.dowel-dark, [data-dowel-theme="dark"], and the prefers-color-scheme rule on :root:not(.dowel-light):not([data-dowel-theme="light"])), what .dowel-root actually supplies, and a retheming snippet that states the :root requirement. Custom properties resolve on the declaring element, so a nested --dowel-accent override leaves the color-mix-derived hover behind. Root README aligned but kept short; both now state dowel is ESM-only. - user-select: none on .dowel-icon-btn, matching button.css and menu.css. - pretest builds before the CSS contract suite, so a fresh clone no longer fails four tests with "has not been built". Not circular and ~2.4s; the test keeps a named message as a backstop for bare vitest runs. - src/index.ts drops the "appended here by each component task" scaffolding. - Docs site gets a light/dark toggle in the nav, dogfooding IconButton and driving data-dowel-theme from state. No DOM access, so prerendering is unaffected and all three pages still emit. Covered by a new onClick-forwarding test on IconButton. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 18 ++++-- apps/docs/src/docs.css | 8 +++ apps/docs/src/routes/__root.tsx | 59 +++++++++++++++++- packages/dowel/README.md | 60 +++++++++++++++++-- packages/dowel/package.json | 2 + .../components/icon-button/icon-button.css | 1 + .../icon-button/icon-button.test.tsx | 16 ++++- packages/dowel/src/index.ts | 4 +- packages/dowel/test/css-contract.test.ts | 21 +++++-- 9 files changed, 168 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index be0f12f..bb21fd2 100644 --- a/README.md +++ b/README.md @@ -26,21 +26,29 @@ import { Button } from "dowel"; ``` That is the whole setup. No Tailwind, no PostCSS config, no preset, no -`components.json`, no copy-in generator. +`components.json`, no copy-in generator. dowel is ESM-only. ## What dowel is - **A real package.** Import components, bump a version, get the fixes. Your UI does not drift across apps. - **Opinionated on purpose.** There is no per-component override API. Retheming - is three CSS variables: `--dowel-hue`, `--dowel-accent` and - `--dowel-accent-fg`. Hover states derive from the accent automatically. -- **Light and dark from day one**, in one stylesheet, by class, data attribute - or system preference. + is three CSS variables declared on `:root`: `--dowel-hue`, `--dowel-accent` + and `--dowel-accent-fg`. Hover and focus derive from the accent + automatically. +- **Light and dark from day one**, in one stylesheet. Dark comes on via + `.dowel-dark`, via `data-dowel-theme="dark"`, or from + `prefers-color-scheme` — and `.dowel-light` / `data-dowel-theme="light"` on + `` pins light against a dark OS. - **Accessible by construction.** Behaviour comes from [Base UI](https://base-ui.com); every component is keyboard-tested and axe-checked. +Wrap your app in `.dowel-root` for the type and surface defaults. The full +theming reference — every selector, every knob — lives in +[`packages/dowel/README.md`](packages/dowel/README.md), and +[dowel.sh](https://dowel.sh) has a light/dark toggle in the nav. + ## What dowel is not Not headless, not framework-agnostic, not a Tailwind plugin, not customisable diff --git a/apps/docs/src/docs.css b/apps/docs/src/docs.css index f3e92fc..675ac39 100644 --- a/apps/docs/src/docs.css +++ b/apps/docs/src/docs.css @@ -28,6 +28,14 @@ body { color: var(--dowel-text-1); } +/* Pushes the theme toggle to the trailing edge. dowel components accept no + className, so the layout hook lives on a wrapper the docs own. */ +.docs-nav-end { + display: flex; + align-items: center; + margin-inline-start: auto; +} + .docs-main { max-width: 42rem; margin: 0 auto; diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index 90930e5..b8f6d27 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -5,7 +5,8 @@ import { Scripts, createRootRoute, } from "@tanstack/react-router"; -import { Tooltip } from "dowel"; +import { IconButton, Tooltip } from "dowel"; +import { useState } from "react"; // The docs self-host Inter; dowel itself ships no typeface, it only names // "Inter Variable" first in --dowel-font. @@ -29,12 +30,54 @@ export const Route = createRootRoute({ component: RootDocument, }); +// Icons are the docs' own, not dowel's — dowel ships components, not an icon +// set. Each shows the mode the button switches TO. +const SunIcon = () => ( + +); + +const MoonIcon = () => ( + +); + function RootDocument() { + // Purely declarative: React owns the attribute on , so the toggle + // touches no DOM API and the prerender never reads `document`. The initial + // value is a constant, so the prerendered markup and the first client render + // agree and hydration is clean. + const [theme, setTheme] = useState<"dark" | "light">("dark"); + const next = theme === "dark" ? "light" : "dark"; + return ( // The theme hooks onto rather than a wrapper div so the page // background reaches the viewport edges instead of stopping at the - // content box. - + // content box. On :root the attribute also wins over the + // prefers-color-scheme rule, which is guarded with + // :not([data-dowel-theme="light"]). + @@ -49,6 +92,16 @@ function RootDocument() { dowel Button Tooltip + {/* dowel components take no className, so the layout hook is a + wrapper the docs own. */} +
+ setTheme(next)} + > + {theme === "dark" ? : } + +
diff --git a/packages/dowel/README.md b/packages/dowel/README.md index 0239048..f31a2be 100644 --- a/packages/dowel/README.md +++ b/packages/dowel/README.md @@ -20,8 +20,8 @@ import { Button } from "dowel"; ; ``` -That is the whole setup. Light and dark ship in the one stylesheet, switched -by class, data attribute or system preference. +That is the whole setup. dowel is **ESM-only** — the `exports` map has no +`require` condition, so it needs a bundler or Node's ESM loader. ## Components @@ -33,10 +33,58 @@ keyboard-tested and axe-checked. ## Theming -Three CSS variables — `--dowel-hue`, `--dowel-accent` and `--dowel-accent-fg` -— are the only supported knobs. Hover states derive from `--dowel-accent` -automatically. There is no per-component `className` or `style` override API, -by design. If you need a different button, dowel is the wrong library. +There is no per-component `className` or `style` override API, by design. If +you need a different button, dowel is the wrong library. What you do get is a +root class, a theme switch and three variables. + +### `.dowel-root` + +Put it on an element that wraps your app. It supplies dowel's page defaults: +`--dowel-font`, the 13px `--dowel-fs-small` size, the 450 `--dowel-fw-normal` +weight, `--dowel-tracking`, `--dowel-text-2` for text and `--dowel-bg-1` for +the background. + +```tsx +
{/* your app */}
+``` + +Put it on `` or `` if you want that background to reach the +viewport edges instead of stopping at a content box. Any element carrying a +`data-dowel-theme` attribute picks up the same defaults, so a themed wrapper +does not need both. + +### Dark mode + +Light and dark ship in the one stylesheet. Three ways to reach dark: + +- **`.dowel-dark`** — a class on any element. Applies to it and its subtree. +- **`[data-dowel-theme="dark"]`** — an attribute, same scope. This is the one + to drive from a toggle. +- **System preference** — `@media (prefers-color-scheme: dark)` matching + `:root:not(.dowel-light):not([data-dowel-theme="light"])`. It applies at the + document root only, and the `:not()` guards are the light escape hatch: put + `class="dowel-light"` or `data-dowel-theme="light"` on `` to pin light + against a dark OS setting. There is no `.dowel-light` token block — light is + the `:root` default, so that class exists purely as the opt-out. + +### Retheming + +Three custom properties, and **they must be declared on `:root`**: + +```css +:root { + --dowel-hue: 210; /* a number, not a colour — retints every neutral */ + --dowel-accent: lch(49% 62 210); + --dowel-accent-fg: lch(100% 0 0); /* text drawn on the accent */ +} +``` + +`--dowel-accent-hover` (a `color-mix()` against the accent) and `--dowel-focus` +(a plain alias) both derive from `--dowel-accent`, so hover and focus follow a +retint for free. Custom properties resolve on the element that declares them, +which is why `:root` is the supported surface: override `--dowel-accent` on a +nested wrapper and the inherited `--dowel-accent-hover` still resolves against +the default accent, so buttons snap back to purple on hover. ## Typeface diff --git a/packages/dowel/package.json b/packages/dowel/package.json index 0752b04..cd5d49f 100644 --- a/packages/dowel/package.json +++ b/packages/dowel/package.json @@ -40,7 +40,9 @@ }, "scripts": { "build": "tsdown && node scripts/build-css.mjs", + "pretest": "pnpm build", "test": "vitest run", + "pretest:watch": "pnpm build", "test:watch": "vitest", "typecheck": "tsc --noEmit" }, diff --git a/packages/dowel/src/components/icon-button/icon-button.css b/packages/dowel/src/components/icon-button/icon-button.css index ea5a45f..f6e9bca 100644 --- a/packages/dowel/src/components/icon-button/icon-button.css +++ b/packages/dowel/src/components/icon-button/icon-button.css @@ -14,6 +14,7 @@ border-radius: var(--dowel-radius-pill); transition: var(--dowel-transition); cursor: default; + user-select: none; } .dowel-icon-btn[data-size="sm"] { diff --git a/packages/dowel/src/components/icon-button/icon-button.test.tsx b/packages/dowel/src/components/icon-button/icon-button.test.tsx index 67a5b68..ab41751 100644 --- a/packages/dowel/src/components/icon-button/icon-button.test.tsx +++ b/packages/dowel/src/components/icon-button/icon-button.test.tsx @@ -1,5 +1,6 @@ import { render, screen } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; +import userEvent from "@testing-library/user-event"; +import { describe, expect, it, vi } from "vitest"; import { expectNoA11yViolations } from "../../../test/setup"; import { renderBoth } from "../../../test/render"; import { IconButton } from "./index"; @@ -27,6 +28,19 @@ describe("IconButton", () => { expect(btn.dataset.size).toBe("md"); }); + // The docs site's theme toggle is an IconButton, so onClick surviving the + // spread-then-override ordering is load-bearing, not incidental. + it("fires onClick", async () => { + const onClick = vi.fn(); + render( + + + , + ); + await userEvent.click(screen.getByRole("button")); + expect(onClick).toHaveBeenCalledOnce(); + }); + it("supports the sm size", () => { render( diff --git a/packages/dowel/src/index.ts b/packages/dowel/src/index.ts index e3e164e..b25c3ab 100644 --- a/packages/dowel/src/index.ts +++ b/packages/dowel/src/index.ts @@ -1,4 +1,6 @@ -// Components are appended here by each component task. +// dowel's public surface. Everything importable from "dowel" is listed here; +// anything not exported below is internal and may change without a major bump. +// The stylesheet is a separate entry point: import "dowel/dowel.css" once. export { Button } from "./components/button"; export type { ButtonProps } from "./components/button"; export { IconButton } from "./components/icon-button"; diff --git a/packages/dowel/test/css-contract.test.ts b/packages/dowel/test/css-contract.test.ts index e1a07f8..efd62a3 100644 --- a/packages/dowel/test/css-contract.test.ts +++ b/packages/dowel/test/css-contract.test.ts @@ -4,14 +4,25 @@ import { describe, expect, it } from "vitest"; const DIST = resolve(import.meta.dirname, "..", "dist", "dowel.css"); +// This suite asserts against build output, not source. `pretest` builds first, +// so a fresh clone cannot land here with dist/ missing; the message is the +// backstop for anyone invoking vitest directly and bypassing the hook. +const NOT_BUILT = + `${DIST} is missing. This suite asserts against build output.\n` + + `Run \`pnpm --filter dowel build\` first (\`pnpm --filter dowel test\` does it for you).`; + +function readDist(): string { + if (!existsSync(DIST)) throw new Error(NOT_BUILT); + return readFileSync(DIST, "utf8"); +} + describe("dowel.css build contract", () => { it("has been built", () => { - // Run `pnpm --filter dowel build` before this suite. - expect(existsSync(DIST)).toBe(true); + expect(existsSync(DIST), NOT_BUILT).toBe(true); }); it("resolves every var() it references", () => { - const css = readFileSync(DIST, "utf8"); + const css = readDist(); const defined = new Set( [...css.matchAll(/(--dowel-[\w-]+)\s*:/g)].map((m) => m[1]), ); @@ -24,10 +35,10 @@ describe("dowel.css build contract", () => { }); it("inlines every @import", () => { - expect(readFileSync(DIST, "utf8")).not.toContain("@import"); + expect(readDist()).not.toContain("@import"); }); it("keeps the cascade layer names", () => { - expect(readFileSync(DIST, "utf8")).toContain("@layer"); + expect(readDist()).toContain("@layer"); }); }); From 2d0c2efeb882ec55fc4acdeda21b5352bed4477d Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 19:07:32 +0530 Subject: [PATCH 28/33] Change the accent from azure to teal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old lch(49% 62 285) rendered as a saturated azure that read as generic Bootstrap blue. Teal is karnstack's house colour, and hue 195 at a restrained chroma is ours rather than a borrowed brand. Light is lch(52% 32 195) (#198b89). Dark lifts it to lch(68% 36 195) so it carries on near-black surfaces — 7.71:1 against bg-1. Chroma runs out sooner than it looks at this hue: lch(64% 40 195) and anything more saturated at that lightness clips the sRGB green channel, so the dark value sits just inside the edge. --dowel-accent-fg now differs by theme, which is the substantive change. Teal is luminous for its lightness, so the dark accent leaves white text at 2.43:1 — worse than the azure's already-failing 3.31:1. Dark therefore draws a near-black ink from the accent's own hue: 6.42:1 at rest, 7.44:1 on hover. Light keeps white at 4.12:1. That is under AA, and deliberately so: against an L=52 background no ink clears 4.5:1 in both states — black trades the numbers round at 5.10:1 rest and 4.20:1 hover. White wins on the hover state and reads visibly cleaner at 13px. Reaching AA at rest needs the accent itself to drop to about lch(49% 32 195), which is a call for whoever owns the brand value. The color-mix() hover derivations are untouched, so overriding --dowel-accent still carries into hover and focus. Co-Authored-By: Claude Opus 5 (1M context) --- packages/dowel/README.md | 12 +++++++++++- packages/dowel/src/tokens/dark.css | 28 ++++++++++++++++++++-------- packages/dowel/src/tokens/light.css | 13 ++++++++++--- 3 files changed, 41 insertions(+), 12 deletions(-) diff --git a/packages/dowel/README.md b/packages/dowel/README.md index f31a2be..d800642 100644 --- a/packages/dowel/README.md +++ b/packages/dowel/README.md @@ -84,7 +84,17 @@ Three custom properties, and **they must be declared on `:root`**: retint for free. Custom properties resolve on the element that declares them, which is why `:root` is the supported surface: override `--dowel-accent` on a nested wrapper and the inherited `--dowel-accent-hover` still resolves against -the default accent, so buttons snap back to purple on hover. +the default accent, so buttons snap back to teal on hover. + +`--dowel-accent-fg` is the one knob you have to think about, because dowel's +own two themes do not agree on it. The default accent is teal — +`lch(52% 32 195)` in light, lifted to `lch(68% 36 195)` in dark so it still +carries on near-black surfaces. Teal is luminous for its lightness, so the +dark accent leaves white text at 2.4:1; dark mode therefore draws a near-black +ink (`lch(14% 6 195)`, 6.4:1) on the accent instead of white. If you retheme to +a darker or less luminous hue, set `--dowel-accent-fg` to white and check the +result in both themes — the value that works for one is not automatically +right for the other. ## Typeface diff --git a/packages/dowel/src/tokens/dark.css b/packages/dowel/src/tokens/dark.css index 124d864..f71653b 100644 --- a/packages/dowel/src/tokens/dark.css +++ b/packages/dowel/src/tokens/dark.css @@ -19,11 +19,17 @@ --dowel-text-3: lch(61.803% 1.2 var(--dowel-hue)); --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); - --dowel-accent: lch(58% 62 285); - /* Dark hovers LIGHTEN. Derived from the accent so overrides follow; - 85% toward white matches the old hardcoded lch(64% 62 285). */ + /* Same teal, lifted to sit on near-black surfaces: 7.71:1 against bg-1. + Chroma tops out around here — lch(64% 40 195) and anything more + saturated at this lightness clips the sRGB green channel. */ + --dowel-accent: lch(68% 36 195); + /* Dark hovers LIGHTEN. Derived from the accent so overrides follow. */ --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 85%, white); - --dowel-accent-fg: lch(100% 0 0); + /* Inverted from light on purpose. Teal is luminous for its lightness, so + a teal bright enough to read on near-black leaves white at 2.43:1 — + unusable. A near-black ink from the accent's own hue gives 6.42:1 at + rest and 7.44:1 on hover. */ + --dowel-accent-fg: lch(14% 6 195); --dowel-focus: var(--dowel-accent); --dowel-danger: lch(58% 68 28); @@ -58,11 +64,17 @@ --dowel-text-3: lch(61.803% 1.2 var(--dowel-hue)); --dowel-text-4: lch(36.975% 1.2 var(--dowel-hue)); - --dowel-accent: lch(58% 62 285); - /* Dark hovers LIGHTEN. Derived from the accent so overrides follow; - 85% toward white matches the old hardcoded lch(64% 62 285). */ + /* Same teal, lifted to sit on near-black surfaces: 7.71:1 against bg-1. + Chroma tops out around here — lch(64% 40 195) and anything more + saturated at this lightness clips the sRGB green channel. */ + --dowel-accent: lch(68% 36 195); + /* Dark hovers LIGHTEN. Derived from the accent so overrides follow. */ --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 85%, white); - --dowel-accent-fg: lch(100% 0 0); + /* Inverted from light on purpose. Teal is luminous for its lightness, + so a teal bright enough to read on near-black leaves white at + 2.43:1 — unusable. A near-black ink from the accent's own hue gives + 6.42:1 at rest and 7.44:1 on hover. */ + --dowel-accent-fg: lch(14% 6 195); --dowel-focus: var(--dowel-accent); --dowel-danger: lch(58% 68 28); diff --git a/packages/dowel/src/tokens/light.css b/packages/dowel/src/tokens/light.css index 3f37965..0736068 100644 --- a/packages/dowel/src/tokens/light.css +++ b/packages/dowel/src/tokens/light.css @@ -20,11 +20,18 @@ --dowel-text-3: lch(48% 1.2 var(--dowel-hue)); --dowel-text-4: lch(61.803% 1.2 var(--dowel-hue)); - /* accent — ours, deliberately not Linear's 295 brand hue */ - --dowel-accent: lch(49% 62 285); + /* accent — teal, karnstack's house colour. Deliberately not Linear's 295 + indigo: hue 195 at a restrained chroma reads as ours, not as a borrowed + brand. #198b89 in sRGB, comfortably inside the gamut. */ + --dowel-accent: lch(52% 32 195); /* Derived from the accent so a --dowel-accent override retheming stays - coherent on hover. 92% matches the old hardcoded lch(44% 62 285). */ + coherent on hover. Light hovers DARKEN: 92% toward black. */ --dowel-accent-hover: color-mix(in oklch, var(--dowel-accent) 92%, black); + /* White on this teal is 4.12:1 at rest and 5.00:1 on hover. Black would + trade those round — 5.10:1 at rest, 4.20:1 on hover — so no single ink + clears 4.5:1 in both states against an L=52 background. White is the + one that reads clean at 13px and holds up on the state the pointer is + actually over. Dark mode inverts this; see dark.css. */ --dowel-accent-fg: lch(100% 0 0); --dowel-focus: var(--dowel-accent); From 8941efb26ebdab4d0586b88a7e858f5ec2febe47 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 19:07:44 +0530 Subject: [PATCH 29/33] Rebuild the docs site as a real documentation site MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The docs rendered as unstyled prose on a dark background. This gives them shadcn's three-column shape: sticky header, sticky sidebar nav, a content column at 72ch, and an "On this page" rail with scroll-spy — collapsing to one column with a hamburger disclosure below 64rem. Plain CSS on dowel's own tokens throughout. No Tailwind, in the library or the docs. Local --docs-* tokens exist only where dowel has none: page-scale spacing (dowel's space scale tops out at 18px, sized for the inside of a control), layout widths, and syntax colours. Demos weld the preview surface and its source into one bordered container so the two cannot drift, and every preview is real dowel imported from the package. Syntax highlighting is a ~60-line TSX lexer rather than a dependency — Shiki and Prism are each larger than everything else in this app combined, and the docs only ever highlight snippets we wrote. It is a pure string -> tokens function, so the prerender can run it. Eleven pages prerender: landing, an index, and eight component pages. The theme toggle now defaults to the OS rather than hardcoding dark. That is also the only hydration-safe initial value — the server cannot read the OS setting, so asserting nothing is the one choice that always matches the client. Both icons ship and CSS picks the one matching the resolved theme, which a JS-chosen icon could not do in prerendered markup. Two library fixes fell out of dogfooding: Button and IconButton rendered through the documented render={
} escape hatch were arriving underlined from the UA sheet. Co-Authored-By: Claude Opus 5 (1M context) --- apps/docs/src/components/code-block.tsx | 89 ++ apps/docs/src/components/demo.tsx | 33 + apps/docs/src/components/docs-page.tsx | 185 ++++ apps/docs/src/components/icons.tsx | 96 ++ apps/docs/src/components/sidebar-nav.tsx | 35 + apps/docs/src/docs.css | 957 +++++++++++++++++- apps/docs/src/lib/highlight.ts | 101 ++ apps/docs/src/lib/nav.ts | 82 ++ apps/docs/src/routeTree.gen.ts | 220 +++- apps/docs/src/routes/__root.tsx | 209 +++- apps/docs/src/routes/components/badge.tsx | 66 ++ apps/docs/src/routes/components/button.tsx | 204 +++- apps/docs/src/routes/components/dialog.tsx | 117 +++ .../src/routes/components/icon-button.tsx | 117 +++ apps/docs/src/routes/components/index.tsx | 19 + apps/docs/src/routes/components/input.tsx | 100 ++ apps/docs/src/routes/components/kbd.tsx | 58 ++ apps/docs/src/routes/components/menu.tsx | 105 ++ apps/docs/src/routes/components/route.tsx | 27 + apps/docs/src/routes/components/tooltip.tsx | 133 ++- apps/docs/src/routes/index.tsx | 193 +++- .../dowel/src/components/button/button.css | 4 + .../components/icon-button/icon-button.css | 3 + 23 files changed, 2948 insertions(+), 205 deletions(-) create mode 100644 apps/docs/src/components/code-block.tsx create mode 100644 apps/docs/src/components/demo.tsx create mode 100644 apps/docs/src/components/docs-page.tsx create mode 100644 apps/docs/src/components/icons.tsx create mode 100644 apps/docs/src/components/sidebar-nav.tsx create mode 100644 apps/docs/src/lib/highlight.ts create mode 100644 apps/docs/src/lib/nav.ts create mode 100644 apps/docs/src/routes/components/badge.tsx create mode 100644 apps/docs/src/routes/components/dialog.tsx create mode 100644 apps/docs/src/routes/components/icon-button.tsx create mode 100644 apps/docs/src/routes/components/index.tsx create mode 100644 apps/docs/src/routes/components/input.tsx create mode 100644 apps/docs/src/routes/components/kbd.tsx create mode 100644 apps/docs/src/routes/components/menu.tsx create mode 100644 apps/docs/src/routes/components/route.tsx diff --git a/apps/docs/src/components/code-block.tsx b/apps/docs/src/components/code-block.tsx new file mode 100644 index 0000000..27729bd --- /dev/null +++ b/apps/docs/src/components/code-block.tsx @@ -0,0 +1,89 @@ +import { IconButton, Tooltip } from "dowel"; +import { useEffect, useState } from "react"; + +import { tokenize } from "../lib/highlight"; +import { CheckIcon, CopyIcon } from "./icons"; + +function CopyButton({ value }: { value: string }) { + const [copied, setCopied] = useState(false); + + // The confirmation is a timer, so it has to be cleaned up: without this a + // copy immediately before navigating away sets state on an unmounted tree. + useEffect(() => { + if (!copied) return; + const id = setTimeout(() => setCopied(false), 1600); + return () => clearTimeout(id); + }, [copied]); + + async function copy() { + // Guarded rather than assumed: the Clipboard API is absent on insecure + // origins, and a docs page should not throw because it is being read + // over plain http on someone's LAN. + if (!navigator.clipboard) return; + try { + await navigator.clipboard.writeText(value); + setCopied(true); + } catch { + // A denied clipboard permission is not worth an error state here. + } + } + + return ( + + + {copied ? : } + + } + /> + + + {copied ? "Copied" : "Copy"} + + + + ); +} + +export type CodeBlockProps = { + code: string; + /** Shown in the block's title bar. Also picks the tokenizer's mood. */ + lang?: string; +}; + +export function CodeBlock({ code, lang = "tsx" }: CodeBlockProps) { + const source = code.trim(); + // Shell snippets have no TSX to find; running the tokenizer over them only + // produces false positives, so they render as one plain token. + const tokens = + lang === "bash" || lang === "css" + ? [{ kind: "plain" as const, text: source }] + : tokenize(source); + + return ( +
+
+ {lang} + +
+
+        
+          {tokens.map((t, i) =>
+            t.kind === "plain" ? (
+              t.text
+            ) : (
+              
+                {t.text}
+              
+            ),
+          )}
+        
+      
+
+ ); +} diff --git a/apps/docs/src/components/demo.tsx b/apps/docs/src/components/demo.tsx new file mode 100644 index 0000000..78722bc --- /dev/null +++ b/apps/docs/src/components/demo.tsx @@ -0,0 +1,33 @@ +import type { ReactNode } from "react"; + +import { CodeBlock } from "./code-block"; + +export type DemoProps = { + /** The live components. Real dowel, never a mock-up of one. */ + children: ReactNode; + /** The source that produces exactly what is in the preview. */ + code: string; + /** + * `row` centres a wrapping row of controls — right for buttons and badges. + * `stack` is for things with width, like a field or a menu. `start` is for + * a single trigger that should not float in the middle of the surface. + */ + layout?: "row" | "stack" | "start"; +}; + +/** + * A demo is a preview surface and its source welded together, because the + * two drifting apart is the failure mode of every component doc. They share + * one bordered container so the code reads as the caption to the picture + * rather than as an unrelated block that happens to sit underneath. + */ +export function Demo({ children, code, layout = "row" }: DemoProps) { + return ( +
+
+ {children} +
+ +
+ ); +} diff --git a/apps/docs/src/components/docs-page.tsx b/apps/docs/src/components/docs-page.tsx new file mode 100644 index 0000000..43e8d71 --- /dev/null +++ b/apps/docs/src/components/docs-page.tsx @@ -0,0 +1,185 @@ +import { Link, useRouterState } from "@tanstack/react-router"; +import type { ReactNode } from "react"; +import { useEffect, useState } from "react"; + +import { componentNav } from "../lib/nav"; +import { ArrowRightIcon } from "./icons"; + +export type TocEntry = { id: string; title: string }; + +/** + * A section heading that the table of contents can point at. The `id` is + * supplied rather than derived from the title so the two lists cannot drift: + * the same string is the anchor target and the TOC href, and a mismatch is a + * dead link the moment a heading is reworded. + */ +export function Section({ + id, + title, + children, +}: { + id: string; + title: string; + children: ReactNode; +}) { + return ( +
+

+ + {title} + +

+ {children} +
+ ); +} + +/** + * Highlights the heading currently in view. Runs only in an effect, so the + * prerender never touches IntersectionObserver, and it degrades to a plain + * list of links if the API is missing. + */ +function useActiveHeading(toc: TocEntry[]) { + const [active, setActive] = useState(null); + + useEffect(() => { + if (toc.length === 0 || typeof IntersectionObserver === "undefined") return; + + const seen = new Map(); + const observer = new IntersectionObserver( + (entries) => { + for (const e of entries) seen.set(e.target.id, e.isIntersecting); + // First visible heading in document order wins, so scrolling up and + // down through a section does not flip the highlight around. + const first = toc.find((t) => seen.get(t.id)); + if (first) setActive(first.id); + }, + // Bias the band towards the top of the viewport: the heading you are + // reading under is the one that just left the top, not the one in the + // vertical middle of the screen. + { rootMargin: "-80px 0px -70% 0px", threshold: 0 }, + ); + + const nodes = toc + .map((t) => document.getElementById(t.id)) + .filter((n): n is HTMLElement => n !== null); + for (const n of nodes) observer.observe(n); + return () => observer.disconnect(); + }, [toc]); + + return active; +} + +function PageFooterNav() { + const pathname = useRouterState({ + select: (s) => s.location.pathname, + }); + // Trailing slashes appear on the prerendered pages but not in dev, so + // normalise before comparing or every page loses its prev/next in one of + // the two environments. + const current = pathname.replace(/\/+$/, ""); + const i = componentNav.findIndex((item) => item.to === current); + if (i === -1) return null; + + const prev = componentNav[i - 1]; + const next = componentNav[i + 1]; + + return ( + + ); +} + +export type DocsPageProps = { + eyebrow?: string; + title: string; + lead: string; + toc?: TocEntry[]; + children: ReactNode; +}; + +/** + * Returns a fragment, not a wrapper: the content column and the table of + * contents are two cells of the shell's grid, so putting a div around them + * would collapse the three-column layout into two. + */ +export function DocsPage({ + eyebrow = "Components", + title, + lead, + toc = [], + children, +}: DocsPageProps) { + const active = useActiveHeading(toc); + + return ( + <> +
+
+
+

{eyebrow}

+

{title}

+

{lead}

+
+ {children} +
+ +
+ + + + ); +} + +/** The card grid used by the landing page and the components index. */ +export function ComponentGrid() { + return ( +
    + {componentNav.map((item) => ( +
  • + + + {item.title} + + + {item.summary} + +
  • + ))} +
+ ); +} diff --git a/apps/docs/src/components/icons.tsx b/apps/docs/src/components/icons.tsx new file mode 100644 index 0000000..a5f8965 --- /dev/null +++ b/apps/docs/src/components/icons.tsx @@ -0,0 +1,96 @@ +/** + * The docs' own icon set. dowel ships components, not icons, and the docs + * deliberately take no icon dependency — so these are hand-authored at a + * single 16px grid with one stroke weight, which is what keeps them looking + * like one set rather than a pile of clip art. + * + * All of them inherit `currentColor` and carry `aria-hidden`: every icon here + * sits inside a control that already has a text label or an IconButton + * `label`, so none of them is ever the accessible name. + */ + +type IconProps = { size?: number }; + +const stroke = { + fill: "none", + stroke: "currentColor", + strokeWidth: 1.25, + strokeLinecap: "round", + strokeLinejoin: "round", +} as const; + +function Svg({ + size = 16, + children, +}: IconProps & { children: React.ReactNode }) { + return ( + + ); +} + +export const SunIcon = (p: IconProps) => ( + + + + +); + +export const MoonIcon = (p: IconProps) => ( + + + +); + +export const MenuIcon = (p: IconProps) => ( + + + +); + +export const CloseIcon = (p: IconProps) => ( + + + +); + +export const CopyIcon = (p: IconProps) => ( + + + + +); + +export const CheckIcon = (p: IconProps) => ( + + + +); + +export const ArrowRightIcon = (p: IconProps) => ( + + + +); + +/** The GitHub mark is a filled glyph, so it opts out of the stroke preset. */ +export const GitHubIcon = ({ size = 16 }: IconProps) => ( + +); diff --git a/apps/docs/src/components/sidebar-nav.tsx b/apps/docs/src/components/sidebar-nav.tsx new file mode 100644 index 0000000..66682f0 --- /dev/null +++ b/apps/docs/src/components/sidebar-nav.tsx @@ -0,0 +1,35 @@ +import { Link } from "@tanstack/react-router"; + +import { nav } from "../lib/nav"; + +/** + * One nav, rendered twice: once in the sticky desktop sidebar and once in the + * mobile disclosure panel. Sharing the component is what stops the two from + * listing different components, which is the usual way a mobile menu rots. + */ +export function SidebarNav({ onNavigate }: { onNavigate?: () => void }) { + return ( + + ); +} diff --git a/apps/docs/src/docs.css b/apps/docs/src/docs.css index 675ac39..a5318f5 100644 --- a/apps/docs/src/docs.css +++ b/apps/docs/src/docs.css @@ -1,84 +1,975 @@ /* The docs site's own layout. Deliberately not part of dowel: page chrome is - an application concern, and dowel ships components, not a shell. Colour and - type come from dowel's public tokens so the docs stay in step with the - library; page-scale spacing does not, because dowel's space scale tops out - at 18px — it is sized for the inside of a control, not for a page. */ + an application concern, and dowel ships components, not a shell. + + Everything here is plain CSS on dowel's public tokens — no Tailwind, no + preprocessor — because the docs are the library's first consumer, and a + consumer that needs a build pipeline to use dowel would be evidence against + the whole premise. Colour, type, radius and motion all come from + --dowel-*. The only values defined locally are the ones dowel has no token + for: page-scale spacing (dowel's space scale tops out at 18px — it is sized + for the inside of a control, not for a page), layout widths, and syntax + highlighting colours. Those live in --docs-*. */ + +/* ---------------------------------------------------------------- tokens */ + +:root { + /* Lets native scrollbars and form controls follow the theme. The explicit + overrides below mirror dowel's own precedence: an explicit choice on + :root beats the OS. */ + color-scheme: light dark; + + --docs-header-h: 3.5rem; + --docs-sidebar-w: 15rem; + --docs-toc-w: 14rem; + --docs-max: 88rem; + --docs-gutter: 1.25rem; + + /* Elevation is a light-mode affordance; the dark block drops it. */ + --docs-panel-shadow: var(--dowel-shadow-popover); + + /* Syntax colours. Low chroma on purpose — a code block that out-colours + the components it documents is a code block competing with the page. */ + --docs-code-bg: lch(97.5% 0.85 var(--dowel-hue)); + --docs-code-comment: var(--dowel-text-4); + --docs-code-keyword: lch(46% 52 310); + --docs-code-string: lch(45% 42 145); + --docs-code-tag: lch(45% 34 195); + --docs-code-attr: lch(48% 42 55); +} + +:root[data-dowel-theme="light"] { + color-scheme: light; +} + +.dowel-dark, +[data-dowel-theme="dark"] { + color-scheme: dark; + + --docs-panel-shadow: none; + + --docs-code-bg: lch(7.9% 1 var(--dowel-hue)); + --docs-code-comment: var(--dowel-text-4); + --docs-code-keyword: lch(73% 38 310); + --docs-code-string: lch(73% 36 145); + --docs-code-tag: lch(72% 34 195); + --docs-code-attr: lch(77% 40 70); +} + +@media (prefers-color-scheme: dark) { + :root:not(.dowel-light):not([data-dowel-theme="light"]) { + color-scheme: dark; + + --docs-panel-shadow: none; + + --docs-code-bg: lch(7.9% 1 var(--dowel-hue)); + --docs-code-comment: var(--dowel-text-4); + --docs-code-keyword: lch(73% 38 310); + --docs-code-string: lch(73% 36 145); + --docs-code-tag: lch(72% 34 195); + --docs-code-attr: lch(77% 40 70); + } +} + +@media (min-width: 40rem) { + :root { + --docs-gutter: 2rem; + } +} + +/* ------------------------------------------------------------------ base */ + +* { + box-sizing: border-box; +} body { margin: 0; - min-height: 100vh; + min-height: 100dvh; + -webkit-font-smoothing: antialiased; } -.docs-nav { +/* Anchor links jump to a heading that would otherwise land under the sticky + header. */ +:target, +[id] { + scroll-margin-top: calc(var(--docs-header-h) + 1.5rem); +} + +@media (prefers-reduced-motion: no-preference) { + html { + scroll-behavior: smooth; + } +} + +.docs-icon { + flex: none; + display: block; +} + +/* ---------------------------------------------------------------- header */ + +.docs-header { + position: sticky; + inset-block-start: 0; + z-index: 40; + background-color: color-mix(in srgb, var(--dowel-bg-1) 88%, transparent); + backdrop-filter: blur(12px); + border-block-end: 1px solid var(--dowel-border-1); +} + +.docs-header-inner { + display: flex; + align-items: center; + gap: var(--dowel-space-6); + block-size: var(--docs-header-h); + max-inline-size: var(--docs-max); + margin-inline: auto; + padding-inline: var(--docs-gutter); +} + +.docs-header-mobile { display: flex; +} + +.docs-brand { + text-decoration: none; + color: var(--dowel-text-1); +} + +.docs-wordmark { + display: flex; + align-items: center; + gap: var(--dowel-space-3); + font-size: var(--dowel-fs-lg); + font-weight: var(--dowel-fw-semibold); + letter-spacing: var(--dowel-tracking); + line-height: 1; +} + +.docs-mark { + color: var(--dowel-accent); +} + +.docs-header-nav { + display: none; align-items: center; gap: var(--dowel-space-8); - padding: var(--dowel-space-6) 1.5rem; - border-bottom: 1px solid var(--dowel-border-1); + margin-inline-start: var(--dowel-space-6); +} + +.docs-header-nav a { + color: var(--dowel-text-3); + text-decoration: none; + font-size: var(--dowel-fs-small); +} + +.docs-header-nav a:hover, +.docs-header-nav a[data-status="active"] { + color: var(--dowel-text-1); +} + +/* dowel components accept no className, so every layout hook is a wrapper + the docs own. */ +.docs-header-end { + display: flex; + align-items: center; + gap: var(--dowel-space-2); + margin-inline-start: auto; +} + +/* Both icons ship in the markup; CSS picks the one matching the resolved + theme, so the prerendered HTML is correct under either OS setting. The + selector chain mirrors dowel's: explicit attribute, then system. */ +.docs-theme-icon { + display: none; +} +.docs-theme-icon[data-icon="moon"] { + display: block; +} +@media (prefers-color-scheme: dark) { + :root:not([data-dowel-theme="light"]) .docs-theme-icon[data-icon="moon"] { + display: none; + } + :root:not([data-dowel-theme="light"]) .docs-theme-icon[data-icon="sun"] { + display: block; + } +} +:root[data-dowel-theme="dark"] .docs-theme-icon[data-icon="moon"] { + display: none; +} +:root[data-dowel-theme="dark"] .docs-theme-icon[data-icon="sun"] { + display: block; +} +:root[data-dowel-theme="light"] .docs-theme-icon[data-icon="moon"] { + display: block; +} +:root[data-dowel-theme="light"] .docs-theme-icon[data-icon="sun"] { + display: none; +} + +/* ------------------------------------------------------------ mobile nav */ + +.docs-mobile-nav { + display: none; + max-block-size: calc(100dvh - var(--docs-header-h)); + overflow-y: auto; + padding: var(--dowel-space-8) var(--docs-gutter) 2rem; + background-color: var(--dowel-bg-1); + border-block-start: 1px solid var(--dowel-border-1); +} + +:root[data-nav-open] .docs-mobile-nav { + display: block; +} + +/* ------------------------------------------------------------------- nav */ + +.docs-nav-section { + margin-block-end: var(--dowel-space-8); +} + +.docs-nav-heading { + margin: 0 0 var(--dowel-space-3); + padding-inline-start: var(--dowel-space-5); + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-semibold); + color: var(--dowel-text-1); +} + +.docs-nav ul { + display: flex; + flex-direction: column; + gap: 1px; + margin: 0; + padding: 0; + list-style: none; } .docs-nav a { + display: block; + padding: var(--dowel-space-3) var(--dowel-space-5); + border-radius: var(--dowel-radius-sm); + font-size: var(--dowel-fs-small); color: var(--dowel-text-3); text-decoration: none; transition: var(--dowel-transition); } -.docs-nav a:hover, +.docs-nav a:hover { + color: var(--dowel-text-2); + background-color: var(--dowel-bg-2); +} + +/* The current page is marked with a muted surface and an accent rule, never + a filled accent background — and the weight never changes between states, + so the list does not reflow as you navigate. */ .docs-nav a[data-status="active"] { color: var(--dowel-text-1); + background-color: var(--dowel-bg-3); + box-shadow: inset 2px 0 0 var(--dowel-accent); +} + +/* ----------------------------------------------------------------- shell */ + +.docs-shell { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 0; + max-inline-size: var(--docs-max); + margin-inline: auto; + padding-inline: var(--docs-gutter); +} + +.docs-sidebar { + display: none; +} + +@media (min-width: 64rem) { + .docs-shell { + grid-template-columns: var(--docs-sidebar-w) minmax(0, 1fr); + gap: 3rem; + } + + .docs-sidebar { + display: block; + position: sticky; + inset-block-start: var(--docs-header-h); + block-size: calc(100dvh - var(--docs-header-h)); + overflow-y: auto; + margin-inline-start: calc(var(--docs-gutter) * -1); + padding: 2rem 1rem 3rem var(--docs-gutter); + border-inline-end: 1px solid var(--dowel-border-1); + } + + :root[data-nav-open] .docs-mobile-nav { + display: none; + } + + .docs-header-mobile { + display: none; + } + + .docs-header-nav { + display: flex; + } +} + +@media (min-width: 80rem) { + .docs-shell { + grid-template-columns: + var(--docs-sidebar-w) minmax(0, 1fr) + var(--docs-toc-w); + } +} + +/* --------------------------------------------------------------- content */ + +.docs-content { + /* Grid children default to min-width:auto and would refuse to shrink below + a long line of code, pushing the whole layout wide. */ + min-inline-size: 0; + container-type: inline-size; + padding-block: 2.5rem 5rem; +} + +.docs-article { + max-inline-size: 72ch; +} + +.docs-article-head { + padding-block-end: 1.75rem; + border-block-end: 1px solid var(--dowel-border-1); +} + +.docs-eyebrow { + margin: 0 0 var(--dowel-space-4); + font-family: var(--dowel-mono); + font-size: var(--dowel-fs-mini); + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--dowel-text-3); +} + +.docs-content h1 { + margin: 0; + font-size: clamp(1.875rem, 1.4rem + 1.6vw, var(--dowel-fs-title1)); + font-weight: var(--dowel-fw-semibold); + letter-spacing: var(--dowel-tracking-title); + line-height: 1.15; + color: var(--dowel-text-1); + text-wrap: balance; +} + +.docs-lead { + margin: var(--dowel-space-7) 0 0; + max-inline-size: 62ch; + font-size: 1.0625rem; + line-height: 1.6; + color: var(--dowel-text-3); + text-wrap: pretty; +} + +.docs-section { + margin-block-start: 3.5rem; +} + +.docs-section h2 { + margin: 0 0 var(--dowel-space-7); + font-size: var(--dowel-fs-title2); + font-weight: var(--dowel-fw-semibold); + letter-spacing: var(--dowel-tracking-title); + color: var(--dowel-text-1); +} + +.docs-anchor { + color: inherit; + text-decoration: none; +} + +.docs-anchor::after { + content: "#"; + margin-inline-start: var(--dowel-space-4); + color: var(--dowel-text-4); + opacity: 0; +} + +.docs-anchor:hover::after { + opacity: 1; +} + +.docs-article p { + margin: 0 0 var(--dowel-space-8); + /* 16px on mobile, dropping to dowel's own 15px base once there is room — + body copy below 16px on a phone is not readable. */ + font-size: 1rem; + line-height: 1.65; + color: var(--dowel-text-2); + text-wrap: pretty; +} + +@media (min-width: 40rem) { + .docs-article p { + font-size: var(--dowel-fs-base); + } +} + +/* Prose links only. The whole selector sits in :where() so it carries zero + specificity — a component with its own link styling (the card grid, which + renders
  • ) then wins on a plain class rather than needing an + override chain. */ +:where(.docs-article :where(p, li, td) a) { + color: var(--dowel-text-1); + text-decoration: underline; + text-decoration-thickness: 1px; + text-underline-offset: 3px; + text-decoration-color: var(--dowel-accent); +} + +:where(.docs-article :where(p, li, td) a:hover) { + text-decoration-color: var(--dowel-text-1); +} + +/* Inline code only. `pre code` is scoped out so the block keeps its own + colours instead of turning into a row of chips. */ +.docs-article :where(p, li, td, th, .docs-note) code { + padding: 0.1em 0.35em; + font-family: var(--dowel-mono); + font-size: 0.875em; + border-radius: var(--dowel-radius-sm); + background-color: var(--dowel-bg-3); + color: var(--dowel-text-1); +} + +.docs-note { + margin: var(--dowel-space-8) 0; + padding: var(--dowel-space-7) var(--dowel-space-8); + border-radius: var(--dowel-radius); + background-color: var(--dowel-bg-2); + box-shadow: inset 2px 0 0 var(--dowel-accent); + font-size: var(--dowel-fs-small); + line-height: 1.65; + color: var(--dowel-text-3); +} + +.docs-inline-sample { + margin: 0; + max-inline-size: 48ch; + font-size: var(--dowel-fs-base); + line-height: 1.7; + color: var(--dowel-text-2); +} + +/* ------------------------------------------------------------------ demo */ + +.docs-demo { + margin-block: var(--dowel-space-8) 0; + border: 1px solid var(--dowel-border-1); + border-radius: var(--dowel-radius-lg); + overflow: hidden; + background-color: var(--dowel-bg-1); } -/* Pushes the theme toggle to the trailing edge. dowel components accept no - className, so the layout hook lives on a wrapper the docs own. */ -.docs-nav-end { +.docs-demo-preview { display: flex; + flex-wrap: wrap; align-items: center; + justify-content: center; + gap: var(--dowel-space-6); + min-block-size: 8.5rem; + padding: 2rem var(--dowel-space-8); +} + +.docs-demo-preview[data-layout="stack"] { + flex-direction: column; + align-items: stretch; + gap: var(--dowel-space-8); + max-inline-size: 22rem; + margin-inline: auto; +} + +.docs-demo-preview[data-layout="start"] { + justify-content: flex-start; +} + +/* The code block is welded to the preview: no radius of its own, one shared + border. */ +.docs-demo .docs-code { + border: 0; + border-block-start: 1px solid var(--dowel-border-1); + border-radius: 0; +} + +.docs-dialog-actions { + display: flex; + justify-content: flex-end; + gap: var(--dowel-space-4); + margin-block-start: var(--dowel-space-2); +} + +/* ------------------------------------------------------------------ code */ + +.docs-code { + border: 1px solid var(--dowel-border-1); + border-radius: var(--dowel-radius-lg); + overflow: hidden; + background-color: var(--docs-code-bg); +} + +.docs-code-bar { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--dowel-space-2) var(--dowel-space-2) var(--dowel-space-2) + var(--dowel-space-7); + border-block-end: 1px solid var(--dowel-border-1); +} + +.docs-code-lang { + font-family: var(--dowel-mono); + font-size: var(--dowel-fs-micro); + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--dowel-text-3); +} + +.docs-code-pre { + margin: 0; + padding: var(--dowel-space-7) var(--dowel-space-8); + overflow-x: auto; + font-family: var(--dowel-mono); + font-size: var(--dowel-fs-mini); + line-height: 1.75; + letter-spacing: 0; + color: var(--dowel-text-2); + tab-size: 2; +} + +.tk-comment { + color: var(--docs-code-comment); + font-style: italic; +} +.tk-string { + color: var(--docs-code-string); +} +.tk-number { + color: var(--docs-code-string); +} +.tk-keyword { + color: var(--docs-code-keyword); +} +.tk-tag, +.tk-type { + color: var(--docs-code-tag); +} +.tk-attr { + color: var(--docs-code-attr); +} + +/* ----------------------------------------------------------------- table */ + +.docs-table-wrap { + margin-block: var(--dowel-space-8); + overflow-x: auto; +} + +.docs-table { + inline-size: 100%; + border-collapse: collapse; + font-size: var(--dowel-fs-small); +} + +.docs-table th { + padding: var(--dowel-space-4) var(--dowel-space-6) var(--dowel-space-4) 0; + text-align: start; + font-weight: var(--dowel-fw-medium); + color: var(--dowel-text-3); + border-block-end: 1px solid var(--dowel-border-2); + white-space: nowrap; +} + +.docs-table td { + padding: var(--dowel-space-6) var(--dowel-space-6) var(--dowel-space-6) 0; + vertical-align: top; + color: var(--dowel-text-2); + border-block-end: 1px solid var(--dowel-border-1); +} + +/* ------------------------------------------------------------------- toc */ + +.docs-toc { + display: none; +} + +@media (min-width: 80rem) { + .docs-toc { + display: block; + } +} + +.docs-toc-inner { + position: sticky; + inset-block-start: calc(var(--docs-header-h) + 2.5rem); + padding-block: 2.5rem 3rem; +} + +.docs-toc-title { + margin: 0 0 var(--dowel-space-5); + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-semibold); + color: var(--dowel-text-1); +} + +.docs-toc ul { + margin: 0; + padding: 0; + list-style: none; + border-inline-start: 1px solid var(--dowel-border-1); +} + +.docs-toc a { + display: block; + margin-inline-start: -1px; + padding: var(--dowel-space-3) 0 var(--dowel-space-3) var(--dowel-space-7); + border-inline-start: 1px solid transparent; + font-size: var(--dowel-fs-small); + line-height: 1.4; + color: var(--dowel-text-3); + text-decoration: none; +} + +.docs-toc a:hover { + color: var(--dowel-text-2); +} + +.docs-toc a[data-active] { + color: var(--dowel-text-1); + border-inline-start-color: var(--dowel-accent); +} + +/* --------------------------------------------------------------- pagenav */ + +.docs-pagenav { + display: flex; + justify-content: space-between; + gap: var(--dowel-space-8); + max-inline-size: 72ch; + margin-block-start: 4rem; +} + +.docs-pagenav-link { + display: flex; + flex-direction: column; + gap: var(--dowel-space-1); + padding: var(--dowel-space-6) var(--dowel-space-8); + border: 1px solid var(--dowel-border-1); + border-radius: var(--dowel-radius); + text-decoration: none; + transition: var(--dowel-transition); +} + +.docs-pagenav-link:hover { + background-color: var(--dowel-bg-2); + border-color: var(--dowel-border-2); +} + +.docs-pagenav-link[data-dir="next"] { + text-align: end; margin-inline-start: auto; } -.docs-main { - max-width: 42rem; - margin: 0 auto; - padding: 3rem 1.5rem 6rem; +.docs-pagenav-label { + font-size: var(--dowel-fs-mini); + color: var(--dowel-text-4); +} + +.docs-pagenav-title { + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-medium); + color: var(--dowel-text-1); +} + +/* ----------------------------------------------------------------- cards */ + +.docs-cards { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--dowel-space-6); + margin: var(--dowel-space-8) 0 0; + padding: 0; + list-style: none; +} + +/* Container queries, not viewport queries: this grid renders both inside the + narrow docs column and across the full-width landing page, and what it + needs to know is how much room it has, not how wide the window is. */ +@container (min-width: 34rem) { + .docs-cards { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@container (min-width: 58rem) { + .docs-cards { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } +} + +.docs-card { + display: flex; + flex-direction: column; + gap: var(--dowel-space-3); + block-size: 100%; + padding: var(--dowel-space-8); + border: 1px solid var(--dowel-border-1); + border-radius: var(--dowel-radius-lg); + text-decoration: none; + transition: var(--dowel-transition); } -.docs-main h1 { - font-size: var(--dowel-fs-title1); +.docs-card:hover { + background-color: var(--dowel-bg-2); + border-color: var(--dowel-border-2); +} + +.docs-card-title { + display: flex; + align-items: center; + gap: var(--dowel-space-3); + font-size: var(--dowel-fs-base); + font-weight: var(--dowel-fw-medium); + color: var(--dowel-text-1); +} + +.docs-card-title .docs-icon { + color: var(--dowel-text-4); +} + +.docs-card:hover .docs-card-title .docs-icon { + color: var(--dowel-accent); +} + +.docs-card-summary { + font-size: var(--dowel-fs-small); + line-height: 1.55; + color: var(--dowel-text-3); +} + +/* --------------------------------------------------------------- landing */ + +.docs-landing { + max-inline-size: var(--docs-max); + margin-inline: auto; + padding-inline: var(--docs-gutter); +} + +.docs-hero { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: 2.5rem; + padding-block: 3.5rem 1rem; +} + +@media (min-width: 64rem) { + .docs-hero { + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + align-items: center; + gap: 4rem; + padding-block: 5.5rem 2rem; + } +} + +.docs-hero-copy h1 { + margin: var(--dowel-space-7) 0 0; + max-inline-size: 18ch; + font-size: clamp(2.25rem, 1.5rem + 3vw, 3.25rem); font-weight: var(--dowel-fw-semibold); letter-spacing: var(--dowel-tracking-title); + line-height: 1.05; color: var(--dowel-text-1); - margin: 0 0 var(--dowel-space-6); + text-wrap: balance; +} + +.docs-hero-copy .docs-lead { + max-inline-size: 52ch; +} + +.docs-hero-copy code { + padding: 0.1em 0.35em; + font-family: var(--dowel-mono); + font-size: 0.875em; + border-radius: var(--dowel-radius-sm); + background-color: var(--dowel-bg-3); + color: var(--dowel-text-2); +} + +.docs-hero-actions { + display: flex; + flex-wrap: wrap; + gap: var(--dowel-space-6); + margin-block-start: var(--dowel-space-8); } -.docs-main h2 { - font-size: var(--dowel-fs-title3); +.docs-hero-panel { + border: 1px solid var(--dowel-border-2); + border-radius: var(--dowel-radius-lg); + background-color: var(--dowel-bg-elevated); + box-shadow: var(--docs-panel-shadow); + overflow: hidden; +} + +.docs-hero-panel-bar { + display: flex; + align-items: center; + gap: var(--dowel-space-3); + padding: var(--dowel-space-5) var(--dowel-space-7); + border-block-end: 1px solid var(--dowel-border-1); + background-color: var(--dowel-bg-2); +} + +.docs-hero-dot { + inline-size: 8px; + block-size: 8px; + border-radius: var(--dowel-radius-pill); + background-color: var(--dowel-border-3); +} + +.docs-hero-panel-body { + display: flex; + flex-direction: column; + gap: var(--dowel-space-8); + padding: var(--dowel-space-8); +} + +.docs-hero-row { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--dowel-space-6); +} + +.docs-section-block { + container-type: inline-size; + padding-block: 3.5rem; + border-block-start: 1px solid var(--dowel-border-1); +} + +.docs-section-block h2 { + margin: 0; + font-size: var(--dowel-fs-title2); font-weight: var(--dowel-fw-semibold); + letter-spacing: var(--dowel-tracking-title); color: var(--dowel-text-1); - margin: 3rem 0 var(--dowel-space-6); } -.docs-main p { - margin: 0 0 1rem; +.docs-section-lead { + margin: var(--dowel-space-5) 0 0; + max-inline-size: 58ch; + font-size: var(--dowel-fs-base); line-height: 1.6; + color: var(--dowel-text-3); } -.docs-main code { +.docs-install { + display: grid; + gap: var(--dowel-space-6); + margin-block-start: var(--dowel-space-8); +} + +@container (min-width: 52rem) { + .docs-install { + grid-template-columns: minmax(0, 1fr) minmax(0, 1.6fr); + align-items: start; + } +} + +.docs-install-side { + display: grid; + align-content: start; + gap: var(--dowel-space-8); +} + +.docs-fineprint { + margin: 0; + max-inline-size: 68ch; + font-size: var(--dowel-fs-small); + line-height: 1.6; + color: var(--dowel-text-3); +} + +.docs-fineprint code { font-family: var(--dowel-mono); - font-size: var(--dowel-fs-mini); + color: var(--dowel-text-2); +} + +.docs-principles { + display: grid; + grid-template-columns: minmax(0, 1fr); + gap: var(--dowel-space-8); + margin: var(--dowel-space-8) 0 0; + padding: 0; + list-style: none; +} + +@container (min-width: 52rem) { + .docs-principles { + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 2.5rem; + } +} + +.docs-principles h3 { + margin: 0 0 var(--dowel-space-4); + font-size: var(--dowel-fs-base); + font-weight: var(--dowel-fw-semibold); color: var(--dowel-text-1); } -/* A row of live components. */ -.docs-row { +.docs-principles p { + margin: 0; + font-size: var(--dowel-fs-small); + line-height: 1.65; + color: var(--dowel-text-3); +} + +/* ---------------------------------------------------------------- footer */ + +.docs-footer { + border-block-start: 1px solid var(--dowel-border-1); + padding-block: 2.5rem 4rem; +} + +.docs-footer-inner { display: flex; flex-wrap: wrap; align-items: center; - gap: var(--dowel-space-4); + justify-content: space-between; + gap: var(--dowel-space-8); } -/* A constraint a consumer has to know about, not decoration. */ -.docs-note { - border-left: 2px solid var(--dowel-border-3); - padding-left: 1rem; +.docs-footer p { + margin: 0; + max-inline-size: 62ch; + font-size: var(--dowel-fs-small); + line-height: 1.6; color: var(--dowel-text-3); } + +.docs-footer a { + color: var(--dowel-text-2); + font-weight: var(--dowel-fw-normal); +} + +.docs-footer-cta { + display: flex; + align-items: center; + gap: var(--dowel-space-3); + font-size: var(--dowel-fs-small); + font-weight: var(--dowel-fw-medium); + color: var(--dowel-text-1); + text-decoration: none; + white-space: nowrap; +} + +.docs-footer-cta:hover { + color: var(--dowel-accent); +} diff --git a/apps/docs/src/lib/highlight.ts b/apps/docs/src/lib/highlight.ts new file mode 100644 index 0000000..cae1d01 --- /dev/null +++ b/apps/docs/src/lib/highlight.ts @@ -0,0 +1,101 @@ +/** + * A ~60-line TSX tokenizer, in place of a syntax-highlighting dependency. + * + * Shiki and Prism are both larger than every other dependency in this app put + * together, and the docs only ever highlight snippets we wrote ourselves. So + * this trades generality for size: it is a lexer, not a parser, it will + * mis-colour code it was never given, and that is an acceptable deal for a + * fixed set of hand-written samples. + * + * It is a pure string -> array function with no DOM access, which is what + * lets the prerender run it on the server. + */ + +export type TokenKind = + | "comment" + | "string" + | "keyword" + | "tag" + | "attr" + | "type" + | "number" + | "plain"; + +export type Token = { kind: TokenKind; text: string }; + +const KEYWORDS = [ + "import", + "export", + "from", + "const", + "let", + "var", + "function", + "return", + "type", + "interface", + "extends", + "as", + "default", + "new", + "async", + "await", + "if", + "else", + "true", + "false", + "null", + "undefined", +].join("|"); + +// Alternation order IS precedence: a `//` inside a string must lose to the +// string rule, so strings and comments come before everything structural. +const PATTERN = new RegExp( + [ + String.raw`(\/\/[^\n]*|\/\*[\s\S]*?\*\/)`, // 1 comment + String.raw`("(?:[^"\\\n]|\\.)*"|'(?:[^'\\\n]|\\.)*'|` + + "`(?:[^`\\\\]|\\\\.)*`)", // 2 string + String.raw`(<\/?[A-Za-z][\w.]*)`, // 3 JSX tag, incl. Tooltip.Root + String.raw`\b(${KEYWORDS})\b`, // 4 keyword + // An identifier immediately before `=` is a JSX attribute or an + // assignment target. `=>` and `==` are excluded so arrow params and + // comparisons are not mistaken for attributes. + String.raw`([A-Za-z_$][\w$]*)(?=\s*=(?![=>]))`, // 5 attribute + String.raw`\b([A-Z][A-Za-z0-9]*)\b`, // 6 component / type name + String.raw`\b(\d+(?:\.\d+)?)\b`, // 7 number + ].join("|"), + "g", +); + +const KIND_BY_GROUP: TokenKind[] = [ + "comment", + "string", + "tag", + "keyword", + "attr", + "type", + "number", +]; + +export function tokenize(code: string): Token[] { + const tokens: Token[] = []; + let last = 0; + PATTERN.lastIndex = 0; + + for (let m = PATTERN.exec(code); m !== null; m = PATTERN.exec(code)) { + if (m.index > last) { + tokens.push({ kind: "plain", text: code.slice(last, m.index) }); + } + const group = KIND_BY_GROUP.findIndex((_, i) => m[i + 1] !== undefined); + tokens.push({ + kind: group === -1 ? "plain" : (KIND_BY_GROUP[group] as TokenKind), + text: m[0], + }); + last = m.index + m[0].length; + } + + if (last < code.length) { + tokens.push({ kind: "plain", text: code.slice(last) }); + } + return tokens; +} diff --git a/apps/docs/src/lib/nav.ts b/apps/docs/src/lib/nav.ts new file mode 100644 index 0000000..738f40b --- /dev/null +++ b/apps/docs/src/lib/nav.ts @@ -0,0 +1,82 @@ +import type { LinkProps } from "@tanstack/react-router"; + +/** + * `to` is the router's own union of known paths, not `string`, so a typo in + * this table is a typecheck failure rather than a dead link discovered in a + * screenshot. It is also what the prerender crawler follows: every page the + * build must emit is reachable from the sidebar, so adding an entry here is + * the only step needed to get a route prerendered. + */ +export type NavItem = { + title: string; + to: LinkProps["to"]; + /** One line, shown on the components index and the landing grid. */ + summary: string; +}; + +export type NavSection = { + title: string; + items: NavItem[]; +}; + +export const componentNav: NavItem[] = [ + { + title: "Badge", + to: "/components/badge", + summary: "A compact status or metadata pill in five tones.", + }, + { + title: "Button", + to: "/components/button", + summary: "The default control. Four variants, two sizes.", + }, + { + title: "Dialog", + to: "/components/dialog", + summary: "A modal on the modal elevation tier, labelled by its title.", + }, + { + title: "Icon Button", + to: "/components/icon-button", + summary: "A square control for a single icon, with a required label.", + }, + { + title: "Input", + to: "/components/input", + summary: "A text field, and the Field parts that label and describe it.", + }, + { + title: "Kbd", + to: "/components/kbd", + summary: "A keyboard shortcut rendered one key per cap.", + }, + { + title: "Menu", + to: "/components/menu", + summary: "A dropdown with keyboard navigation and typeahead.", + }, + { + title: "Tooltip", + to: "/components/tooltip", + summary: "A hover and focus label on the popover elevation tier.", + }, +]; + +export const nav: NavSection[] = [ + { + title: "Getting started", + items: [ + { + title: "Introduction", + to: "/", + summary: "What dowel is, and what it refuses to be.", + }, + { + title: "All components", + to: "/components", + summary: "Everything the package exports, in one list.", + }, + ], + }, + { title: "Components", items: componentNav }, +]; diff --git a/apps/docs/src/routeTree.gen.ts b/apps/docs/src/routeTree.gen.ts index 79c9704..21ebcba 100644 --- a/apps/docs/src/routeTree.gen.ts +++ b/apps/docs/src/routeTree.gen.ts @@ -10,7 +10,15 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' +import { Route as ComponentsRouteRouteImport } from './routes/components/route' +import { Route as ComponentsIndexRouteImport } from './routes/components/index' +import { Route as ComponentsBadgeRouteImport } from './routes/components/badge' import { Route as ComponentsButtonRouteImport } from './routes/components/button' +import { Route as ComponentsDialogRouteImport } from './routes/components/dialog' +import { Route as ComponentsIconButtonRouteImport } from './routes/components/icon-button' +import { Route as ComponentsInputRouteImport } from './routes/components/input' +import { Route as ComponentsKbdRouteImport } from './routes/components/kbd' +import { Route as ComponentsMenuRouteImport } from './routes/components/menu' import { Route as ComponentsTooltipRouteImport } from './routes/components/tooltip' const IndexRoute = IndexRouteImport.update({ @@ -18,45 +26,140 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const ComponentsButtonRoute = ComponentsButtonRouteImport.update({ - id: '/components/button', - path: '/components/button', +const ComponentsRouteRoute = ComponentsRouteRouteImport.update({ + id: '/components', + path: '/components', getParentRoute: () => rootRouteImport, } as any) +const ComponentsIndexRoute = ComponentsIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsBadgeRoute = ComponentsBadgeRouteImport.update({ + id: '/badge', + path: '/badge', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsButtonRoute = ComponentsButtonRouteImport.update({ + id: '/button', + path: '/button', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsDialogRoute = ComponentsDialogRouteImport.update({ + id: '/dialog', + path: '/dialog', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsIconButtonRoute = ComponentsIconButtonRouteImport.update({ + id: '/icon-button', + path: '/icon-button', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsInputRoute = ComponentsInputRouteImport.update({ + id: '/input', + path: '/input', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsKbdRoute = ComponentsKbdRouteImport.update({ + id: '/kbd', + path: '/kbd', + getParentRoute: () => ComponentsRouteRoute, +} as any) +const ComponentsMenuRoute = ComponentsMenuRouteImport.update({ + id: '/menu', + path: '/menu', + getParentRoute: () => ComponentsRouteRoute, +} as any) const ComponentsTooltipRoute = ComponentsTooltipRouteImport.update({ - id: '/components/tooltip', - path: '/components/tooltip', - getParentRoute: () => rootRouteImport, + id: '/tooltip', + path: '/tooltip', + getParentRoute: () => ComponentsRouteRoute, } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/components': typeof ComponentsRouteRouteWithChildren + '/components/badge': typeof ComponentsBadgeRoute '/components/button': typeof ComponentsButtonRoute + '/components/dialog': typeof ComponentsDialogRoute + '/components/icon-button': typeof ComponentsIconButtonRoute + '/components/input': typeof ComponentsInputRoute + '/components/kbd': typeof ComponentsKbdRoute + '/components/menu': typeof ComponentsMenuRoute '/components/tooltip': typeof ComponentsTooltipRoute + '/components/': typeof ComponentsIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/components/badge': typeof ComponentsBadgeRoute '/components/button': typeof ComponentsButtonRoute + '/components/dialog': typeof ComponentsDialogRoute + '/components/icon-button': typeof ComponentsIconButtonRoute + '/components/input': typeof ComponentsInputRoute + '/components/kbd': typeof ComponentsKbdRoute + '/components/menu': typeof ComponentsMenuRoute '/components/tooltip': typeof ComponentsTooltipRoute + '/components': typeof ComponentsIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/components': typeof ComponentsRouteRouteWithChildren + '/components/badge': typeof ComponentsBadgeRoute '/components/button': typeof ComponentsButtonRoute + '/components/dialog': typeof ComponentsDialogRoute + '/components/icon-button': typeof ComponentsIconButtonRoute + '/components/input': typeof ComponentsInputRoute + '/components/kbd': typeof ComponentsKbdRoute + '/components/menu': typeof ComponentsMenuRoute '/components/tooltip': typeof ComponentsTooltipRoute + '/components/': typeof ComponentsIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/components/button' | '/components/tooltip' + fullPaths: + | '/' + | '/components' + | '/components/badge' + | '/components/button' + | '/components/dialog' + | '/components/icon-button' + | '/components/input' + | '/components/kbd' + | '/components/menu' + | '/components/tooltip' + | '/components/' fileRoutesByTo: FileRoutesByTo - to: '/' | '/components/button' | '/components/tooltip' - id: '__root__' | '/' | '/components/button' | '/components/tooltip' + to: + | '/' + | '/components/badge' + | '/components/button' + | '/components/dialog' + | '/components/icon-button' + | '/components/input' + | '/components/kbd' + | '/components/menu' + | '/components/tooltip' + | '/components' + id: + | '__root__' + | '/' + | '/components' + | '/components/badge' + | '/components/button' + | '/components/dialog' + | '/components/icon-button' + | '/components/input' + | '/components/kbd' + | '/components/menu' + | '/components/tooltip' + | '/components/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute - ComponentsButtonRoute: typeof ComponentsButtonRoute - ComponentsTooltipRoute: typeof ComponentsTooltipRoute + ComponentsRouteRoute: typeof ComponentsRouteRouteWithChildren } declare module '@tanstack/react-router' { @@ -68,27 +171,110 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/components': { + id: '/components' + path: '/components' + fullPath: '/components' + preLoaderRoute: typeof ComponentsRouteRouteImport + parentRoute: typeof rootRouteImport + } + '/components/': { + id: '/components/' + path: '/' + fullPath: '/components/' + preLoaderRoute: typeof ComponentsIndexRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/badge': { + id: '/components/badge' + path: '/badge' + fullPath: '/components/badge' + preLoaderRoute: typeof ComponentsBadgeRouteImport + parentRoute: typeof ComponentsRouteRoute + } '/components/button': { id: '/components/button' - path: '/components/button' + path: '/button' fullPath: '/components/button' preLoaderRoute: typeof ComponentsButtonRouteImport - parentRoute: typeof rootRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/dialog': { + id: '/components/dialog' + path: '/dialog' + fullPath: '/components/dialog' + preLoaderRoute: typeof ComponentsDialogRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/icon-button': { + id: '/components/icon-button' + path: '/icon-button' + fullPath: '/components/icon-button' + preLoaderRoute: typeof ComponentsIconButtonRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/input': { + id: '/components/input' + path: '/input' + fullPath: '/components/input' + preLoaderRoute: typeof ComponentsInputRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/kbd': { + id: '/components/kbd' + path: '/kbd' + fullPath: '/components/kbd' + preLoaderRoute: typeof ComponentsKbdRouteImport + parentRoute: typeof ComponentsRouteRoute + } + '/components/menu': { + id: '/components/menu' + path: '/menu' + fullPath: '/components/menu' + preLoaderRoute: typeof ComponentsMenuRouteImport + parentRoute: typeof ComponentsRouteRoute } '/components/tooltip': { id: '/components/tooltip' - path: '/components/tooltip' + path: '/tooltip' fullPath: '/components/tooltip' preLoaderRoute: typeof ComponentsTooltipRouteImport - parentRoute: typeof rootRouteImport + parentRoute: typeof ComponentsRouteRoute } } } -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, +interface ComponentsRouteRouteChildren { + ComponentsBadgeRoute: typeof ComponentsBadgeRoute + ComponentsButtonRoute: typeof ComponentsButtonRoute + ComponentsDialogRoute: typeof ComponentsDialogRoute + ComponentsIconButtonRoute: typeof ComponentsIconButtonRoute + ComponentsInputRoute: typeof ComponentsInputRoute + ComponentsKbdRoute: typeof ComponentsKbdRoute + ComponentsMenuRoute: typeof ComponentsMenuRoute + ComponentsTooltipRoute: typeof ComponentsTooltipRoute + ComponentsIndexRoute: typeof ComponentsIndexRoute +} + +const ComponentsRouteRouteChildren: ComponentsRouteRouteChildren = { + ComponentsBadgeRoute: ComponentsBadgeRoute, ComponentsButtonRoute: ComponentsButtonRoute, + ComponentsDialogRoute: ComponentsDialogRoute, + ComponentsIconButtonRoute: ComponentsIconButtonRoute, + ComponentsInputRoute: ComponentsInputRoute, + ComponentsKbdRoute: ComponentsKbdRoute, + ComponentsMenuRoute: ComponentsMenuRoute, ComponentsTooltipRoute: ComponentsTooltipRoute, + ComponentsIndexRoute: ComponentsIndexRoute, +} + +const ComponentsRouteRouteWithChildren = ComponentsRouteRoute._addFileChildren( + ComponentsRouteRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + IndexRoute: IndexRoute, + ComponentsRouteRoute: ComponentsRouteRouteWithChildren, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index b8f6d27..1fb8005 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -5,15 +5,26 @@ import { Scripts, createRootRoute, } from "@tanstack/react-router"; -import { IconButton, Tooltip } from "dowel"; +import { Badge, IconButton, Tooltip } from "dowel"; import { useState } from "react"; +import { + CloseIcon, + GitHubIcon, + MenuIcon, + MoonIcon, + SunIcon, +} from "../components/icons"; +import { SidebarNav } from "../components/sidebar-nav"; + // The docs self-host Inter; dowel itself ships no typeface, it only names // "Inter Variable" first in --dowel-font. import "@fontsource-variable/inter"; import "dowel/dowel.css"; import "../docs.css"; +const REPO = "https://github.com/karnstack/dowel"; + export const Route = createRootRoute({ head: () => ({ meta: [ @@ -30,46 +41,87 @@ export const Route = createRootRoute({ component: RootDocument, }); -// Icons are the docs' own, not dowel's — dowel ships components, not an icon -// set. Each shows the mode the button switches TO. -const SunIcon = () => ( - -); +/** A dowel: the small turned pin that joins two pieces of wood. */ +function Wordmark() { + return ( + + + dowel + + ); +} -const MoonIcon = () => ( - -); +function ThemeToggle({ + theme, + onToggle, +}: { + theme: "light" | "dark" | null; + onToggle: () => void; +}) { + return ( + + + {/* Both icons ship; CSS shows the one matching the resolved + theme. That keeps the prerendered markup correct under either + OS setting, which a JS-chosen icon could not be. */} + + + + + + + + } + /> + + + + {theme === null ? "Theme: system" : `Theme: ${theme}`} + + + + + ); +} function RootDocument() { - // Purely declarative: React owns the attribute on , so the toggle - // touches no DOM API and the prerender never reads `document`. The initial - // value is a constant, so the prerendered markup and the first client render - // agree and hydration is clean. - const [theme, setTheme] = useState<"dark" | "light">("dark"); - const next = theme === "dark" ? "light" : "dark"; + // `null` means "no explicit choice": the attribute is left off and + // dowel's prefers-color-scheme rule decides. That is both the better + // default and the hydration-safe one — the server cannot read the OS + // setting, so the only initial value that always matches the client is the + // one that asserts nothing. + const [theme, setTheme] = useState<"light" | "dark" | null>(null); + const [navOpen, setNavOpen] = useState(false); + + function toggleTheme() { + const resolved = + theme ?? + (window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"); + setTheme(resolved === "dark" ? "light" : "dark"); + } return ( // The theme hooks onto rather than a wrapper div so the page @@ -77,7 +129,12 @@ function RootDocument() { // content box. On :root the attribute also wins over the // prefers-color-scheme rule, which is guarded with // :not([data-dowel-theme="light"]). - + @@ -88,21 +145,67 @@ function RootDocument() { reaches opens instantly instead of waiting again. */} - + + diff --git a/apps/docs/src/routes/components/badge.tsx b/apps/docs/src/routes/components/badge.tsx new file mode 100644 index 0000000..320fae0 --- /dev/null +++ b/apps/docs/src/routes/components/badge.tsx @@ -0,0 +1,66 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Badge } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/badge")({ + component: BadgeDocs, +}); + +const toc = [ + { id: "tones", title: "Tones" }, + { id: "in-context", title: "In context" }, +]; + +function BadgeDocs() { + return ( + +
    +

    + Tone carries meaning, not decoration. neutral is the + default and the right answer most of the time; the four coloured tones + all read as state. +

    + Neutral +Accent +Shipped +Deprecated +Breaking`} + > + Neutral + Accent + Shipped + Deprecated + Breaking + +
    + +
    +

    + A badge sits at 20px so it aligns to a line of body text rather than + to a control. It renders a span, so it flows inline. +

    + + dowel is pre-1.0 — the API will + change between minor versions. +

    `} + > +

    + dowel is pre-1.0 — the API will change + between minor versions. +

    +
    +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/button.tsx b/apps/docs/src/routes/components/button.tsx index 36d0c88..2837843 100644 --- a/apps/docs/src/routes/components/button.tsx +++ b/apps/docs/src/routes/components/button.tsx @@ -1,47 +1,177 @@ import { createFileRoute } from "@tanstack/react-router"; -import { Button, IconButton } from "dowel"; +import { Button } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; export const Route = createFileRoute("/components/button")({ component: ButtonDocs, }); +const toc = [ + { id: "variants", title: "Variants" }, + { id: "sizes", title: "Sizes" }, + { id: "states", title: "States" }, + { id: "as-a-link", title: "As a link" }, + { id: "props", title: "Props" }, +]; + function ButtonDocs() { return ( -
    -

    Button

    -

    - The default control. Four variants, two sizes, and no appearance props —{" "} - className and style are omitted from the type - and neutralised at runtime. -

    - -

    Variants

    -
    - - - - -
    - -

    Sizes and states

    -
    - - - -
    - -

    IconButton

    -

    - For a control whose content is an icon. label is required — - an icon alone never names a control, so the accessible name is part of - the API rather than something a caller can forget. -

    -
    - × - - + - -
    -
    + +
    +

    + Variant is visual weight, not colour choice. Use one primary per + screen; everything else is secondary or ghost, and danger is reserved + for an action that destroys something. +

    + Primary + + +`} + > + + + + + +
    + +
    +

    + Two, and only two. md is the 28px control height every + other dowel control shares; sm is 24px for dense rows and + toolbars. +

    + Small +`} + > + + + +
    + +
    +

    + A disabled button keeps its variant and drops to half opacity. Hover + derives from the accent, so a retheme carries into it automatically. +

    + Disabled +`} + > + + + +
    + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    PropTypeDefault
    + variant + + + "primary" | "secondary" | + "ghost" | "danger" + + + "secondary" +
    + size + + "sm" | "md" + + "md" +
    + render + + ReactElement +
    + nativeButton + + boolean + + true +
    +
    +

    + className and style are absent from{" "} + ButtonProps and neutralised at runtime. If you need a + different button, dowel is the wrong library — that is the point. +

    +
    +
    ); } diff --git a/apps/docs/src/routes/components/dialog.tsx b/apps/docs/src/routes/components/dialog.tsx new file mode 100644 index 0000000..f669d93 --- /dev/null +++ b/apps/docs/src/routes/components/dialog.tsx @@ -0,0 +1,117 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Button, Dialog, Field, Input } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/dialog")({ + component: DialogDocs, +}); + +const toc = [ + { id: "anatomy", title: "Anatomy" }, + { id: "with-a-form", title: "With a form" }, +]; + +function DialogDocs() { + return ( + +
    +

    + Compose Root, Trigger, Portal,{" "} + Backdrop, Popup, Title,{" "} + Description and Close. Focus trapping, + scroll locking and Escape handling come from Base UI. +

    + + Delete workspace} /> + + + + Delete workspace + + This removes every project in it. It cannot be undone. + + Cancel} /> + Delete} /> + + +`} + > + + Delete workspace} /> + + + + Delete workspace + + This removes every project in it. It cannot be undone. + +
    + Cancel} /> + Delete} + /> +
    +
    +
    +
    +
    +

    + Dialog.Portal renders a plain div. Never put + an inline transform or filter on it — either + one creates a containing block and silently breaks the popup's{" "} + position: fixed. +

    +
    + +
    +

    + The popup is a flex column with a fixed gap, so a field stack drops + straight in without a wrapper. +

    + + New project + + Name + + + Create} /> +`} + > + + New project} + /> + + + + New project + + Name + + +
    + Cancel} /> + Create} + /> +
    +
    +
    +
    +
    +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/icon-button.tsx b/apps/docs/src/routes/components/icon-button.tsx new file mode 100644 index 0000000..d211bf1 --- /dev/null +++ b/apps/docs/src/routes/components/icon-button.tsx @@ -0,0 +1,117 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { IconButton, Tooltip } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; +import { + CheckIcon, + CloseIcon, + CopyIcon, + MenuIcon, +} from "../../components/icons"; + +export const Route = createFileRoute("/components/icon-button")({ + component: IconButtonDocs, +}); + +const toc = [ + { id: "label", title: "The label is required" }, + { id: "variants", title: "Variants and sizes" }, + { id: "with-a-tooltip", title: "With a tooltip" }, +]; + +function IconButtonDocs() { + return ( + +
    +

    + An icon alone never names a control, so label is a + required prop and aria-label is omitted from the type. + There is no way to render one of these without a name. +

    + + +`} + > + + + + +
    + +
    +

    + Ghost by default, because these usually sit in a toolbar where a + filled control would shout. secondary gives it a surface + when it needs to read as a discrete target. +

    + + + + + + + +`} + > + + + + + + + + + + + + + +
    + +
    +

    + A Tooltip is a visual label only — it is not announced. Pairing it + with an IconButton is the supported combination precisely because the + button already carries its own name. +

    + + } + /> + + + Copy code + + +`} + > + + + + + } + /> + + + Copy code + + + + +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/index.tsx b/apps/docs/src/routes/components/index.tsx new file mode 100644 index 0000000..dff30ae --- /dev/null +++ b/apps/docs/src/routes/components/index.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from "@tanstack/react-router"; + +import { ComponentGrid, DocsPage } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/")({ + component: ComponentsIndex, +}); + +function ComponentsIndex() { + return ( + + + + ); +} diff --git a/apps/docs/src/routes/components/input.tsx b/apps/docs/src/routes/components/input.tsx new file mode 100644 index 0000000..c6c1d7b --- /dev/null +++ b/apps/docs/src/routes/components/input.tsx @@ -0,0 +1,100 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Field, Input } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/input")({ + component: InputDocs, +}); + +const toc = [ + { id: "sizes", title: "Sizes" }, + { id: "field", title: "Field" }, + { id: "validation", title: "Validation" }, +]; + +function InputDocs() { + return ( + +
    +

    + md is the 28px control height shared with Button.{" "} + lg is 36px, for a field that is the point of the screen + rather than one cell in a dense row. The native size{" "} + attribute is omitted from the type so dowel's visual scale can + take the name. +

    + +`} + > + + + +
    + +
    +

    + A dowel Input inside Field.Root is + associated with its label automatically. Field.Label{" "} + omits htmlFor on purpose: a hand-written one would win + over the generated association, which is the exact bug Field exists to + remove. +

    + + Workspace name + + + Lowercase letters and dashes only. + +`} + > + + Workspace name + + + Lowercase letters and dashes only. + + + +
    + +
    +

    + invalid sets aria-invalid and turns the + border to the danger tone. It is applied conditionally, so leaving it + unset never clobbers an aria-invalid that Field + validation computed — add Field.Error with a{" "} + match to surface the message Field computes. +

    + + Email + + + +`} + > + + Email + + + + +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/kbd.tsx b/apps/docs/src/routes/components/kbd.tsx new file mode 100644 index 0000000..6fb886b --- /dev/null +++ b/apps/docs/src/routes/components/kbd.tsx @@ -0,0 +1,58 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Kbd } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/kbd")({ + component: KbdDocs, +}); + +const toc = [ + { id: "keys", title: "Keys" }, + { id: "why-an-array", title: "Why an array" }, +]; + +function KbdDocs() { + return ( + +
    +

    + Each entry becomes its own kbd element at a fixed 17px + cap, so a two-key shortcut and a four-key shortcut sit on the same + baseline. +

    + + +`} + > + + + + +
    + +
    +

    + Because a string would put the separator in the caller's hands, + and then half the app writes Cmd+K and the other half + writes Cmd K. children is omitted from{" "} + KbdProps for the same reason. +

    +

    + dowel does not translate key names. Meta renders as + "Meta", not as a platform glyph — mapping to the right + symbol per OS is application knowledge, and it needs the user agent to + get right. +

    +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/menu.tsx b/apps/docs/src/routes/components/menu.tsx new file mode 100644 index 0000000..0bd39c6 --- /dev/null +++ b/apps/docs/src/routes/components/menu.tsx @@ -0,0 +1,105 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Button, Menu } from "dowel"; + +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; + +export const Route = createFileRoute("/components/menu")({ + component: MenuDocs, +}); + +const toc = [ + { id: "anatomy", title: "Anatomy" }, + { id: "groups", title: "Groups and separators" }, +]; + +function MenuDocs() { + return ( + +
    +

    + The highlighted item is styled through data-highlighted{" "} + rather than :hover, so the mouse and the keyboard produce + exactly the same state — arrow down and hover cannot disagree. +

    + + Open menu} /> + + + + Duplicate + Rename + Move to… + + + +`} + > + + Open menu} /> + + + + Duplicate + Rename + Move to… + + + + + +
    + +
    +

    + Group and GroupLabel name a run of related + items; Separator is the hairline between runs. Use one or + the other — a labelled group that also has a rule above it is saying + the same thing twice. +

    + + + This project + Duplicate + Archive + + + Delete +`} + > + + Actions} /> + + + + + This project + Duplicate + Archive + + + Delete + + + + + +

    + Menu.Positioner defaults to a 4px sideOffset + . It is set before the prop spread, so it is a default you can + override, not a mandate. +

    +
    +
    + ); +} diff --git a/apps/docs/src/routes/components/route.tsx b/apps/docs/src/routes/components/route.tsx new file mode 100644 index 0000000..6848548 --- /dev/null +++ b/apps/docs/src/routes/components/route.tsx @@ -0,0 +1,27 @@ +import { Outlet, createFileRoute } from "@tanstack/react-router"; + +import { SidebarNav } from "../../components/sidebar-nav"; + +/** + * A layout route, so the sidebar is mounted once for the whole `/components` + * subtree instead of re-rendering per page. That is what lets the sidebar + * keep its scroll position when you move between components. + */ +export const Route = createFileRoute("/components")({ + component: ComponentsLayout, +}); + +function ComponentsLayout() { + return ( +
    + + {/* The page supplies two grid cells: the content column and the table + of contents. See DocsPage. */} + +
    + ); +} diff --git a/apps/docs/src/routes/components/tooltip.tsx b/apps/docs/src/routes/components/tooltip.tsx index 10b1aed..c06a55c 100644 --- a/apps/docs/src/routes/components/tooltip.tsx +++ b/apps/docs/src/routes/components/tooltip.tsx @@ -1,46 +1,113 @@ import { createFileRoute } from "@tanstack/react-router"; import { IconButton, Tooltip } from "dowel"; +import { Demo } from "../../components/demo"; +import { DocsPage, Section } from "../../components/docs-page"; +import { CheckIcon, CopyIcon, MenuIcon } from "../../components/icons"; + export const Route = createFileRoute("/components/tooltip")({ component: TooltipDocs, }); +const toc = [ + { id: "anatomy", title: "Anatomy" }, + { id: "shared-delay", title: "The shared delay" }, + { id: "visual-only", title: "A tooltip is a visual label only" }, +]; + function TooltipDocs() { return ( -
    -

    Tooltip

    -

    - A hover and focus label on the popover elevation tier. Compose{" "} - Root, Trigger, Portal,{" "} - Positioner and Popup, with a single{" "} - Tooltip.Provider wrapping the app so adjacent tooltips - share one delay and the second one opens instantly. -

    + +
    +

    + Compose Root, Trigger, Portal,{" "} + Positioner and Popup. The positioner + defaults to a 6px sideOffset. +

    + + } + /> + + + Copy link + + +`} + > + + + + + } + /> + + + Copy link + + + + +
    -
    - - 🔗} - /> - - - Copy link - - - -
    +
    +

    + A single Tooltip.Provider at the app root is what lets + adjacent tooltips share one delay: the first waits, and the next one + the pointer reaches opens instantly instead of waiting again. Hover + across this row to feel it. +

    + + +`} + > + {[ + { label: "Copy", icon: }, + { label: "Confirm", icon: }, + { label: "More", icon: }, + ].map((t) => ( + + + {t.icon} + + } + /> + + + {t.label} + + + + ))} + +
    -

    A tooltip is a visual label only

    -

    - Base UI, which dowel builds on, deliberately does not associate the - popup with its trigger — there is no aria-describedby and - no touch affordance. So the tooltip text does not reach a screen reader, - and the trigger has to carry its own accessible name.{" "} - IconButton enforces exactly that with its required{" "} - label prop, which is why the example above wraps one. If - the hover content is information a user cannot do without, it does not - belong in a Tooltip — reach for a Popover, which is announced. -

    -
    +
    +

    + Base UI, which dowel builds on, deliberately does not associate the + popup with its trigger — there is no aria-describedby and + no touch affordance. So the tooltip text does not reach a screen + reader, and the trigger has to carry its own accessible name.{" "} + IconButton enforces exactly that with its required{" "} + label prop, which is why every example above wraps one. + If the hover content is information a user cannot do without, it does + not belong in a Tooltip — reach for a Popover, which is announced. +

    +
    + ); } diff --git a/apps/docs/src/routes/index.tsx b/apps/docs/src/routes/index.tsx index a763d92..baa4874 100644 --- a/apps/docs/src/routes/index.tsx +++ b/apps/docs/src/routes/index.tsx @@ -1,42 +1,171 @@ import { Link, createFileRoute } from "@tanstack/react-router"; -import { Badge, Button, Kbd } from "dowel"; +import { Badge, Button, Field, Input, Kbd, Tooltip, IconButton } from "dowel"; + +import { CodeBlock } from "../components/code-block"; +import { ComponentGrid } from "../components/docs-page"; +import { ArrowRightIcon, CopyIcon, GitHubIcon } from "../components/icons"; export const Route = createFileRoute("/")({ component: Home, }); +const INSTALL = `pnpm add dowel`; + +const USAGE = `import "dowel/dowel.css"; +import { Button } from "dowel"; + +export function Save() { + return ; +}`; + +const PRINCIPLES = [ + { + title: "A real package", + body: "Import components, bump a version, get the fixes. Your UI does not drift across apps, because there is no copy of it in your repo to drift.", + }, + { + title: "Opinionated on purpose", + body: "No per-component override API. className and style are omitted from every prop type and neutralised at runtime, so one look survives contact with a deadline.", + }, + { + title: "Light and dark from day one", + body: "One stylesheet, three activation paths, and an explicit choice that always beats the OS. Retheming is three CSS variables on :root.", + }, +]; + function Home() { return ( -
    -

    dowel

    -

    An opinionated React component library. One look, well made.

    -

    - Every component ships its own appearance and refuses{" "} - className and style. What you compose is - behaviour and content; what you get back is one coherent surface. -

    - -
    - - - v0.1.0 - -
    - -

    Install

    -

    - pnpm add dowel, then import the single stylesheet once:{" "} - import "dowel/dowel.css". dowel ships no typeface - — its --dowel-font token names Inter Variable{" "} - first, and the app supplies it. This site self-hosts it via{" "} - @fontsource-variable/inter. -

    - -

    Components

    -

    - Button ·{" "} - Tooltip -

    -
    +
    +
    +
    + Pre-1.0 · MIT +

    One look, well made.

    +

    + dowel is an opinionated React component library. Every component + ships its own appearance and refuses className and{" "} + style. What you compose is behaviour and content; what + you get back is one coherent surface. +

    +
    + + +
    +
    + + {/* Not a screenshot: the hero panel is the library, running. */} +
    +
    + + + +
    +
    +
    + + + +
    + + Workspace + + +
    + v0.1.0 + Passing + + + + + + } + /> + + + Copy + + + +
    +
    +
    +
    + +
    +

    Install

    +

    + One package, one stylesheet, no build config. dowel is ESM-only. +

    +
    +
    + +

    + dowel ships no typeface — its --dowel-font token + names Inter Variable first and the app supplies it. + This site self-hosts it via{" "} + @fontsource-variable/inter. +

    +
    + +
    +
    + +
    +

    What dowel is

    +
      + {PRINCIPLES.map((p) => ( +
    • +

      {p.title}

      +

      {p.body}

      +
    • + ))} +
    +
    + +
    +

    Components

    +

    + Eight of them. Behaviour comes from Base UI; every one is + keyboard-tested and axe-checked. +

    + +
    + +
    +
    ); } diff --git a/packages/dowel/src/components/button/button.css b/packages/dowel/src/components/button/button.css index 761d86f..ff102ff 100644 --- a/packages/dowel/src/components/button/button.css +++ b/packages/dowel/src/components/button/button.css @@ -19,6 +19,10 @@ transition: var(--dowel-transition); cursor: default; user-select: none; + /* `render={}` is a supported escape hatch, and an anchor arrives + underlined from the UA sheet. A button that is a link still has to look + like a button. */ + text-decoration: none; } .dowel-btn[data-size="sm"] { diff --git a/packages/dowel/src/components/icon-button/icon-button.css b/packages/dowel/src/components/icon-button/icon-button.css index f6e9bca..5682369 100644 --- a/packages/dowel/src/components/icon-button/icon-button.css +++ b/packages/dowel/src/components/icon-button/icon-button.css @@ -15,6 +15,9 @@ transition: var(--dowel-transition); cursor: default; user-select: none; + /* `render={}` is a supported escape hatch, and an anchor arrives + underlined from the UA sheet. */ + text-decoration: none; } .dowel-icon-btn[data-size="sm"] { From bc11e4f1d55618bb09598d8e93179ab4eae04e1a Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 19:20:57 +0530 Subject: [PATCH 30/33] Add a D keyboard shortcut for the theme toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pressing D — bare, either case — flips the docs theme that the header toggle already flips, and the toggle's tooltip advertises it with Kbd. The listener is attached in an effect so it only ever exists in the browser; the site is statically prerendered and there is no document while the HTML is generated. Three guards keep a bare-letter shortcut from stealing keystrokes. It yields to anything the user is typing into — input, textarea, select, or a computed contenteditable, so nodes nested inside an editing host count too. It yields to a held Meta/Ctrl/Alt so it can never shadow a browser shortcut, and to defaultPrevented so a handler closer to the keystroke wins. And it yields while a Dialog or Menu is open, matched on Base UI's [data-open] rather than the popup's presence in the DOM: the attribute is dropped for the closing animation while the element is still mounted, so presence would suppress the key after the overlay had visually gone. toggleTheme now reads the media query before setTheme and resolves the current theme in a functional updater, which keeps the updater pure and leaves the toggle depending on nothing — so the listener is attached once instead of rebound on every flip. Co-Authored-By: Claude Opus 5 (1M context) --- apps/docs/src/docs.css | 8 +++ apps/docs/src/routes/__root.tsx | 86 ++++++++++++++++++++++++++++----- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/apps/docs/src/docs.css b/apps/docs/src/docs.css index a5318f5..122197b 100644 --- a/apps/docs/src/docs.css +++ b/apps/docs/src/docs.css @@ -207,6 +207,14 @@ body { display: none; } +/* A tooltip that ends in a Kbd: the label and the cap sit on one baseline + with a gap, instead of the cap hanging off the end of a text run. */ +.docs-tooltip-hint { + display: inline-flex; + align-items: center; + gap: var(--dowel-space-3); +} + /* ------------------------------------------------------------ mobile nav */ .docs-mobile-nav { diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index 1fb8005..d79acda 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -5,8 +5,8 @@ import { Scripts, createRootRoute, } from "@tanstack/react-router"; -import { Badge, IconButton, Tooltip } from "dowel"; -import { useState } from "react"; +import { Badge, IconButton, Kbd, Tooltip } from "dowel"; +import { useCallback, useEffect, useState } from "react"; import { CloseIcon, @@ -25,6 +25,39 @@ import "../docs.css"; const REPO = "https://github.com/karnstack/dowel"; +/** The bare key that flips the theme. Matched case-insensitively. */ +const THEME_KEY = "d"; + +/** + * True when the keystroke belongs to something the user is typing into. The + * docs are full of live Input and Field demos, so a bare-letter shortcut has + * to yield to them or it eats characters. `isContentEditable` is computed + * rather than read off the attribute, so it is also true for a node nested + * inside an editing host. + */ +function isTypingTarget(target: EventTarget | null) { + if (!(target instanceof HTMLElement)) return false; + if (target.isContentEditable) return true; + const tag = target.tagName; + return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT"; +} + +/** + * True while a Dialog or Menu is open. An open overlay is its own keyboard + * context: Menu spends bare letters on typeahead, and a shortcut firing + * under a modal would restyle a page the user cannot see. Base UI carries + * `data-open` on the popup for exactly as long as it is open — it is gone + * again during the closing animation — so the attribute is the honest + * signal where the popup merely being in the DOM is not. + */ +function isOverlayOpen() { + return ( + document.querySelector( + '[role="dialog"][data-open], [role="alertdialog"][data-open], [role="menu"][data-open]', + ) !== null + ); +} + export const Route = createRootRoute({ head: () => ({ meta: [ @@ -97,7 +130,14 @@ function ThemeToggle({ - {theme === null ? "Theme: system" : `Theme: ${theme}`} + {/* The shortcut is advertised where the control already + explains itself, rather than as a second thing in the + header. Kbd carries no className, so the row is a span + the docs own. */} + + {theme === null ? "Theme: system" : `Theme: ${theme}`} + + @@ -114,14 +154,38 @@ function RootDocument() { const [theme, setTheme] = useState<"light" | "dark" | null>(null); const [navOpen, setNavOpen] = useState(false); - function toggleTheme() { - const resolved = - theme ?? - (window.matchMedia("(prefers-color-scheme: dark)").matches - ? "dark" - : "light"); - setTheme(resolved === "dark" ? "light" : "dark"); - } + // The media query is read outside the updater so the updater stays pure, + // which leaves the toggle depending on nothing: the keydown listener below + // is attached once instead of being torn down and rebound on every flip. + const toggleTheme = useCallback(() => { + const systemDark = window.matchMedia( + "(prefers-color-scheme: dark)", + ).matches; + setTheme((current) => { + const resolved = current ?? (systemDark ? "dark" : "light"); + return resolved === "dark" ? "light" : "dark"; + }); + }, []); + + // `D` toggles the theme. The listener lives in an effect so it only ever + // exists in the browser — the site is prerendered, and there is no + // document to listen on while the HTML is being generated. + useEffect(() => { + function onKeyDown(event: KeyboardEvent) { + // Bare `D` only. A held Meta/Ctrl/Alt means the user is reaching for a + // browser or OS shortcut and this must never shadow one. Shift is not + // in the list: it is how a capital D gets typed. + if (event.metaKey || event.ctrlKey || event.altKey) return; + if (event.key.toLowerCase() !== THEME_KEY) return; + // Someone closer to the keystroke already claimed it. + if (event.defaultPrevented) return; + if (isTypingTarget(event.target) || isOverlayOpen()) return; + toggleTheme(); + } + + document.addEventListener("keydown", onKeyDown); + return () => document.removeEventListener("keydown", onKeyDown); + }, [toggleTheme]); return ( // The theme hooks onto rather than a wrapper div so the page From b5c4dafd0755d215a03794296b421a64fa85b5a1 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 19:51:55 +0530 Subject: [PATCH 31/33] Hold the release and give the docs a favicon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pending minor changeset would have published dowel@0.1.0 the moment this branch merged. More refactors are queued before the API is worth freezing, so it goes. Changesets then fails CI on "packages changed, no changeset found" — the whole branch is new relative to main — so an empty changeset takes its place, which is the documented way to say this change needs no release. `changeset status` is green again and dowel stays at 0.0.0. The docs had no favicon. The mark is the header wordmark's pin (a dowel: the small turned pin that joins two pieces) set into an accent tile. The bare pin is right at 18px against the page background, but a favicon is 16px on chrome the site does not control, and there a lone diagonal capsule reads as a faint slash on light chrome and vanishes into dark. The tile brings its own contrast, so the mark holds either way, and the pin knocks out of it at a width that survives 16px. icon.svg is primary and follows the OS scheme via prefers-color-scheme, tracking dowel-accent under both. favicon.ico (16 and 32) and apple-touch-icon.png cover what will not take an SVG. They are declared on the root route, so all eleven prerendered pages carry them. Co-Authored-By: Claude Opus 5 (1M context) --- .changeset/first-release-minor.md | 5 ---- .changeset/no-release-yet.md | 2 ++ apps/docs/public/apple-touch-icon.png | Bin 0 -> 1125 bytes apps/docs/public/favicon.ico | Bin 0 -> 645 bytes apps/docs/public/icon.svg | 39 ++++++++++++++++++++++++++ apps/docs/src/routes/__root.tsx | 11 ++++++++ 6 files changed, 52 insertions(+), 5 deletions(-) delete mode 100644 .changeset/first-release-minor.md create mode 100644 .changeset/no-release-yet.md create mode 100644 apps/docs/public/apple-touch-icon.png create mode 100644 apps/docs/public/favicon.ico create mode 100644 apps/docs/public/icon.svg diff --git a/.changeset/first-release-minor.md b/.changeset/first-release-minor.md deleted file mode 100644 index 9cb180f..0000000 --- a/.changeset/first-release-minor.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"dowel": minor ---- - -First release: token system, build pipeline, and eight components. diff --git a/.changeset/no-release-yet.md b/.changeset/no-release-yet.md new file mode 100644 index 0000000..a845151 --- /dev/null +++ b/.changeset/no-release-yet.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/apps/docs/public/apple-touch-icon.png b/apps/docs/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e36a55d1e30b4cbaeebaadf15fbb4b0758acbb8e GIT binary patch literal 1125 zcmeAS@N?(olHy`uVBq!ia0vp^TR@nD4M^IaWiw)6VA1h(aSW-r_4ck|-W3OthKDiD z!UfEJ2Y8E^{3b|WP!3Yin!*;Snz^8BTa%Wj^R67$xw}i2c&C4fadDDv5?}v#ruqHJ z|CHXox_|TJ`;B|Qzj-@b?!=*a{ERJZ9J~Tj3MLTJA*11ff(eLZSjgzq3?`XDN}RwX zn!*yjdHd_Hzn?F|FB3P1UuMm@w*B!kwF>MDPd~pp{rumTzjnRPtN;3c*6rQ9>%M+I zu26ZxU(88-v(WKZ)^3lEtL@_m%X8BATC;BBrFTVr>-I%&o(qdRFXu1yfAi7P?Sz=+nJf(ET=UpDPPQ!cu&t*+dX>9jm* zDNvpL4zKHbre9mex!3Ve$#ol`;?*|tKnto?yEk41aWBW%URlQZ_qz4L`L6`( zt}o-9`;yTF$ljVM0J6mkWSkR_VTH`dz6@58JL|%&EoQzMxl%1aYu5S#U2<(1&}OK# zkTB4MEoML~qh_PYqo{)$12YbxEpI1CGo~TZc_6DWtbEya8{~2)XH+fbcR)&)XMywt zgWdPNc-KBPlcKFJZ-s-LyD|(!N>^t(DTmHp_j8iP!mTeu!uf%k^3SAR7nG{JRdV)s z22cq<-@=Kje1)Y}0{xF*Ec3lyV+VA6$*RvXKr76jC6u~lJjuLldY{uTBllPT%D;>z zK=I3SwkU5goBQrvKG3A^o9l~wGM;2yj-0o^B_p~&arMNflI1OWEgAd0=hlAHFjz9d zYWA6yyogsfwx`!L3M}Nl8+`s}f6^V}8DHnNw&?Asdt>$TsMz9X#ZoPPMemgT_WRzA z17?uw$vUT^HYS($ZWc@CGiyEa(KX|#?EMzM4MoSN?ww#0c3)Jnok-V3w=><8VP$f^E&7qM!Cg3_Ckim>vGP zm$7o}(3#`EhfyJBd%k`bV|d&?m9q>2Y?)4M3eKKJ3{P@W7(R)esCJQVkXx$2z?CqG zhrv}*Lu+EJ<0M7}9xtCCi>5Tr@L?(9H2Z&el2Pi+&P#827$R1s$X~GvzYTOcgQu&X z%Q~loCM^C`07@%>{aIX9^#vF#b`XD_ovz4t$U(sMxs-*B`2&_a4ct?N3z%&jr!4T` zb!21{S;^GdxTHy|pfr@VJ^Gulhg4A6?$q|D>hX?N>o-rnFS}??R&!nKt?7MBT2tN6 zr(a*`@rm8vDp})C-jxqsUET7xPj9y^W|&~x_qbB;(8O9T>w>chI=dc5ZFz3Iy4f@J zpM=aerS(s$1lPVi!qp + dowel + + + + + diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index d79acda..aea3fbf 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -70,6 +70,17 @@ export const Route = createRootRoute({ "dowel is an opinionated React component library. One look, well made.", }, ], + // Declared on the root route so every prerendered page carries them. + // The order is the one browsers resolve best from: the ICO first for the + // ones that only read ICO, then the SVG, which anything modern prefers + // and which is the only file that follows the OS colour scheme. The + // assets themselves are static, so they live in public/ and are served + // from the site root. See public/icon.svg for the mark. + links: [ + { rel: "icon", href: "/favicon.ico", sizes: "32x32" }, + { rel: "icon", href: "/icon.svg", type: "image/svg+xml" }, + { rel: "apple-touch-icon", href: "/apple-touch-icon.png" }, + ], }), component: RootDocument, }); From 84c0bd0e145c23c59f78a725c8a55d89a3140152 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 20:00:32 +0530 Subject: [PATCH 32/33] Record the repo house rules in CLAUDE.md The notes existed in the worktree but were never tracked, so nothing in a fresh checkout carried them. The dash rule in particular has to live in the repo to be worth anything. Co-Authored-By: Claude Opus 5 (1M context) --- CLAUDE.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..c9074c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# dowel - agent notes + +## Writing style + +- **Never use em dashes (`—`) or en dashes (`–`) as punctuation.** Not in code + comments, commit messages, PR descriptions, docs prose, README copy, or + replies to the user. Rewrite the sentence, or use a comma, colon, + parenthesis, or full stop instead. +- A hyphen inside a compound word (`build-time`, `zero-runtime`) is fine. The + rule is about dashes standing in for punctuation. +- This applies to text you generate anywhere in this repo and to anything you + say about it. + +## Workflow + +- `main` is protected. Never commit to or merge into `main` locally. +- All work happens on a branch and lands via a pull request, even for one-line + fixes. Push the branch, open the PR with `gh pr create`, hand back the URL. +- Package manager is pnpm. Never `npm` or `npx`. Node and pnpm versions are + pinned in `mise.toml`; CI installs them via `jdx/mise-action`. +- TypeScript is pinned to 5.9.x. Do not move to 7.x (the Go port) without a + deliberate decision: it generates the `.d.ts` every consumer depends on. + +## Library rules (packages/dowel) + +- **No override API.** `className` and `style` are omitted from every public + prop type and neutralised at runtime. Do not add them back. If someone needs + a different button, dowel is the wrong library. That is the point. +- Spread `{...props}` FIRST, then `className`, `style={undefined}`, `data-*`. + Spreading last lets a consumer spread strip the class and it typechecks + clean, because JSX spreads skip excess-property checks. +- Variants are `data-*` attributes, never props that map to class names. +- All custom properties are prefixed `--dowel-`, all classes `dowel-`. +- Only `border`, `background-color`, `color`, `opacity` may transition. Never + `all`, never transform or size on hover. +- Hairlines are `0.5px`. Controls are 28px. Base font weight is 450, UI labels + 500, workhorse size 13px. +- Hover rules need `:hover:not(:disabled):not([aria-disabled="true"])`, since + `:not(:disabled)` is true for an anchor. +- No Tailwind, no CSS-in-JS, no class-name helper (`cx`/`clsx`). Hand-authored + plain CSS, bundled by Lightning CSS. +- Component `@import`s go in the import block at the TOP of `src/index.css`. + Lightning CSS errors on a late `@import`. + +## Testing + +- jsdom cannot verify styling. It ignores every rule inside `@layer` and never + substitutes `var()`. Do not write assertions about computed colour, geometry + or hover: they cannot fail. Verify CSS against the built `dist/dowel.css`. +- Vitest intercepts `console`. Grepping the run log cannot observe + `console.error`. Use `vi.spyOn(console, "error")`. +- Base UI overlays open asynchronously. Use `findByRole`/`waitFor`, never a + synchronous `getByRole` after a click: the synchronous form does not just + fail, it can make the whole test pass vacuously. +- `pnpm test` requires a build first (the CSS contract test asserts against + `dist/`). A `pretest` script handles this locally; CI builds before testing. + +## Release + +- changesets. A pending changeset in `.changeset/` triggers an npm publish on + merge to main. If a change should not release, add an empty changeset. +- Secrets `NPM_TOKEN` and `CLOUDFLARE_API_TOKEN` live at the **karnstack org** + level with visibility ALL. Never create repo-level copies: a repo secret + shadows the org one and silently breaks rotations. From c512d80a1878bdc997d5f8104a4b85507290c880 Mon Sep 17 00:00:00 2001 From: Karn Date: Sun, 9 Aug 2026 20:00:43 +0530 Subject: [PATCH 33/33] Read the docs version badge from the package The badge was hand-typed as v0.1.0 in two places while the package sits at 0.0.0 with nothing published, so the site advertised a release that does not exist. Hardcoding it also guarantees the same drift on the next bump. vite.config.ts now imports packages/dowel/package.json by relative path and bakes the version in with `define`. The relative path is required: dowel's exports map does not expose ./package.json, so the bare specifier cannot resolve. A `define` is a literal substitution, which is what keeps it working under static prerendering, where there is no runtime to read a file. src/lib/version.ts turns that into the copy both call sites render. While the manifest holds the unpublished 0.0.0 placeholder the badge reads "unreleased", because a version number nobody can install is worse than no number. It is a condition on the value, not a second string, so the first real publish flips it to v with no docs change. Co-Authored-By: Claude Opus 5 (1M context) --- apps/docs/src/lib/version.ts | 27 +++++++++++++++++++++++++++ apps/docs/src/routes/__root.tsx | 3 ++- apps/docs/src/routes/index.tsx | 3 ++- apps/docs/src/vite-env.d.ts | 6 ++++++ apps/docs/vite.config.ts | 13 +++++++++++++ 5 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 apps/docs/src/lib/version.ts create mode 100644 apps/docs/src/vite-env.d.ts diff --git a/apps/docs/src/lib/version.ts b/apps/docs/src/lib/version.ts new file mode 100644 index 0000000..a6ad8b8 --- /dev/null +++ b/apps/docs/src/lib/version.ts @@ -0,0 +1,27 @@ +/** + * What the docs are allowed to say about dowel's version. + * + * The number itself is read from packages/dowel/package.json at build time + * (see the `define` in vite.config.ts), so the badge cannot drift from the + * package the way a hand-typed one did. + */ + +/** + * changesets has not assigned a version yet, so the manifest still carries + * the placeholder every unpublished package starts on. Nothing is on npm at + * this point, which makes "v0.0.0" a number no reader can install. + */ +const UNPUBLISHED = "0.0.0"; + +/** The raw version from the library manifest, for example `0.2.0`. */ +export const dowelVersion = __DOWEL_VERSION__; + +/** True once a real version exists, meaning the first publish has happened. */ +export const isPublished = dowelVersion !== UNPUBLISHED; + +/** + * The badge copy. "unreleased" while the placeholder is in place, `v` plus + * the number afterwards. It is a condition on the value rather than a second + * string to edit, so the first release flips it with no docs change. + */ +export const versionLabel = isPublished ? `v${dowelVersion}` : "unreleased"; diff --git a/apps/docs/src/routes/__root.tsx b/apps/docs/src/routes/__root.tsx index aea3fbf..2504311 100644 --- a/apps/docs/src/routes/__root.tsx +++ b/apps/docs/src/routes/__root.tsx @@ -16,6 +16,7 @@ import { SunIcon, } from "../components/icons"; import { SidebarNav } from "../components/sidebar-nav"; +import { versionLabel } from "../lib/version"; // The docs self-host Inter; dowel itself ships no typeface, it only names // "Inter Variable" first in --dowel-font. @@ -236,7 +237,7 @@ function RootDocument() { - v0.1.0 + {versionLabel}