@handamade/psi-mcp 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.27.0",
2
+ "version": "0.28.0",
3
3
  "themes": [
4
4
  "light",
5
5
  "dark",
@@ -1178,7 +1178,7 @@
1178
1178
  },
1179
1179
  {
1180
1180
  "name": "NavBar",
1181
- "description": "Top navigation bar with brand, nav-link, and trailing-action slots.",
1181
+ "description": "Top navigation bar with brand, nav-link, and trailing-action slots. Plain text in a slot takes `--psi-navbar-fg`, the foreground of the theme on the bar, so `data-psi-theme` on the `NavBar` reaches it (D111).",
1182
1182
  "props": [
1183
1183
  {
1184
1184
  "name": "brand",
@@ -1250,7 +1250,7 @@
1250
1250
  "order": 3
1251
1251
  }
1252
1252
  ],
1253
- "doc": "<!-- Generated by emit-docs.ts — do not edit. -->\n# NavBar\n\nTop navigation bar with brand, nav-link, and trailing-action slots.\n\n## Props\n\n| Prop | Type | Default | Required | Description |\n|---|---|---|---|---|\n| `brand` | `ReactNode` | — | no | Brand slot (wordmark / logo link), leading edge. |\n| `actions` | `ReactNode` | — | no | Trailing actions slot (theme switch, CTA). |\n| `children` | `ReactNode` | — | no | Nav links. The `<nav>` renders only when there are children, so a bar with a brand and actions and no links has no empty landmark (D88). |\n| `navLabel` | `string` | — | no | Accessible name of the links' `<nav>` (its `aria-label`). Rest props land on the `<header>`, so this is the only way to label the `<nav>` (D88). |\n| `fluid` | `boolean` | false | no | Lay the row out at full width: it drops `psi-container` (the centred max-width column) and keeps the `--psi-gutter` side padding. For an application header over a full-width frame (D88). |\n| `ref` | `Ref<HTMLElement>` | — | no | Forwarded ref to the header element. |\n| `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |\n\n## Theming\n\nOverride `--psi-navbar-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).\n\n\n\n## Rules\n\n- One accent per visual group; everything else neutral or ghost.\n- danger only for actions with real consequences.\n- Sizes are px numbers (24|32|40|48), never S/M/L.\n- Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.\n- Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.\n- --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).\n- Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.\n- Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"start\" of Fields with one-line labels, where a field's error line is the Field's own and hangs below its control (D87, D92).\n- Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.\n"
1253
+ "doc": "<!-- Generated by emit-docs.ts — do not edit. -->\n# NavBar\n\nTop navigation bar with brand, nav-link, and trailing-action slots. Plain text in a slot takes `--psi-navbar-fg`, the foreground of the theme on the bar, so `data-psi-theme` on the `NavBar` reaches it (D111).\n\n## Props\n\n| Prop | Type | Default | Required | Description |\n|---|---|---|---|---|\n| `brand` | `ReactNode` | — | no | Brand slot (wordmark / logo link), leading edge. |\n| `actions` | `ReactNode` | — | no | Trailing actions slot (theme switch, CTA). |\n| `children` | `ReactNode` | — | no | Nav links. The `<nav>` renders only when there are children, so a bar with a brand and actions and no links has no empty landmark (D88). |\n| `navLabel` | `string` | — | no | Accessible name of the links' `<nav>` (its `aria-label`). Rest props land on the `<header>`, so this is the only way to label the `<nav>` (D88). |\n| `fluid` | `boolean` | false | no | Lay the row out at full width: it drops `psi-container` (the centred max-width column) and keeps the `--psi-gutter` side padding. For an application header over a full-width frame (D88). |\n| `ref` | `Ref<HTMLElement>` | — | no | Forwarded ref to the header element. |\n| `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |\n\n## Theming\n\nOverride `--psi-navbar-*` custom properties at any scope; interactive states derive automatically (L - 0.04 hover, L - 0.08 active).\n\n\n\n## Rules\n\n- One accent per visual group; everything else neutral or ghost.\n- danger only for actions with real consequences.\n- Sizes are px numbers (24|32|40|48), never S/M/L.\n- Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.\n- Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.\n- --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).\n- Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.\n- Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"start\" of Fields with one-line labels, where a field's error line is the Field's own and hangs below its control (D87, D92).\n- Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.\n"
1254
1254
  },
1255
1255
  {
1256
1256
  "name": "SkipLink",
@@ -1424,7 +1424,7 @@
1424
1424
  },
1425
1425
  {
1426
1426
  "name": "AppShell",
1427
- "description": "The frame of an application (D88): a header row over a sidebar and a `<main>` that each scroll on their own, filling the viewport (`100dvh`). Because the header has a row of its own and `main` is its own scroller, nothing scrolls under the header, so a focused element is never obscured by it (WCAG 2.2 *Focus Not Obscured*) without `scroll-padding`. `main` carries `id={mainId}` and `tabIndex={-1}`: the target of a `SkipLink`, not a tab stop; focused by the skip link it draws the shared focus ring inside its box. A closed sidebar is `hidden`, not narrowed to icons; there is no responsive drawer and no stored open state, so this is a desktop frame. A page whose document must scroll as a whole does not use `AppShell`. Mount it at the root of the page with no margin or padding around it: it is `100dvh` tall, so anything around it makes the document scroll and slides the frame. Do not place a `ToastRegion` at `top-start` in an app with a skip link: the native top layer paints over the focused link.",
1427
+ "description": "The frame of an application (D88): a header row over a sidebar and a `<main>` that each scroll on their own, filling the viewport (`100dvh`). Because the header has a row of its own and `main` is its own scroller, nothing scrolls under the header, so a focused element is never obscured by it (WCAG 2.2 *Focus Not Obscured*) without `scroll-padding`. `main` carries `id={mainId}` and `tabIndex={-1}`: the target of a `SkipLink`, not a tab stop; focused by the skip link it draws the shared focus ring inside its box. A closed sidebar is `hidden`, not narrowed to icons; there is no responsive drawer and no stored open state, so this is a desktop frame. A page whose document must scroll as a whole does not use `AppShell`. Mount it at the root of the page with no margin or padding around it: it is `100dvh` tall, so anything around it makes the document scroll and slides the frame. Do not place a `ToastRegion` at `top-start` in an app with a skip link: the native top layer paints over the focused link. A theme can make `main` a surface beside a one-colour header and sidebar, with no CSS of the consumer's (D110): `--psi-app-shell-bg`, which must equal the canvas of the theme on the header and the sidebar, `--psi-app-shell-main-bg` and `--psi-app-shell-main-radius`. All three paint nothing by default. The corner is square unless a header and a sidebar both meet `main`. Put the header under that theme with `data-psi-theme` on the `NavBar`, whose rest props land on its `<header>`, and remove its line with `--psi-navbar-border-width: 0`.",
1428
1428
  "props": [
1429
1429
  {
1430
1430
  "name": "skipLink",
@@ -1535,7 +1535,7 @@
1535
1535
  "order": 4
1536
1536
  }
1537
1537
  ],
1538
- "doc": "<!-- Generated by emit-docs.ts — do not edit. -->\n# AppShell\n\nThe frame of an application (D88): a header row over a sidebar and a `<main>` that each scroll on their own, filling the viewport (`100dvh`). Because the header has a row of its own and `main` is its own scroller, nothing scrolls under the header, so a focused element is never obscured by it (WCAG 2.2 *Focus Not Obscured*) without `scroll-padding`. `main` carries `id={mainId}` and `tabIndex={-1}`: the target of a `SkipLink`, not a tab stop; focused by the skip link it draws the shared focus ring inside its box. A closed sidebar is `hidden`, not narrowed to icons; there is no responsive drawer and no stored open state, so this is a desktop frame. A page whose document must scroll as a whole does not use `AppShell`. Mount it at the root of the page with no margin or padding around it: it is `100dvh` tall, so anything around it makes the document scroll and slides the frame. Do not place a `ToastRegion` at `top-start` in an app with a skip link: the native top layer paints over the focused link.\n\n## Props\n\n| Prop | Type | Default | Required | Description |\n|---|---|---|---|---|\n| `skipLink` | `ReactNode` | — | no | A `SkipLink` (D88), rendered first so it is the first tab stop. Point its `href` at `#` plus `mainId`. |\n| `header` | `ReactNode` | — | no | The header row, usually a `NavBar` (D88). It sits in a row of its own above the sidebar and `main`, so nothing scrolls under it. |\n| `sidebar` | `ReactNode` | — | no | The sidebar's content, usually a `NavTree` (D88). The shell wraps it in a `<div>`, not an `<aside>`: a `NavTree` is already the landmark. |\n| `children` | `ReactNode` | — | no | The page, rendered inside `<main>` (D88). |\n| `sidebarOpen` | `boolean` | true | no | Whether the sidebar is shown. Controlled: wire your Menu toggle's `aria-expanded` and this to one piece of state. Closed, the sidebar is `hidden`, so it leaves the layout and the accessibility tree, and stays in the DOM so the toggle's `aria-controls` keeps naming an element (D88). |\n| `sidebarTheme` | `string` | — | no | A theme name for the sidebar alone, such as `\"dark\"`: sets `data-psi-theme` on the sidebar element, so every token inside re-resolves under that theme while the rest of the page keeps its own (D88). The sidebar paints `--psi-bg-primary` itself; a theme paints no background. |\n| `sidebarId` | `string` | sidebar | no | `id` of the sidebar element, which a toggle names with `aria-controls` (D88). |\n| `mainId` | `string` | main | no | `id` of `<main>`, which a `SkipLink`'s `href` names (D88). |\n| `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |\n| `ref` | `Ref<HTMLDivElement>` | — | no | Forwarded ref to the root `<div>`. |\n\n## Keyboard & assistive tech\n\n| Keys | Behavior |\n|---|---|\n| Tab | The first stop is the skip link (when given), then the header, the sidebar's links and buttons, and the links of main, in document order. The shell adds no stop of its own: main has tabIndex={-1}. |\n| Enter (on the skip link) | Follows the fragment href: focus moves to main, drawing the shared focus ring inside its box, and the next Tab continues from main's first link. |\n\nA grid filling the viewport: the header sits in a row of its own and main is its own scroller, so nothing scrolls under the header and a focused element is never obscured by it (WCAG 2.2 Focus Not Obscured) without scroll-padding, which does nothing on a main that is not the document's scroller (D88). main carries id={mainId} and tabIndex={-1}, the skip link's target. A closed sidebar is `hidden`, so it leaves the layout and the accessibility tree while its id stays in the DOM for a toggle's aria-controls; the toggle's aria-expanded and sidebarOpen are wired by the consumer, since the shell is controlled and stores nothing. The sidebar is a div, not an aside: the NavTree inside is already the navigation landmark. sidebarTheme sets data-psi-theme on the sidebar alone, so its tokens re-resolve under that theme in a page that keeps its own; the sidebar paints --psi-bg-primary itself. Desktop frame only: no responsive drawer. Do not place a ToastRegion at top-start in an app with a skip link.\n\n## Theming\n\nOverride `--psi-app-shell-*` custom properties at any scope.\n\n\n\n## Rules\n\n- One accent per visual group; everything else neutral or ghost.\n- danger only for actions with real consequences.\n- Sizes are px numbers (24|32|40|48), never S/M/L.\n- Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.\n- Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.\n- --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).\n- Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.\n- Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"start\" of Fields with one-line labels, where a field's error line is the Field's own and hangs below its control (D87, D92).\n- Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.\n"
1538
+ "doc": "<!-- Generated by emit-docs.ts — do not edit. -->\n# AppShell\n\nThe frame of an application (D88): a header row over a sidebar and a `<main>` that each scroll on their own, filling the viewport (`100dvh`). Because the header has a row of its own and `main` is its own scroller, nothing scrolls under the header, so a focused element is never obscured by it (WCAG 2.2 *Focus Not Obscured*) without `scroll-padding`. `main` carries `id={mainId}` and `tabIndex={-1}`: the target of a `SkipLink`, not a tab stop; focused by the skip link it draws the shared focus ring inside its box. A closed sidebar is `hidden`, not narrowed to icons; there is no responsive drawer and no stored open state, so this is a desktop frame. A page whose document must scroll as a whole does not use `AppShell`. Mount it at the root of the page with no margin or padding around it: it is `100dvh` tall, so anything around it makes the document scroll and slides the frame. Do not place a `ToastRegion` at `top-start` in an app with a skip link: the native top layer paints over the focused link. A theme can make `main` a surface beside a one-colour header and sidebar, with no CSS of the consumer's (D110): `--psi-app-shell-bg`, which must equal the canvas of the theme on the header and the sidebar, `--psi-app-shell-main-bg` and `--psi-app-shell-main-radius`. All three paint nothing by default. The corner is square unless a header and a sidebar both meet `main`. Put the header under that theme with `data-psi-theme` on the `NavBar`, whose rest props land on its `<header>`, and remove its line with `--psi-navbar-border-width: 0`.\n\n## Props\n\n| Prop | Type | Default | Required | Description |\n|---|---|---|---|---|\n| `skipLink` | `ReactNode` | — | no | A `SkipLink` (D88), rendered first so it is the first tab stop. Point its `href` at `#` plus `mainId`. |\n| `header` | `ReactNode` | — | no | The header row, usually a `NavBar` (D88). It sits in a row of its own above the sidebar and `main`, so nothing scrolls under it. |\n| `sidebar` | `ReactNode` | — | no | The sidebar's content, usually a `NavTree` (D88). The shell wraps it in a `<div>`, not an `<aside>`: a `NavTree` is already the landmark. |\n| `children` | `ReactNode` | — | no | The page, rendered inside `<main>` (D88). |\n| `sidebarOpen` | `boolean` | true | no | Whether the sidebar is shown. Controlled: wire your Menu toggle's `aria-expanded` and this to one piece of state. Closed, the sidebar is `hidden`, so it leaves the layout and the accessibility tree, and stays in the DOM so the toggle's `aria-controls` keeps naming an element (D88). |\n| `sidebarTheme` | `string` | — | no | A theme name for the sidebar alone, such as `\"dark\"`: sets `data-psi-theme` on the sidebar element, so every token inside re-resolves under that theme while the rest of the page keeps its own (D88). The sidebar paints `--psi-bg-primary` itself; a theme paints no background. |\n| `sidebarId` | `string` | sidebar | no | `id` of the sidebar element, which a toggle names with `aria-controls` (D88). |\n| `mainId` | `string` | main | no | `id` of `<main>`, which a `SkipLink`'s `href` names (D88). |\n| `className` | `string` | — | no | Additional CSS class name(s) merged onto the component's root element. |\n| `ref` | `Ref<HTMLDivElement>` | — | no | Forwarded ref to the root `<div>`. |\n\n## Keyboard & assistive tech\n\n| Keys | Behavior |\n|---|---|\n| Tab | The first stop is the skip link (when given), then the header, the sidebar's links and buttons, and the links of main, in document order. The shell adds no stop of its own: main has tabIndex={-1}. |\n| Enter (on the skip link) | Follows the fragment href: focus moves to main, drawing the shared focus ring inside its box, and the next Tab continues from main's first link. |\n\nA grid filling the viewport: the header sits in a row of its own and main is its own scroller, so nothing scrolls under the header and a focused element is never obscured by it (WCAG 2.2 Focus Not Obscured) without scroll-padding, which does nothing on a main that is not the document's scroller (D88). main carries id={mainId} and tabIndex={-1}, the skip link's target. A closed sidebar is `hidden`, so it leaves the layout and the accessibility tree while its id stays in the DOM for a toggle's aria-controls; the toggle's aria-expanded and sidebarOpen are wired by the consumer, since the shell is controlled and stores nothing. The sidebar is a div, not an aside: the NavTree inside is already the navigation landmark. sidebarTheme sets data-psi-theme on the sidebar alone, so its tokens re-resolve under that theme in a page that keeps its own; the sidebar paints --psi-bg-primary itself. Desktop frame only: no responsive drawer. Do not place a ToastRegion at top-start in an app with a skip link. main's rounded corner (--psi-app-shell-main-radius, D110) keeps the skip link's ring: the inset outline follows the curve, measured in Chromium in LTR and RTL.\n\n## Theming\n\nOverride `--psi-app-shell-*` custom properties at any scope.\n\n\n\n## Rules\n\n- One accent per visual group; everything else neutral or ghost.\n- danger only for actions with real consequences.\n- Sizes are px numbers (24|32|40|48), never S/M/L.\n- Typography tokens are --psi-text-{size}-{lineHeight}-{weight}.\n- Override component tokens (--psi-{component}-*), not semantic tokens, for one-off theming.\n- --psi-button-font overrides button typography across all sizes (documented D34 override; ember → mono).\n- Wrap labeled form controls in Field — label association, description/error line, aria-describedby and aria-invalid come wired; don't hand-roll label+message rows.\n- Field is for a control with a *visible* label. A toolbar filter control named by aria-label or placeholder takes neither a Field nor a hand-rolled label row — compare filter-toolbar (no labels) with table-pagination (\"Rows per page\" visible, so Field). Filters with visible labels submitted together by a Find button are filter-form: a Toolbar as=\"form\" align=\"start\" of Fields with one-line labels, where a field's error line is the Field's own and hangs below its control (D87, D92).\n- Use Dialog for blocking modal flows — title/footer slots, dismissible gate; danger stays on the footer Buttons, one accent per group.\n"
1539
1539
  },
1540
1540
  {
1541
1541
  "name": "AspectRatio",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@handamade/psi-mcp",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "license": "MIT",
5
5
  "description": "MCP server and agent-context generator for the Psi (Ψ) design system",
6
6
  "type": "module",
@@ -33,8 +33,8 @@
33
33
  "tsx": "^4.22.4",
34
34
  "typescript": "^5.7.0",
35
35
  "vitest": "^3.2.6",
36
- "@handamade/psi-react": "0.27.0",
37
- "@handamade/psi-tokens": "0.27.0"
36
+ "@handamade/psi-tokens": "0.28.0",
37
+ "@handamade/psi-react": "0.28.0"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "tsx scripts/build-index.ts && tsc -p tsconfig.build.json"