From 786a283f3165d19af1be75d69dad5b3ae2535d4f Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 05:17:28 +0200 Subject: [PATCH 1/5] docs: specify turbo component library migration --- ...26-06-20-turbo-component-library-design.md | 186 ++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 docs/superpowers/specs/2026-06-20-turbo-component-library-design.md diff --git a/docs/superpowers/specs/2026-06-20-turbo-component-library-design.md b/docs/superpowers/specs/2026-06-20-turbo-component-library-design.md new file mode 100644 index 0000000..ba0fefc --- /dev/null +++ b/docs/superpowers/specs/2026-06-20-turbo-component-library-design.md @@ -0,0 +1,186 @@ +# Turbo Component Library Migration Design + +## Context + +The current Dimension Lab website is a single Bun/Vite React package. It owns +the browser app, Bun production server, dashboard model, persistence, datasource +adapters, reusable UI components, Storybook, Playwright checks, and container +deployment from one `package.json`. + +The UI components are already mostly generic and content-free under +`src/lib/ui`, but they are not reusable by another project because they live +inside the app package, depend on app path aliases, and share the app build, +test, and Storybook configuration. One file in that area, +`src/lib/ui/model-renderer.ts`, imports the Dimension Lab dashboard model and +therefore is an app adapter, not reusable component-library code. + +The migration goal is to turn the repository into a Turborepo workspace where +the Dimension Lab website consumes a separate reusable React component package. + +## Target Repository Shape + +Use one application workspace and one component-library workspace: + +```text +. +├── apps/ +│ └── web/ +│ ├── src/ +│ ├── tests/ +│ ├── drizzle/ +│ ├── Containerfile +│ └── package.json +├── packages/ +│ └── ui/ +│ ├── src/ +│ ├── .storybook/ +│ ├── package.json +│ └── tsconfig.json +├── package.json +├── turbo.json +├── tsconfig.base.json +└── bun.lock +``` + +The root package is private and contains only workspace orchestration: +workspaces, Turbo scripts, shared dev dependencies where useful, and the lock +file. Runtime dependencies belong to the workspace that imports them. + +## Package Ownership + +`packages/ui` is a compiled React library named `@dimensionlab/ui`. + +It owns: + +- Reusable React components currently under `src/lib/ui/components`. +- UI CSS tokens and component styles. +- Theme helpers currently under `src/lib/ui/theme.ts`. +- Generic UI prop/data types currently under `src/lib/ui/types.ts`. +- Generic formatting helpers currently under `src/lib/ui/format.ts`. +- Generic Storybook stories and story fixtures. +- UI render tests, boundary tests, and Storybook inventory tests. + +It must not import from the website app, the dashboard model, server modules, +database modules, datasource modules, or deployment files. + +The library publishes explicit package exports: + +- `@dimensionlab/ui` for component and type exports. +- `@dimensionlab/ui/styles.css` for the combined token/component CSS entry. +- Optional explicit subpath exports for future direct imports where useful. + +The compiled output goes to `packages/ui/dist` and includes JavaScript, +declaration files, and copied CSS. The package stays private for now but is +structured so it can later be published or moved into another Dimension Lab repo +without taking the website runtime with it. + +`apps/web` owns: + +- The Vite React browser app. +- The Bun production server and API routes. +- Dashboard model, fixtures, schema, validation, and model migrations. +- Drizzle/SQLite persistence and checked-in SQL migrations. +- Datasource adapters and runtime dashboard loading. +- Agent dashboard configuration endpoint. +- The `dashboardDocumentToUiDashboard` adapter that maps the app model to UI + package props. +- Playwright e2e tests and deployment container. + +## Why A Compiled Package + +The component package should be compiled rather than a just-in-time source +package. This is slightly more setup, but it better fits reuse outside the +current app because consumers can import stable JavaScript and declarations +instead of relying on their bundler to transpile this repo's TypeScript source. +It also gives Turbo a cacheable `@dimensionlab/ui#build` task. + +## Build And Task Graph + +Root scripts delegate through Turbo: + +- `bun run dev` runs the web dev server and any required dependency tasks. +- `bun run build` runs package builds in dependency order. +- `bun run check` runs TypeScript checks for all workspaces. +- `bun run test:unit` runs Vitest unit tests for all workspaces. +- `bun run build-storybook` builds Storybook from `packages/ui`. +- `bun run test:e2e` runs the web app Playwright suite. +- `bun run test:qa` is the full release gate. + +`turbo.json` defines `build`, `check`, `test:unit`, `build-storybook`, +`test:e2e`, and `test:qa` tasks. Build outputs include `dist/**`, +`storybook-static/**`, and `build/**` as appropriate. + +The web app depends on `@dimensionlab/ui` using Bun workspace syntax. The web +Vite config aliases `$lib` to `apps/web/src/lib`; the UI package should not use +that app alias. + +## Storybook + +Storybook moves with the component package. It should load +`@dimensionlab/ui/styles.css`, use React Vite Storybook, and keep the existing +generic story inventory. Environment-specific Dimension Lab labels, hostnames, +links, fallback values, and datasource names remain forbidden in package UI +source and stories. + +The repository should no longer have a root Storybook tied to the web app. + +## Deployment + +The deployed website remains the same service from the outside: + +- Production command remains `bun build/index.js` inside the runtime image. +- The container still exposes port `3000` and mounts `/data`. +- Runtime environment variables and database behavior remain unchanged. + +The `Containerfile` moves to `apps/web/Containerfile` or remains root with +updated workspace-aware copy/build steps. The chosen layout must preserve the +existing Podman service contract used by `dimensionlab-website.service`. + +## Testing Strategy + +The migration must add or update tests that prove the new boundaries: + +- The root package is a Bun workspace with `apps/*` and `packages/*`. +- The web app imports UI from `@dimensionlab/ui`, not from local copied + component files. +- `packages/ui` does not import from `apps/web`, `$lib/server`, `$lib/model`, + or any website runtime module. +- `dashboardDocumentToUiDashboard` lives in `apps/web` and is tested there. +- Storybook inventory is evaluated against `packages/ui`. +- The full QA gate still covers typecheck, unit tests, app build, Storybook + build, and Playwright desktop/mobile checks. + +## Completion Criteria + +The migration is complete only when current evidence proves all of these: + +- Root `package.json` is a private workspace root with Turbo scripts. +- `turbo.json` exists and models the workspace task graph. +- The web application lives under `apps/web`. +- The reusable React component library lives under `packages/ui`. +- `packages/ui/package.json` is named `@dimensionlab/ui` and has compiled + exports for code, types, and CSS. +- The web app depends on `@dimensionlab/ui` through the workspace. +- Reusable components and Storybook have been removed from the web app package. +- App-specific model/server/database/datasource code has not moved into + `packages/ui`. +- `dashboardDocumentToUiDashboard` is outside the UI package. +- `bun run check` passes from the root. +- `bun run test:unit` passes from the root. +- `bun run build` passes from the root. +- `bun run build-storybook` passes from the root. +- `bun run test:e2e` passes from the root. +- The production container can still be built and run with the same service + contract. + +## Migration Approach + +Implement this on branch `codex/turbo-component-library` in focused commits: + +1. Add the workspace/Turbo scaffolding and boundary tests. +2. Move UI code and Storybook to `packages/ui`. +3. Move the app runtime into `apps/web` and wire it to `@dimensionlab/ui`. +4. Move the model-to-UI adapter into the web app. +5. Update build, test, Playwright, Storybook, README, and container paths. +6. Run the full QA gate, push a ready PR, perform independent review, resolve + blockers, merge to `main`, and deploy only when checks and review are clean. From 4eadd21f7153790b6c177722614405d12f7681c6 Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 05:20:31 +0200 Subject: [PATCH 2/5] docs: plan turbo component library migration --- .../2026-06-20-turbo-component-library.md | 436 ++++++++++++++++++ 1 file changed, 436 insertions(+) create mode 100644 docs/superpowers/plans/2026-06-20-turbo-component-library.md diff --git a/docs/superpowers/plans/2026-06-20-turbo-component-library.md b/docs/superpowers/plans/2026-06-20-turbo-component-library.md new file mode 100644 index 0000000..e53586c --- /dev/null +++ b/docs/superpowers/plans/2026-06-20-turbo-component-library.md @@ -0,0 +1,436 @@ +# Turbo Component Library Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Convert the single-package Dimension Lab website into a Turborepo workspace where `apps/web` consumes a compiled reusable React component library from `packages/ui`. + +**Architecture:** The root becomes a private Bun workspace with Turbo orchestration. `packages/ui` owns generic reusable UI components, CSS, theme helpers, Storybook, and package-level tests. `apps/web` owns the website runtime, model/server/database logic, the model-to-UI adapter, e2e tests, and deployment container. + +**Tech Stack:** Bun workspaces, Turborepo, Vite, React 19, TypeScript, Storybook React Vite, Tailwind CSS v4, shadcn CSS, Vitest, Playwright, Drizzle ORM, Bun SQLite. + +--- + +## File Structure + +- Create root `turbo.json`: cacheable task graph for `build`, `check`, `test:unit`, `build-storybook`, `test:e2e`, and `test:qa`. +- Create root `tsconfig.base.json`: shared strict TypeScript defaults. +- Modify root `package.json`: private workspace root with `apps/*` and `packages/*`, Turbo scripts, and `turbo` dev dependency only. +- Create `packages/ui/package.json`: compiled `@dimensionlab/ui` package with code and CSS exports. +- Create `packages/ui/tsconfig.json` and `packages/ui/tsconfig.build.json`: package typecheck and declaration/JS build config. +- Create `packages/ui/src/styles.css`: library style entry importing font, uPlot CSS, tokens, and component CSS. +- Move `src/lib/ui/components/**` to `packages/ui/src/components/**`. +- Move `src/lib/ui/stories/**` to `packages/ui/src/stories/**`. +- Move `src/lib/ui/tokens.css` to `packages/ui/src/tokens.css`. +- Move `src/lib/ui/theme.ts`, `types.ts`, `format.ts`, `fixtures.ts`, and `index.ts` to `packages/ui/src/**`. +- Move `src/lib/ui/components/styles.css` to `packages/ui/src/components/styles.css`. +- Move `.storybook/**` to `packages/ui/.storybook/**`. +- Move unused shadcn primitives from `src/lib/components/ui/**` to `packages/ui/src/primitives/**` and update their `cn` import to package-local `src/utils.ts`. +- Move `src/lib/utils.ts` to `packages/ui/src/utils.ts`. +- Move app runtime files into `apps/web`: `src/App.tsx`, `src/main.tsx`, `src/app.css`, `src/server/**`, `src/lib/model/**`, `src/lib/server/**`, `src/lib/testing/**`, `src/vite-env.d.ts`, `src/page.test.tsx`, `src/server/dev.ts`, `tests/**`, `drizzle/**`, `Containerfile`, `index.html`, `playwright.config.ts`, `vite.config.ts`, `drizzle.config.ts`, and app-specific README/deployment files. +- Move `src/lib/ui/model-renderer.ts` and `model-renderer.test.ts` into `apps/web/src/lib/ui-adapter/**`. +- Create `apps/web/package.json`, `apps/web/tsconfig.json`, `apps/web/vite.config.ts`, and `apps/web/playwright.config.ts`. +- Update `apps/web/src/App.tsx` to import components/types from `@dimensionlab/ui` and import the adapter from `$lib/ui-adapter/model-renderer`. +- Update `apps/web/src/app.css` to import `@dimensionlab/ui/styles.css` instead of local UI CSS files. +- Update tests that read paths so package boundary tests inspect `packages/ui` and app tests inspect `apps/web`. +- Update `README.md` to describe the workspace commands, package boundaries, Storybook location, and deployment path. + +## Task 1: Workspace And Boundary Tests + +**Files:** +- Modify: `package.json` +- Create: `turbo.json` +- Create: `tsconfig.base.json` +- Create: `packages/ui/package.json` +- Create: `packages/ui/tsconfig.json` +- Create: `packages/ui/tsconfig.build.json` +- Create: `apps/web/package.json` +- Create: `apps/web/tsconfig.json` +- Create: `apps/web/src/lib/workspace-boundary.test.ts` + +- [ ] **Step 1: Write the failing workspace boundary test** + +Create `apps/web/src/lib/workspace-boundary.test.ts`: + +```ts +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, test } from "vitest"; + +const root = join(import.meta.dir, "../../../.."); + +describe("workspace boundaries", () => { + test("declares the root as a turbo-managed bun workspace", () => { + const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { + private?: boolean; + scripts?: Record; + workspaces?: string[]; + }; + + expect(packageJson.private).toBe(true); + expect(packageJson.workspaces).toEqual(["apps/*", "packages/*"]); + expect(packageJson.scripts?.build).toBe("turbo build"); + expect(existsSync(join(root, "turbo.json"))).toBe(true); + }); + + test("keeps the website app and reusable UI library as separate packages", () => { + const webPackage = JSON.parse( + readFileSync(join(root, "apps/web/package.json"), "utf8"), + ) as { dependencies?: Record; name?: string }; + const uiPackage = JSON.parse( + readFileSync(join(root, "packages/ui/package.json"), "utf8"), + ) as { exports?: Record; name?: string }; + + expect(webPackage.name).toBe("@dimensionlab/web"); + expect(webPackage.dependencies?.["@dimensionlab/ui"]).toBe("workspace:*"); + expect(uiPackage.name).toBe("@dimensionlab/ui"); + expect(uiPackage.exports).toHaveProperty("."); + expect(uiPackage.exports).toHaveProperty("./styles.css"); + }); +}); +``` + +- [ ] **Step 2: Run the test to verify it fails** + +Run: + +```sh +bunx vitest run apps/web/src/lib/workspace-boundary.test.ts +``` + +Expected: FAIL because `apps/web`, `packages/ui`, and `turbo.json` do not exist. + +- [ ] **Step 3: Add minimal workspace manifests** + +Create root `package.json` as the workspace orchestrator: + +```json +{ + "name": "dimensionlab", + "version": "0.0.1", + "private": true, + "type": "module", + "packageManager": "bun@1.3.14", + "workspaces": ["apps/*", "packages/*"], + "scripts": { + "dev": "turbo dev --filter=@dimensionlab/web", + "build": "turbo build", + "preview": "bun --cwd apps/web run preview", + "storybook": "turbo storybook --filter=@dimensionlab/ui", + "build-storybook": "turbo build-storybook --filter=@dimensionlab/ui", + "check": "turbo check", + "test": "turbo test:unit", + "test:unit": "turbo test:unit", + "test:e2e": "turbo test:e2e --filter=@dimensionlab/web", + "test:qa": "turbo test:qa", + "db:generate": "bun --cwd apps/web run db:generate", + "db:check": "bun --cwd apps/web run db:check" + }, + "devDependencies": { + "turbo": "^2.5.0" + } +} +``` + +Create `turbo.json`: + +```json +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "build/**"] + }, + "check": { + "dependsOn": ["^build"], + "outputs": [] + }, + "test:unit": { + "dependsOn": ["^build"], + "outputs": [] + }, + "build-storybook": { + "dependsOn": ["^build"], + "outputs": ["storybook-static/**"] + }, + "test:e2e": { + "dependsOn": ["build", "^build"], + "outputs": ["test-results/**", "playwright-report/**"] + }, + "test:qa": { + "dependsOn": ["check", "test:unit", "build", "build-storybook", "test:e2e"], + "outputs": [] + }, + "dev": { + "cache": false, + "persistent": true + }, + "storybook": { + "cache": false, + "persistent": true + } + } +} +``` + +Create minimal `packages/ui/package.json` with `@dimensionlab/ui` exports and +minimal `apps/web/package.json` with `@dimensionlab/ui` as a workspace +dependency. Move the full dependency lists in Task 2 and Task 3. + +- [ ] **Step 4: Run the boundary test** + +Run: + +```sh +bunx vitest run apps/web/src/lib/workspace-boundary.test.ts +``` + +Expected: PASS. + +- [ ] **Step 5: Commit** + +```sh +git add package.json turbo.json tsconfig.base.json apps/web/package.json apps/web/tsconfig.json apps/web/src/lib/workspace-boundary.test.ts packages/ui/package.json packages/ui/tsconfig.json packages/ui/tsconfig.build.json +git commit -m "build: add turbo workspace manifests" +``` + +## Task 2: Extract The UI Package + +**Files:** +- Move: `src/lib/ui/components/**` to `packages/ui/src/components/**` +- Move: `src/lib/ui/stories/**` to `packages/ui/src/stories/**` +- Move: `src/lib/ui/{index.ts,types.ts,theme.ts,format.ts,fixtures.ts,tokens.css}` to `packages/ui/src/**` +- Move: `.storybook/**` to `packages/ui/.storybook/**` +- Move: `src/lib/components/ui/**` to `packages/ui/src/primitives/**` +- Move: `src/lib/utils.ts` to `packages/ui/src/utils.ts` +- Create: `packages/ui/src/styles.css` +- Modify: `packages/ui/src/index.ts` +- Modify: `packages/ui/src/storybook.test.ts` +- Modify: `packages/ui/src/content-boundary.test.ts` + +- [ ] **Step 1: Write the failing UI isolation assertion** + +Update the package boundary tests to read from `packages/ui/src` and assert no +imports from `apps/web`, `$lib/server`, or `$lib/model` exist: + +```ts +expect(source).not.toMatch(/from ["'](?:apps\/web|\$lib\/server|\$lib\/model)/); +``` + +Run: + +```sh +bunx vitest run packages/ui/src/content-boundary.test.ts +``` + +Expected: FAIL until the files move and the app-specific adapter is removed. + +- [ ] **Step 2: Move generic UI files** + +Run mechanical moves with `git mv`. Move `model-renderer.ts` out of the UI +package in Task 3 rather than into `packages/ui`. + +- [ ] **Step 3: Add the UI style entry** + +Create `packages/ui/src/styles.css`: + +```css +@import "@fontsource-variable/geist"; +@import "uplot/dist/uPlot.min.css"; +@import "./tokens.css"; +@import "./components/styles.css"; +``` + +- [ ] **Step 4: Make package imports relative or package-local** + +Update moved primitive files to import `cn` from `../utils`. +Remove `dashboardDocumentToUiDashboard` from `packages/ui/src/index.ts`. + +- [ ] **Step 5: Build the package** + +Run: + +```sh +bun --cwd packages/ui run build +bun --cwd packages/ui run check +bun --cwd packages/ui run test:unit +``` + +Expected: PASS and `packages/ui/dist` contains `index.js`, `index.d.ts`, and +CSS files. + +- [ ] **Step 6: Commit** + +```sh +git add packages/ui src/lib/ui src/lib/components src/lib/utils.ts +git commit -m "refactor(ui): extract reusable component package" +``` + +## Task 3: Move The Web App Workspace + +**Files:** +- Move: `src/**` app files that are not reusable UI to `apps/web/src/**` +- Move: `tests/**` to `apps/web/tests/**` +- Move: `drizzle/**` to `apps/web/drizzle/**` +- Move: `Containerfile` to `apps/web/Containerfile` +- Move: `index.html`, `vite.config.ts`, `playwright.config.ts`, `drizzle.config.ts` to `apps/web/**` +- Create: `apps/web/src/lib/ui-adapter/model-renderer.ts` +- Modify: `apps/web/src/App.tsx` +- Modify: `apps/web/src/app.css` + +- [ ] **Step 1: Move the model adapter out of UI** + +Move `src/lib/ui/model-renderer.ts` to +`apps/web/src/lib/ui-adapter/model-renderer.ts` and update imports from +`$lib/model` plus UI types from `@dimensionlab/ui`. + +- [ ] **Step 2: Move app runtime and tests** + +Use `git mv` for app/server/model/test/deployment files into `apps/web`. + +- [ ] **Step 3: Update app imports** + +In `apps/web/src/App.tsx`, import UI components and types from +`@dimensionlab/ui`, and import `dashboardDocumentToUiDashboard` from +`$lib/ui-adapter/model-renderer`. + +In `apps/web/src/app.css`, replace local UI imports with: + +```css +@import "tailwindcss"; +@import "tw-animate-css"; +@import "shadcn/tailwind.css"; +@import "@dimensionlab/ui/styles.css"; +``` + +- [ ] **Step 4: Run app typecheck and unit tests** + +Run: + +```sh +bun --cwd apps/web run check +bun --cwd apps/web run test:unit +``` + +Expected: PASS. + +- [ ] **Step 5: Commit** + +```sh +git add apps/web src tests drizzle Containerfile index.html vite.config.ts playwright.config.ts drizzle.config.ts +git commit -m "refactor(web): move website into app workspace" +``` + +## Task 4: Wire Turbo, Storybook, Playwright, And Container + +**Files:** +- Modify: `apps/web/playwright.config.ts` +- Modify: `apps/web/Containerfile` +- Modify: `apps/web/vite.config.ts` +- Modify: `packages/ui/.storybook/main.ts` +- Modify: `packages/ui/.storybook/preview.ts` +- Modify: `tests/e2e/storybook-server.ts` after move to `apps/web/tests/e2e/storybook-server.ts` +- Modify: `README.md` + +- [ ] **Step 1: Update Playwright commands for workspaces** + +In `apps/web/playwright.config.ts`, make the web server command build from the +workspace root or app directory consistently: + +```ts +command: `DISABLE_LIVE_DATASOURCES=1 bun run build && DISABLE_LIVE_DATASOURCES=1 DATABASE_URL=${databaseUrl} HOST=127.0.0.1 PORT=${port} bun build/index.js` +``` + +This command runs inside `apps/web` when invoked through the app package script. + +- [ ] **Step 2: Update Storybook package paths** + +`packages/ui/.storybook/main.ts` should use `../src/**/*.stories.@(js|ts|tsx)`. +`packages/ui/.storybook/preview.ts` should import `../src/styles.css` and use +generic MSW handlers only if they are moved into the UI package. + +- [ ] **Step 3: Update the container build** + +`apps/web/Containerfile` should build from the repository root context or copy +only the workspace files it needs. Preserve the runtime command: + +```dockerfile +CMD ["bun", "build/index.js"] +``` + +- [ ] **Step 4: Run full root checks** + +Run: + +```sh +bun install +bun run check +bun run test:unit +bun run build +bun run build-storybook +bun run test:e2e +``` + +Expected: PASS. + +- [ ] **Step 5: Commit** + +```sh +git add README.md apps/web packages/ui package.json turbo.json bun.lock +git commit -m "build: wire turbo workspace qa" +``` + +## Task 5: Final QA, PR, Review, Merge, Deploy + +**Files:** +- No planned source edits unless verification finds blockers. + +- [ ] **Step 1: Run release gate** + +Run: + +```sh +bun run test:qa +``` + +Expected: PASS. + +- [ ] **Step 2: Push and open PR** + +Run: + +```sh +git push -u origin codex/turbo-component-library +``` + +Open a ready PR against `main` titled: + +```text +refactor: migrate dashboard to turbo component library +``` + +- [ ] **Step 3: Independent review** + +Send an independent reviewer to inspect the PR diff against the objective: +Turbo repo, `apps/web`, compiled `packages/ui`, reusable components removed +from app, Storybook with UI package, root QA passing, no server behavior change. + +- [ ] **Step 4: Resolve blockers** + +For each blocking review finding, write or update a failing test first, verify +the failure, implement the fix, run targeted checks, commit, push, and re-review. + +- [ ] **Step 5: Merge and deploy** + +When review and checks are clean, merge the PR into `main`, sync the production +checkout, rebuild the Podman image, restart `dimensionlab-website.service`, and +verify `https://dimensionlab.net/` with a browser smoke check. + +## Self-Review + +- Spec coverage: every completion criterion in the design maps to Task 1 through + Task 5. +- Placeholder scan: no task says TBD, TODO, or "add tests" without commands. +- Type consistency: package names are consistently `@dimensionlab/ui` and + `@dimensionlab/web`; the app adapter path is consistently + `$lib/ui-adapter/model-renderer`. From 87261b5a3f01f1aa4c157d2d5b121959df48fae8 Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 05:23:04 +0200 Subject: [PATCH 3/5] build: add turbo workspace manifests --- apps/web/package.json | 49 ++++++++++++++ apps/web/src/lib/workspace-boundary.test.ts | 37 +++++++++++ apps/web/tsconfig.json | 19 ++++++ package.json | 73 ++++++--------------- packages/ui/package.json | 50 ++++++++++++++ packages/ui/tsconfig.build.json | 18 +++++ packages/ui/tsconfig.json | 8 +++ tsconfig.base.json | 18 +++++ turbo.json | 43 ++++++++++++ 9 files changed, 261 insertions(+), 54 deletions(-) create mode 100644 apps/web/package.json create mode 100644 apps/web/src/lib/workspace-boundary.test.ts create mode 100644 apps/web/tsconfig.json create mode 100644 packages/ui/package.json create mode 100644 packages/ui/tsconfig.build.json create mode 100644 packages/ui/tsconfig.json create mode 100644 tsconfig.base.json create mode 100644 turbo.json diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..285a94b --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,49 @@ +{ + "name": "@dimensionlab/web", + "version": "0.0.1", + "private": true, + "type": "module", + "scripts": { + "dev": "vite --host 0.0.0.0", + "build": "vite build && bun build src/server/index.ts --target bun --outdir build", + "preview": "HOST=0.0.0.0 PORT=4173 bun build/index.js", + "check": "tsc --noEmit", + "test": "vitest run", + "test:unit": "vitest run", + "test:e2e": "env -u NO_COLOR playwright test", + "test:qa": "bun run check && bun run test:unit && bun run build && bun run test:e2e", + "db:generate": "drizzle-kit generate", + "db:check": "drizzle-kit check" + }, + "dependencies": { + "@dimensionlab/ui": "workspace:*", + "@sinclair/typebox": "^0.34.49", + "ajv": "^8.20.0", + "ajv-formats": "^3.0.1", + "drizzle-orm": "^0.45.2", + "react": "^19.2.7", + "react-dom": "^19.2.7" + }, + "devDependencies": { + "@axe-core/playwright": "^4.11.3", + "@playwright/test": "^1.61.0", + "@tailwindcss/vite": "^4.3.1", + "@types/bun": "^1.3.14", + "@types/node": "^25.9.3", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.2", + "drizzle-kit": "^0.31.10", + "msw": "^2.14.6", + "shadcn": "^4.11.0", + "tailwindcss": "^4.3.1", + "typescript": "^6.0.3", + "vite": "^8.0.16", + "vitest": "^4.1.9" + }, + "msw": { + "workerDirectory": [ + "static" + ] + } +} diff --git a/apps/web/src/lib/workspace-boundary.test.ts b/apps/web/src/lib/workspace-boundary.test.ts new file mode 100644 index 0000000..21c4020 --- /dev/null +++ b/apps/web/src/lib/workspace-boundary.test.ts @@ -0,0 +1,37 @@ +import { existsSync, readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, test } from "vitest"; + +const root = process.cwd(); + +describe("workspace boundaries", () => { + test("declares the root as a turbo-managed bun workspace", () => { + const packageJson = JSON.parse( + readFileSync(join(root, "package.json"), "utf8"), + ) as { + private?: boolean; + scripts?: Record; + workspaces?: string[]; + }; + + expect(packageJson.private).toBe(true); + expect(packageJson.workspaces).toEqual(["apps/*", "packages/*"]); + expect(packageJson.scripts?.build).toBe("turbo build"); + expect(existsSync(join(root, "turbo.json"))).toBe(true); + }); + + test("keeps the website app and reusable UI library as separate packages", () => { + const webPackage = JSON.parse( + readFileSync(join(root, "apps/web/package.json"), "utf8"), + ) as { dependencies?: Record; name?: string }; + const uiPackage = JSON.parse( + readFileSync(join(root, "packages/ui/package.json"), "utf8"), + ) as { exports?: Record; name?: string }; + + expect(webPackage.name).toBe("@dimensionlab/web"); + expect(webPackage.dependencies?.["@dimensionlab/ui"]).toBe("workspace:*"); + expect(uiPackage.name).toBe("@dimensionlab/ui"); + expect(uiPackage.exports).toHaveProperty("."); + expect(uiPackage.exports).toHaveProperty("./styles.css"); + }); +}); diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json new file mode 100644 index 0000000..120931b --- /dev/null +++ b/apps/web/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": ".", + "paths": { + "$lib/*": ["src/lib/*"] + }, + "types": ["node", "bun-types", "react", "react-dom", "vite/client"] + }, + "include": [ + "src/**/*.ts", + "src/**/*.tsx", + "tests/**/*.ts", + "vite.config.ts", + "playwright.config.ts", + "drizzle.config.ts" + ], + "exclude": ["build", "dist", "node_modules"] +} diff --git a/package.json b/package.json index f1cf4ab..ba20908 100644 --- a/package.json +++ b/package.json @@ -1,63 +1,28 @@ { - "name": "dimensionlab-website", + "name": "dimensionlab", "version": "0.0.1", "private": true, "type": "module", + "packageManager": "bun@1.3.14", + "workspaces": [ + "apps/*", + "packages/*" + ], "scripts": { - "dev": "bun src/server/dev.ts", - "build": "vite build && bun build src/server/index.ts --target bun --outdir build", - "preview": "HOST=0.0.0.0 PORT=4173 bun build/index.js", - "storybook": "storybook dev -p 6006 --host 0.0.0.0", - "build-storybook": "storybook build", - "check": "tsc --noEmit", - "test": "vitest run", - "test:unit": "vitest run", - "test:e2e": "env -u NO_COLOR playwright test", - "test:qa": "bun run check && bun run test:unit && bun run build && bun run build-storybook && bun run test:e2e", - "db:generate": "drizzle-kit generate", - "db:check": "drizzle-kit check" - }, - "dependencies": { - "@fontsource-variable/geist": "^5.2.9", - "@iconify/react": "^6.0.2", - "@sinclair/typebox": "^0.34.49", - "ajv": "^8.20.0", - "ajv-formats": "^3.0.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "drizzle-orm": "^0.45.2", - "lucide-react": "^1.21.0", - "radix-ui": "^1.6.0", - "react": "^19.2.7", - "react-dom": "^19.2.7", - "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0", - "uplot": "^1.6.32" + "dev": "turbo dev --filter=@dimensionlab/web", + "build": "turbo build", + "preview": "bun --cwd apps/web run preview", + "storybook": "turbo storybook --filter=@dimensionlab/ui", + "build-storybook": "turbo build-storybook --filter=@dimensionlab/ui", + "check": "turbo check", + "test": "turbo test:unit", + "test:unit": "turbo test:unit", + "test:e2e": "turbo test:e2e --filter=@dimensionlab/web", + "test:qa": "turbo test:qa", + "db:generate": "bun --cwd apps/web run db:generate", + "db:check": "bun --cwd apps/web run db:check" }, "devDependencies": { - "@axe-core/playwright": "^4.11.3", - "@playwright/test": "^1.61.0", - "@storybook/addon-a11y": "^10.4.6", - "@storybook/addon-vitest": "^10.4.6", - "@storybook/react-vite": "^10.4.6", - "@tailwindcss/vite": "^4.3.1", - "@types/bun": "^1.3.14", - "@types/node": "^25.9.3", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.2", - "drizzle-kit": "^0.31.10", - "msw": "^2.14.6", - "shadcn": "^4.11.0", - "storybook": "^10.4.6", - "tailwindcss": "^4.3.1", - "typescript": "^6.0.3", - "vite": "^8.0.16", - "vitest": "^4.1.9" - }, - "msw": { - "workerDirectory": [ - "static" - ] + "turbo": "^2.5.0" } } diff --git a/packages/ui/package.json b/packages/ui/package.json new file mode 100644 index 0000000..3afa92e --- /dev/null +++ b/packages/ui/package.json @@ -0,0 +1,50 @@ +{ + "name": "@dimensionlab/ui", + "version": "0.0.1", + "private": true, + "type": "module", + "sideEffects": ["*.css", "**/*.css"], + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./styles.css": "./dist/styles.css", + "./tokens.css": "./dist/tokens.css" + }, + "scripts": { + "build": "rm -rf dist && tsc -p tsconfig.build.json && mkdir -p dist/components && cp src/styles.css dist/styles.css && cp src/tokens.css dist/tokens.css && cp src/components/styles.css dist/components/styles.css", + "check": "tsc --noEmit", + "test": "vitest run", + "test:unit": "vitest run", + "storybook": "storybook dev -p 6006 --host 0.0.0.0", + "build-storybook": "storybook build" + }, + "dependencies": { + "@fontsource-variable/geist": "^5.2.9", + "@iconify/react": "^6.0.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.21.0", + "radix-ui": "^1.6.0", + "tailwind-merge": "^3.6.0", + "tw-animate-css": "^1.4.0", + "uplot": "^1.6.32" + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@storybook/addon-a11y": "^10.4.6", + "@storybook/addon-vitest": "^10.4.6", + "@storybook/react-vite": "^10.4.6", + "@types/bun": "^1.3.14", + "@types/node": "^25.9.3", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "typescript": "^6.0.3", + "vite": "^8.0.16", + "vitest": "^4.1.9" + } +} diff --git a/packages/ui/tsconfig.build.json b/packages/ui/tsconfig.build.json new file mode 100644 index 0000000..d942d59 --- /dev/null +++ b/packages/ui/tsconfig.build.json @@ -0,0 +1,18 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "emitDeclarationOnly": false, + "noEmit": false, + "outDir": "dist", + "rootDir": "src" + }, + "exclude": [ + "dist", + "node_modules", + "storybook-static", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/stories/**" + ] +} diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json new file mode 100644 index 0000000..2ad97bc --- /dev/null +++ b/packages/ui/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "baseUrl": "." + }, + "include": ["src/**/*.ts", "src/**/*.tsx", ".storybook/**/*.ts"], + "exclude": ["dist", "node_modules", "storybook-static"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..6e74a60 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "ignoreDeprecations": "6.0", + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "target": "ES2022", + "types": ["node", "bun-types", "react", "react-dom", "vite/client"] + } +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 0000000..ee027a8 --- /dev/null +++ b/turbo.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": ["dist/**", "build/**"] + }, + "check": { + "dependsOn": ["^build"], + "outputs": [] + }, + "test:unit": { + "dependsOn": ["^build"], + "outputs": [] + }, + "build-storybook": { + "dependsOn": ["^build"], + "outputs": ["storybook-static/**"] + }, + "test:e2e": { + "dependsOn": ["build", "^build"], + "outputs": ["test-results/**", "playwright-report/**"] + }, + "test:qa": { + "dependsOn": [ + "check", + "test:unit", + "build", + "build-storybook", + "test:e2e" + ], + "outputs": [] + }, + "dev": { + "cache": false, + "persistent": true + }, + "storybook": { + "cache": false, + "persistent": true + } + } +} From 2664804e91a56705ec8ba099b01f9da2e5809f8d Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 05:27:09 +0200 Subject: [PATCH 4/5] refactor(ui): extract reusable component package --- .../ui/.storybook}/main.ts | 4 - .../ui/.storybook}/preview.ts | 22 ++---- .../ui/src}/components/Badge.tsx | 0 .../ui/src}/components/Button.tsx | 0 .../ui/src}/components/CornerBracketFrame.tsx | 0 .../ui/src}/components/DashboardFrame.tsx | 0 .../ui/src}/components/DashboardHeader.tsx | 0 .../src}/components/DiagonalStripeField.tsx | 0 .../ui/src}/components/FooterCell.tsx | 0 .../ui/src}/components/FooterStatusCell.tsx | 0 .../ui/src}/components/GridFrame.tsx | 0 .../ui/src}/components/IconButton.tsx | 0 .../ui/src}/components/IconGlyph.tsx | 0 .../ui/src}/components/LineChart.tsx | 0 .../ui/src}/components/ModuleCard.tsx | 0 .../ui/src}/components/Panel.tsx | 0 .../ui/src}/components/ProgressMeter.tsx | 0 .../ui/src}/components/ScanlineField.tsx | 0 .../ui/src}/components/Separator.tsx | 0 .../ui/src}/components/ServiceGroupPanel.tsx | 0 .../ui/src}/components/ServicePanel.tsx | 0 .../ui/src}/components/ServiceRow.tsx | 0 .../ui/src}/components/SignalTrace.tsx | 0 .../ui/src}/components/Sparkline.tsx | 0 .../ui/src}/components/StatusBadge.tsx | 0 .../ui/src}/components/StatusStrip.tsx | 0 .../ui/src}/components/SystemState.tsx | 0 .../ui/src}/components/TelemetryCard.tsx | 0 .../ui/src}/components/TelemetryGrid.tsx | 0 .../ui/src}/components/TelemetryStrip.tsx | 0 .../ui/src}/components/ThemeToggle.tsx | 0 .../ui/src}/components/WeatherModule.tsx | 0 .../ui/src}/components/render.test.tsx | 0 .../ui/src}/components/styles.css | 0 packages/ui/src/content-boundary.test.ts | 74 +++++++++++++++++++ {src/lib/ui => packages/ui/src}/fixtures.ts | 0 {src/lib/ui => packages/ui/src}/format.ts | 0 {src/lib/ui => packages/ui/src}/index.ts | 1 - .../ui/src/primitives}/alert.tsx | 2 +- .../ui/src/primitives}/badge.tsx | 2 +- .../ui/src/primitives}/button.tsx | 2 +- .../ui/src/primitives}/card.tsx | 2 +- .../ui/src/primitives}/progress.tsx | 2 +- .../ui/src/primitives}/separator.tsx | 2 +- .../ui/src/primitives}/skeleton.tsx | 2 +- .../ui/src}/stories/Badge.stories.tsx | 0 .../ui/src}/stories/Button.stories.tsx | 0 .../stories/CornerBracketFrame.stories.tsx | 0 .../src}/stories/DashboardFrame.stories.tsx | 0 .../src}/stories/DashboardHeader.stories.tsx | 0 .../stories/DashboardOnePager.stories.tsx | 0 .../stories/DiagonalStripeField.stories.tsx | 0 .../ui/src}/stories/FooterCell.stories.tsx | 0 .../src}/stories/FooterStatusCell.stories.tsx | 0 .../ui/src}/stories/GridFrame.stories.tsx | 0 .../ui/src}/stories/IconButton.stories.tsx | 0 .../ui/src}/stories/IconGlyph.stories.tsx | 0 .../ui/src}/stories/LineChart.stories.tsx | 0 .../ui/src}/stories/ModuleCard.stories.tsx | 0 .../ui/src}/stories/Panel.stories.tsx | 0 .../ui/src}/stories/ProgressMeter.stories.tsx | 0 .../ui/src}/stories/ScanlineField.stories.tsx | 0 .../ui/src}/stories/Separator.stories.tsx | 0 .../stories/ServiceGroupPanel.stories.tsx | 0 .../ui/src}/stories/ServicePanel.stories.tsx | 0 .../ui/src}/stories/ServiceRow.stories.tsx | 0 .../ui/src}/stories/SignalTrace.stories.tsx | 0 .../ui/src}/stories/Sparkline.stories.tsx | 0 .../ui/src}/stories/StatusBadge.stories.tsx | 0 .../ui/src}/stories/StatusStrip.stories.tsx | 0 .../ui/src}/stories/SystemState.stories.tsx | 0 .../ui/src}/stories/TelemetryCard.stories.tsx | 0 .../ui/src}/stories/TelemetryGrid.stories.tsx | 0 .../src}/stories/TelemetryStrip.stories.tsx | 0 .../ui/src}/stories/ThemeToggle.stories.tsx | 0 .../ui/src}/stories/WeatherModule.stories.tsx | 0 .../ui/src}/stories/story-data.ts | 0 .../ui => packages/ui/src}/storybook.test.ts | 18 +++-- packages/ui/src/styles.css | 4 + {src/lib/ui => packages/ui/src}/theme.test.ts | 0 {src/lib/ui => packages/ui/src}/theme.ts | 0 {src/lib/ui => packages/ui/src}/tokens.css | 0 {src/lib/ui => packages/ui/src}/types.ts | 0 {src/lib => packages/ui/src}/utils.ts | 0 packages/ui/tsconfig.build.json | 1 + src/lib/ui/content-boundary.test.ts | 49 ------------ 86 files changed, 102 insertions(+), 85 deletions(-) rename {.storybook => packages/ui/.storybook}/main.ts (83%) rename {.storybook => packages/ui/.storybook}/preview.ts (63%) rename {src/lib/ui => packages/ui/src}/components/Badge.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/Button.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/CornerBracketFrame.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/DashboardFrame.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/DashboardHeader.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/DiagonalStripeField.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/FooterCell.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/FooterStatusCell.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/GridFrame.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/IconButton.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/IconGlyph.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/LineChart.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ModuleCard.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/Panel.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ProgressMeter.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ScanlineField.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/Separator.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ServiceGroupPanel.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ServicePanel.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ServiceRow.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/SignalTrace.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/Sparkline.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/StatusBadge.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/StatusStrip.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/SystemState.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/TelemetryCard.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/TelemetryGrid.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/TelemetryStrip.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/ThemeToggle.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/WeatherModule.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/render.test.tsx (100%) rename {src/lib/ui => packages/ui/src}/components/styles.css (100%) create mode 100644 packages/ui/src/content-boundary.test.ts rename {src/lib/ui => packages/ui/src}/fixtures.ts (100%) rename {src/lib/ui => packages/ui/src}/format.ts (100%) rename {src/lib/ui => packages/ui/src}/index.ts (96%) rename {src/lib/components/ui => packages/ui/src/primitives}/alert.tsx (98%) rename {src/lib/components/ui => packages/ui/src/primitives}/badge.tsx (98%) rename {src/lib/components/ui => packages/ui/src/primitives}/button.tsx (99%) rename {src/lib/components/ui => packages/ui/src/primitives}/card.tsx (98%) rename {src/lib/components/ui => packages/ui/src/primitives}/progress.tsx (95%) rename {src/lib/components/ui => packages/ui/src/primitives}/separator.tsx (95%) rename {src/lib/components/ui => packages/ui/src/primitives}/skeleton.tsx (88%) rename {src/lib/ui => packages/ui/src}/stories/Badge.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/Button.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/CornerBracketFrame.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/DashboardFrame.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/DashboardHeader.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/DashboardOnePager.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/DiagonalStripeField.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/FooterCell.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/FooterStatusCell.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/GridFrame.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/IconButton.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/IconGlyph.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/LineChart.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ModuleCard.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/Panel.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ProgressMeter.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ScanlineField.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/Separator.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ServiceGroupPanel.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ServicePanel.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ServiceRow.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/SignalTrace.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/Sparkline.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/StatusBadge.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/StatusStrip.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/SystemState.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/TelemetryCard.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/TelemetryGrid.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/TelemetryStrip.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/ThemeToggle.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/WeatherModule.stories.tsx (100%) rename {src/lib/ui => packages/ui/src}/stories/story-data.ts (100%) rename {src/lib/ui => packages/ui/src}/storybook.test.ts (86%) create mode 100644 packages/ui/src/styles.css rename {src/lib/ui => packages/ui/src}/theme.test.ts (100%) rename {src/lib/ui => packages/ui/src}/theme.ts (100%) rename {src/lib/ui => packages/ui/src}/tokens.css (100%) rename {src/lib/ui => packages/ui/src}/types.ts (100%) rename {src/lib => packages/ui/src}/utils.ts (100%) delete mode 100644 src/lib/ui/content-boundary.test.ts diff --git a/.storybook/main.ts b/packages/ui/.storybook/main.ts similarity index 83% rename from .storybook/main.ts rename to packages/ui/.storybook/main.ts index cfd3a61..34237be 100644 --- a/.storybook/main.ts +++ b/packages/ui/.storybook/main.ts @@ -2,7 +2,6 @@ import type { StorybookConfig } from "@storybook/react-vite"; const config: StorybookConfig = { stories: ["../src/**/*.stories.@(js|ts|tsx)"], - staticDirs: ["../static"], addons: [ "@storybook/addon-a11y", "@storybook/addon-vitest", @@ -11,9 +10,6 @@ const config: StorybookConfig = { name: "@storybook/react-vite", options: {}, }, - docs: { - autodocs: "tag", - }, }; export default config; diff --git a/.storybook/preview.ts b/packages/ui/.storybook/preview.ts similarity index 63% rename from .storybook/preview.ts rename to packages/ui/.storybook/preview.ts index 964f47b..87df132 100644 --- a/.storybook/preview.ts +++ b/packages/ui/.storybook/preview.ts @@ -1,17 +1,5 @@ -import "../src/app.css"; +import "../src/styles.css"; import type { Preview } from "@storybook/react-vite"; -import { setupWorker } from "msw/browser"; -import { externalApiHandlers } from "../src/lib/testing/external-api-mocks"; - -if (typeof window !== "undefined") { - const worker = setupWorker(...externalApiHandlers); - void worker.start({ - onUnhandledRequest: "bypass", - serviceWorker: { - url: "/mockServiceWorker.js", - }, - }); -} const preview: Preview = { decorators: [ @@ -44,10 +32,10 @@ const preview: Preview = { backgrounds: { default: "canvas", values: [ - { name: "canvas", value: "#020302" }, - { name: "raised", value: "#0b0d0c" }, - { name: "light canvas", value: "#f3f5ed" }, - { name: "light raised", value: "#eef1e7" }, + { name: "canvas", value: "#0b0f0d" }, + { name: "raised", value: "#151d18" }, + { name: "light canvas", value: "#eef2e7" }, + { name: "light raised", value: "#f1f5ea" }, ], }, controls: { diff --git a/src/lib/ui/components/Badge.tsx b/packages/ui/src/components/Badge.tsx similarity index 100% rename from src/lib/ui/components/Badge.tsx rename to packages/ui/src/components/Badge.tsx diff --git a/src/lib/ui/components/Button.tsx b/packages/ui/src/components/Button.tsx similarity index 100% rename from src/lib/ui/components/Button.tsx rename to packages/ui/src/components/Button.tsx diff --git a/src/lib/ui/components/CornerBracketFrame.tsx b/packages/ui/src/components/CornerBracketFrame.tsx similarity index 100% rename from src/lib/ui/components/CornerBracketFrame.tsx rename to packages/ui/src/components/CornerBracketFrame.tsx diff --git a/src/lib/ui/components/DashboardFrame.tsx b/packages/ui/src/components/DashboardFrame.tsx similarity index 100% rename from src/lib/ui/components/DashboardFrame.tsx rename to packages/ui/src/components/DashboardFrame.tsx diff --git a/src/lib/ui/components/DashboardHeader.tsx b/packages/ui/src/components/DashboardHeader.tsx similarity index 100% rename from src/lib/ui/components/DashboardHeader.tsx rename to packages/ui/src/components/DashboardHeader.tsx diff --git a/src/lib/ui/components/DiagonalStripeField.tsx b/packages/ui/src/components/DiagonalStripeField.tsx similarity index 100% rename from src/lib/ui/components/DiagonalStripeField.tsx rename to packages/ui/src/components/DiagonalStripeField.tsx diff --git a/src/lib/ui/components/FooterCell.tsx b/packages/ui/src/components/FooterCell.tsx similarity index 100% rename from src/lib/ui/components/FooterCell.tsx rename to packages/ui/src/components/FooterCell.tsx diff --git a/src/lib/ui/components/FooterStatusCell.tsx b/packages/ui/src/components/FooterStatusCell.tsx similarity index 100% rename from src/lib/ui/components/FooterStatusCell.tsx rename to packages/ui/src/components/FooterStatusCell.tsx diff --git a/src/lib/ui/components/GridFrame.tsx b/packages/ui/src/components/GridFrame.tsx similarity index 100% rename from src/lib/ui/components/GridFrame.tsx rename to packages/ui/src/components/GridFrame.tsx diff --git a/src/lib/ui/components/IconButton.tsx b/packages/ui/src/components/IconButton.tsx similarity index 100% rename from src/lib/ui/components/IconButton.tsx rename to packages/ui/src/components/IconButton.tsx diff --git a/src/lib/ui/components/IconGlyph.tsx b/packages/ui/src/components/IconGlyph.tsx similarity index 100% rename from src/lib/ui/components/IconGlyph.tsx rename to packages/ui/src/components/IconGlyph.tsx diff --git a/src/lib/ui/components/LineChart.tsx b/packages/ui/src/components/LineChart.tsx similarity index 100% rename from src/lib/ui/components/LineChart.tsx rename to packages/ui/src/components/LineChart.tsx diff --git a/src/lib/ui/components/ModuleCard.tsx b/packages/ui/src/components/ModuleCard.tsx similarity index 100% rename from src/lib/ui/components/ModuleCard.tsx rename to packages/ui/src/components/ModuleCard.tsx diff --git a/src/lib/ui/components/Panel.tsx b/packages/ui/src/components/Panel.tsx similarity index 100% rename from src/lib/ui/components/Panel.tsx rename to packages/ui/src/components/Panel.tsx diff --git a/src/lib/ui/components/ProgressMeter.tsx b/packages/ui/src/components/ProgressMeter.tsx similarity index 100% rename from src/lib/ui/components/ProgressMeter.tsx rename to packages/ui/src/components/ProgressMeter.tsx diff --git a/src/lib/ui/components/ScanlineField.tsx b/packages/ui/src/components/ScanlineField.tsx similarity index 100% rename from src/lib/ui/components/ScanlineField.tsx rename to packages/ui/src/components/ScanlineField.tsx diff --git a/src/lib/ui/components/Separator.tsx b/packages/ui/src/components/Separator.tsx similarity index 100% rename from src/lib/ui/components/Separator.tsx rename to packages/ui/src/components/Separator.tsx diff --git a/src/lib/ui/components/ServiceGroupPanel.tsx b/packages/ui/src/components/ServiceGroupPanel.tsx similarity index 100% rename from src/lib/ui/components/ServiceGroupPanel.tsx rename to packages/ui/src/components/ServiceGroupPanel.tsx diff --git a/src/lib/ui/components/ServicePanel.tsx b/packages/ui/src/components/ServicePanel.tsx similarity index 100% rename from src/lib/ui/components/ServicePanel.tsx rename to packages/ui/src/components/ServicePanel.tsx diff --git a/src/lib/ui/components/ServiceRow.tsx b/packages/ui/src/components/ServiceRow.tsx similarity index 100% rename from src/lib/ui/components/ServiceRow.tsx rename to packages/ui/src/components/ServiceRow.tsx diff --git a/src/lib/ui/components/SignalTrace.tsx b/packages/ui/src/components/SignalTrace.tsx similarity index 100% rename from src/lib/ui/components/SignalTrace.tsx rename to packages/ui/src/components/SignalTrace.tsx diff --git a/src/lib/ui/components/Sparkline.tsx b/packages/ui/src/components/Sparkline.tsx similarity index 100% rename from src/lib/ui/components/Sparkline.tsx rename to packages/ui/src/components/Sparkline.tsx diff --git a/src/lib/ui/components/StatusBadge.tsx b/packages/ui/src/components/StatusBadge.tsx similarity index 100% rename from src/lib/ui/components/StatusBadge.tsx rename to packages/ui/src/components/StatusBadge.tsx diff --git a/src/lib/ui/components/StatusStrip.tsx b/packages/ui/src/components/StatusStrip.tsx similarity index 100% rename from src/lib/ui/components/StatusStrip.tsx rename to packages/ui/src/components/StatusStrip.tsx diff --git a/src/lib/ui/components/SystemState.tsx b/packages/ui/src/components/SystemState.tsx similarity index 100% rename from src/lib/ui/components/SystemState.tsx rename to packages/ui/src/components/SystemState.tsx diff --git a/src/lib/ui/components/TelemetryCard.tsx b/packages/ui/src/components/TelemetryCard.tsx similarity index 100% rename from src/lib/ui/components/TelemetryCard.tsx rename to packages/ui/src/components/TelemetryCard.tsx diff --git a/src/lib/ui/components/TelemetryGrid.tsx b/packages/ui/src/components/TelemetryGrid.tsx similarity index 100% rename from src/lib/ui/components/TelemetryGrid.tsx rename to packages/ui/src/components/TelemetryGrid.tsx diff --git a/src/lib/ui/components/TelemetryStrip.tsx b/packages/ui/src/components/TelemetryStrip.tsx similarity index 100% rename from src/lib/ui/components/TelemetryStrip.tsx rename to packages/ui/src/components/TelemetryStrip.tsx diff --git a/src/lib/ui/components/ThemeToggle.tsx b/packages/ui/src/components/ThemeToggle.tsx similarity index 100% rename from src/lib/ui/components/ThemeToggle.tsx rename to packages/ui/src/components/ThemeToggle.tsx diff --git a/src/lib/ui/components/WeatherModule.tsx b/packages/ui/src/components/WeatherModule.tsx similarity index 100% rename from src/lib/ui/components/WeatherModule.tsx rename to packages/ui/src/components/WeatherModule.tsx diff --git a/src/lib/ui/components/render.test.tsx b/packages/ui/src/components/render.test.tsx similarity index 100% rename from src/lib/ui/components/render.test.tsx rename to packages/ui/src/components/render.test.tsx diff --git a/src/lib/ui/components/styles.css b/packages/ui/src/components/styles.css similarity index 100% rename from src/lib/ui/components/styles.css rename to packages/ui/src/components/styles.css diff --git a/packages/ui/src/content-boundary.test.ts b/packages/ui/src/content-boundary.test.ts new file mode 100644 index 0000000..80b85ce --- /dev/null +++ b/packages/ui/src/content-boundary.test.ts @@ -0,0 +1,74 @@ +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, test } from "vitest"; + +const root = process.cwd(); +const uiSourceRoot = existsSync(join(root, "packages/ui/src")) + ? join(root, "packages/ui/src") + : join(root, "src"); + +const forbiddenTerms = [ + "dimensionlab", + "dimension lab", + "vaultwarden", + "forgejo", + "grafana", + "uptime kuma", + "prometheus", + "backrest", + "open webui", + "comfyui", + "adminer", + "cockpit", + "ollama", +]; + +describe("UI package content boundary", () => { + test("contains the reusable dashboard component inventory", () => { + expect(existsSync(join(uiSourceRoot, "index.ts"))).toBe(true); + expect(existsSync(join(uiSourceRoot, "components/DashboardFrame.tsx"))).toBe( + true, + ); + expect(existsSync(join(uiSourceRoot, "components/ThemeToggle.tsx"))).toBe( + true, + ); + expect(existsSync(join(uiSourceRoot, "styles.css"))).toBe(true); + }); + + test("keeps environment-specific content out of reusable UI source", () => { + const source = readUiSource(uiSourceRoot).toLowerCase(); + + expect(forbiddenTerms.filter((term) => source.includes(term))).toEqual([]); + }); + + test("does not import website runtime modules", () => { + const source = readUiSource(uiSourceRoot); + + expect(source).not.toMatch( + /from ["'](?:apps\/web|\$lib\/server|\$lib\/model)/, + ); + expect(source).not.toContain("../web/"); + }); + + test("keeps icon rendering driven by icon identifiers", () => { + const source = readUiSource(uiSourceRoot); + + expect(source).not.toContain("@iconify-json/"); + expect(source).not.toContain("/icons/"); + }); +}); + +function readUiSource(path: string): string { + if (!existsSync(path)) return ""; + + const stats = statSync(path); + if (stats.isFile()) { + if (path.endsWith(".test.ts") || path.endsWith(".test.tsx")) return ""; + if (!/\.(tsx|ts|css)$/.test(path)) return ""; + return readFileSync(path, "utf8"); + } + + return readdirSync(path) + .map((entry) => readUiSource(join(path, entry))) + .join("\n"); +} diff --git a/src/lib/ui/fixtures.ts b/packages/ui/src/fixtures.ts similarity index 100% rename from src/lib/ui/fixtures.ts rename to packages/ui/src/fixtures.ts diff --git a/src/lib/ui/format.ts b/packages/ui/src/format.ts similarity index 100% rename from src/lib/ui/format.ts rename to packages/ui/src/format.ts diff --git a/src/lib/ui/index.ts b/packages/ui/src/index.ts similarity index 96% rename from src/lib/ui/index.ts rename to packages/ui/src/index.ts index 9eb5448..a9a83df 100644 --- a/src/lib/ui/index.ts +++ b/packages/ui/src/index.ts @@ -29,7 +29,6 @@ export { TelemetryStrip } from "./components/TelemetryStrip"; export { ThemeToggle } from "./components/ThemeToggle"; export { WeatherModule } from "./components/WeatherModule"; export { dashboardPreviewFixtures } from "./fixtures"; -export { dashboardDocumentToUiDashboard } from "./model-renderer"; export { getNextUiTheme, isUiTheme, diff --git a/src/lib/components/ui/alert.tsx b/packages/ui/src/primitives/alert.tsx similarity index 98% rename from src/lib/components/ui/alert.tsx rename to packages/ui/src/primitives/alert.tsx index fc34841..d5d1609 100644 --- a/src/lib/components/ui/alert.tsx +++ b/packages/ui/src/primitives/alert.tsx @@ -1,7 +1,7 @@ import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "$lib/utils" +import { cn } from "../utils" const alertVariants = cva( "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4", diff --git a/src/lib/components/ui/badge.tsx b/packages/ui/src/primitives/badge.tsx similarity index 98% rename from src/lib/components/ui/badge.tsx rename to packages/ui/src/primitives/badge.tsx index a3d91d7..baa1535 100644 --- a/src/lib/components/ui/badge.tsx +++ b/packages/ui/src/primitives/badge.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { Slot } from "radix-ui" -import { cn } from "$lib/utils" +import { cn } from "../utils" const badgeVariants = cva( "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", diff --git a/src/lib/components/ui/button.tsx b/packages/ui/src/primitives/button.tsx similarity index 99% rename from src/lib/components/ui/button.tsx rename to packages/ui/src/primitives/button.tsx index 8dda5f2..e37f9b0 100644 --- a/src/lib/components/ui/button.tsx +++ b/packages/ui/src/primitives/button.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { Slot } from "radix-ui" -import { cn } from "$lib/utils" +import { cn } from "../utils" const buttonVariants = cva( "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", diff --git a/src/lib/components/ui/card.tsx b/packages/ui/src/primitives/card.tsx similarity index 98% rename from src/lib/components/ui/card.tsx rename to packages/ui/src/primitives/card.tsx index bcb9e07..e5e5996 100644 --- a/src/lib/components/ui/card.tsx +++ b/packages/ui/src/primitives/card.tsx @@ -1,6 +1,6 @@ import * as React from "react" -import { cn } from "$lib/utils" +import { cn } from "../utils" function Card({ className, diff --git a/src/lib/components/ui/progress.tsx b/packages/ui/src/primitives/progress.tsx similarity index 95% rename from src/lib/components/ui/progress.tsx rename to packages/ui/src/primitives/progress.tsx index 65d0a6b..59fac6c 100644 --- a/src/lib/components/ui/progress.tsx +++ b/packages/ui/src/primitives/progress.tsx @@ -1,7 +1,7 @@ import * as React from "react" import { Progress as ProgressPrimitive } from "radix-ui" -import { cn } from "$lib/utils" +import { cn } from "../utils" function Progress({ className, diff --git a/src/lib/components/ui/separator.tsx b/packages/ui/src/primitives/separator.tsx similarity index 95% rename from src/lib/components/ui/separator.tsx rename to packages/ui/src/primitives/separator.tsx index 84e3c64..ee9f6cb 100644 --- a/src/lib/components/ui/separator.tsx +++ b/packages/ui/src/primitives/separator.tsx @@ -3,7 +3,7 @@ import * as React from "react" import { Separator as SeparatorPrimitive } from "radix-ui" -import { cn } from "$lib/utils" +import { cn } from "../utils" function Separator({ className, diff --git a/src/lib/components/ui/skeleton.tsx b/packages/ui/src/primitives/skeleton.tsx similarity index 88% rename from src/lib/components/ui/skeleton.tsx rename to packages/ui/src/primitives/skeleton.tsx index 61466a8..a1df193 100644 --- a/src/lib/components/ui/skeleton.tsx +++ b/packages/ui/src/primitives/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from "$lib/utils" +import { cn } from "../utils" function Skeleton({ className, ...props }: React.ComponentProps<"div">) { return ( diff --git a/src/lib/ui/stories/Badge.stories.tsx b/packages/ui/src/stories/Badge.stories.tsx similarity index 100% rename from src/lib/ui/stories/Badge.stories.tsx rename to packages/ui/src/stories/Badge.stories.tsx diff --git a/src/lib/ui/stories/Button.stories.tsx b/packages/ui/src/stories/Button.stories.tsx similarity index 100% rename from src/lib/ui/stories/Button.stories.tsx rename to packages/ui/src/stories/Button.stories.tsx diff --git a/src/lib/ui/stories/CornerBracketFrame.stories.tsx b/packages/ui/src/stories/CornerBracketFrame.stories.tsx similarity index 100% rename from src/lib/ui/stories/CornerBracketFrame.stories.tsx rename to packages/ui/src/stories/CornerBracketFrame.stories.tsx diff --git a/src/lib/ui/stories/DashboardFrame.stories.tsx b/packages/ui/src/stories/DashboardFrame.stories.tsx similarity index 100% rename from src/lib/ui/stories/DashboardFrame.stories.tsx rename to packages/ui/src/stories/DashboardFrame.stories.tsx diff --git a/src/lib/ui/stories/DashboardHeader.stories.tsx b/packages/ui/src/stories/DashboardHeader.stories.tsx similarity index 100% rename from src/lib/ui/stories/DashboardHeader.stories.tsx rename to packages/ui/src/stories/DashboardHeader.stories.tsx diff --git a/src/lib/ui/stories/DashboardOnePager.stories.tsx b/packages/ui/src/stories/DashboardOnePager.stories.tsx similarity index 100% rename from src/lib/ui/stories/DashboardOnePager.stories.tsx rename to packages/ui/src/stories/DashboardOnePager.stories.tsx diff --git a/src/lib/ui/stories/DiagonalStripeField.stories.tsx b/packages/ui/src/stories/DiagonalStripeField.stories.tsx similarity index 100% rename from src/lib/ui/stories/DiagonalStripeField.stories.tsx rename to packages/ui/src/stories/DiagonalStripeField.stories.tsx diff --git a/src/lib/ui/stories/FooterCell.stories.tsx b/packages/ui/src/stories/FooterCell.stories.tsx similarity index 100% rename from src/lib/ui/stories/FooterCell.stories.tsx rename to packages/ui/src/stories/FooterCell.stories.tsx diff --git a/src/lib/ui/stories/FooterStatusCell.stories.tsx b/packages/ui/src/stories/FooterStatusCell.stories.tsx similarity index 100% rename from src/lib/ui/stories/FooterStatusCell.stories.tsx rename to packages/ui/src/stories/FooterStatusCell.stories.tsx diff --git a/src/lib/ui/stories/GridFrame.stories.tsx b/packages/ui/src/stories/GridFrame.stories.tsx similarity index 100% rename from src/lib/ui/stories/GridFrame.stories.tsx rename to packages/ui/src/stories/GridFrame.stories.tsx diff --git a/src/lib/ui/stories/IconButton.stories.tsx b/packages/ui/src/stories/IconButton.stories.tsx similarity index 100% rename from src/lib/ui/stories/IconButton.stories.tsx rename to packages/ui/src/stories/IconButton.stories.tsx diff --git a/src/lib/ui/stories/IconGlyph.stories.tsx b/packages/ui/src/stories/IconGlyph.stories.tsx similarity index 100% rename from src/lib/ui/stories/IconGlyph.stories.tsx rename to packages/ui/src/stories/IconGlyph.stories.tsx diff --git a/src/lib/ui/stories/LineChart.stories.tsx b/packages/ui/src/stories/LineChart.stories.tsx similarity index 100% rename from src/lib/ui/stories/LineChart.stories.tsx rename to packages/ui/src/stories/LineChart.stories.tsx diff --git a/src/lib/ui/stories/ModuleCard.stories.tsx b/packages/ui/src/stories/ModuleCard.stories.tsx similarity index 100% rename from src/lib/ui/stories/ModuleCard.stories.tsx rename to packages/ui/src/stories/ModuleCard.stories.tsx diff --git a/src/lib/ui/stories/Panel.stories.tsx b/packages/ui/src/stories/Panel.stories.tsx similarity index 100% rename from src/lib/ui/stories/Panel.stories.tsx rename to packages/ui/src/stories/Panel.stories.tsx diff --git a/src/lib/ui/stories/ProgressMeter.stories.tsx b/packages/ui/src/stories/ProgressMeter.stories.tsx similarity index 100% rename from src/lib/ui/stories/ProgressMeter.stories.tsx rename to packages/ui/src/stories/ProgressMeter.stories.tsx diff --git a/src/lib/ui/stories/ScanlineField.stories.tsx b/packages/ui/src/stories/ScanlineField.stories.tsx similarity index 100% rename from src/lib/ui/stories/ScanlineField.stories.tsx rename to packages/ui/src/stories/ScanlineField.stories.tsx diff --git a/src/lib/ui/stories/Separator.stories.tsx b/packages/ui/src/stories/Separator.stories.tsx similarity index 100% rename from src/lib/ui/stories/Separator.stories.tsx rename to packages/ui/src/stories/Separator.stories.tsx diff --git a/src/lib/ui/stories/ServiceGroupPanel.stories.tsx b/packages/ui/src/stories/ServiceGroupPanel.stories.tsx similarity index 100% rename from src/lib/ui/stories/ServiceGroupPanel.stories.tsx rename to packages/ui/src/stories/ServiceGroupPanel.stories.tsx diff --git a/src/lib/ui/stories/ServicePanel.stories.tsx b/packages/ui/src/stories/ServicePanel.stories.tsx similarity index 100% rename from src/lib/ui/stories/ServicePanel.stories.tsx rename to packages/ui/src/stories/ServicePanel.stories.tsx diff --git a/src/lib/ui/stories/ServiceRow.stories.tsx b/packages/ui/src/stories/ServiceRow.stories.tsx similarity index 100% rename from src/lib/ui/stories/ServiceRow.stories.tsx rename to packages/ui/src/stories/ServiceRow.stories.tsx diff --git a/src/lib/ui/stories/SignalTrace.stories.tsx b/packages/ui/src/stories/SignalTrace.stories.tsx similarity index 100% rename from src/lib/ui/stories/SignalTrace.stories.tsx rename to packages/ui/src/stories/SignalTrace.stories.tsx diff --git a/src/lib/ui/stories/Sparkline.stories.tsx b/packages/ui/src/stories/Sparkline.stories.tsx similarity index 100% rename from src/lib/ui/stories/Sparkline.stories.tsx rename to packages/ui/src/stories/Sparkline.stories.tsx diff --git a/src/lib/ui/stories/StatusBadge.stories.tsx b/packages/ui/src/stories/StatusBadge.stories.tsx similarity index 100% rename from src/lib/ui/stories/StatusBadge.stories.tsx rename to packages/ui/src/stories/StatusBadge.stories.tsx diff --git a/src/lib/ui/stories/StatusStrip.stories.tsx b/packages/ui/src/stories/StatusStrip.stories.tsx similarity index 100% rename from src/lib/ui/stories/StatusStrip.stories.tsx rename to packages/ui/src/stories/StatusStrip.stories.tsx diff --git a/src/lib/ui/stories/SystemState.stories.tsx b/packages/ui/src/stories/SystemState.stories.tsx similarity index 100% rename from src/lib/ui/stories/SystemState.stories.tsx rename to packages/ui/src/stories/SystemState.stories.tsx diff --git a/src/lib/ui/stories/TelemetryCard.stories.tsx b/packages/ui/src/stories/TelemetryCard.stories.tsx similarity index 100% rename from src/lib/ui/stories/TelemetryCard.stories.tsx rename to packages/ui/src/stories/TelemetryCard.stories.tsx diff --git a/src/lib/ui/stories/TelemetryGrid.stories.tsx b/packages/ui/src/stories/TelemetryGrid.stories.tsx similarity index 100% rename from src/lib/ui/stories/TelemetryGrid.stories.tsx rename to packages/ui/src/stories/TelemetryGrid.stories.tsx diff --git a/src/lib/ui/stories/TelemetryStrip.stories.tsx b/packages/ui/src/stories/TelemetryStrip.stories.tsx similarity index 100% rename from src/lib/ui/stories/TelemetryStrip.stories.tsx rename to packages/ui/src/stories/TelemetryStrip.stories.tsx diff --git a/src/lib/ui/stories/ThemeToggle.stories.tsx b/packages/ui/src/stories/ThemeToggle.stories.tsx similarity index 100% rename from src/lib/ui/stories/ThemeToggle.stories.tsx rename to packages/ui/src/stories/ThemeToggle.stories.tsx diff --git a/src/lib/ui/stories/WeatherModule.stories.tsx b/packages/ui/src/stories/WeatherModule.stories.tsx similarity index 100% rename from src/lib/ui/stories/WeatherModule.stories.tsx rename to packages/ui/src/stories/WeatherModule.stories.tsx diff --git a/src/lib/ui/stories/story-data.ts b/packages/ui/src/stories/story-data.ts similarity index 100% rename from src/lib/ui/stories/story-data.ts rename to packages/ui/src/stories/story-data.ts diff --git a/src/lib/ui/storybook.test.ts b/packages/ui/src/storybook.test.ts similarity index 86% rename from src/lib/ui/storybook.test.ts rename to packages/ui/src/storybook.test.ts index aa76cad..4ef14ba 100644 --- a/src/lib/ui/storybook.test.ts +++ b/packages/ui/src/storybook.test.ts @@ -3,8 +3,11 @@ import { join } from "node:path"; import { describe, expect, test } from "vitest"; const root = process.cwd(); -const componentsDir = join(root, "src/lib/ui/components"); -const storiesDir = join(root, "src/lib/ui/stories"); +const packageRoot = existsSync(join(root, "packages/ui/package.json")) + ? join(root, "packages/ui") + : root; +const componentsDir = join(packageRoot, "src/components"); +const storiesDir = join(packageRoot, "src/stories"); const requiredStoryFiles = [ "Badge.stories.tsx", @@ -49,7 +52,7 @@ const forbiddenStoryContent = [ describe("Storybook inventory", () => { test("exposes scripts for local and static Storybook review", () => { - const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as { + const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")) as { scripts?: Record; }; @@ -65,21 +68,22 @@ describe("Storybook inventory", () => { }); test("loads dashboard component styles through the global app stylesheet", () => { - const appStyles = readFileSync(join(root, "src/app.css"), "utf8"); + const packageStyles = readFileSync(join(packageRoot, "src/styles.css"), "utf8"); const dashboardFrame = readFileSync( join(componentsDir, "DashboardFrame.tsx"), "utf8", ); - expect(appStyles).toContain('./lib/ui/components/styles.css'); + expect(packageStyles).toContain('./components/styles.css'); expect(dashboardFrame).not.toContain('./styles.css'); }); test("configures Storybook theme switching for reusable components", () => { - const previewSource = readFileSync(join(root, ".storybook/preview.ts"), "utf8"); + const previewSource = readFileSync(join(packageRoot, ".storybook/preview.ts"), "utf8"); expect(previewSource).toContain("globalTypes"); expect(previewSource).toContain("data-ui-theme"); + expect(previewSource).toContain("../src/styles.css"); }); test("keeps component and story files paired as the UI inventory changes", () => { @@ -135,7 +139,7 @@ describe("Storybook inventory", () => { test("does not add deferred form/navigation primitives", () => { for (const component of ["Input", "ToggleGroup", "ScrollArea"]) { - expect(existsSync(join(root, `src/lib/ui/components/${component}.tsx`))).toBe(false); + expect(existsSync(join(componentsDir, `${component}.tsx`))).toBe(false); expect(existsSync(join(storiesDir, `${component}.stories.tsx`))).toBe(false); } }); diff --git a/packages/ui/src/styles.css b/packages/ui/src/styles.css new file mode 100644 index 0000000..fd6f4a2 --- /dev/null +++ b/packages/ui/src/styles.css @@ -0,0 +1,4 @@ +@import "@fontsource-variable/geist"; +@import "uplot/dist/uPlot.min.css"; +@import "./tokens.css"; +@import "./components/styles.css"; diff --git a/src/lib/ui/theme.test.ts b/packages/ui/src/theme.test.ts similarity index 100% rename from src/lib/ui/theme.test.ts rename to packages/ui/src/theme.test.ts diff --git a/src/lib/ui/theme.ts b/packages/ui/src/theme.ts similarity index 100% rename from src/lib/ui/theme.ts rename to packages/ui/src/theme.ts diff --git a/src/lib/ui/tokens.css b/packages/ui/src/tokens.css similarity index 100% rename from src/lib/ui/tokens.css rename to packages/ui/src/tokens.css diff --git a/src/lib/ui/types.ts b/packages/ui/src/types.ts similarity index 100% rename from src/lib/ui/types.ts rename to packages/ui/src/types.ts diff --git a/src/lib/utils.ts b/packages/ui/src/utils.ts similarity index 100% rename from src/lib/utils.ts rename to packages/ui/src/utils.ts diff --git a/packages/ui/tsconfig.build.json b/packages/ui/tsconfig.build.json index d942d59..bd56366 100644 --- a/packages/ui/tsconfig.build.json +++ b/packages/ui/tsconfig.build.json @@ -7,6 +7,7 @@ "outDir": "dist", "rootDir": "src" }, + "include": ["src/**/*.ts", "src/**/*.tsx"], "exclude": [ "dist", "node_modules", diff --git a/src/lib/ui/content-boundary.test.ts b/src/lib/ui/content-boundary.test.ts deleted file mode 100644 index 6613644..0000000 --- a/src/lib/ui/content-boundary.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { readdirSync, readFileSync, statSync } from "node:fs"; -import { join } from "node:path"; -import { describe, expect, test } from "vitest"; - -const forbiddenTerms = [ - "dimensionlab", - "dimension lab", - "vaultwarden", - "forgejo", - "grafana", - "uptime kuma", - "prometheus", - "backrest", - "open webui", - "comfyui", - "adminer", - "cockpit", - "ollama", -]; - -describe("UI content boundary", () => { - test("keeps environment-specific content out of reusable UI source", () => { - const source = readUiSource(join(process.cwd(), "src", "lib", "ui")); - const normalized = source.toLowerCase(); - - expect( - forbiddenTerms.filter((term) => normalized.includes(term)), - ).toEqual([]); - }); - - test("keeps icon rendering driven by icon identifiers", () => { - const source = readUiSource(join(process.cwd(), "src", "lib", "ui")); - - expect(source).not.toContain("@iconify-json/"); - expect(source).not.toContain("/icons/"); - }); -}); - -function readUiSource(path: string): string { - const stats = statSync(path); - if (stats.isFile()) { - if (path.endsWith(".test.ts")) return ""; - return readFileSync(path, "utf8"); - } - - return readdirSync(path) - .map((entry) => readUiSource(join(path, entry))) - .join("\n"); -} From b4e626a8687888c42ec5f48af69c51c721e3486d Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 05:41:36 +0200 Subject: [PATCH 5/5] refactor(web): move website into turbo app workspace --- .gitignore | 5 + Containerfile | 31 -- README.md | 96 +++--- apps/web/Containerfile | 32 ++ .../web/drizzle.config.ts | 0 .../drizzle}/0000_dashboard_persistence.sql | 0 .../web/drizzle}/meta/0000_snapshot.json | 0 .../web/drizzle}/meta/_journal.json | 0 index.html => apps/web/index.html | 0 apps/web/package.json | 6 +- .../web/playwright.config.ts | 2 +- {src => apps/web/src}/App.test.tsx | 0 {src => apps/web/src}/App.tsx | 4 +- {src => apps/web/src}/app.css | 4 +- .../lib/model/fixtures/dimensionlab.test.ts | 0 .../src}/lib/model/fixtures/dimensionlab.ts | 0 .../web/src}/lib/model/fixtures/generic.ts | 0 .../web/src}/lib/model/fixtures/index.ts | 0 {src => apps/web/src}/lib/model/index.ts | 0 .../web/src}/lib/model/schema.test.ts | 0 {src => apps/web/src}/lib/model/schema.ts | 0 {src => apps/web/src}/lib/model/validation.ts | 0 .../src}/lib/presentation-boundary.test.ts | 26 +- .../server/agent-config/agent-config.test.ts | 0 .../web/src}/lib/server/agent-config/index.ts | 0 .../web/src}/lib/server/dashboard.test.ts | 0 {src => apps/web/src}/lib/server/dashboard.ts | 0 .../datasources/dashboard-datasources.test.ts | 0 .../web/src}/lib/server/datasources/index.ts | 0 .../web/src}/lib/server/db/connection.ts | 0 .../lib/server/db/dashboard-store.test.ts | 0 .../web/src}/lib/server/db/dashboard-store.ts | 0 .../web/src}/lib/server/db/migrations.ts | 0 .../lib/server/db/model-migrations.test.ts | 0 .../src}/lib/server/db/model-migrations.ts | 0 {src => apps/web/src}/lib/server/db/schema.ts | 0 .../lib/testing/external-api-mocks.test.ts | 0 .../src}/lib/testing/external-api-mocks.ts | 0 .../lib/ui-adapter}/model-renderer.test.ts | 15 +- .../web/src/lib/ui-adapter}/model-renderer.ts | 2 +- apps/web/src/lib/workspace-boundary.test.ts | 4 +- {src => apps/web/src}/main.tsx | 0 {src => apps/web/src}/page.test.tsx | 0 {src => apps/web/src}/server/dev.test.ts | 0 {src => apps/web/src}/server/dev.ts | 0 {src => apps/web/src}/server/index.ts | 0 .../server/routes/agent-dashboard.test.ts | 0 .../web/src}/server/routes/agent-dashboard.ts | 0 .../web/src}/server/routes/dashboard.test.ts | 0 .../web/src}/server/routes/dashboard.ts | 0 {src => apps/web/src}/vite-env.d.ts | 0 .../web/static}/mockServiceWorker.js | 0 .../web/tests}/e2e/dashboard.spec.ts | 0 ...shboard-desktop-chromium-desktop-linux.png | Bin ...d-light-desktop-chromium-desktop-linux.png | Bin ...dashboard-mobile-chromium-mobile-linux.png | Bin .../web/tests}/e2e/storybook-server.ts | 6 +- .../web/tests}/e2e/storybook.spec.ts | 0 apps/web/tsconfig.json | 2 + vite.config.ts => apps/web/vite.config.ts | 6 + bun.lock | 322 +++++++++--------- .../ui/components.json | 12 +- packages/ui/package.json | 2 + packages/ui/src/css.d.ts | 1 + tsconfig.base.json | 2 +- tsconfig.json | 36 +- 66 files changed, 318 insertions(+), 298 deletions(-) delete mode 100644 Containerfile create mode 100644 apps/web/Containerfile rename drizzle.config.ts => apps/web/drizzle.config.ts (100%) rename {drizzle => apps/web/drizzle}/0000_dashboard_persistence.sql (100%) rename {drizzle => apps/web/drizzle}/meta/0000_snapshot.json (100%) rename {drizzle => apps/web/drizzle}/meta/_journal.json (100%) rename index.html => apps/web/index.html (100%) rename playwright.config.ts => apps/web/playwright.config.ts (90%) rename {src => apps/web/src}/App.test.tsx (100%) rename {src => apps/web/src}/App.tsx (97%) rename {src => apps/web/src}/app.css (97%) rename {src => apps/web/src}/lib/model/fixtures/dimensionlab.test.ts (100%) rename {src => apps/web/src}/lib/model/fixtures/dimensionlab.ts (100%) rename {src => apps/web/src}/lib/model/fixtures/generic.ts (100%) rename {src => apps/web/src}/lib/model/fixtures/index.ts (100%) rename {src => apps/web/src}/lib/model/index.ts (100%) rename {src => apps/web/src}/lib/model/schema.test.ts (100%) rename {src => apps/web/src}/lib/model/schema.ts (100%) rename {src => apps/web/src}/lib/model/validation.ts (100%) rename {src => apps/web/src}/lib/presentation-boundary.test.ts (64%) rename {src => apps/web/src}/lib/server/agent-config/agent-config.test.ts (100%) rename {src => apps/web/src}/lib/server/agent-config/index.ts (100%) rename {src => apps/web/src}/lib/server/dashboard.test.ts (100%) rename {src => apps/web/src}/lib/server/dashboard.ts (100%) rename {src => apps/web/src}/lib/server/datasources/dashboard-datasources.test.ts (100%) rename {src => apps/web/src}/lib/server/datasources/index.ts (100%) rename {src => apps/web/src}/lib/server/db/connection.ts (100%) rename {src => apps/web/src}/lib/server/db/dashboard-store.test.ts (100%) rename {src => apps/web/src}/lib/server/db/dashboard-store.ts (100%) rename {src => apps/web/src}/lib/server/db/migrations.ts (100%) rename {src => apps/web/src}/lib/server/db/model-migrations.test.ts (100%) rename {src => apps/web/src}/lib/server/db/model-migrations.ts (100%) rename {src => apps/web/src}/lib/server/db/schema.ts (100%) rename {src => apps/web/src}/lib/testing/external-api-mocks.test.ts (100%) rename {src => apps/web/src}/lib/testing/external-api-mocks.ts (100%) rename {src/lib/ui => apps/web/src/lib/ui-adapter}/model-renderer.test.ts (89%) rename {src/lib/ui => apps/web/src/lib/ui-adapter}/model-renderer.ts (98%) rename {src => apps/web/src}/main.tsx (100%) rename {src => apps/web/src}/page.test.tsx (100%) rename {src => apps/web/src}/server/dev.test.ts (100%) rename {src => apps/web/src}/server/dev.ts (100%) rename {src => apps/web/src}/server/index.ts (100%) rename {src => apps/web/src}/server/routes/agent-dashboard.test.ts (100%) rename {src => apps/web/src}/server/routes/agent-dashboard.ts (100%) rename {src => apps/web/src}/server/routes/dashboard.test.ts (100%) rename {src => apps/web/src}/server/routes/dashboard.ts (100%) rename {src => apps/web/src}/vite-env.d.ts (100%) rename {static => apps/web/static}/mockServiceWorker.js (100%) rename {tests => apps/web/tests}/e2e/dashboard.spec.ts (100%) rename {tests => apps/web/tests}/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png (100%) rename {tests => apps/web/tests}/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png (100%) rename {tests => apps/web/tests}/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png (100%) rename {tests => apps/web/tests}/e2e/storybook-server.ts (82%) rename {tests => apps/web/tests}/e2e/storybook.spec.ts (100%) rename vite.config.ts => apps/web/vite.config.ts (76%) rename components.json => packages/ui/components.json (67%) create mode 100644 packages/ui/src/css.d.ts diff --git a/.gitignore b/.gitignore index 7d8d6c4..b6326bb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ node_modules/ build/ dist/ .vite/ +.turbo/ +apps/*/.turbo/ +packages/*/.turbo/ .env .env.* @@ -10,6 +13,8 @@ dist/ data/*.sqlite data/*.sqlite-* +apps/*/data/*.sqlite +apps/*/data/*.sqlite-* coverage/ playwright-report/ test-results/ diff --git a/Containerfile b/Containerfile deleted file mode 100644 index d19c2d0..0000000 --- a/Containerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM docker.io/oven/bun:1.3.14 AS deps - -WORKDIR /app -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile - -FROM deps AS build - -COPY . . -RUN bun run build - -FROM docker.io/oven/bun:1.3.14 AS runtime - -WORKDIR /app -ENV NODE_ENV=production -ENV HOST=0.0.0.0 -ENV PORT=3000 -ENV DATABASE_URL=file:/data/dimensionlab.sqlite -ENV DASHBOARD_MIGRATIONS_DIR=/app/drizzle - -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile --production -COPY --from=build /app/build ./build -COPY --from=build /app/dist ./dist -COPY --from=build /app/drizzle ./drizzle - -RUN mkdir -p /data -VOLUME ["/data"] -EXPOSE 3000 - -CMD ["bun", "build/index.js"] diff --git a/README.md b/README.md index f39dcb5..59dedf6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,20 @@ # Dimension Lab Website -Standalone React runtime for the Dimension Lab system overview dashboard. +Turbo/Bun workspace for the Dimension Lab system overview dashboard and its +reusable React component library. This project is not a Homepage customization and does not depend on Homepage runtime, frontend code, or configuration. The dashboard will be model-driven: -the reusable renderer stays content-free, while environment-specific data lives -in validated dashboard model state. +the reusable UI package stays content-free, while environment-specific data +lives in validated dashboard model state inside the web app. + +## Workspace Layout + +- `apps/web`: Vite React website, Bun API server, model fixtures, Drizzle + persistence, Playwright e2e checks, and container build. +- `packages/ui`: reusable dashboard React components, design tokens, + shadcn/radix primitives, generic fixtures, and Storybook. +- `docs/superpowers`: migration specs and execution plans used for this repo. ## Development @@ -18,18 +27,18 @@ This MVP uses Drizzle with Bun SQLite for local file-backed persistence. ## Scripts -- `bun run dev`: start the local Vite development server with a Bun API proxy. -- `bun run check`: run TypeScript checks. -- `bun run test`: run Vitest. +- `bun run dev`: start the web app dev runtime through Turbo. +- `bun run check`: run TypeScript checks in all workspaces. +- `bun run test`: run the unit test stage in all workspaces. - `bun run test:unit`: run Vitest explicitly as the unit test stage. - `bun run test:e2e`: build and run Playwright browser smoke and QA checks. -- `bun run test:qa`: run the MVP release gate. -- `bun run build`: build the production app. -- `bun run preview`: preview the production build. -- `bun run storybook`: start the component explorer on port 6006. -- `bun run build-storybook`: build the static Storybook review artifact. -- `bun run db:generate`: generate Drizzle migrations from the server schema. -- `bun run db:check`: validate migration consistency. +- `bun run test:qa`: run the release gate through Turbo. +- `bun run build`: build the UI package, production website, and Bun server. +- `bun run preview`: preview the production web build. +- `bun run storybook`: start the UI package component explorer on port 6006. +- `bun run build-storybook`: build the UI package static Storybook artifact. +- `bun run db:generate`: generate web app Drizzle migrations. +- `bun run db:check`: validate web app migration consistency. ## Persistence @@ -40,14 +49,14 @@ URL is: DATABASE_URL=file:./data/dimensionlab.sqlite ``` -SQLite files under `data/` are ignored. Drizzle schema lives in -`src/lib/server/db/schema.ts`; tracked migrations live in `drizzle/`. Runtime -startup applies the checked-in dashboard migrations before reads or writes. If -the app is launched from outside the repo tree, set `DASHBOARD_MIGRATIONS_DIR` -to the tracked migrations directory. The current driver is `bun:sqlite`, which -keeps this repo installable in the Bun workflow. The store boundary is isolated -so a later Postgres driver can replace the SQLite connection without changing -the dashboard model or renderer. +SQLite files under `data/` and `apps/*/data/` are ignored. Drizzle schema lives +in `apps/web/src/lib/server/db/schema.ts`; tracked migrations live in +`apps/web/drizzle/`. Runtime startup applies the checked-in dashboard migrations +before reads or writes. If the app is launched from outside the web app tree, +set `DASHBOARD_MIGRATIONS_DIR` to the tracked migrations directory. The current +driver is `bun:sqlite`, which keeps this repo installable in the Bun workflow. +The store boundary is isolated so a later Postgres driver can replace the +SQLite connection without changing the dashboard model or renderer. Stored dashboard documents pass through a version migration boundary before reads or writes; the MVP supports `dashboard.v1` and fails unsupported versions with an explicit migration error. @@ -55,25 +64,26 @@ with an explicit migration error. ## Seed Data The initial Dimension Lab dashboard lives in -`src/lib/model/fixtures/dimensionlab.ts` as validated model data. It includes -the first-screen telemetry, service groups, status strip, weather module, -Iconify icon identifiers, links, and datasource references. Values that are not -live yet are labeled as fallback values in the data so later datasource adapters -can replace them without changing presentation components. +`apps/web/src/lib/model/fixtures/dimensionlab.ts` as validated model data. It +includes the first-screen telemetry, service groups, status strip, weather +module, Iconify icon identifiers, links, and datasource references. Values that +are not live yet are labeled as fallback values in the data so later datasource +adapters can replace them without changing presentation components. ## Runtime Shape -The browser app is built with Vite and React. Local development starts Vite for -HMR and a loopback Bun API server for `/api/*` routes. Production uses a small -Bun HTTP server at `build/index.js` to serve the Vite `dist/` assets and JSON -API routes. The current persistence runtime is Bun because the MVP SQLite -driver is `bun:sqlite`. +The browser app in `apps/web` is built with Vite and React. Local development +starts Vite for HMR and a loopback Bun API server for `/api/*` routes. +Production uses a small Bun HTTP server at `apps/web/build/index.js` to serve +the Vite `apps/web/dist/` assets and JSON API routes. The current persistence +runtime is Bun because the MVP SQLite driver is `bun:sqlite`. ## Storybook -Storybook covers the reusable UI components with generic fixtures only. Stories -must not import environment-specific dashboard content; the presentation layer -accepts labels, values, icons, status, and links through typed props. +Storybook lives with `packages/ui` and covers the reusable UI components with +generic fixtures only. Stories must not import environment-specific dashboard +content; the presentation layer accepts labels, values, icons, status, and links +through typed props. ## MVP QA Gate @@ -106,27 +116,29 @@ belong in validated model data, not reusable components. ## Deployment Notes -The production build emits Vite client assets under `dist/` and a Bun server -entry at `build/index.js`. A minimal deployment flow is: +The production build emits Vite client assets under `apps/web/dist/` and a Bun +server entry at `apps/web/build/index.js`. A minimal deployment flow is: ```sh bun install --frozen-lockfile bun run build +cd apps/web DATABASE_URL=file:/data/dimensionlab.sqlite HOST=0.0.0.0 PORT=3000 bun build/index.js ``` Mount `/data` or set `DATABASE_URL` to another persistent SQLite path. If the process starts outside the repository root, set `DASHBOARD_MIGRATIONS_DIR` to -the checked-in `drizzle/` directory so startup migrations can run. +the checked-in `apps/web/drizzle/` directory so startup migrations can run. ### Internal Container -The checked-in `Containerfile` builds the React client and Bun server into a -runtime image. For the Dimension Lab internal host, run it behind Caddy on a -loopback port and mount persistent state at `/data`: +The checked-in `apps/web/Containerfile` builds the React client and Bun server +from the workspace root into a runtime image. For the Dimension Lab internal +host, run it behind Caddy on a loopback port and mount persistent state at +`/data`: ```sh -podman build -t localhost/dimensionlab-website:latest . +podman build -f apps/web/Containerfile -t localhost/dimensionlab-website:latest . podman run --rm \ --publish 127.0.0.1:25341:3000 \ --volume "$HOME/containers/dimensionlab-website/data:/data:Z" \ @@ -137,4 +149,4 @@ podman run --rm \ The env file must provide `AGENT_CONFIG_TOKEN`. Runtime defaults inside the image set `HOST=0.0.0.0`, `PORT=3000`, `DATABASE_URL=file:/data/dimensionlab.sqlite`, and -`DASHBOARD_MIGRATIONS_DIR=/app/drizzle`. +`DASHBOARD_MIGRATIONS_DIR=/repo/apps/web/drizzle`. diff --git a/apps/web/Containerfile b/apps/web/Containerfile new file mode 100644 index 0000000..912f780 --- /dev/null +++ b/apps/web/Containerfile @@ -0,0 +1,32 @@ +FROM docker.io/oven/bun:1.3.14 AS deps + +WORKDIR /repo +ENV PATH=/repo/apps/web/node_modules/.bin:/repo/packages/ui/node_modules/.bin:/repo/node_modules/.bin:$PATH +COPY package.json bun.lock turbo.json tsconfig.base.json ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/ui/package.json packages/ui/package.json +RUN bun install --frozen-lockfile + +FROM deps AS build + +COPY . . +RUN bun install --frozen-lockfile && bun run build + +FROM deps AS runtime + +WORKDIR /repo/apps/web +ENV NODE_ENV=production +ENV HOST=0.0.0.0 +ENV PORT=3000 +ENV DATABASE_URL=file:/data/dimensionlab.sqlite +ENV DASHBOARD_MIGRATIONS_DIR=/repo/apps/web/drizzle + +COPY --from=build /repo/apps/web/build ./build +COPY --from=build /repo/apps/web/dist ./dist +COPY --from=build /repo/apps/web/drizzle ./drizzle + +RUN mkdir -p /data +VOLUME ["/data"] +EXPOSE 3000 + +CMD ["bun", "build/index.js"] diff --git a/drizzle.config.ts b/apps/web/drizzle.config.ts similarity index 100% rename from drizzle.config.ts rename to apps/web/drizzle.config.ts diff --git a/drizzle/0000_dashboard_persistence.sql b/apps/web/drizzle/0000_dashboard_persistence.sql similarity index 100% rename from drizzle/0000_dashboard_persistence.sql rename to apps/web/drizzle/0000_dashboard_persistence.sql diff --git a/drizzle/meta/0000_snapshot.json b/apps/web/drizzle/meta/0000_snapshot.json similarity index 100% rename from drizzle/meta/0000_snapshot.json rename to apps/web/drizzle/meta/0000_snapshot.json diff --git a/drizzle/meta/_journal.json b/apps/web/drizzle/meta/_journal.json similarity index 100% rename from drizzle/meta/_journal.json rename to apps/web/drizzle/meta/_journal.json diff --git a/index.html b/apps/web/index.html similarity index 100% rename from index.html rename to apps/web/index.html diff --git a/apps/web/package.json b/apps/web/package.json index 285a94b..7875aa8 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -4,7 +4,7 @@ "private": true, "type": "module", "scripts": { - "dev": "vite --host 0.0.0.0", + "dev": "bun src/server/dev.ts", "build": "vite build && bun build src/server/index.ts --target bun --outdir build", "preview": "HOST=0.0.0.0 PORT=4173 bun build/index.js", "check": "tsc --noEmit", @@ -22,7 +22,8 @@ "ajv-formats": "^3.0.1", "drizzle-orm": "^0.45.2", "react": "^19.2.7", - "react-dom": "^19.2.7" + "react-dom": "^19.2.7", + "tw-animate-css": "^1.4.0" }, "devDependencies": { "@axe-core/playwright": "^4.11.3", @@ -33,6 +34,7 @@ "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", + "bun-types": "^1.3.14", "drizzle-kit": "^0.31.10", "msw": "^2.14.6", "shadcn": "^4.11.0", diff --git a/playwright.config.ts b/apps/web/playwright.config.ts similarity index 90% rename from playwright.config.ts rename to apps/web/playwright.config.ts index 3593452..afba557 100644 --- a/playwright.config.ts +++ b/apps/web/playwright.config.ts @@ -27,7 +27,7 @@ export default defineConfig({ timeout: 120_000, }, { - command: `bun run build-storybook && STORYBOOK_STATIC_PORT=${storybookPort} bun tests/e2e/storybook-server.ts`, + command: `cd ../.. && bun run build-storybook && STORYBOOK_STATIC_PORT=${storybookPort} bun apps/web/tests/e2e/storybook-server.ts`, url: storybookURL, reuseExistingServer: false, timeout: 120_000, diff --git a/src/App.test.tsx b/apps/web/src/App.test.tsx similarity index 100% rename from src/App.test.tsx rename to apps/web/src/App.test.tsx diff --git a/src/App.tsx b/apps/web/src/App.tsx similarity index 97% rename from src/App.tsx rename to apps/web/src/App.tsx index 99cdc5e..0099173 100644 --- a/src/App.tsx +++ b/apps/web/src/App.tsx @@ -1,15 +1,15 @@ import { useEffect, useState } from "react"; import type { DashboardRuntimeState } from "$lib/server/dashboard"; +import { dashboardDocumentToUiDashboard } from "$lib/ui-adapter/model-renderer"; import { DashboardFrame, SystemState, ThemeToggle, - dashboardDocumentToUiDashboard, persistUiTheme, resolveInitialUiTheme, type UiTheme, type UiSeverity, -} from "$lib/ui"; +} from "@dimensionlab/ui"; const loadingDashboardState: DashboardRuntimeState = { state: "loading", diff --git a/src/app.css b/apps/web/src/app.css similarity index 97% rename from src/app.css rename to apps/web/src/app.css index 0345394..eeaabcb 100644 --- a/src/app.css +++ b/apps/web/src/app.css @@ -1,9 +1,7 @@ @import "tailwindcss"; -@import "./lib/ui/tokens.css"; -@import "./lib/ui/components/styles.css"; +@import "@dimensionlab/ui/styles.css"; @import "tw-animate-css"; @import "shadcn/tailwind.css"; -@import "@fontsource-variable/geist"; @custom-variant dark (&:is(.dark *)); diff --git a/src/lib/model/fixtures/dimensionlab.test.ts b/apps/web/src/lib/model/fixtures/dimensionlab.test.ts similarity index 100% rename from src/lib/model/fixtures/dimensionlab.test.ts rename to apps/web/src/lib/model/fixtures/dimensionlab.test.ts diff --git a/src/lib/model/fixtures/dimensionlab.ts b/apps/web/src/lib/model/fixtures/dimensionlab.ts similarity index 100% rename from src/lib/model/fixtures/dimensionlab.ts rename to apps/web/src/lib/model/fixtures/dimensionlab.ts diff --git a/src/lib/model/fixtures/generic.ts b/apps/web/src/lib/model/fixtures/generic.ts similarity index 100% rename from src/lib/model/fixtures/generic.ts rename to apps/web/src/lib/model/fixtures/generic.ts diff --git a/src/lib/model/fixtures/index.ts b/apps/web/src/lib/model/fixtures/index.ts similarity index 100% rename from src/lib/model/fixtures/index.ts rename to apps/web/src/lib/model/fixtures/index.ts diff --git a/src/lib/model/index.ts b/apps/web/src/lib/model/index.ts similarity index 100% rename from src/lib/model/index.ts rename to apps/web/src/lib/model/index.ts diff --git a/src/lib/model/schema.test.ts b/apps/web/src/lib/model/schema.test.ts similarity index 100% rename from src/lib/model/schema.test.ts rename to apps/web/src/lib/model/schema.test.ts diff --git a/src/lib/model/schema.ts b/apps/web/src/lib/model/schema.ts similarity index 100% rename from src/lib/model/schema.ts rename to apps/web/src/lib/model/schema.ts diff --git a/src/lib/model/validation.ts b/apps/web/src/lib/model/validation.ts similarity index 100% rename from src/lib/model/validation.ts rename to apps/web/src/lib/model/validation.ts diff --git a/src/lib/presentation-boundary.test.ts b/apps/web/src/lib/presentation-boundary.test.ts similarity index 64% rename from src/lib/presentation-boundary.test.ts rename to apps/web/src/lib/presentation-boundary.test.ts index 8e29ece..7daf204 100644 --- a/src/lib/presentation-boundary.test.ts +++ b/apps/web/src/lib/presentation-boundary.test.ts @@ -2,9 +2,17 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; +const appRoot = process.cwd().endsWith(`${join("apps", "web")}`) + ? process.cwd() + : join(process.cwd(), "apps", "web"); +const repoRoot = existsSync(join(process.cwd(), "turbo.json")) + ? process.cwd() + : join(appRoot, "..", ".."); const presentationRoots = [ - join(process.cwd(), "src", "lib", "ui"), - join(process.cwd(), "src", "routes"), + join(repoRoot, "packages", "ui", "src"), + join(appRoot, "src", "App.tsx"), + join(appRoot, "src", "app.css"), + join(appRoot, "src", "lib", "ui-adapter"), ]; const forbiddenTerms = [ @@ -26,7 +34,9 @@ const forbiddenTerms = [ describe("presentation content boundary", () => { test("keeps environment-specific content out of route and UI implementation", () => { - const source = presentationRoots.map(readPresentationSource).join("\n").toLowerCase(); + const source = withoutInternalPackageScope( + presentationRoots.map(readPresentationSource).join("\n").toLowerCase(), + ); expect(forbiddenTerms.filter((term) => source.includes(term))).toEqual([]); }); @@ -34,7 +44,7 @@ describe("presentation content boundary", () => { test("does not keep legacy presentation component files in the React runtime", () => { const legacyExtension = [".sve", "lte"].join(""); - expect(findFiles(join(process.cwd(), "src"), legacyExtension)).toEqual([]); + expect(findFiles(join(appRoot, "src"), legacyExtension)).toEqual([]); }); }); @@ -44,7 +54,9 @@ function readPresentationSource(path: string): string { const stats = statSync(path); if (stats.isFile()) { if (path.endsWith(".test.ts")) return ""; - if (path.includes(`${join("src", "lib", "ui", "stories")}${"/"}`)) return ""; + if (path.includes(`${join("packages", "ui", "src", "stories")}${"/"}`)) { + return ""; + } if (!/\.(tsx|ts|js|mjs|css|json)$/.test(path)) return ""; return readFileSync(path, "utf8"); } @@ -60,3 +72,7 @@ function findFiles(path: string, extension: string): string[] { return readdirSync(path).flatMap((entry) => findFiles(join(path, entry), extension)); } + +function withoutInternalPackageScope(source: string): string { + return source.replaceAll("@dimensionlab/ui", "@internal/ui"); +} diff --git a/src/lib/server/agent-config/agent-config.test.ts b/apps/web/src/lib/server/agent-config/agent-config.test.ts similarity index 100% rename from src/lib/server/agent-config/agent-config.test.ts rename to apps/web/src/lib/server/agent-config/agent-config.test.ts diff --git a/src/lib/server/agent-config/index.ts b/apps/web/src/lib/server/agent-config/index.ts similarity index 100% rename from src/lib/server/agent-config/index.ts rename to apps/web/src/lib/server/agent-config/index.ts diff --git a/src/lib/server/dashboard.test.ts b/apps/web/src/lib/server/dashboard.test.ts similarity index 100% rename from src/lib/server/dashboard.test.ts rename to apps/web/src/lib/server/dashboard.test.ts diff --git a/src/lib/server/dashboard.ts b/apps/web/src/lib/server/dashboard.ts similarity index 100% rename from src/lib/server/dashboard.ts rename to apps/web/src/lib/server/dashboard.ts diff --git a/src/lib/server/datasources/dashboard-datasources.test.ts b/apps/web/src/lib/server/datasources/dashboard-datasources.test.ts similarity index 100% rename from src/lib/server/datasources/dashboard-datasources.test.ts rename to apps/web/src/lib/server/datasources/dashboard-datasources.test.ts diff --git a/src/lib/server/datasources/index.ts b/apps/web/src/lib/server/datasources/index.ts similarity index 100% rename from src/lib/server/datasources/index.ts rename to apps/web/src/lib/server/datasources/index.ts diff --git a/src/lib/server/db/connection.ts b/apps/web/src/lib/server/db/connection.ts similarity index 100% rename from src/lib/server/db/connection.ts rename to apps/web/src/lib/server/db/connection.ts diff --git a/src/lib/server/db/dashboard-store.test.ts b/apps/web/src/lib/server/db/dashboard-store.test.ts similarity index 100% rename from src/lib/server/db/dashboard-store.test.ts rename to apps/web/src/lib/server/db/dashboard-store.test.ts diff --git a/src/lib/server/db/dashboard-store.ts b/apps/web/src/lib/server/db/dashboard-store.ts similarity index 100% rename from src/lib/server/db/dashboard-store.ts rename to apps/web/src/lib/server/db/dashboard-store.ts diff --git a/src/lib/server/db/migrations.ts b/apps/web/src/lib/server/db/migrations.ts similarity index 100% rename from src/lib/server/db/migrations.ts rename to apps/web/src/lib/server/db/migrations.ts diff --git a/src/lib/server/db/model-migrations.test.ts b/apps/web/src/lib/server/db/model-migrations.test.ts similarity index 100% rename from src/lib/server/db/model-migrations.test.ts rename to apps/web/src/lib/server/db/model-migrations.test.ts diff --git a/src/lib/server/db/model-migrations.ts b/apps/web/src/lib/server/db/model-migrations.ts similarity index 100% rename from src/lib/server/db/model-migrations.ts rename to apps/web/src/lib/server/db/model-migrations.ts diff --git a/src/lib/server/db/schema.ts b/apps/web/src/lib/server/db/schema.ts similarity index 100% rename from src/lib/server/db/schema.ts rename to apps/web/src/lib/server/db/schema.ts diff --git a/src/lib/testing/external-api-mocks.test.ts b/apps/web/src/lib/testing/external-api-mocks.test.ts similarity index 100% rename from src/lib/testing/external-api-mocks.test.ts rename to apps/web/src/lib/testing/external-api-mocks.test.ts diff --git a/src/lib/testing/external-api-mocks.ts b/apps/web/src/lib/testing/external-api-mocks.ts similarity index 100% rename from src/lib/testing/external-api-mocks.ts rename to apps/web/src/lib/testing/external-api-mocks.ts diff --git a/src/lib/ui/model-renderer.test.ts b/apps/web/src/lib/ui-adapter/model-renderer.test.ts similarity index 89% rename from src/lib/ui/model-renderer.test.ts rename to apps/web/src/lib/ui-adapter/model-renderer.test.ts index 62942ee..fb04ec4 100644 --- a/src/lib/ui/model-renderer.test.ts +++ b/apps/web/src/lib/ui-adapter/model-renderer.test.ts @@ -1,13 +1,15 @@ import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; -import { - type DashboardDocument, -} from "$lib/model"; +import { type DashboardDocument } from "$lib/model"; import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab"; import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { dashboardDocumentToUiDashboard } from "./model-renderer"; +const appRoot = process.cwd().endsWith(`${join("apps", "web")}`) + ? process.cwd() + : join(process.cwd(), "apps", "web"); + describe("dashboard model renderer", () => { test("projects the Dimension Lab model into UI component props", () => { const dashboard = dashboardDocumentToUiDashboard(dimensionLabDashboardFixture); @@ -86,7 +88,12 @@ describe("dashboard model renderer", () => { }); test("does not hardcode environment-specific content in mapper source", () => { - const source = readFileSync(join(process.cwd(), "src/lib/ui/model-renderer.ts"), "utf8").toLowerCase(); + const source = readFileSync( + join(appRoot, "src/lib/ui-adapter/model-renderer.ts"), + "utf8", + ) + .toLowerCase() + .replaceAll("@dimensionlab/ui", "@internal/ui"); expect(source).not.toContain("dimension"); expect(source).not.toContain("vaultwarden"); diff --git a/src/lib/ui/model-renderer.ts b/apps/web/src/lib/ui-adapter/model-renderer.ts similarity index 98% rename from src/lib/ui/model-renderer.ts rename to apps/web/src/lib/ui-adapter/model-renderer.ts index d5f3647..1db2173 100644 --- a/src/lib/ui/model-renderer.ts +++ b/apps/web/src/lib/ui-adapter/model-renderer.ts @@ -14,7 +14,7 @@ import type { UiServiceRow, UiStatusItem, UiTelemetryCard, -} from "./types"; +} from "@dimensionlab/ui"; export function dashboardDocumentToUiDashboard( document: DashboardDocument, diff --git a/apps/web/src/lib/workspace-boundary.test.ts b/apps/web/src/lib/workspace-boundary.test.ts index 21c4020..66a11c5 100644 --- a/apps/web/src/lib/workspace-boundary.test.ts +++ b/apps/web/src/lib/workspace-boundary.test.ts @@ -2,7 +2,9 @@ import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, test } from "vitest"; -const root = process.cwd(); +const root = existsSync(join(process.cwd(), "turbo.json")) + ? process.cwd() + : join(process.cwd(), "..", ".."); describe("workspace boundaries", () => { test("declares the root as a turbo-managed bun workspace", () => { diff --git a/src/main.tsx b/apps/web/src/main.tsx similarity index 100% rename from src/main.tsx rename to apps/web/src/main.tsx diff --git a/src/page.test.tsx b/apps/web/src/page.test.tsx similarity index 100% rename from src/page.test.tsx rename to apps/web/src/page.test.tsx diff --git a/src/server/dev.test.ts b/apps/web/src/server/dev.test.ts similarity index 100% rename from src/server/dev.test.ts rename to apps/web/src/server/dev.test.ts diff --git a/src/server/dev.ts b/apps/web/src/server/dev.ts similarity index 100% rename from src/server/dev.ts rename to apps/web/src/server/dev.ts diff --git a/src/server/index.ts b/apps/web/src/server/index.ts similarity index 100% rename from src/server/index.ts rename to apps/web/src/server/index.ts diff --git a/src/server/routes/agent-dashboard.test.ts b/apps/web/src/server/routes/agent-dashboard.test.ts similarity index 100% rename from src/server/routes/agent-dashboard.test.ts rename to apps/web/src/server/routes/agent-dashboard.test.ts diff --git a/src/server/routes/agent-dashboard.ts b/apps/web/src/server/routes/agent-dashboard.ts similarity index 100% rename from src/server/routes/agent-dashboard.ts rename to apps/web/src/server/routes/agent-dashboard.ts diff --git a/src/server/routes/dashboard.test.ts b/apps/web/src/server/routes/dashboard.test.ts similarity index 100% rename from src/server/routes/dashboard.test.ts rename to apps/web/src/server/routes/dashboard.test.ts diff --git a/src/server/routes/dashboard.ts b/apps/web/src/server/routes/dashboard.ts similarity index 100% rename from src/server/routes/dashboard.ts rename to apps/web/src/server/routes/dashboard.ts diff --git a/src/vite-env.d.ts b/apps/web/src/vite-env.d.ts similarity index 100% rename from src/vite-env.d.ts rename to apps/web/src/vite-env.d.ts diff --git a/static/mockServiceWorker.js b/apps/web/static/mockServiceWorker.js similarity index 100% rename from static/mockServiceWorker.js rename to apps/web/static/mockServiceWorker.js diff --git a/tests/e2e/dashboard.spec.ts b/apps/web/tests/e2e/dashboard.spec.ts similarity index 100% rename from tests/e2e/dashboard.spec.ts rename to apps/web/tests/e2e/dashboard.spec.ts diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png similarity index 100% rename from tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png rename to apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-desktop-chromium-desktop-linux.png diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png similarity index 100% rename from tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png rename to apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-light-desktop-chromium-desktop-linux.png diff --git a/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png b/apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png similarity index 100% rename from tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png rename to apps/web/tests/e2e/dashboard.spec.ts-snapshots/dashboard-mobile-chromium-mobile-linux.png diff --git a/tests/e2e/storybook-server.ts b/apps/web/tests/e2e/storybook-server.ts similarity index 82% rename from tests/e2e/storybook-server.ts rename to apps/web/tests/e2e/storybook-server.ts index e6e3f63..7544654 100644 --- a/tests/e2e/storybook-server.ts +++ b/apps/web/tests/e2e/storybook-server.ts @@ -1,11 +1,13 @@ import { existsSync } from "node:fs"; import { resolve, sep } from "node:path"; -const root = resolve(process.cwd(), "storybook-static"); +const root = resolve(process.cwd(), "packages/ui/storybook-static"); const port = Number(process.env.STORYBOOK_STATIC_PORT || 6007); if (!existsSync(resolve(root, "iframe.html"))) { - throw new Error("storybook-static is missing. Run bun run build-storybook first."); + throw new Error( + "packages/ui/storybook-static is missing. Run bun run build-storybook first.", + ); } Bun.serve({ diff --git a/tests/e2e/storybook.spec.ts b/apps/web/tests/e2e/storybook.spec.ts similarity index 100% rename from tests/e2e/storybook.spec.ts rename to apps/web/tests/e2e/storybook.spec.ts diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index 120931b..edfa07d 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -3,6 +3,8 @@ "compilerOptions": { "baseUrl": ".", "paths": { + "@dimensionlab/ui": ["../../packages/ui/src/index.ts"], + "@dimensionlab/ui/styles.css": ["../../packages/ui/src/styles.css"], "$lib/*": ["src/lib/*"] }, "types": ["node", "bun-types", "react", "react-dom", "vite/client"] diff --git a/vite.config.ts b/apps/web/vite.config.ts similarity index 76% rename from vite.config.ts rename to apps/web/vite.config.ts index ceff746..d88ee57 100644 --- a/vite.config.ts +++ b/apps/web/vite.config.ts @@ -24,6 +24,12 @@ export default defineConfig({ plugins: [react(), tailwindcss()], resolve: { alias: { + "@dimensionlab/ui/styles.css": fileURLToPath( + new URL("../../packages/ui/src/styles.css", import.meta.url), + ), + "@dimensionlab/ui": fileURLToPath( + new URL("../../packages/ui/src/index.ts", import.meta.url), + ), $lib: fileURLToPath(new URL("./src/lib", import.meta.url)), }, }, diff --git a/bun.lock b/bun.lock index 36ab7d6..543520d 100644 --- a/bun.lock +++ b/bun.lock @@ -4,45 +4,75 @@ "workspaces": { "": { "name": "dimensionlab-website", + "devDependencies": { + "turbo": "^2.5.0", + }, + }, + "apps/web": { + "name": "@dimensionlab/web", + "version": "0.0.1", "dependencies": { - "@fontsource-variable/geist": "^5.2.9", - "@iconify/react": "^6.0.2", + "@dimensionlab/ui": "workspace:*", "@sinclair/typebox": "^0.34.49", "ajv": "^8.20.0", "ajv-formats": "^3.0.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", "drizzle-orm": "^0.45.2", - "lucide-react": "^1.21.0", - "radix-ui": "^1.6.0", "react": "^19.2.7", "react-dom": "^19.2.7", - "tailwind-merge": "^3.6.0", "tw-animate-css": "^1.4.0", - "uplot": "^1.6.32", }, "devDependencies": { "@axe-core/playwright": "^4.11.3", "@playwright/test": "^1.61.0", - "@storybook/addon-a11y": "^10.4.6", - "@storybook/addon-vitest": "^10.4.6", - "@storybook/react-vite": "^10.4.6", "@tailwindcss/vite": "^4.3.1", "@types/bun": "^1.3.14", "@types/node": "^25.9.3", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.2", + "bun-types": "^1.3.14", "drizzle-kit": "^0.31.10", "msw": "^2.14.6", "shadcn": "^4.11.0", - "storybook": "^10.4.6", "tailwindcss": "^4.3.1", "typescript": "^6.0.3", "vite": "^8.0.16", "vitest": "^4.1.9", }, }, + "packages/ui": { + "name": "@dimensionlab/ui", + "version": "0.0.1", + "dependencies": { + "@fontsource-variable/geist": "^5.2.9", + "@iconify/react": "^6.0.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^1.21.0", + "radix-ui": "^1.6.0", + "tailwind-merge": "^3.6.0", + "tw-animate-css": "^1.4.0", + "uplot": "^1.6.32", + }, + "devDependencies": { + "@storybook/addon-a11y": "^10.4.6", + "@storybook/addon-vitest": "^10.4.6", + "@storybook/react-vite": "^10.4.6", + "@types/bun": "^1.3.14", + "@types/node": "^25.9.3", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "bun-types": "^1.3.14", + "storybook": "^10.4.6", + "typescript": "^6.0.3", + "vite": "^8.0.16", + "vitest": "^4.1.9", + }, + "peerDependencies": { + "react": "^19.0.0", + "react-dom": "^19.0.0", + }, + }, }, "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.5.0", "", {}, "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q=="], @@ -107,6 +137,10 @@ "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], + "@dimensionlab/ui": ["@dimensionlab/ui@workspace:packages/ui"], + + "@dimensionlab/web": ["@dimensionlab/web@workspace:apps/web"], + "@dotenvx/dotenvx": ["@dotenvx/dotenvx@1.74.3", "", { "dependencies": { "commander": "^11.1.0", "conf": "^10.2.0", "dotenv": "^17.2.1", "eciesjs": "^0.4.10", "enquirer": "^2.4.1", "env-paths": "^2.2.1", "execa": "^5.1.1", "fdir": "^6.2.0", "ignore": "^5.3.0", "object-treeify": "1.1.33", "open": "^8.4.2", "picomatch": "^4.0.4", "systeminformation": "^5.22.11", "undici": "^7.11.0", "which": "^4.0.0", "yocto-spinner": "^1.1.0" }, "bin": { "dotenvx": "src/cli/dotenvx.js" } }, "sha512-+VjTIiOCApjB0K4a41+SkN18gTetmhU9UN2JD8LHeNUqo/38FmtgvdtWsW/WWN1dyYRGS7XKeAAb/ruy7x1tRw=="], "@drizzle-team/brocli": ["@drizzle-team/brocli@0.10.2", "", {}, "sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w=="], @@ -123,57 +157,57 @@ "@esbuild-kit/esm-loader": ["@esbuild-kit/esm-loader@2.6.5", "", { "dependencies": { "@esbuild-kit/core-utils": "^3.3.2", "get-tsconfig": "^4.7.0" } }, "sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], "@floating-ui/core": ["@floating-ui/core@1.7.5", "", { "dependencies": { "@floating-ui/utils": "^0.2.11" } }, "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ=="], @@ -587,6 +621,18 @@ "@ts-morph/common": ["@ts-morph/common@0.27.0", "", { "dependencies": { "fast-glob": "^3.3.3", "minimatch": "^10.0.1", "path-browserify": "^1.0.1" } }, "sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ=="], + "@turbo/darwin-64": ["@turbo/darwin-64@2.9.18", "", { "os": "darwin", "cpu": "x64" }, "sha512-9f27peFu16ur8c0v9nUFUEyBnbKuuFsUTjHFWfmwGfzySBXbHwzU44QhZon6Mznz0cHsIr3984NQj/bVrnGSRw=="], + + "@turbo/darwin-arm64": ["@turbo/darwin-arm64@2.9.18", "", { "os": "darwin", "cpu": "arm64" }, "sha512-9A6TMRq/Ib+QnbhLlgkhOm+624wO4pzSQ/yQviQfWHOlFvaYxdnIAYmu2H6TS6y7kSVL0DvzNe04NbESTOzFVQ=="], + + "@turbo/linux-64": ["@turbo/linux-64@2.9.18", "", { "os": "linux", "cpu": "x64" }, "sha512-zCdIDtz69AnbYh913elJRRoF3QY5aa2HNnf+4rAkc7bQ+tWujiDkCNV7stazOUPggaDvhKIf2Z87qHftTeXSkw=="], + + "@turbo/linux-arm64": ["@turbo/linux-arm64@2.9.18", "", { "os": "linux", "cpu": "arm64" }, "sha512-Va1kXI04naMgYwqv/5Dfa36dTDx8015U7oaQAjrXa45ua9OoFjSV4OmvkML4EmXvUclQHCiBRbY8bvd0jV7eAg=="], + + "@turbo/windows-64": ["@turbo/windows-64@2.9.18", "", { "os": "win32", "cpu": "x64" }, "sha512-m0kDhZANxSNz9ck1ybogFscHabriAsp4eDFNrN/1H5WrgTF7b3VlcPZnhuO3v2+E2KnCbeAc+UUT10BZZHdDKw=="], + + "@turbo/windows-arm64": ["@turbo/windows-arm64@2.9.18", "", { "os": "win32", "cpu": "arm64" }, "sha512-nUdR8WqoomUys9iIQmG45TMiizJ+5BV8egSeLLZba/AWblyp3fVBcIH1kSE58OtK4g2YzbMJEth6Ttv9w5rqMA=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], @@ -655,7 +701,7 @@ "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], @@ -669,7 +715,7 @@ "atomically": ["atomically@1.7.0", "", {}, "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w=="], - "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], + "axe-core": ["axe-core@4.12.1", "", {}, "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA=="], "balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], @@ -819,7 +865,7 @@ "es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -945,7 +991,7 @@ "is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="], - "is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + "is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], "is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="], @@ -975,7 +1021,7 @@ "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], - "is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + "is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], @@ -1101,7 +1147,7 @@ "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], - "open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], + "open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], @@ -1331,6 +1377,8 @@ "tsx": ["tsx@4.22.4", "", { "dependencies": { "esbuild": "~0.28.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg=="], + "turbo": ["turbo@2.9.18", "", { "optionalDependencies": { "@turbo/darwin-64": "2.9.18", "@turbo/darwin-arm64": "2.9.18", "@turbo/linux-64": "2.9.18", "@turbo/linux-arm64": "2.9.18", "@turbo/windows-64": "2.9.18", "@turbo/windows-arm64": "2.9.18" }, "bin": { "turbo": "bin/turbo" } }, "sha512-bwabv6PupzeavybzEoArBAkwq5fnzwf8OFnRtpHwnviFWuwJPFxtyH+aVp36TmIqK3aYYgtTJ3J0m2ysxxSzQg=="], + "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], "type-fest": ["type-fest@5.7.0", "", { "dependencies": { "tagged-tag": "^1.0.0" } }, "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg=="], @@ -1387,7 +1435,7 @@ "ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="], - "wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], + "wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], @@ -1405,6 +1453,8 @@ "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], + "@axe-core/playwright/axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], + "@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "@babel/helper-compilation-targets/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -1429,8 +1479,6 @@ "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], - "@storybook/addon-a11y/axe-core": ["axe-core@4.12.1", "", {}, "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA=="], - "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="], @@ -1467,12 +1515,12 @@ "dot-prop/is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="], + "drizzle-kit/esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "enquirer/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "express/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], - "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], - "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], @@ -1489,8 +1537,6 @@ "pretty-format/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "pretty-format/ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], - "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "redent/strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], @@ -1501,23 +1547,19 @@ "router/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], - "storybook/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], - - "storybook/open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="], + "shadcn/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "tsx/esbuild": ["esbuild@0.28.1", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.28.1", "@esbuild/android-arm": "0.28.1", "@esbuild/android-arm64": "0.28.1", "@esbuild/android-x64": "0.28.1", "@esbuild/darwin-arm64": "0.28.1", "@esbuild/darwin-x64": "0.28.1", "@esbuild/freebsd-arm64": "0.28.1", "@esbuild/freebsd-x64": "0.28.1", "@esbuild/linux-arm": "0.28.1", "@esbuild/linux-arm64": "0.28.1", "@esbuild/linux-ia32": "0.28.1", "@esbuild/linux-loong64": "0.28.1", "@esbuild/linux-mips64el": "0.28.1", "@esbuild/linux-ppc64": "0.28.1", "@esbuild/linux-riscv64": "0.28.1", "@esbuild/linux-s390x": "0.28.1", "@esbuild/linux-x64": "0.28.1", "@esbuild/netbsd-arm64": "0.28.1", "@esbuild/netbsd-x64": "0.28.1", "@esbuild/openbsd-arm64": "0.28.1", "@esbuild/openbsd-x64": "0.28.1", "@esbuild/openharmony-arm64": "0.28.1", "@esbuild/sunos-x64": "0.28.1", "@esbuild/win32-arm64": "0.28.1", "@esbuild/win32-ia32": "0.28.1", "@esbuild/win32-x64": "0.28.1" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw=="], - "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "vitest/@vitest/expect": ["@vitest/expect@4.1.9", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA=="], "vitest/@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="], - "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "wsl-utils/is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], + "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "@dotenvx/dotenvx/execa/get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="], @@ -1533,6 +1575,10 @@ "@dotenvx/dotenvx/open/define-lazy-prop": ["define-lazy-prop@2.0.0", "", {}, "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og=="], + "@dotenvx/dotenvx/open/is-docker": ["is-docker@2.2.1", "", { "bin": { "is-docker": "cli.js" } }, "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ=="], + + "@dotenvx/dotenvx/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -1585,122 +1631,68 @@ "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "drizzle-kit/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "drizzle-kit/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "drizzle-kit/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "drizzle-kit/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "drizzle-kit/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "drizzle-kit/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "drizzle-kit/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "drizzle-kit/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "drizzle-kit/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "drizzle-kit/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "drizzle-kit/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "drizzle-kit/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "drizzle-kit/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "drizzle-kit/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "drizzle-kit/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "drizzle-kit/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "drizzle-kit/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "drizzle-kit/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "drizzle-kit/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "drizzle-kit/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "drizzle-kit/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "drizzle-kit/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "drizzle-kit/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "drizzle-kit/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "drizzle-kit/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "drizzle-kit/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "enquirer/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ora/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "storybook/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - - "storybook/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - - "storybook/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - - "storybook/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - - "storybook/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - - "storybook/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - - "storybook/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - - "storybook/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - - "storybook/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - - "storybook/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - - "storybook/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - - "storybook/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - - "storybook/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - - "storybook/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - - "storybook/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - - "storybook/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - - "storybook/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - - "storybook/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - - "storybook/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - - "storybook/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - - "storybook/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - - "storybook/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - - "storybook/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - - "storybook/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - - "storybook/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - - "storybook/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], - - "storybook/open/wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="], + "shadcn/open/wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.28.1", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ=="], - - "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.28.1", "", { "os": "android", "cpu": "arm" }, "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ=="], - - "tsx/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.28.1", "", { "os": "android", "cpu": "arm64" }, "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg=="], - - "tsx/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.28.1", "", { "os": "android", "cpu": "x64" }, "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng=="], - - "tsx/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.28.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q=="], - - "tsx/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.28.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ=="], - - "tsx/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.28.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw=="], - - "tsx/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.28.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ=="], - - "tsx/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.28.1", "", { "os": "linux", "cpu": "arm" }, "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ=="], - - "tsx/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.28.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g=="], - - "tsx/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.28.1", "", { "os": "linux", "cpu": "ia32" }, "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w=="], - - "tsx/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg=="], - - "tsx/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ=="], - - "tsx/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.28.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ=="], - - "tsx/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.28.1", "", { "os": "linux", "cpu": "none" }, "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ=="], - - "tsx/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.28.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag=="], - - "tsx/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.28.1", "", { "os": "linux", "cpu": "x64" }, "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA=="], - - "tsx/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw=="], - - "tsx/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.28.1", "", { "os": "none", "cpu": "x64" }, "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg=="], - - "tsx/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.28.1", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q=="], - - "tsx/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.28.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw=="], - - "tsx/esbuild/@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.28.1", "", { "os": "none", "cpu": "arm64" }, "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg=="], - - "tsx/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.28.1", "", { "os": "sunos", "cpu": "x64" }, "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ=="], - - "tsx/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.28.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA=="], - - "tsx/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.28.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg=="], - - "tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.28.1", "", { "os": "win32", "cpu": "x64" }, "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A=="], - "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], - - "storybook/open/wsl-utils/is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="], } } diff --git a/components.json b/packages/ui/components.json similarity index 67% rename from components.json rename to packages/ui/components.json index f8d8d59..d951369 100644 --- a/components.json +++ b/packages/ui/components.json @@ -5,7 +5,7 @@ "tsx": true, "tailwind": { "config": "", - "css": "src/app.css", + "css": "src/styles.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" @@ -13,11 +13,11 @@ "iconLibrary": "lucide", "rtl": false, "aliases": { - "components": "$lib/components", - "utils": "$lib/utils", - "ui": "$lib/components/ui", - "lib": "$lib/.", - "hooks": "$lib/hooks" + "components": "src/components", + "utils": "src/utils", + "ui": "src/primitives", + "lib": "src", + "hooks": "src/hooks" }, "menuColor": "default", "menuAccent": "subtle", diff --git a/packages/ui/package.json b/packages/ui/package.json index 3afa92e..cb7febf 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -43,6 +43,8 @@ "@types/node": "^25.9.3", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", + "bun-types": "^1.3.14", + "storybook": "^10.4.6", "typescript": "^6.0.3", "vite": "^8.0.16", "vitest": "^4.1.9" diff --git a/packages/ui/src/css.d.ts b/packages/ui/src/css.d.ts new file mode 100644 index 0000000..cbe652d --- /dev/null +++ b/packages/ui/src/css.d.ts @@ -0,0 +1 @@ +declare module "*.css"; diff --git a/tsconfig.base.json b/tsconfig.base.json index 6e74a60..dcde3db 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -13,6 +13,6 @@ "sourceMap": true, "strict": true, "target": "ES2022", - "types": ["node", "bun-types", "react", "react-dom", "vite/client"] + "types": ["node", "bun-types", "react", "react-dom"] } } diff --git a/tsconfig.json b/tsconfig.json index ab92320..57ef1e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,35 +1,7 @@ { - "compilerOptions": { - "allowJs": true, - "baseUrl": ".", - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "ignoreDeprecations": "6.0", - "jsx": "react-jsx", - "module": "ESNext", - "moduleResolution": "bundler", - "paths": { - "$lib/*": ["src/lib/*"] - }, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "target": "ES2022", - "types": ["node", "bun-types", "react", "react-dom"] - }, - "include": [ - "src/**/*.ts", - "src/**/*.tsx", - "tests/**/*.ts", - "vite.config.ts", - "playwright.config.ts", - "drizzle.config.ts" - ], - "exclude": [ - "build", - "node_modules", - ".storybook" + "files": [], + "references": [ + { "path": "./apps/web" }, + { "path": "./packages/ui" } ] }