diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e773fc1..d3cdd4cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,11 @@ contract — modules that are not exported are internal and may change in any re `StepListItem` and `StepDisplayStatus` stay public from `ui/step-list`; `GateShellModel` and `GateKind` from `ui/gate-block`. For `cn`, compose `clsx` and `tailwind-merge` directly: `twMerge(clsx(...inputs))`. +- **Four peers are optional and their modules are subpath-only.** `@radix-ui/react-dialog`, + `@radix-ui/react-dropdown-menu`, `@radix-ui/react-tooltip` and `sonner` move to + `peerDependenciesMeta`. `ui/dialog`, `ui/command-palette`, `ui/mic-permission-dialog`, + `ui/menu`, `ui/tooltip` and `ui/toast` leave the root barrel; import them by subpath + (`import { Toaster, toast } from "@corbits/react-ui/ui/toast"`). ### Added diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index c1da5e20..9743ee43 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -10,10 +10,13 @@ Why the library exists lives in [PRODUCT.md](./PRODUCT.md). - Package name `@corbits/react-ui`, ESM (`"type": "module"`), license `LGPL-2.1-only`. - Required peers: `react` and `react-dom` (`^18.2.0 || ^19.0.0`), - `lucide-react` (`^0.545.0 || ^1.0.0`), `sonner` (`^2.0.7`), - `@radix-ui/react-dialog` (`^1.1.15`), `@radix-ui/react-dropdown-menu` - (`^2.1.16`), `@radix-ui/react-slot` (`^1.2.3`), - `@radix-ui/react-tooltip` (`^1.2.8`). + `lucide-react` (`^0.545.0 || ^1.0.0`), `@radix-ui/react-slot` (`^1.2.3`). +- Optional peers (`peerDependenciesMeta`), each imported only by subpath-only + modules kept out of the root barrel (`BARREL_EXCLUDED`): + `@radix-ui/react-dialog` (`^1.1.15`; `ui/dialog`, `ui/command-palette`, + `ui/mic-permission-dialog`), `@radix-ui/react-dropdown-menu` (`^2.1.16`; + `ui/menu`), `@radix-ui/react-tooltip` (`^1.2.8`; `ui/tooltip`), `sonner` + (`^2.0.7`; `ui/toast`). - Direct dependencies: `class-variance-authority`, `clsx`, `tailwind-merge`. - `sideEffects` is CSS-only (`**/*.css`). `files` publishes `dist/` only. diff --git a/README.md b/README.md index 96fd562d..72616506 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,16 @@ React components for agent and workflow surfaces — chat, runs, artifacts, anal ## Runtime support -Node >= 24 consumes built `dist/`. Peers: `react` and `react-dom` (18 or 19), `lucide-react`, `sonner`, `@radix-ui/react-dialog`, `@radix-ui/react-dropdown-menu`, `@radix-ui/react-slot`, `@radix-ui/react-tooltip`. +Node >= 24 consumes built `dist/`. Required peers: `react` and `react-dom` (18 or 19), `lucide-react`, `@radix-ui/react-slot`. + +Optional peers, needed only by the subpaths that import them. These modules are not in the root barrel; import them by subpath: + +| Peer | Subpaths | +| --- | --- | +| `@radix-ui/react-dialog` | `ui/dialog`, `ui/command-palette`, `ui/mic-permission-dialog` | +| `@radix-ui/react-dropdown-menu` | `ui/menu` | +| `@radix-ui/react-tooltip` | `ui/tooltip` | +| `sonner` | `ui/toast` | ## Quickstart @@ -26,7 +35,8 @@ Import the stylesheet once at the app root, before your own CSS (no Tailwind bui ```tsx import "@corbits/react-ui/styles.css"; -import { ThemeProvider, Toaster, toast } from "@corbits/react-ui"; +import { ThemeProvider } from "@corbits/react-ui"; +import { Toaster, toast } from "@corbits/react-ui/ui/toast"; export function Root() { return ( @@ -70,7 +80,7 @@ export function NotFoundPage() { } ``` -Every component is importable by subpath (`@corbits/react-ui/ui/button`) or from the root (`@corbits/react-ui`). +Every component is importable by subpath (`@corbits/react-ui/ui/button`); all but the optional-peer modules above are also exported from the root (`@corbits/react-ui`). The brand faces (Red Hat Display, Space Mono) are named by the theme but not bundled. Load them yourself, or the stack falls through to system fonts; to use a face loaded under a generated name, override `--font-sans` / `--font-mono`. diff --git a/bun.lock b/bun.lock index 77cd011b..264ba480 100644 --- a/bun.lock +++ b/bun.lock @@ -44,6 +44,12 @@ "react-dom": "^18.2.0 || ^19.0.0", "sonner": "^2.0.7", }, + "optionalPeers": [ + "@radix-ui/react-dialog", + "@radix-ui/react-dropdown-menu", + "@radix-ui/react-tooltip", + "sonner", + ], }, }, "packages": { diff --git a/package.json b/package.json index dab8de38..3301f364 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,20 @@ "react-dom": "^18.2.0 || ^19.0.0", "sonner": "^2.0.7" }, + "peerDependenciesMeta": { + "@radix-ui/react-dialog": { + "optional": true + }, + "@radix-ui/react-dropdown-menu": { + "optional": true + }, + "@radix-ui/react-tooltip": { + "optional": true + }, + "sonner": { + "optional": true + } + }, "devDependencies": { "@eslint/js": "^9.39.1", "@happy-dom/global-registrator": "^20.11.2", diff --git a/scripts/dep-guard.mjs b/scripts/dep-guard.mjs index 488423ed..c508fbb0 100644 --- a/scripts/dep-guard.mjs +++ b/scripts/dep-guard.mjs @@ -17,7 +17,10 @@ import { dirname, join, relative, resolve } from "node:path"; const ROOT = new URL("../src", import.meta.url).pathname; -const OPTIONAL_PEERS = []; +const PKG = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")); +const OPTIONAL_PEERS = Object.keys(PKG.peerDependenciesMeta).filter( + (peer) => PKG.peerDependenciesMeta[peer].optional, +); const RULES = [ { label: "@workbench/*", pattern: /["'](@workbench\/[^"']*)["']/g, allow: () => false }, diff --git a/scripts/generate-exports.mjs b/scripts/generate-exports.mjs index 2abdd5d8..b3e1e284 100644 --- a/scripts/generate-exports.mjs +++ b/scripts/generate-exports.mjs @@ -30,7 +30,14 @@ const INTERNAL = new Set([ "lib/workflow-registry", "ui/chat-dock-timing", ]); -const BARREL_EXCLUDED = new Set(); +const BARREL_EXCLUDED = new Set([ + "ui/command-palette", + "ui/dialog", + "ui/menu", + "ui/mic-permission-dialog", + "ui/toast", + "ui/tooltip", +]); const walk = (dir) => readdirSync(dir).flatMap((entry) => { diff --git a/src/index.ts b/src/index.ts index a7064e55..98ff32a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,12 @@ // on purpose and must be imported by their subpath. The barrel is a single // module: anything it re-exports is loaded by any root import, so one such // re-export would make the optional peer mandatory for everyone. +// @corbits/react-ui/ui/command-palette +// @corbits/react-ui/ui/dialog +// @corbits/react-ui/ui/menu +// @corbits/react-ui/ui/mic-permission-dialog +// @corbits/react-ui/ui/toast +// @corbits/react-ui/ui/tooltip export * from "./blocks/login/auth-layout.js"; export * from "./blocks/login/login-form.js"; @@ -49,12 +55,10 @@ export * from "./ui/chat-input.js"; export * from "./ui/chat-panel.js"; export * from "./ui/chat-thread.js"; export * from "./ui/checkbox.js"; -export * from "./ui/command-palette.js"; export * from "./ui/compare-body.js"; export * from "./ui/confirm-button.js"; export * from "./ui/corbits-mark.js"; export * from "./ui/csv-table.js"; -export * from "./ui/dialog.js"; export * from "./ui/dictation-status-line.js"; export * from "./ui/dither-canvas.js"; export * from "./ui/embed-body.js"; @@ -72,10 +76,8 @@ export * from "./ui/library-page-header.js"; export * from "./ui/list-detail.js"; export * from "./ui/live-run-banner.js"; export * from "./ui/live-status-line.js"; -export * from "./ui/menu.js"; export * from "./ui/message-bubble.js"; export * from "./ui/mic-button.js"; -export * from "./ui/mic-permission-dialog.js"; export * from "./ui/notifications-bell.js"; export * from "./ui/now-cards.js"; export * from "./ui/page-panel.js"; @@ -114,12 +116,10 @@ export * from "./ui/thinking-indicator.js"; export * from "./ui/thinking-label.js"; export * from "./ui/thinking-mark.js"; export * from "./ui/time-series-chart.js"; -export * from "./ui/toast.js"; export * from "./ui/token-mosaic.js"; export * from "./ui/tool-block.js"; export * from "./ui/tool-narrative.js"; export * from "./ui/tool-picker.js"; -export * from "./ui/tooltip.js"; export * from "./ui/top-bar.js"; export * from "./ui/trace-waterfall.js"; export * from "./ui/typing-indicator.js";