@godxjp/ui-mcp 31.17.1 → 31.19.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.
- package/dist/index.js +23 -7
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -875,7 +875,23 @@ import { Flex } from "@godxjp/ui/layout";
|
|
|
875
875
|
|
|
876
876
|
<CodeBlock maxHeight="sm" language="json" aria-label="Response body">{body}</CodeBlock>
|
|
877
877
|
<CodeBlock size="xs" maxHeight="md" aria-label="Console">{consoleText}</CodeBlock>
|
|
878
|
-
<CodeBlock copyable language="bash">pnpm add @godxjp/ui</CodeBlock>`,storyPath:"data-display/CodeBlock.stories.tsx",rules:[]},{name:"
|
|
878
|
+
<CodeBlock copyable language="bash">pnpm add @godxjp/ui</CodeBlock>`,storyPath:"data-display/CodeBlock.stories.tsx",rules:[]},{name:"Markdown",group:"data-display",importPath:"@godxjp/markdown",tagline:"The one Markdown renderer for GoDX apps (sibling package `@godxjp/markdown`, versioned with the kit): GFM + GitHub heading anchors, one sanitiser schema (no raw HTML; http/https/mailto/relative URLs only), and ```mermaid drawn only after its SVG passes a fail-closed gate (gh#1108). Wrap it in `Prose`.",props:[{name:"children",type:"string",required:!0,description:"The Markdown source."},{name:"remarkPlugins",type:"PluggableList",description:"Host markers (callouts, embeds), run after GFM. Their output is still sanitised \u2014 allow marker attributes with `schema`."},{name:"schema",type:"{ tagNames?: string[]; attributes?: Record<string, \u2026> }",description:"Tags / attributes a host plugin needs. ADDITIVE: it can never widen the URL policy or bring raw HTML back."},{name:"resolveUrl",type:"(url, key) => string | undefined",description:"Map a host scheme (`asset:\u2026`, wiki links) to a real URL. Runs BEFORE the sanitiser, which then judges the result."},{name:"headingId",type:"({ depth, text, index }) => string | undefined",description:"Server-assigned anchors instead of GitHub slugs."},{name:"components",type:"Components",description:"Element overrides (react-markdown). A host `pre` replaces the Mermaid default."},{name:"rehypePlugins",type:"PluggableList",description:"Run AFTER the sanitiser \u2014 presentation only."},{name:"mermaid",type:"boolean",defaultValue:"true",description:"`false` keeps ```mermaid fences as code. Diagrams need the optional peer `mermaid`."}],usage:["DO `<Prose><Markdown>{body}</Markdown></Prose>` for every rendered body (wiki page, issue, mail).","DO record `MARKDOWN_FORMAT` and `RENDERER_VERSION` on stored versions.","DON'T assemble react-markdown + rehype-sanitize per app, add rehype-raw, or render Mermaid yourself \u2014 `checkMermaidSvg` is the gate."],related:["Prose \u2014 the typography wrapper this renders into.","MarkdownEditor \u2014 editing; its preview is this renderer.","CodeBlock \u2014 a single block of preformatted text, not a document."],example:`import { Prose } from "@godxjp/ui/data-display";
|
|
879
|
+
import { Markdown } from "@godxjp/markdown";
|
|
880
|
+
|
|
881
|
+
<Prose measure="narrow">
|
|
882
|
+
<Markdown resolveUrl={(url) => (url.startsWith("asset:") ? assetUrl(url) : undefined)}>
|
|
883
|
+
{page.body}
|
|
884
|
+
</Markdown>
|
|
885
|
+
</Prose>`,docPath:"docs/data-display/markdown.tsx",storyPath:"data-display/markdown.tsx",rules:[]},{name:"MarkdownEditor",group:"data-entry",importPath:"@godxjp/editor",tagline:"Markdown editor (sibling package `@godxjp/editor`, versioned with the kit): the kit's Textarea with a WAI-ARIA formatting toolbar, write / preview / side by side (preview via `@godxjp/markdown`), IME-safe \u2318/Ctrl+B\xB7I\xB7K, one undo step per toolbar edit, and paste / drop / attach through a host `upload` (gh#1109).",props:[{name:"value",type:"string",description:"Controlled Markdown text (with `onValueChange`)."},{name:"defaultValue",type:"string",description:"Uncontrolled initial text."},{name:"onValueChange",type:"(value: string) => void",description:"Every change: typing, toolbar, uploads, undo."},{name:"mode",type:'"write" | "preview" | "split"',description:"Controlled view (with `onModeChange`); `defaultMode` for uncontrolled. Default `write`."},{name:"upload",type:"(file: File) => Promise<{ url: string; name?: string }>",description:"Stores a pasted / dropped / attached file; the HOST decides where. A placeholder becomes `` (images) or `[name](url)`; a failure removes it and names the file."},{name:"uploadBlockedReason",type:"string | null",description:"Refuses files with this message, with or without `upload`; the attach button carries the reason and shows it instead of opening the picker (gh#1114)."},{name:"renderPreview",type:"(value: string) => ReactNode",description:"Replace the preview (an app with its own embeds)."},{name:"actions",type:"{ key, label, icon, run(api) }[]",description:"Extra toolbar actions \u2014 the extension point for new block types (`api`: value, selection, edit, insert, focus)."},{name:"labels",type:"Partial<MarkdownEditorLabels>",description:"Override any string (defaults: the kit's ja / en / vi catalogue)."}],usage:["DO put it inside `FormField` \u2014 `id` / `aria-*` reach the textarea.","DO keep a host autocomplete (ChatSuggestion) by passing its `onKeyDown`: it runs first, and `preventDefault()` skips the editor's shortcut.","DON'T build a toolbar over a bare Textarea, or a rich-text surface \u2014 this is the shared editor."],related:["Textarea \u2014 plain multi-line text; this is Textarea plus Markdown tooling.","Markdown \u2014 rendering a stored body."],example:`import { MarkdownEditor } from "@godxjp/editor";
|
|
886
|
+
import { FormField } from "@godxjp/ui/data-entry";
|
|
887
|
+
|
|
888
|
+
<FormField id="body" label="\u672C\u6587">
|
|
889
|
+
<MarkdownEditor
|
|
890
|
+
value={body}
|
|
891
|
+
onValueChange={setBody}
|
|
892
|
+
upload={async (file) => ({ url: await storage.put(file), name: file.name })}
|
|
893
|
+
/>
|
|
894
|
+
</FormField>`,docPath:"docs/data-entry/markdown-editor.tsx",storyPath:"data-entry/markdown-editor.tsx",rules:[]},{name:"Prose",group:"data-display",tagline:"Typography for rendered content (Markdown, CMS bodies, issue descriptions): styles the semantic HTML inside it from the tokens, with no opinion about where the HTML comes from. To RENDER Markdown, use the sibling package `@godxjp/markdown` inside it \u2014 `<Prose><Markdown>{body}</Markdown></Prose>` \u2014 the one GFM renderer + sanitiser + Mermaid gate shared by every GoDX app; do not assemble react-markdown + rehype-sanitize per app (gh#1108).",props:[{name:"size",type:'"sm" | "md"',defaultValue:'"md"',description:"Body size. `md` is the page body size (a wiki page); `sm` is the compact step (an issue description, a comment)."},{name:"imageSize",type:'"fit" | "original"',defaultValue:'"fit"',description:"`fit` scales images to the column; `original` shows them at their authored size and the container scrolls horizontally."},{name:"measure",type:'"narrow" | "medium" | "wide"',description:"Reading line length (gh#1112): caps the inline size at the `--page-measure-*` token \u2014 the same vocabulary and tokens as `Flex measure` / `PageContainer measure` (narrow 42rem, medium 48rem sit in the 45\u201375 character band). A cap, not a centred column: the body keeps its start edge under its heading. Use it on a document/wiki/decision body instead of narrowing the whole page column; title and metadata stay full width. Unset = full width."},{name:"children",type:"ReactNode",description:"The rendered content."},{name:"className",type:"string",description:"Extra classes on the container."}],usage:['DO import from `@godxjp/ui/data-display`: `import { Prose } from "@godxjp/ui/data-display";`',"DO wrap the OUTPUT of your Markdown pipeline (react-markdown + remark-gfm + rehype-sanitize), a sanitised CMS string via `dangerouslySetInnerHTML`, or plain JSX. Prose styles descendants: h1..h4, p, ul/ol/li, blockquote, code, pre, table/th/td, a, img, hr.","DO keep soft line breaks the pipeline's job (remark-breaks or the renderer's `breaks` option). Prose does not turn single newlines into `<br>`; it is typography, not parsing.","DON'T restate the heading scale, table cell measures or list rhythm with `[&_h1]:text-lg [&_td]:border \u2026` utilities: they copy token values and never follow a retune. Prose reads --heading-h1..h4, --table-cell-padding-* and --prose-* directly.","DO tune the link through --prose-link-color and --prose-link-decoration-line (gh#717). The ink defaults to hsl(var(--primary)) resolved at the anchor, so a scoped [data-tenant] re-tint reaches it; the resting underline defaults to `underline` and is a SEPARATE knob from --text-link-decoration-line, because Prose is running text and there the underline is a WCAG 1.4.1 requirement, not a taste.","DO style a state your own renderer knows about by marking the anchor and selecting it: `a[data-unresolved]` (a wiki link whose target does not exist yet), then re-declare --prose-link-color inside that selector. Prose writes data-* on its ROOT ONLY \u2014 never on a descendant \u2014 so every data-* on an `a` inside it is yours and stays selectable. That is a promise held by a test (prose-link-717.test.tsx), not a coincidence; there is no prop for it, the same way `TableRow data-expanded-row` is an attribute rather than an API.","DON'T sanitise inside Prose: it renders whatever HTML it is given. Sanitise before (rehype-sanitize, or the server).","DON'T use Prose for UI text (labels, descriptions, empty states): those are Text / Heading. Prose is for a document."],useCases:["A wiki page rendered from Markdown, at the page body size.",'A wiki page whose renderer marks links it knows something extra about: `<a data-unresolved="true">` for a target nobody has written yet, re-declaring --prose-link-color as --text-error inside `a[data-unresolved]` so it reads differently from a link that resolves.','An issue description or a comment in a tracker, `size="sm"`.',"A bug-report intake inbox that renders the report body (description, steps, environment table) sent by a browser extension.","A CMS article body delivered as sanitised HTML.","An email preview rendered from stored HTML."],related:["CodeBlock - a standalone block of preformatted text; Prose delegates its `pre` to the same tokens.","Text / Heading - UI copy, not a document.","LegalDocumentShell - a whole legal document with its own table of contents and section anchors; use Prose for the body of arbitrary rendered content.","ScrollArea - if a very long document needs its own scroll viewport, wrap Prose in ScrollArea."],example:`import { Prose } from "@godxjp/ui/data-display";
|
|
879
895
|
import Markdown from "react-markdown";
|
|
880
896
|
import remarkGfm from "remark-gfm";
|
|
881
897
|
|
|
@@ -1129,7 +1145,7 @@ export function PrioritySelect({ value, onValueChange }) {
|
|
|
1129
1145
|
// the row rhythm. A <div className="flex items-center gap-2"> around a bare <Label> loses all three.
|
|
1130
1146
|
<Field id="stackable" label="\u4ED6\u30AF\u30FC\u30DD\u30F3\u3068\u306E\u4F75\u7528\u3092\u8A31\u53EF" description="\u4F1A\u8A08\u6642\u306B\u81EA\u52D5\u3067\u5408\u7B97\u3055\u308C\u307E\u3059">
|
|
1131
1147
|
<Switch id="stackable" checked={stackable} onCheckedChange={setStackable} />
|
|
1132
|
-
</Field>`,storyPath:"data-entry/Switch.stories.tsx",rules:[]},{name:"Textarea",group:"data-entry",tagline:"Styled wrapper around native <textarea>. Pair with FormField for labelled fields.",props:[{name:"padRaw",type:"PadRawProp",description:"Measured padding escape, stamped on the field."},{name:"pad",type:"PadProp",description:"Instance padding with logical asymmetric sides."},{name:"status",type:'"error" | "warning"',description:"Validation state the field paints \u2014 Ant Design `status`. `error` also reports `aria-invalid`, so the red boundary and what a screen reader hears are one fact; `warning` paints only, because a warning is not a validity failure. antd's `success`/`validating` are not implemented: antd only draws them together with its `hasFeedback` icon slot, which FormField owns here."},{name:"variant",type:'"outlined" | "filled" | "borderless" | "default" | "ghost"',defaultValue:'"outlined"',description:"Chrome level \u2014 Ant Design `variant`. `outlined` is the historical field; `filled` swaps the boundary for a tinted surface (dense forms); `borderless` drops both, for a field inside a box that already draws one. antd's fourth member `underlined` is deliberately absent \u2014 a single bottom rule is a Material convention and SmartHR, the JP authority here, draws every field as a full box. This library's older `default`/`ghost` are still accepted and resolve to `outlined`/`borderless`."},{name:"size",type:'"sm" | "md" | "lg"',defaultValue:'"md"',description:"Control height tier \u2014 reads the shared `--control-height` ladder."},{name:"autoSize",type:"boolean | { minRows?: number; maxRows?: number }",description:"antd `autoSize`. `true` is `autoGrow`; the object form carries the row bounds with it, so `autoSize={{ minRows: 2, maxRows: 6 }}` is `autoGrow minRows={2} maxRows={6}`. An explicit `minRows`/`maxRows` still wins."},{name:"count",type:"{ max?: number; show?: boolean; formatter?: (info: { value: string; count: number; max?: number }) => React.ReactNode; strategy?: (value: string) => number }",description:"Character counter \u2014 Ant Design `count`. Counts CODE POINTS by default, so one emoji and one \u5168\u89D2 kanji are each worth one. It REPORTS an overrun (`data-exceeded`) and never edits the value: antd's `exceedFormatter` truncates while the user types, which in Japanese cuts a live IME conversion in half."},{name:"id",type:"string",description:"Associates with a <Label htmlFor>."},{name:"rows",type:"number",description:"Visible text rows."},{name:"value",type:"string",description:"Controlled value."},{name:"onChange",type:"React.ChangeEventHandler<HTMLTextAreaElement>",description:"Change handler."},{name:"allowClear",type:"boolean",defaultValue:"false",description:"Opt-in inline \u2715 at the top-end that clears the field while it holds text (controlled + uncontrolled). Off by default."},{name:"onClear",type:"() => void",description:"Called after the field is cleared via the inline \u2715 (requires `allowClear`)."},{name:"autoGrow",type:"boolean",defaultValue:"false",description:"The floor is `minRows` (or `rows`, when that is the only one given) and never undercuts the `--control-height` tier, so a resting one-row composer still lines up with the Button beside it; past `maxRows` the control stops growing and scrolls internally. Sizing happens in CSS from a hidden replica of the text, so it follows a paste, an IME composition, a programmatic value change, a font swap and a container resize \u2014 not just typing \u2014 and the component never writes `style.height`, never reads `scrollHeight` and never moves `scrollTop`. Works controlled and uncontrolled. Off by default: an existing Textarea keeps the exact geometry it has today."},{name:"minRows",type:"number",defaultValue:"1 (--textarea-autogrow-min-height-rows)",description:"Floor in text rows while `autoGrow`. Theme-global default is the `--textarea-autogrow-min-height-rows` token; this prop overrides it per instance. Ignored when `autoGrow` is false."},{name:"maxRows",type:"number",defaultValue:"8 (--textarea-autogrow-max-height-rows)",description:"Ceiling in text rows while `autoGrow`; beyond it the box stops growing and scrolls internally rather than pushing the page. Pass `0` for no ceiling \u2014 only correct inside an owning scroll container. Theme-global default is the `--textarea-autogrow-max-height-rows` token. Ignored when `autoGrow` is false."},{name:"onValueChange",type:"(value: string) => void",description:"Immediate value callback; native onChange remains supported. Shared form bindings are emitted once."}],usage:['DO reach for `variant="ghost"` ONLY when a parent surface already draws the box and owns focus \u2014 the composer Card, an inline edit cell. A standalone field keeps the default: without its own border it reads as plain text, not something you can type into.',"DO always wrap Textarea in FormField when it appears in a form \u2014 FormField clones aria-describedby, aria-required, and aria-invalid onto the child, giving error/helper announcements and screen-reader labelling for free. Pass matching id props to both.","DO use the godx-ui Textarea (`import { Textarea } from '@godxjp/ui/data-entry'`) \u2014 never a raw `<textarea>`. The component applies the `ui-control-multiline` token class that picks up density, focus-ring, and border tokens from the design system.","DO control the value with `value` + `onChange` in React-managed forms (e.g. Inertia `useForm`). Textarea is a plain `forwardRef` over the native element so it accepts all standard `HTMLTextAreaElement` attributes \u2014 `rows`, `maxLength`, `disabled`, `name`, `placeholder`, `readOnly` all pass through directly.","DO pass `name` when the textarea sits inside an HTML `<form>` for native form submission or when Inertia's `useForm` destructures field values by key \u2014 the `name` attribute maps the value into the form data bag.","DON'T apply manual height or padding classes directly on Textarea to simulate a taller field \u2014 use `rows` for a fixed height, or `autoGrow` for a box that grows with its content.","DON'T hand-roll auto-grow with an `onInput` handler that sets `style.height` from `scrollHeight` \u2014 that is a forced synchronous reflow on every keystroke, it re-derives the library's own box model (`--control-height`, `--control-padding-x`, `--control-border-width`), it freezes the height against the density axis and a tenant retheme, and it misses the paths that matter: a paste, an IME composition in ja/vi, a programmatic reset after submit, and a webfont swap. Pass `autoGrow` instead \u2014 the library owns the measurement in CSS.",'DO reach for `autoGrow` + `minRows`/`maxRows` for a chat or comment composer: `<Textarea autoGrow minRows={1} maxRows={8} />` starts one row tall, grows line by line as the author types or pastes, scrolls internally past the ceiling, and collapses back to `minRows` the moment a controlled value is reset to `""` after send. Bound it in ROWS, never in px \u2014 a row count survives a density change and a `--font-size-base` retheme.',"DON'T hand-roll label + error markup next to a bare Textarea. Always use FormField: it injects aria-invalid (red ring on the control), renders a `role='alert'` error paragraph, and links them via aria-describedby automatically."],useCases:["Free-text memo or note fields on an invoice or transaction detail form \u2014 e.g. '\u5099\u8003 / Notes' that can hold multi-line internal comments alongside structured Invoice fields.","Rejection reason or approval comment in an admin workflow dialog \u2014 a short-to-medium text block a reviewer types before confirming an action in a Dialog or Sheet.","Address or multi-line description input on a vendor / partner entity form where a single-line Input would be too restrictive.","Email body composer or message template editor in a lightweight CRM or notification settings screen where rich text is not required.","Audit log annotation \u2014 allowing an accountant to attach a plain-text explanation to a manual journal entry or adjustment record."],related:["Input \u2014 use Input for single-line values (names, amounts, codes). Use Textarea only when the expected value spans multiple lines or could be longer than ~80 characters.","FormField \u2014 always the parent wrapper for Textarea in forms; provides label, helper text, error message, and injects all required aria attributes onto the Textarea child automatically.","Select \u2014 when the user must pick from a finite set of multi-line-looking options (e.g. template choices) use Select, not a Textarea presenting options as free text.","Select with showSearch or SearchSelect \u2014 if the multi-line field is actually a tag/token input or a constrained lookup, prefer Select (showSearch) or SearchSelect over a Textarea that the user types into freely."],example:`import { Textarea } from "@godxjp/ui/data-entry";
|
|
1148
|
+
</Field>`,storyPath:"data-entry/Switch.stories.tsx",rules:[]},{name:"Textarea",group:"data-entry",tagline:"Styled wrapper around native <textarea>. Pair with FormField for labelled fields. For a Markdown body (wiki page, issue description, mail) use the sibling package `@godxjp/editor` `MarkdownEditor` \u2014 this Textarea plus a formatting toolbar, preview through `@godxjp/markdown`, and paste/drop upload through a host `upload` function (gh#1109).",props:[{name:"padRaw",type:"PadRawProp",description:"Measured padding escape, stamped on the field."},{name:"pad",type:"PadProp",description:"Instance padding with logical asymmetric sides."},{name:"status",type:'"error" | "warning"',description:"Validation state the field paints \u2014 Ant Design `status`. `error` also reports `aria-invalid`, so the red boundary and what a screen reader hears are one fact; `warning` paints only, because a warning is not a validity failure. antd's `success`/`validating` are not implemented: antd only draws them together with its `hasFeedback` icon slot, which FormField owns here."},{name:"variant",type:'"outlined" | "filled" | "borderless" | "default" | "ghost"',defaultValue:'"outlined"',description:"Chrome level \u2014 Ant Design `variant`. `outlined` is the historical field; `filled` swaps the boundary for a tinted surface (dense forms); `borderless` drops both, for a field inside a box that already draws one. antd's fourth member `underlined` is deliberately absent \u2014 a single bottom rule is a Material convention and SmartHR, the JP authority here, draws every field as a full box. This library's older `default`/`ghost` are still accepted and resolve to `outlined`/`borderless`."},{name:"size",type:'"sm" | "md" | "lg"',defaultValue:'"md"',description:"Control height tier \u2014 reads the shared `--control-height` ladder."},{name:"autoSize",type:"boolean | { minRows?: number; maxRows?: number }",description:"antd `autoSize`. `true` is `autoGrow`; the object form carries the row bounds with it, so `autoSize={{ minRows: 2, maxRows: 6 }}` is `autoGrow minRows={2} maxRows={6}`. An explicit `minRows`/`maxRows` still wins."},{name:"count",type:"{ max?: number; show?: boolean; formatter?: (info: { value: string; count: number; max?: number }) => React.ReactNode; strategy?: (value: string) => number }",description:"Character counter \u2014 Ant Design `count`. Counts CODE POINTS by default, so one emoji and one \u5168\u89D2 kanji are each worth one. It REPORTS an overrun (`data-exceeded`) and never edits the value: antd's `exceedFormatter` truncates while the user types, which in Japanese cuts a live IME conversion in half."},{name:"id",type:"string",description:"Associates with a <Label htmlFor>."},{name:"rows",type:"number",description:"Visible text rows."},{name:"value",type:"string",description:"Controlled value."},{name:"onChange",type:"React.ChangeEventHandler<HTMLTextAreaElement>",description:"Change handler."},{name:"allowClear",type:"boolean",defaultValue:"false",description:"Opt-in inline \u2715 at the top-end that clears the field while it holds text (controlled + uncontrolled). Off by default."},{name:"onClear",type:"() => void",description:"Called after the field is cleared via the inline \u2715 (requires `allowClear`)."},{name:"autoGrow",type:"boolean",defaultValue:"false",description:"The floor is `minRows` (or `rows`, when that is the only one given) and never undercuts the `--control-height` tier, so a resting one-row composer still lines up with the Button beside it; past `maxRows` the control stops growing and scrolls internally. Sizing happens in CSS from a hidden replica of the text, so it follows a paste, an IME composition, a programmatic value change, a font swap and a container resize \u2014 not just typing \u2014 and the component never writes `style.height`, never reads `scrollHeight` and never moves `scrollTop`. Works controlled and uncontrolled. Off by default: an existing Textarea keeps the exact geometry it has today."},{name:"minRows",type:"number",defaultValue:"1 (--textarea-autogrow-min-height-rows)",description:"Floor in text rows while `autoGrow`. Theme-global default is the `--textarea-autogrow-min-height-rows` token; this prop overrides it per instance. Ignored when `autoGrow` is false."},{name:"maxRows",type:"number",defaultValue:"8 (--textarea-autogrow-max-height-rows)",description:"Ceiling in text rows while `autoGrow`; beyond it the box stops growing and scrolls internally rather than pushing the page. Pass `0` for no ceiling \u2014 only correct inside an owning scroll container. Theme-global default is the `--textarea-autogrow-max-height-rows` token. Ignored when `autoGrow` is false."},{name:"onValueChange",type:"(value: string) => void",description:"Immediate value callback; native onChange remains supported. Shared form bindings are emitted once."}],usage:['DO reach for `variant="ghost"` ONLY when a parent surface already draws the box and owns focus \u2014 the composer Card, an inline edit cell. A standalone field keeps the default: without its own border it reads as plain text, not something you can type into.',"DO always wrap Textarea in FormField when it appears in a form \u2014 FormField clones aria-describedby, aria-required, and aria-invalid onto the child, giving error/helper announcements and screen-reader labelling for free. Pass matching id props to both.","DO use the godx-ui Textarea (`import { Textarea } from '@godxjp/ui/data-entry'`) \u2014 never a raw `<textarea>`. The component applies the `ui-control-multiline` token class that picks up density, focus-ring, and border tokens from the design system.","DO control the value with `value` + `onChange` in React-managed forms (e.g. Inertia `useForm`). Textarea is a plain `forwardRef` over the native element so it accepts all standard `HTMLTextAreaElement` attributes \u2014 `rows`, `maxLength`, `disabled`, `name`, `placeholder`, `readOnly` all pass through directly.","DO pass `name` when the textarea sits inside an HTML `<form>` for native form submission or when Inertia's `useForm` destructures field values by key \u2014 the `name` attribute maps the value into the form data bag.","DON'T apply manual height or padding classes directly on Textarea to simulate a taller field \u2014 use `rows` for a fixed height, or `autoGrow` for a box that grows with its content.","DON'T hand-roll auto-grow with an `onInput` handler that sets `style.height` from `scrollHeight` \u2014 that is a forced synchronous reflow on every keystroke, it re-derives the library's own box model (`--control-height`, `--control-padding-x`, `--control-border-width`), it freezes the height against the density axis and a tenant retheme, and it misses the paths that matter: a paste, an IME composition in ja/vi, a programmatic reset after submit, and a webfont swap. Pass `autoGrow` instead \u2014 the library owns the measurement in CSS.",'DO reach for `autoGrow` + `minRows`/`maxRows` for a chat or comment composer: `<Textarea autoGrow minRows={1} maxRows={8} />` starts one row tall, grows line by line as the author types or pastes, scrolls internally past the ceiling, and collapses back to `minRows` the moment a controlled value is reset to `""` after send. Bound it in ROWS, never in px \u2014 a row count survives a density change and a `--font-size-base` retheme.',"DON'T hand-roll label + error markup next to a bare Textarea. Always use FormField: it injects aria-invalid (red ring on the control), renders a `role='alert'` error paragraph, and links them via aria-describedby automatically."],useCases:["Free-text memo or note fields on an invoice or transaction detail form \u2014 e.g. '\u5099\u8003 / Notes' that can hold multi-line internal comments alongside structured Invoice fields.","Rejection reason or approval comment in an admin workflow dialog \u2014 a short-to-medium text block a reviewer types before confirming an action in a Dialog or Sheet.","Address or multi-line description input on a vendor / partner entity form where a single-line Input would be too restrictive.","Email body composer or message template editor in a lightweight CRM or notification settings screen where rich text is not required.","Audit log annotation \u2014 allowing an accountant to attach a plain-text explanation to a manual journal entry or adjustment record."],related:["Input \u2014 use Input for single-line values (names, amounts, codes). Use Textarea only when the expected value spans multiple lines or could be longer than ~80 characters.","FormField \u2014 always the parent wrapper for Textarea in forms; provides label, helper text, error message, and injects all required aria attributes onto the Textarea child automatically.","Select \u2014 when the user must pick from a finite set of multi-line-looking options (e.g. template choices) use Select, not a Textarea presenting options as free text.","Select with showSearch or SearchSelect \u2014 if the multi-line field is actually a tag/token input or a constrained lookup, prefer Select (showSearch) or SearchSelect over a Textarea that the user types into freely."],example:`import { Textarea } from "@godxjp/ui/data-entry";
|
|
1133
1149
|
|
|
1134
1150
|
<Textarea id="notes" rows={4} placeholder="\u81EA\u7531\u8A18\u8FF0" value={notes} onChange={(e) => setNotes(e.target.value)} />
|
|
1135
1151
|
|
|
@@ -2579,7 +2595,7 @@ const messages: ChatMessageProp[] = [
|
|
|
2579
2595
|
`),docPath:"data-entry/chat-composer.tsx",storyPath:"data-entry/ChatSuggestion.stories.tsx",rules:[2,3,6]},{name:"Conversations",group:"navigation",tagline:"The session rail of a chat surface (Ant Design X Conversations): past conversations, the current one marked with aria-current, a per-row overflow menu, and recency buckets \u2014 the whole rail one roving-tabindex tab stop, not one tab stop per conversation.",props:[{name:"items",type:"(ConversationsItemProp | ConversationsDividerProp)[]",description:'The rows. A conversation is { key, label?, group?, icon?, disabled? }; a rule between runs is { type: "divider", key?, dashed? }. Ant Design X `items`.'},{name:"activeKey",type:"string",description:"Controlled selection \u2014 the key of the conversation on screen. Ant Design X `activeKey`."},{name:"defaultActiveKey",type:"string",description:"Uncontrolled initial selection. Ant Design X `defaultActiveKey`."},{name:"onActiveChange",type:"(key: string, item?: ConversationsItemProp | ConversationsDividerProp) => void",description:"Fires with the picked key and the entry behind it. Ant Design X `onActiveChange`."},{name:"menu",type:"ConversationsMenuProp | ((conversation: ConversationsItemProp) => ConversationsMenuProp | undefined)",description:"The per-row overflow menu: { items: [{ key, label, icon?, danger?, disabled? }], onClick?, triggerLabel? }. Pass a function to vary it per row, or return undefined for a row that has no menu. Ant Design X `menu` (antd MenuProps there)."},{name:"groupable",type:"boolean | ConversationsGroupableProp",description:"Bucket rows by their `group` field. The object form takes label (node or (group) => node), collapsible (boolean or (group) => boolean), defaultExpandedKeys, expandedKeys and onExpand. Ant Design X `groupable`."},{name:"creation",type:"ConversationsCreationProp",description:'The "new conversation" button pinned above the rail: { label?, icon?, disabled?, onClick? }. Ant Design X `creation`.'},{name:"label",type:"string",description:"Accessible name of the rail (a plain string \u2014 it lands on aria-label). Localized default otherwise."},{name:"id",type:"string",description:"DOM id of the rail root."}],usage:["DO give every conversation a stable `key` \u2014 it is what activeKey, onActiveChange and the menu callback all address. A key that changes on re-render moves the selection.","DO reach for `menu` for rename/delete instead of adding a second Button to each row. The trigger is keyboard-reachable with the forward arrow (\u2192 in LTR, \u2190 in RTL), so a row's second action costs no extra tab stop.",'DO pass `menu.triggerLabel` when the rows are user content: the default names the row, and twelve identical "More actions" buttons are indistinguishable in a screen reader\'s element list.',"DON'T hand-roll the rail out of full-width Buttons plus aria-current. That is one tab stop PER conversation; this is one for the whole rail, with \u2191/\u2193/Home/End inside it.","DON'T expect `styles`/`classNames` from Ant Design X \u2014 they are deliberately not ported. Retune the rail through the --conversations-* tokens (rules #44/#45).",'DO use `groupable={{ collapsible: true }}` for "Today / Previous 7 days": the bucket headings join the same roving order, so collapsing a bucket is reachable without leaving the rail.'],useCases:["The assistant rail of a chat product \u2014 past sessions, the current one marked, rename and delete per row.","Recency buckets over a long history (Today / Yesterday / Previous 7 days) with the older buckets collapsed.","A rail beside ChatBubbleList and ChatComposer: the three are one surface, and Conversations is the half that used to be missing."],related:["ChatBubbleList \u2014 the feed beside this rail; Conversations picks WHICH feed is shown.","ListRow \u2014 a single-line entity row with a trailing action, for short lists inside a Card. It has no selection, no roving focus and no grouping.","Sidebar / NavList \u2014 route navigation. Use those when a row changes the URL; use Conversations when a row changes which conversation the surface is on.","DropdownMenu \u2014 what the per-row `menu` renders; compose it directly when the menu is not attached to a conversation row."],example:['import { Conversations } from "@godxjp/ui/navigation";',"",'const [active, setActive] = useState("c1");',"","<Conversations"," activeKey={active}"," onActiveChange={setActive}"," groupable={{ collapsible: true }}"," creation={{ onClick: () => startNewChat() }}"," items={[",' { key: "c1", label: "\u8ACB\u6C42\u66F8\u306E\u4E0B\u66F8\u304D", group: "today" },',' { key: "c2", label: "\u7D4C\u8CBB\u7CBE\u7B97\u306E\u898F\u5247", group: "today" },',' { key: "c3", label: "\u51FA\u5F35\u624B\u5F53\u306E\u78BA\u8A8D", group: "earlier" },'," ]}"," menu={{"," items: [",' { key: "rename", label: "\u540D\u524D\u3092\u5909\u66F4" },',' { key: "delete", label: "\u524A\u9664", danger: true },'," ],"," onClick: ({ key, conversation }) => run(key, conversation.key),"," }}","/>"].join(`
|
|
2580
2596
|
`),docPath:"navigation/conversations.tsx",storyPath:"navigation/Conversations.stories.tsx",rules:[2,6,23,44,45]},{name:"MegaMenu",group:"navigation",tagline:'A primary site navigation whose top-level items disclose a full-width panel of grouped links (Ant Design `Menu mode="horizontal"` whose SubMenu renders through popupRender). Implements the WAI-ARIA APG Disclosure Navigation pattern \u2014 NOT menu/menubar roles \u2014 with a roving tabindex across the bar, hover intent, Escape-to-trigger, and a narrow layout where the same disclosure lays out in flow.',props:[{name:"items",type:"MegaMenuItemProp[]",description:"The bar. An item WITH a `panel` is a disclosure button (antd SubMenuType); an item WITHOUT one is a plain link (antd MenuItemType). { key, label, href?, icon?, disabled?, panel? } where panel is { groups: [{ key, label?, description?, icon?, links: [{ key, label, href?, description?, icon?, disabled? }] }], footer? }. Ant Design `items`."},{name:"open",type:"string | null",description:"Key of the OPEN PANEL, or null for none. Ant Design `openKeys` collapsed to one level \u2014 a megamenu bar is one level deep, so at most one panel is open and the array would only ever hold zero or one key."},{name:"defaultOpen",type:"string | null",description:"Initial uncontrolled open panel. Ant Design `defaultOpenKeys`.",defaultValue:"null"},{name:"onOpenChange",type:"(key: string | null) => void",description:"Fires with the newly open panel's key, or null when everything closed. Ant Design `onOpenChange`."},{name:"value",type:"string",description:'Key of the item for the CURRENT ROUTE \u2014 renders aria-current="page" on the bar item and on the matching panel link. Ant Design `selectedKeys`, singular because a route is singular.'},{name:"defaultValue",type:"string",description:"Uncontrolled initial current route. Ant Design `defaultSelectedKeys`."},{name:"onValueChange",type:"(key: string) => void",description:"Fires with the activated key (a top-level link or a panel link). Activation always closes the open panel. Ant Design `onClick`."},{name:"size",type:"xs | sm | md | lg",description:"Bar density. The trigger box tracks the matching --control-height tier.",defaultValue:"md"},{name:"triggerAction",type:"click | hover",description:"Ant Design `triggerSubMenuAction`. Default is `click` here where antd defaults to `hover`, because a hover-only trigger has no equivalent on a touch screen; `hover` still accepts click, so touch is never stranded. antd's third value `contextMenu` is not ported.",defaultValue:"click"},{name:"openDelay",type:"number",description:"Ant Design `subMenuOpenDelay`, in MILLISECONDS (antd uses seconds). `hover` only.",defaultValue:"0"},{name:"closeDelay",type:"number",description:"Ant Design `subMenuCloseDelay`, in MILLISECONDS. The hover-intent grace period: the pointer may cross a diagonal toward the panel for this long before anything closes. `hover` only.",defaultValue:"100"},{name:"expandIcon",type:"React.ReactNode | false",description:"Ant Design `expandIcon`, verbatim including its `false` to remove the chevron."},{name:"linkComponent",type:"React.ComponentType<AnchorHTMLAttributes & { href?: string }>",description:"Router link component for every href in the bar and the panels \u2014 same contract and same spelling as Sidebar.linkComponent / NavList.linkComponent."},{name:"label",type:"string",description:"Accessible name of the <nav> landmark (a plain string \u2014 it lands on aria-label). Localized default otherwise."},{name:"id",type:"string",description:"DOM id of the nav root."}],usage:['DO reach for this INSTEAD of DropdownMenu for a site nav. DropdownMenu is react-aria-components Menu, i.e. role="menu" / role="menuitem": a row of them announces a desktop application menubar for what is actually a set of links, and Tab then leaves the whole widget instead of walking the links. That is the classic megamenu a11y defect and it is why this component exists.',"DO give the bar its landmark name through `label` when a page has more than one nav (a primary bar plus a footer nav): two unnamed <nav> landmarks are indistinguishable in a landmark list.","DO drive `value` from your router. A change to it CLOSES the open panel, which is the close-on-route-change half of the contract and needs no router dependency here.","DO use `linkComponent` for a client-side router; the library composes the row and your component renders only the <a>.","DON'T nest a second level inside a panel. A panel is exactly one level deep by construction (`groups[].links[]`) \u2014 antd's arbitrary SubMenu nesting is `Sidebar`/`NavList` territory, not a bar.","DON'T set `triggerAction=\"hover\"` and then also hide the trigger's own affordance: hover still opens on click here precisely so a touch user is not stranded, and WCAG 1.4.13 applies to anything hover-revealed.","DON'T add a `theme=\"dark\"` prop expecting antd's. This library inverts by role scoping ([data-tenant] / per-region), and every surface here is a --mega-menu-* token (rules #44/#45).","KNOW that the open panel is `position: fixed` and its geometry is MEASURED from the bar, not inherited. That is not a preference: an absolutely-positioned panel is clipped away by both surfaces a megamenu lives in (`Topbar`'s slots are `overflow: clip`, `Card` is `overflow: hidden` \u2014 measured at 331px of 348px gone, and not hit-testable). The consequence for you is that an open panel OVERLAYS what is beneath it, so do not leave one open by default in the middle of a scrolling page."],useCases:["A marketing or product site's primary navigation, where Products / Solutions / Resources each open a panel of grouped links rather than a narrow list.","An admin console with several product areas: a top bar where a section opens a panel of its screens, grouped with headings and one-line descriptions.","A documentation site's top bar, where the current page is marked with aria-current in both the bar and the open panel."],related:['DropdownMenu \u2014 a menu of COMMANDS on a trigger (role="menu"). Use it for actions; use MegaMenu for navigation to places.','NavList \u2014 the same idea laid out vertically inside a page (a settings nav). antd\'s `Menu mode="inline"` is Sidebar; `mode="vertical"` is NavList.',"Topbar / TopbarItem \u2014 the APP shell's bar, and NOT where this goes. Measured, both slots break it: `topbar-center` is `display: none` below roughly 1280px (flex at 1440, none at 1024) so the nav vanishes on a laptop, and `topbar-start` is one `overflow: clip` / `flex-wrap: nowrap` row, so the narrow accordion runs out of it (58 elements past the viewport at 375). Put MegaMenu in the site header's own row beside the logo, and give phone width a `Sheet` behind a trigger \u2014 which is what real sites do anyway.","Tabs \u2014 switches which panel of the SAME page is shown. A nav goes somewhere else.","Breadcrumb \u2014 where you are in the hierarchy, not where you can go."],example:['import { MegaMenu } from "@godxjp/ui/navigation";',"","<MegaMenu",' label="\u30E1\u30A4\u30F3\u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3"'," value={route}"," onValueChange={setRoute}",' triggerAction="hover"'," items={["," {",' key: "products",',' label: "\u88FD\u54C1",'," panel: {"," groups: ["," {",' key: "core",',' label: "\u30B3\u30A2",',' description: "\u6BCE\u65E5\u4F7F\u3046\u696D\u52D9\u30A2\u30D7\u30EA",'," links: [",' { key: "hr", label: "\u4EBA\u4E8B\u7BA1\u7406", href: "/hr", description: "\u5F93\u696D\u54E1\u53F0\u5E33\u3068\u7570\u52D5" },',' { key: "payroll", label: "\u7D66\u4E0E\u8A08\u7B97", href: "/payroll" },'," ],"," },"," ],",' footer: <a href="/products">\u3059\u3079\u3066\u306E\u88FD\u54C1\u3092\u898B\u308B</a>,'," },"," },",' { key: "pricing", label: "\u6599\u91D1", href: "/pricing" },'," ]}","/>"].join(`
|
|
2581
2597
|
`),docPath:"navigation/mega-menu.tsx",storyPath:"navigation/MegaMenu.stories.tsx",rules:[2,6,23,44,45]},{name:"Welcome",group:"data-display",tagline:"The greeting block at the head of an empty conversation (Ant Design X Welcome): glyph, greeting, one line under it, and a trailing slot ON THE TITLE ROW \u2014 which is the placement a hand-roll gets wrong.",props:[{name:"icon",type:"React.ReactNode | string",description:'Leading glyph. A STRING beginning with http(s) is rendered as a decorative <img alt=""> (Ant Design X does the same, with alt="icon"); any other string renders as text.'},{name:"title",type:"React.ReactNode",description:"The greeting. Renders as an <h4>, which is Ant Design X's hardcoded Typography.Title level={4}."},{name:"description",type:"React.ReactNode",description:"The line under the greeting."},{name:"extra",type:"React.ReactNode",description:"Trailing slot on the TITLE row \u2014 a dismiss button, a model picker. Not under the description."},{name:"variant",type:'"filled" | "borderless"',defaultValue:'"filled"',description:"filled gives the block its own tinted ground and hairline; borderless lets it sit on the page."},{name:"id",type:"string",description:"DOM id of the block."}],usage:["DO put it above the composer on an empty chat, with ChatSuggestion or a Prompts row beneath it \u2014 that is the surface it belongs to.","DO pass `extra` for the one action the greeting carries (dismiss, switch model). It lands beside the title, top-aligned, so a two-line title does not float it.","DON'T reach for it as a generic page header \u2014 that is PageContainer's title/subtitle/extra, which owns the page rhythm.","DON'T expect a heading-level prop: Ant Design X hardcodes level 4 and this ports that. Wrap it in your own heading hierarchy if the page needs a different rung.","DON'T expect `styles`/`classNames` from Ant Design X \u2014 retune through the --welcome-* tokens."],useCases:["The first screen of an assistant, before the first message.","The head of a fresh conversation started from the Conversations rail.","A feature introduction card inside a chat surface, dismissed through `extra`."],related:["EmptyState \u2014 the general 'nothing here yet' block for a list or a table. Welcome is the chat surface's greeting and carries an icon/title/description/extra shape of its own.","PageContainer \u2014 owns the PAGE header; Welcome sits inside the page body.","ChatSuggestion / ChatBubbleList \u2014 the rest of the same surface."],example:['import { Welcome } from "@godxjp/ui/data-display";','import { Button } from "@godxjp/ui/general";','import { Bot } from "lucide-react";',"","<Welcome"," icon={<Bot />}",' title="\u3053\u3093\u306B\u3061\u306F"',' description="\u8ACB\u6C42\u3001\u7D4C\u8CBB\u3001\u52E4\u6020\u306E\u3053\u3068\u306A\u3089\u304A\u624B\u4F1D\u3044\u3067\u304D\u307E\u3059\u3002"',' extra={<Button variant="ghost" size="sm">\u9589\u3058\u308B</Button>}',"/>"].join(`
|
|
2582
|
-
`),docPath:"data-display/welcome.tsx",storyPath:"data-display/Welcome.stories.tsx",rules:[2,6,44,45]},{name:"Actions",group:"general",tagline:"The strip of actions under an assistant message (Ant Design X Actions): copy, retry, like, and a menu for the rest \u2014 a WAI-ARIA toolbar with ONE tab stop, where Ant X's own strip is <div onClick> with no role and no accessible name.",subParts:["ActionsItem","ActionsCopy","ActionsFeedback"],props:[{name:"items",type:"ActionsItemsProp[]",required:!0,description:"The actions: { key, label?, icon?, onItemClick?, danger?, subItems?, actionRender? }. `label` is the accessible name AND the tooltip. `subItems` folds the action into a menu; `actionRender` replaces it entirely."},{name:"onClick",type:"(info: { item, key, keyPath, domEvent }) => void",description:"Fires for any action WITHOUT its own onItemClick \u2014 a per-item handler wins and this does not also fire, exactly as in Ant Design X. A sub-item reports keyPath [subKey, parentKey]."},{name:"variant",type:'"borderless" | "filled" | "outlined"',defaultValue:'"borderless"',description:"Chrome of the STRIP, not the intent of the buttons (that is `danger` per item)."},{name:"fadeIn",type:"boolean",description:"The strip fades in on mount. Zeroed under prefers-reduced-motion."},{name:"fadeInLeft",type:"boolean",description:"The same fade, arriving along the LOGICAL inline axis (so it mirrors under dir=rtl)."},{name:"label",type:"string",description:"Accessible name of the toolbar (a plain string). Localized default otherwise."},{name:"id",type:"string",description:"DOM id of the strip."}],usage:["DO give every action a `label`. It becomes the accessible name and the tooltip; without one the key is used, which is better than nameless but worse than a sentence.","DO use `subItems` once the strip passes about five actions \u2014 it folds them behind one trigger instead of widening the row under every message.","DO reach for ActionsCopy and ActionsFeedback instead of hand-rolling copy and thumbs: ActionsCopy announces the copy through a live region (a tick alone is invisible to a screen reader), and ActionsFeedback keeps BOTH buttons on screen with aria-pressed rather than hiding the one you did not pick.","DON'T put a form control in the strip. It is a toolbar of buttons with one tab stop; a field inside would be unreachable by Tab.","DON'T expect `dropdownProps`, `triggerSubMenuAction`, `styles` or `classNames` from Ant Design X \u2014 they are not ported; the strip is retuned through the --actions-* tokens."],useCases:["Under an assistant answer: copy, regenerate, like/dislike, and a menu with share and report.",'Under a streaming answer: an ActionsItem with status="running" while the audio plays back, error when it fails.',"In a message hover strip inside ChatBubbleList."],related:["Toolbar / FilterBar \u2014 the list-page filter strip. Actions is the per-message action cluster, not a page-level control bar.","DropdownMenu \u2014 what `subItems` renders; compose it directly when the menu is not one action in a strip.","ChatBubble \u2014 the message the strip belongs to.","CredentialReveal \u2014 a copy affordance for a SECRET field; ActionsCopy copies message text."],example:['import { Actions, ActionsCopy, ActionsFeedback } from "@godxjp/ui/general";','import { RefreshCw, Share2 } from "lucide-react";',"","<Actions",' label="\u56DE\u7B54\u306E\u64CD\u4F5C"'," items={[",' { key: "retry", label: "\u3084\u308A\u76F4\u3059", icon: <RefreshCw />, onItemClick: () => regenerate() },'," {",' key: "more",',' label: "\u305D\u306E\u4ED6",',' subItems: [{ key: "share", label: "\u5171\u6709", icon: <Share2 /> }],'," },",' { key: "copy", actionRender: <ActionsCopy text={answer} /> },',' { key: "feedback", actionRender: <ActionsFeedback value={vote} onChange={setVote} /> },'," ]}"," onClick={({ key }) => run(key)}","/>"].join(`
|
|
2598
|
+
`),docPath:"data-display/welcome.tsx",storyPath:"data-display/Welcome.stories.tsx",rules:[2,6,44,45]},{name:"Actions",group:"general",tagline:"The strip of actions under an assistant message (Ant Design X Actions): copy, retry, like, and a menu for the rest \u2014 a WAI-ARIA toolbar with ONE tab stop, where Ant X's own strip is <div onClick> with no role and no accessible name.",subParts:["ActionsItem","ActionsCopy","ActionsFeedback"],props:[{name:"items",type:"ActionsItemsProp[]",required:!0,description:"The actions: { key, label?, icon?, onItemClick?, disabled?, danger?, subItems?, actionRender? }. `label` is the accessible name AND the tooltip. `disabled` makes a plain action `aria-disabled` and ignores its click, but keeps it focusable and in the arrow-key order (WAI-ARIA toolbar) \u2014 use it for a formatting toolbar while previewing or read-only, not to hide an action (gh#1109). `subItems` folds the action into a menu; `actionRender` replaces it entirely."},{name:"onClick",type:"(info: { item, key, keyPath, domEvent }) => void",description:"Fires for any action WITHOUT its own onItemClick \u2014 a per-item handler wins and this does not also fire, exactly as in Ant Design X. A sub-item reports keyPath [subKey, parentKey]."},{name:"variant",type:'"borderless" | "filled" | "outlined"',defaultValue:'"borderless"',description:"Chrome of the STRIP, not the intent of the buttons (that is `danger` per item)."},{name:"fadeIn",type:"boolean",description:"The strip fades in on mount. Zeroed under prefers-reduced-motion."},{name:"fadeInLeft",type:"boolean",description:"The same fade, arriving along the LOGICAL inline axis (so it mirrors under dir=rtl)."},{name:"label",type:"string",description:"Accessible name of the toolbar (a plain string). Localized default otherwise."},{name:"id",type:"string",description:"DOM id of the strip."}],usage:["DO give every action a `label`. It becomes the accessible name and the tooltip; without one the key is used, which is better than nameless but worse than a sentence.","DO use `subItems` once the strip passes about five actions \u2014 it folds them behind one trigger instead of widening the row under every message.","DO reach for ActionsCopy and ActionsFeedback instead of hand-rolling copy and thumbs: ActionsCopy announces the copy through a live region (a tick alone is invisible to a screen reader), and ActionsFeedback keeps BOTH buttons on screen with aria-pressed rather than hiding the one you did not pick.","DON'T put a form control in the strip. It is a toolbar of buttons with one tab stop; a field inside would be unreachable by Tab.","DON'T expect `dropdownProps`, `triggerSubMenuAction`, `styles` or `classNames` from Ant Design X \u2014 they are not ported; the strip is retuned through the --actions-* tokens."],useCases:["Under an assistant answer: copy, regenerate, like/dislike, and a menu with share and report.",'Under a streaming answer: an ActionsItem with status="running" while the audio plays back, error when it fails.',"In a message hover strip inside ChatBubbleList."],related:["Toolbar / FilterBar \u2014 the list-page filter strip. Actions is the per-message action cluster, not a page-level control bar.","DropdownMenu \u2014 what `subItems` renders; compose it directly when the menu is not one action in a strip.","ChatBubble \u2014 the message the strip belongs to.","CredentialReveal \u2014 a copy affordance for a SECRET field; ActionsCopy copies message text."],example:['import { Actions, ActionsCopy, ActionsFeedback } from "@godxjp/ui/general";','import { RefreshCw, Share2 } from "lucide-react";',"","<Actions",' label="\u56DE\u7B54\u306E\u64CD\u4F5C"'," items={[",' { key: "retry", label: "\u3084\u308A\u76F4\u3059", icon: <RefreshCw />, onItemClick: () => regenerate() },'," {",' key: "more",',' label: "\u305D\u306E\u4ED6",',' subItems: [{ key: "share", label: "\u5171\u6709", icon: <Share2 /> }],'," },",' { key: "copy", actionRender: <ActionsCopy text={answer} /> },',' { key: "feedback", actionRender: <ActionsFeedback value={vote} onChange={setVote} /> },'," ]}"," onClick={({ key }) => run(key)}","/>"].join(`
|
|
2583
2599
|
`),docPath:"general/actions.tsx",storyPath:"general/Actions.stories.tsx",rules:[2,6,23,44,45]},{name:"ThoughtChain",group:"data-display",tagline:"The assistant's reasoning, step by step (Ant Design X ThoughtChain): an ORDERED list of steps, each with an ordinal or a glyph, a status, and a body it can collapse \u2014 where Ant X's own step is a <div onClick> with no role and no aria-expanded.",subParts:["ThoughtChainItem"],props:[{name:"items",type:"ThoughtChainItemsProp[]",description:"The steps: { key?, icon?, title?, description?, content?, footer?, status?, collapsible?, blink?, destroyOnHidden? }. `icon: false` drops the glyph column; omitted, the step shows its 1-based ordinal (Ant Design X's own default)."},{name:"defaultExpandedKeys",type:"string[]",description:"Uncontrolled initially-open steps."},{name:"expandedKeys",type:"string[]",description:"Controlled open steps."},{name:"onExpand",type:"(keys: string[]) => void",description:"Fires with the NEXT open set."},{name:"line",type:'boolean | "solid" | "dashed" | "dotted"',defaultValue:"true",description:'The connector drawn between steps. false draws none. (Ant Design X\'s own type spells the third with a stray U+200C, so `line="dotted"` does not type-check there; the clean spelling is used here.)'},{name:"label",type:"string",description:"Accessible name of the chain (a plain string). Localized default otherwise."},{name:"id",type:"string",description:"DOM id of the chain root."}],usage:["DO give every step a stable `key` \u2014 it is what expandedKeys addresses and what onExpand reports.","DO set `collapsible` on a step whose `content` is long (a tool's raw output, a retrieved passage). The title then becomes a real disclosure button with aria-expanded, keyboard-reachable; without `collapsible` the body is simply always shown.","DO use `status` for how a step ENDED \u2014 loading / success / error / abort. The word rides along in a visually hidden span, so the state is never carried by the tint alone.","DO use `blink` while a step is still streaming; it pulses the title and body and collapses to nothing under prefers-reduced-motion.","DON'T reach for it for events that already happened \u2014 that is Timeline. A thought chain is a run IN PROGRESS, which is why it has loading and abort states and a body that opens.","DON'T expect `styles`/`classNames` from Ant Design X \u2014 retune through the --thought-chain-* tokens."],useCases:["An agent's tool calls under its answer: read the documents, search the policy, draft the reply \u2014 each with its output collapsed.","A long-running job's progress inside a chat: the current step blinking, the finished ones ticked, an aborted one greyed.","ThoughtChainItem alone: the chip an assistant drops inline to name the tool it just reached for."],related:["Timeline \u2014 the same vertical rail for events that ALREADY happened. Use it when nothing is in flight.","Steps \u2014 a wizard's progress across a form. ThoughtChain is the assistant's own reasoning, not the user's path.","Accordion \u2014 a general disclosure list with no rail, no ordinal and no status.","ChatBubble \u2014 the answer the chain explains."],example:['import { ThoughtChain } from "@godxjp/ui/data-display";',"","<ThoughtChain",' label="\u601D\u8003\u306E\u624B\u9806"',' defaultExpandedKeys={["search"]}'," items={[",' { key: "read", title: "\u8CC7\u6599\u3092\u8AAD\u3080", description: "3\u4EF6", status: "success" },'," {",' key: "search",',' title: "\u793E\u5185\u898F\u7A0B\u3092\u691C\u7D22",',' status: "loading",'," collapsible: true,"," blink: true,"," content: <pre>{hits}</pre>,"," },",' { key: "write", title: "\u4E0B\u66F8\u304D\u3092\u66F8\u304F", status: "abort" },'," ]}","/>"].join(`
|
|
2584
2600
|
`),docPath:"data-display/thought-chain.tsx",storyPath:"data-display/ThoughtChain.stories.tsx",rules:[2,6,23,44,45]},{name:"OrgChart",group:"data-display",tagline:"An organization chart: boxes (avatar, name, title, extra) joined by CSS connector lines, top-down; `agent` nodes are dashed. Scrolls horizontally in its own named region when wider than its container, and turns into an indented Tree when the CONTAINER is under 40rem. APG tree view (tree/treeitem/group, roving tabindex, arrow keys).",props:[{name:"data",type:"OrgChartNodeProp[]",description:'The hierarchy: { key, name, title?, avatar?, extra?, variant?: "person" | "agent", children? }. `key` is unique across the chart. `avatar` is usually an <Avatar>; `extra` a Badge or status.'},{name:"renderNode",type:"(node: OrgChartNodeProp) => ReactNode",description:"Replace a box's content. The box, its border, the connectors and the keyboard stay the library's; the narrow Tree form uses it too."},{name:"label",type:"string",description:'Accessible name of the role="tree" (a plain string). Localized default "Organization chart".'},{name:"id",type:"string",description:"DOM id of the root."},{name:"className",type:"string",description:"Root class."}],usage:['DO mark AI agents with `variant: "agent"` \u2014 the box is dashed AND its accessible name ends in a localized "AI agent", so the kind is never carried by the stroke alone.',"DO give it the width it has; the breakpoint is a container query, so a chart in a narrow side panel switches to the Tree form on a wide screen too.","DO retune boxes and lines through the --org-chart-* tokens (node size, gaps, line width/colour, agent border style).","DON'T wrap it in your own overflow-x scroller \u2014 it owns its scroll region, which only becomes a named tab stop when the chart actually overflows.","DON'T use it for an outline users expand and collapse \u2014 that is Tree. OrgChart always shows every node."],useCases:["A company or team org chart with people and AI agents side by side.","Reporting lines on an admin screen, falling back to an indented list in a narrow panel or on a phone."],related:["Tree \u2014 the indented, collapsible outline. OrgChart renders it as its narrow form.","Avatar \u2014 the mark in each box.","Badge \u2014 a status in a box's `extra` slot."],example:['import { Avatar, AvatarFallback, Badge, OrgChart } from "@godxjp/ui/data-display";',"","<OrgChart",' label="Company org chart"'," data={["," {",' key: "ceo", name: "Haruka Tanaka", title: "CEO",',' avatar: <Avatar size="sm"><AvatarFallback>HT</AvatarFallback></Avatar>,'," children: [",' { key: "cto", name: "Kenji Watanabe", title: "CTO" },',' { key: "bot", name: "Review Agent", title: "Code review", variant: "agent",',' extra: <Badge tone="success">Running</Badge> },'," ],"," },"," ]}","/>"].join(`
|
|
2585
2601
|
`),docPath:"data-display/org-chart.tsx",storyPath:"data-display/OrgChart.stories.tsx",rules:[2,6,23,44,45]},{name:"Image",group:"data-display",tagline:'antd `Image` + `Image.PreviewGroup`: a picture that opens in a full-viewport preview (zoom in/out by button, wheel or double-click; rotate; flip; reset; drag to pan). Inside an `ImagePreviewGroup` every Image at any depth joins one gallery in document order \u2014 prev/next buttons, \u2190/\u2192, a "2 / 3" counter. Esc closes; focus is trapped and returned to the picture.',subParts:["ImagePreviewGroup"],props:[{name:"src",type:"string",description:"Image URL."},{name:"alt",type:"string",description:'Required (as on Thumbnail). Also names the trigger "Preview: {alt}". Pass "" for a decorative picture.'},{name:"preview",type:"boolean | { visible?, onVisibleChange?(visible, prevVisible), src?, mask?, scaleStep?, minScale?, maxScale? }",description:"antd shape. `false` = a plain, non-clickable picture that also leaves its group. `src` shows a different (larger) file in the preview. Defaults: scaleStep 0.5, minScale 1, maxScale 50."},{name:"fallback",type:"string",description:"Shown when `src` fails. A failed picture does not open a preview (antd)."},{name:"placeholder",type:"ReactNode | boolean",description:"Painted over the frame until the picture loads; `true` = a muted block."},{name:"width / height",type:"number | string",description:"antd Image: size the FRAME (number = px, or a CSS length); the picture fills it. `width` alone keeps the picture's ratio; a fixed height crops per `fit`. Also set on the img."},{name:"size",type:'"sm" | "md" | "lg"',description:"Thumbnail frame on the Thumbnail height scale (64 / 96 / 160px) at `--thumbnail-width-ratio` (4 / 3), cropped to fill. godx extension; `width`/`height` override the side they name."},{name:"fit",type:'"cover" | "contain"',description:"object-fit inside a fixed frame. Default `cover` once the height is fixed (size or height)."},{name:"caption",type:"ReactNode",description:"Text under the picture in a figure held to the picture's width \u2014 a long file name truncates instead of widening the tile. godx extension."},{name:"className",type:"string",description:"Class on the wrapper (the button)."},{name:"ImagePreviewGroup.items",type:"(string | { src, alt? })[]",description:"Explicit gallery instead of the Images rendered under the group. A clicked child opens at the item with its own src."},{name:"ImagePreviewGroup.preview",type:"boolean | { current?, onChange?(current, prevCurrent), visible?, onVisibleChange?(visible, prevVisible), scaleStep?, minScale?, maxScale? }",description:"antd shape and argument order. `false` turns every Image under the group into a plain picture."},{name:"ImagePreviewGroup.fallback",type:"string",description:"Shown in the preview for a picture that fails to load."}],usage:["DO wrap a rendered Markdown body in ImagePreviewGroup and map its `img` to `<Image>` \u2014 every screenshot of the article then pages in one preview, in reading order.","DO pass `preview={false}` for a logo or icon inside that body so it neither opens nor counts.","DO use `Image.PreviewGroup` or `ImagePreviewGroup` \u2014 the same component.","DON'T hand-roll a lightbox from Dialog + Carousel: it has no zoom, no arrow keys and a 32rem box. That composition is what this replaces.","DON'T nest an Image inside a link or button: the thumbnail is itself a button.",'DO size an attachment gallery with `size="md"` (96px, 4:3, cropped) and put the file name in `caption` \u2014 never an inline style reading `--thumbnail-block-size`.'],useCases:["Screenshots in a test result or wiki page that the reader opens large and pages through.","A product or evidence gallery with zoom and rotate."],related:["Thumbnail \u2014 a framed picture at a fixed height, with no preview.","Prose \u2014 the Markdown body the group usually wraps.","Carousel \u2014 slides on the page itself, not a full-viewport preview."],example:['import { Image, ImagePreviewGroup, Prose } from "@godxjp/ui/data-display";',"","<ImagePreviewGroup>"," <Prose>",' <p><Image src="/shots/before.png" alt="Before the fix" width={320} /></p>',' <p><Image src="/shots/after.png" alt="After the fix" width={320} /></p>',' <p><Image src="/logo.svg" alt="" width={24} preview={false} /></p>'," </Prose>","</ImagePreviewGroup>"].join(`
|
|
@@ -4966,7 +4982,7 @@ A block with no reason is IGNORED and the finding stands. An unclosed block runs
|
|
|
4966
4982
|
The class-shaped rules (gap-*/p-*/m-*, bg-<palette>-*, w-[\u2026], pr-*, dark:*) only read class
|
|
4967
4983
|
expressions \u2014 a className/class attribute, a class-named binding (\`baseClass\`, \`statusStyles\`,
|
|
4968
4984
|
\`badgeVariants\`) or a cn()/clsx()/cva() call \u2014 so prose that merely spells a utility is not a
|
|
4969
|
-
finding and needs no suppression.`,oe=[{id:"dialog-needs-body",severity:"error",category:"composition",standard:null,fix:"Wrap a Dialog/Sheet's middle in <DialogBody>/<SheetBody>. The scroll lives on the body \u2014 the content box is `overflow: hidden` with no max-height \u2014 so an overlay without one clips long content at BOTH ends and takes the footer's buttons with it, leaving Escape as the only way out. It only shows on real data, never on demo data (gh#617)."},{id:"no-utility-spacing",severity:"error",category:"composition",standard:null,fix:"Remove gap-*/p-*/m-* from your own markup; space siblings with <Flex gap> / <ResponsiveGrid>. Page sections are spaced by <PageContainer> (docs/CONSUMER-RULES.md \xA73)."},{id:"no-utility-layout",severity:"error",category:"composition",standard:null,fix:'Replace className="flex \u2026" / "grid \u2026" with <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid columns>.'},{id:"no-hand-rolled-surface",severity:"warn",category:"composition",standard:null,fix:"A rounded+border/bg div is a fake surface \u2014 use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from tokens. A read-only sample of a colour a USER chose is Swatch, which takes that value as a prop (gh#527)."},{id:"sibling-cards-need-flex",severity:"warn",category:"composition",standard:null,fix:'Wrap adjacent <Card>s in <Flex direction="col" gap="lg"> or <ResponsiveGrid>; direct children of PageContainer are spaced by the page already.'},{id:"no-raw-palette-color",severity:"error",category:"tokens",standard:null,fix:"Use semantic tokens (bg-primary, text-muted-foreground), never raw palette (bg-blue-500)."},{id:"no-arbitrary-hex",severity:"error",category:"tokens",standard:null,fix:"No hardcoded hex in className; read design-system color tokens."},{id:"no-arbitrary-spacing",severity:"error",category:"tokens",standard:null,fix:"No p-[13px]/gap-[7px]; use the token scale / <Flex gap> / <PageContainer>."},{id:"no-arbitrary-size",severity:"error",category:"tokens",standard:null,fix:"No w-[37px]/h-[260px]; use token sizes or a sizing prop (min-w-[\u2026] allowed)."},{id:"no-arbitrary-typography",severity:"error",category:"tokens",standard:null,fix:"No text-[20px]/leading-[1.7]; use the golden-ratio type-scale tokens."},{id:"no-arbitrary-radius",severity:"error",category:"tokens",standard:null,fix:"No rounded-[6px]; use rounded-sm/md/lg radius tokens."},{id:"no-off-scale-token-value",severity:"warn",category:"tokens",standard:null,fix:"A design-system knob you override takes a step (style={{ '--card-space-inset': 'var(--space-4)' }}) or a calc() from one (calc(var(--space-4) + 2px)), not a raw 13px. Only axes that HAVE a scale count: space/padding/gap/margin, font-size, radius, icon-size (width/height/size/offset have none yet, so a number there is fine). A value that is genuinely off the grid keeps its literal and says why in place, with a /* scale-exempt: 6px status dot, below --space-1 */ comment on that line or the one above."},{id:"no-dark-color-override",severity:"warn",category:"tokens",standard:null,fix:"Drop dark: color overrides \u2014 semantic tokens already adapt."},{id:"raw-white-black",severity:"warn",category:"tokens",standard:null,fix:"Prefer semantic tokens (text-primary-foreground, bg-background) over raw white/black."},{id:"no-domain-tracking-token",severity:"error",category:"tokens",standard:null,fix:"No package-tracking/domain tokens; use semantic tokens or app theme overrides."},{id:"no-space-xy",severity:"error",category:"tokens",standard:null,fix:"Use <Flex gap> instead of space-x/y-*."},{id:"no-raw-select",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Select> from @godxjp/ui, not a raw <select>."},{id:"no-raw-table",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use the <Table>/<DataTable> family, not a raw <table>."},{id:"no-raw-input",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Input> from @godxjp/ui, not a raw <input>."},{id:"no-raw-textarea",severity:"warn",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Textarea> from @godxjp/ui, not a raw <textarea>."},{id:"no-raw-button",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Button> from @godxjp/ui, not a raw <button>."},{id:"card-manual-padding",severity:"error",category:"composition",standard:null,fix:"Wrap the body in <CardContent>; don't hand-roll padding on <Card>."},{id:"card-needs-content",severity:"error",category:"composition",standard:null,fix:"<Card> body must be in <CardContent> (no padding otherwise); flush only for a full-bleed table."},{id:"bare-control-needs-formfield",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.3.1 \xB7 3.3.2 \xB7 @godxjp/ui FormField (cardinal rule 227)",fix:"Wrap a labelled control in <FormField label=\u2026> \u2014 it owns label\u2194control id wiring, aria/error, AND the field rhythm; never pair a bare <Label> with an <Input>."},{id:"formfield-needs-form",severity:"error",category:"composition",standard:"@godxjp/ui Form (gh#998)",fix:'Wrap FormFields in <Form layout="horizontal" labelWidth controlWidth>; a row of fields is <SpaceCompact> or <Form columns>, never a hand-rolled <Flex>. A field component whose whole output is one FormField is exempt.'},{id:"mixed-button-size",severity:"error",category:"composition",standard:null,fix:'Sibling <Button>s under one parent (through fragments, {cond && \u2026}, ternaries, Tooltip wrappers) share ONE size; a missing size is default; icon-sm pairs with sm, icon-xs with xs, icon with default. Never `size="sm"` beside a default Button in one action row.'},{id:"dialog-form-too-big",severity:"error",category:"composition",standard:"@godxjp/ui form placement (gh#998)",fix:"Three or more FormFields in a Dialog body is a page: give the form its own route. Dialogs hold a confirmation or one or two fields; a side Sheet (drawer) may hold a filter or edit form."},{id:"select-width-hint",severity:"warn",category:"composition",standard:"GOV.UK Design System \xB7 text input width",fix:"Size a Select for its content \u2014 `controlWidth` on the FormField, or once on the <Form>."},{id:"manual-field-error",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 3.3.1",fix:"Use <FormField error=\u2026>, not a hand-rolled <p class='text-destructive'>."},{id:"manual-field-helper",severity:"warn",category:"composition",standard:null,fix:"Use <FormField helper=\u2026>, not a hand-rolled helper <p>."},{id:"status-tone-not-variant",severity:"error",category:"api",standard:null,fix:"Badge/Tag/StatCard status uses tone, not variant (variant is structural)."},{id:"value-callback-on-value-change",severity:"error",category:"api",standard:null,fix:"Abstract value components use onValueChange, not onChange."},{id:"icon-button-needs-name",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 4.1.2 \xB7 1.1.1 \xB7 WAI-ARIA 1.2 \xB7 Accessible Name Computation 1.2",fix:"Name <Button size='icon'> with aria-label={t('\u2026')} OR from content \u2014 a <VisuallyHidden>/sr-only child beside the aria-hidden glyph. Text inside an aria-hidden subtree names nothing."},{id:"img-needs-alt",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 1.1.1 \xB7 HTML Living Standard",fix:"Add alt to every <img> (alt='' if decorative); prefer <Avatar>/<AspectRatio>."},{id:"no-positive-tabindex",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.4.3 \xB7 WAI-ARIA APG",fix:"Use tabIndex 0 or -1 only; never positive \u2014 it breaks focus order."},{id:"hand-rolled-close-glyph",severity:"warn",category:"a11y",standard:"WAI-ARIA 1.2 (dialog) \xB7 WCAG 2.2 SC 4.1.2",fix:"Pass onDismiss to <Alert>, or use <Dialog>/<Sheet>'s built-in labelled close \u2014 not a bare \u2715."},{id:"no-hand-rolled-scrollport",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.1.1 (Keyboard) \xB7 WAI-ARIA 1.2 (group) \xB7 Deque axe-core scrollable-region-focusable",fix:'Replace className="overflow-auto / overflow-y-auto / overflow-x-auto / overflow-scroll" on your own element with <ScrollArea label={t("\u2026")} orientation>, which is the tab stop, the role and the localized name \u2014 and withholds all three while there is nothing to scroll. A browser audit only fails a scrollport whose content has NO focusable child, so the same markup is clean or broken depending on the data; this reads the markup instead (gh#825). overflow-hidden is a clipping box, not a scrollport, and is not flagged.'},{id:"no-emoji-in-ui",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"No emoji in product UI; quiet i18n copy + Lucide icon + Badge tone."},{id:"no-emoji-flag",severity:"warn",category:"i18n",standard:"ISO 3166-1 \xB7 ECMA-402 Intl.DisplayNames \xB7 Unicode UTS #51",fix:"Derive country names from Intl.DisplayNames; no emoji flags."},{id:"hardcoded-currency",severity:"warn",category:"i18n",standard:"ISO 4217 \xB7 ECMA-402 Intl.NumberFormat",fix:"Format money with Intl.NumberFormat({ style: 'currency', currency }), not \xA5{amount}."},{id:"raw-intl-date",severity:"warn",category:"i18n",standard:"ISO 8601 \xB7 IANA tz \xB7 ECMA-402 Intl.DateTimeFormat",fix:"Use formatDate from @godxjp/ui/datetime, not hand-built or locale-default dates."},{id:"no-physical-direction",severity:"warn",category:"rtl",standard:"W3C CSS Logical Properties L1 \xB7 WCAG 2.2 (1.3.2)",fix:"Use logical utilities (ms-/me-/ps-/pe-, start-/end-, text-start/end, border-s/e, rounded-s/e)."},{id:"no-em-dash-in-copy",severity:"warn",category:"copy",standard:"@godxjp/ui reference-design typography",fix:"No em-dash (\u2014) in copy; use a middot \xB7 or two calm sentences."},{id:"lucide-icon-needs-size",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.4.4 \xB7 @godxjp/ui icon scale (--icon-size-*)",fix:'A lucide glyph outside a sizing context draws at its intrinsic 24px. Render it as <Icon as={Lock} size="sm" tone="muted" /> \u2014 the primitive puts it on the --icon-size-* scale and is aria-hidden unless you pass a label.'},{id:"no-hand-rolled-list",severity:"warn",category:"composition",standard:"WAI-ARIA 1.2 (list / listitem) \xB7 HTML Living Standard (ul/ol/li) \xB7 WCAG 2.2 SC 1.3.1",fix:'Build the list as <Flex as="ul" marker="none" direction="col" gap="none"> with <ListRow as="li"> rows \u2014 not a raw <ul>/<ol> (no gap token), not <div role="list">/<div role="listitem">, and never a wrapper around each row: the divider is :not(:last-child) among SIBLINGS, so a row alone in its own wrapper loses it silently (a consumer lost every divider in a settings menu and a dashboard this way). marker="none" keeps the element, the <li> semantics and the gap, and drops the bullet and the --space-5 indent (gh#714). A deliberate exception \u2014 a drag-and-drop Kanban column, an evidence list inside a TableCell \u2014 takes an ui-audit-disable-line that says so.'},{id:"no-hand-rolled-break-anywhere",severity:"warn",category:"composition",standard:"CSS Text 3 \xA75.5 (overflow-wrap) \xB7 WCAG 2.2 SC 1.4.10 (Reflow)",fix:`Replace className="[overflow-wrap:anywhere] break-words whitespace-normal" (or wrap-anywhere) with <Text break="anywhere">, which emits overflow-wrap: anywhere AND releases a table cell's inherited nowrap, so an email, code or id shrinks its column to the viewport. Not whitespace="pre-wrap": its break-word does not lower min-content, so a table cell stays wide (gh#927).`}];function re(e){return e?oe.filter(t=>t.category===e):oe}var le="node node_modules/@godxjp/ui/scripts/visual-audit.mjs <baseUrl> [route \u2026] (optional peers, TESTED range: playwright >=1.55 <2 [1.61.1] + @axe-core/playwright >=4.10 <5 [4.12.1] + axe-core >=4.10 <5 [4.12.1] + a chromium via `playwright install chromium`; --strict for a CI gate, --format json ALWAYS emits valid JSON with a status of ok|partial|error separating infra errors[] from product findings[], --rules to print this catalog)",se=[{id:"css-layers-missing",severity:"error",category:"layout",standard:"@godxjp/ui styles contract (styles / styles/core are the only entries)",fix:"Import `@godxjp/ui/styles` (or `styles/core` without fonts \u2014 86 KB instead of 367 KB gzip, since the bundled @font-face declarations are most of the CSS, gh#971); never cherry-pick *-layout.css \u2014 a missing layer renders naked menus and unsized Select rows."},{id:"control-height-mismatch",severity:"error",category:"layout",standard:"@godxjp/ui control tier (--control-height) \xB7 Nielsen consistency heuristic",fix:"Every control in one row must share --control-height; replace hand-rolled pills with Avatar/Button/Badge, never restyle a control's height."},{id:"mixed-button-height",severity:"error",category:"layout",standard:"@godxjp/ui Button size (one size per row) \xB7 Nielsen consistency heuristic",fix:"Buttons in one flex row must render at one height (within 0.5px): one `size` per row; icon-sm pairs with sm, icon-xs with xs, icon with default. Runtime twin of the static mixed-button-size rule."},{id:"sibling-card-gap",severity:"error",category:"layout",standard:"@godxjp/ui spacing scale (docs/SPACING.md)",fix:'Adjacent Cards need one space step between them \u2014 <Flex direction="col" gap>, <ResponsiveGrid>, or direct children of PageContainer.'},{id:"row-content-starved",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 reflow",fix:`A sibling (a w-full SelectTrigger) takes the row's width and truncates its neighbours \u2014 give the Select width="auto" or move it out of the row.`},{id:"axe-violations",severity:"warn",category:"a11y",standard:"WCAG 2.2 A/AA \xB7 WAI-ARIA 1.2 (axe-core engine)",fix:"Fix each axe node \u2014 contrast (1.4.3), name/role/value (4.1.2), ARIA, landmarks. Runs on the REAL DOM, catching what static analysis cannot."},{id:"target-size-min",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.5.8 (24\xD724 AA) \xB7 2.5.5 (44\xD744 AAA)",fix:"Interactive targets must be \u226524\xD724 CSS px; size from the --control-height tier."},{id:"oversaturated-accent",severity:"warn",category:"color",standard:"@godxjp/ui reference-design \u6E0B\u307F (OKLCH chroma \u2264 0.18)",fix:"Desaturate brand/primary surfaces (OKLCH chroma \u2264 0.18); read --primary tokens, no raw vivid bars. The accent @godxjp/ui itself ships is exempt (gh#823) \u2014 this finding is always a colour someone chose."},{id:"emoji-rendered",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"Remove emoji from rendered product text; quiet i18n copy + Lucide icon + Badge tone."},{id:"alert-controls-misplaced",severity:"warn",category:"layout",standard:"@godxjp/ui Alert anatomy \xB7 WAI-ARIA 1.2 \xB7 WCAG 2.2 SC 4.1.2",fix:"Use <Alert>: one leading tone icon, <Alert.Actions> trailing-right normal width, onDismiss \xD7 top-right, one horizontal row."}];function de(e){return e?se.filter(t=>t.category===e):se}var h={name:"@godxjp/ui-mcp",version:"31.17.1",godxUiCompatibility:"31.17.x",description:"Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 45 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",type:"module",main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",bin:{"godx-ui-mcp":"./dist/index.js"},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org/",access:"public"},repository:{type:"git",url:"git+https://github.com/godx-jp/godxjp-ui.git",directory:"mcp"},homepage:"https://github.com/godx-jp/godxjp-ui/tree/main/mcp#readme",license:"Apache-2.0",scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js",inspect:"npx @modelcontextprotocol/inspector node dist/index.js","type-check":"tsc --noEmit",test:"vitest run",prepublishOnly:"npm run build"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.4.3"},devDependencies:{"@types/node":"^22.10.0",tsup:"^8.5.1",typescript:"^6.0.3",vitest:"^4.1.6"},keywords:["mcp","model-context-protocol","godxjp","ui","design-system","react","claude","cursor"],author:"GoDX (https://godx.jp)",bugs:{url:"https://github.com/godx-jp/godxjp-ui/issues"}};var H=[{name:"list_skills",description:"List every design/taste skill bundled by this MCP (id + name + whenToUse + section ids). Use FIRST to discover skills; then `get_skill_section` to drill in.",inputSchema:{type:"object",properties:{}}},{name:"list_primitives",description:"List every @godxjp/ui primitive/composite/shell (group + tagline per entry). Optionally filter by group. Then `get_component` for one's full API.",inputSchema:{type:"object",properties:{group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_utilities",description:'List every NON-component public export of @godxjp/ui \u2014 hooks, helper functions and constants (cn, formatDate, formatCurrency, toast, useDebouncedValue, buttonVariants, CHART_COLORS, SHOW_PARENT \u2026). Reach for this before hand-writing a className merger, a date/money formatter, a debounce hook or a chart palette: two products in this org each re-implemented `cn` because it could not be found. Optionally filter by kind. Then `get_component name="<name>"` for its signature, usage and example.',inputSchema:{type:"object",properties:{kind:{type:"string",enum:["hook","function","value"],description:"hook = only legal inside a component body; function = callable anywhere; value = a constant to read."}}}},{name:"list_patterns",description:"List every canonical copy-paste code pattern (signup-form, settings-page, data-table-page, async-data-state, confirm-destructive, \u2026); common aliases resolve too. Use before `get_pattern`.",inputSchema:{type:"object",properties:{}}},{name:"list_anti_ai_tells",description:"List every AI-tell pattern to AVOID (optionally by category). Use to self-audit a design before shipping; then `get_anti_ai_tell` for the fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["visual","layout","copy","interaction","imagery","structure"]}}}},{name:"list_redesign_checks",description:"List the redesign audit checklist (50+ checks; optionally by category). Use when auditing an existing project; then `get_redesign_check` for a symptom's fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["typography","color-surface","layout","interactivity","content","components","iconography","code-quality","omissions"]}}}},{name:"list_audit_rules",description:"List the LOCAL static ui-audit rules (scripts/ui-audit.mjs) to run BEFORE any visual review \u2014 each cites the standard it enforces (WCAG/WAI-ARIA/Intl/ISO/IANA/CSS-Logical) + a fix + the run command. Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["tokens","composition","api","a11y","i18n","rtl","copy"]}}}},{name:"list_visual_checks",description:"List the RUNTIME visual-audit checks (scripts/visual-audit.mjs \u2014 Playwright + axe-core) to run against the RUNNING app: contrast/ARIA (axe), target size, rendered-accent chroma, DOM emoji, banner layout. Needs a browser (vs list_audit_rules, static). Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["a11y","color","i18n","layout"]}}}},{name:"get_anti_ai_tell",description:"Fetch ONE anti-AI-tell \u2014 full body + concrete fix. Use after `list_anti_ai_tells`.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Exact tell name from list_anti_ai_tells."}},required:["name"]}},{name:"get_redesign_check",description:"Fetch redesign check(s) matching a symptom snippet. Returns full fix + UI note. Use after `list_redesign_checks`.",inputSchema:{type:"object",properties:{symptom:{type:"string",description:"Fragment of the symptom text (e.g. 'Inter everywhere' / '100vh')."}},required:["symptom"]}},{name:"get_skill_section",description:"Fetch ONE section of ONE skill \u2014 token-efficient. E.g. `skill='soft', section='double-bezel'`. Use after `list_skills` narrowed the relevant skill + section.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Skill id (e.g. 'soft', 'minimalist', 'taste')."},section:{type:"string",description:"Section id within that skill."}},required:["skill","section"]}},{name:"get_component",description:"Full guide for one @godxjp/ui component \u2014 import path, props/types/defaults, HOW to use it (DO/DON'T), WHEN to reach for it (use cases), related components (don't reinvent/confuse), a copy-paste example, story path, and cardinal rules. Use this before hand-rolling anything. Design-token knobs are listed compactly (name+default); pass `verbose:true` for what each token controls.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Component name (e.g. 'Button', 'DataTable')."},verbose:{type:"boolean",description:"Include the full design-token table with a 'what it controls' description per token. Default false (compact token+default only) to save context."}},required:["name"]}},{name:"get_pattern",description:"Full code snippet for one canonical pattern \u2014 copy-paste-ready.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Pattern slug (use list_patterns first)."}},required:["name"]}},{name:"get_rule",description:"Read one cardinal rule from CLAUDE.md (by number) OR all if no number.",inputSchema:{type:"object",properties:{number:{type:"number",description:"Rule number (1-N)."}}}},{name:"get_vocab",description:"Read shared prop-vocabulary type (`SizeProp`, `StatusProp`, `ColorProp`, `LoadingProp`, etc.) OR all if no name.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Vocab type name."}}}},{name:"get_tokens",description:"Read design tokens, optionally filtered by tier category (primitive / semantic / component).",inputSchema:{type:"object",properties:{category:{type:"string",enum:["primitive","semantic","component"]}}}},{name:"list_consumer_skills",description:"List the design skills relevant to an app-dev BUILDING WITH @godxjp/ui (audience consumer/both). Hides core library-maintenance skills. START HERE if you import @godxjp/ui and want guidance (design-to-page, compose-a-screen, taste, \u2026). Returns id + name + whenToUse + section ids.",inputSchema:{type:"object",properties:{}}},{name:"get_consumer_skill",description:"Fetch ONE section of ONE consumer-facing skill. Same as get_skill_section but refuses core-only skills (steers app-devs away from library-maintenance material). Use after list_consumer_skills / route_consumer_task.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Consumer skill id (e.g. 'design-to-page', 'compose-a-screen')."},section:{type:"string",description:"Section id within that skill."}},required:["skill"]}},{name:"route_consumer_task",description:"Natural-language task \u2192 consumer skill+section pointer. Like route_task but only points to consumer-facing skills (never core library-maintenance). Use FIRST when you're building an app with @godxjp/ui.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"draft_bug_report",description:"When @godxjp/ui ITSELF is at fault (missing token, a primitive lacking the controlled-vocabulary prop, a real a11y/behaviour bug, a wrong catalog example) and you cannot follow a rule \u2014 DON'T fake a workaround. This drafts a detailed GitHub issue body + a copy-paste `gh issue create` command so you can report it. Prints the command only; never runs gh.",inputSchema:{type:"object",properties:{summary:{type:"string",description:"One-line title of the bug / blocked rule."},repro:{type:"string",description:"Minimal steps or code to reproduce."},expected:{type:"string",description:"What SHOULD happen (per the rule/spec)."},actual:{type:"string",description:"What actually happens."},component:{type:"string",description:"Affected component name, if any (links to get_component)."},rule:{type:"number",description:"Cardinal rule number that can't be followed, if any."},version:{type:"string",description:"Installed @godxjp/ui version (e.g. '12.1.0')."},env:{type:"string",description:"Environment (browser/OS/framework), if relevant."}},required:["summary"]}},{name:"check_compatibility",description:"Report whether the @godxjp/ui version installed in the target project matches THIS catalog (which describes one release train). A mismatched minor means the props/tokens/patterns may describe a build they never installed (#140). Pass the installed version (`npm ls @godxjp/ui`); call it at the START of a consumer session.",inputSchema:{type:"object",properties:{version:{type:"string",description:"Installed @godxjp/ui version in the target project, e.g. '16.10.0'. Omit to just read the catalog's own version + compatible range."}}}},{name:"route_task",description:"Natural-language task \u2192 skill+section pointer (e.g. 'design a premium agency hero' \u2192 soft/vibe-archetypes). Use FIRST when you don't know which skill applies.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"suggest_primitive",description:"Use case \u2192 primitive recommendation. E.g. 'confirm a destructive delete' \u2192 DangerZone pattern + Dialog suggestion.",inputSchema:{type:"object",properties:{use_case:{type:"string"}},required:["use_case"]}},{name:"search_components",description:"Fuzzy-search primitives by name / tagline / prop. Returns ranked matches.",inputSchema:{type:"object",properties:{query:{type:"string"}},required:["query"]}},{name:"get_frame_coverage",description:"Verified preview-contract coverage for a component (issue #163). Answers 'is this state actually PROVEN?' \u2014 returns, per contract dimension (variants, tones, sizes, shapes, density, controlled/uncontrolled ownership, disabled/read-only/loading/empty/error/success, async retry/cancel/offline, responsive viewport matrix, RTL, long/localized content, keyboard/focus, accessible name/description/error, reduced motion / coarse touch), whether an EXECUTED case proves it (covered), whether nothing proves it (UNTESTED), or whether it cannot exist (not-applicable, with a reason). UNTESTED IS NOT A PASS: never infer that a component supports a state because an example renders. Call this before telling a user a component 'supports' anything. Omit `name` for the repo-wide summary and the tracked known gaps.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Public export name (e.g. 'Button', 'DataTable', 'CardFooter'). Omit for the repo-wide coverage summary."}}}},{name:"lint_jsx",description:"Heuristic check of a JSX snippet for common violations \u2014 raw `<button>` / `<input>`, `color='error'` on Tag/Badge, missing aria-label, missing source.code override on stories with cell renderers (rule 34), etc.",inputSchema:{type:"object",properties:{jsx:{type:"string"}},required:["jsx"]}}],Ie=new Set(H.map(e=>e.name));function ze(e=j()){let t=h.godxUiCompatibility??h.version,a=`@godxjp/ui-mcp ${h.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let o=e.source==="node_modules"?"read from node_modules at answer time":"from GODX_UI_VERSION at launch \u2014 node_modules/@godxjp/ui not resolved";return`${a} \u2014 installed @godxjp/ui ${e.version} (${o})`}function Pe(e=j()){let t=e?L(e.version):null,a=L(h.version);return!e||!t||!a?null:Ue(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${h.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog is BEHIND the package and may be missing props and components that exist. Do not conclude from this answer that a prop is unavailable. Restart the session so the server relaunches on the installed version (run \`npx @godxjp/ui sync-rules\` first if the project's .mcp.json still pins an older @godxjp/ui-mcp).`:t.major===a.major?null:`\u26A0\uFE0F MAJOR MISMATCH: this server is @godxjp/ui-mcp ${h.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog may describe components that do not exist in the installed package. Pin the MCP to @godxjp/ui-mcp@${e.version} (\`npx @godxjp/ui sync-rules\` updates the project's .mcp.json; a registration outside the project needs \`claude mcp remove <key>\`), then restart the agent.`}async function me(e,t){let a=await Le(e,t);if(!Ie.has(e))return a;let o=j(),n=Pe(o);return`${ze(o)}
|
|
4985
|
+
finding and needs no suppression.`,oe=[{id:"dialog-needs-body",severity:"error",category:"composition",standard:null,fix:"Wrap a Dialog/Sheet's middle in <DialogBody>/<SheetBody>. The scroll lives on the body \u2014 the content box is `overflow: hidden` with no max-height \u2014 so an overlay without one clips long content at BOTH ends and takes the footer's buttons with it, leaving Escape as the only way out. It only shows on real data, never on demo data (gh#617)."},{id:"no-utility-spacing",severity:"error",category:"composition",standard:null,fix:"Remove gap-*/p-*/m-* from your own markup; space siblings with <Flex gap> / <ResponsiveGrid>. Page sections are spaced by <PageContainer> (docs/CONSUMER-RULES.md \xA73)."},{id:"no-utility-layout",severity:"error",category:"composition",standard:null,fix:'Replace className="flex \u2026" / "grid \u2026" with <Flex> (row), <Flex direction="col"> (stack) or <ResponsiveGrid columns>.'},{id:"no-hand-rolled-surface",severity:"warn",category:"composition",standard:null,fix:"A rounded+border/bg div is a fake surface \u2014 use Card, Badge, Avatar, ListRow, Descriptions or EmptyState so height, padding and radius come from tokens. A read-only sample of a colour a USER chose is Swatch, which takes that value as a prop (gh#527)."},{id:"sibling-cards-need-flex",severity:"warn",category:"composition",standard:null,fix:'Wrap adjacent <Card>s in <Flex direction="col" gap="lg"> or <ResponsiveGrid>; direct children of PageContainer are spaced by the page already.'},{id:"no-raw-palette-color",severity:"error",category:"tokens",standard:null,fix:"Use semantic tokens (bg-primary, text-muted-foreground), never raw palette (bg-blue-500)."},{id:"no-arbitrary-hex",severity:"error",category:"tokens",standard:null,fix:"No hardcoded hex in className; read design-system color tokens."},{id:"no-arbitrary-spacing",severity:"error",category:"tokens",standard:null,fix:"No p-[13px]/gap-[7px]; use the token scale / <Flex gap> / <PageContainer>."},{id:"no-arbitrary-size",severity:"error",category:"tokens",standard:null,fix:"No w-[37px]/h-[260px]; use token sizes or a sizing prop (min-w-[\u2026] allowed)."},{id:"no-arbitrary-typography",severity:"error",category:"tokens",standard:null,fix:"No text-[20px]/leading-[1.7]; use the golden-ratio type-scale tokens."},{id:"no-arbitrary-radius",severity:"error",category:"tokens",standard:null,fix:"No rounded-[6px]; use rounded-sm/md/lg radius tokens."},{id:"no-off-scale-token-value",severity:"warn",category:"tokens",standard:null,fix:"A design-system knob you override takes a step (style={{ '--card-space-inset': 'var(--space-4)' }}) or a calc() from one (calc(var(--space-4) + 2px)), not a raw 13px. Only axes that HAVE a scale count: space/padding/gap/margin, font-size, radius, icon-size (width/height/size/offset have none yet, so a number there is fine). A value that is genuinely off the grid keeps its literal and says why in place, with a /* scale-exempt: 6px status dot, below --space-1 */ comment on that line or the one above."},{id:"no-dark-color-override",severity:"warn",category:"tokens",standard:null,fix:"Drop dark: color overrides \u2014 semantic tokens already adapt."},{id:"raw-white-black",severity:"warn",category:"tokens",standard:null,fix:"Prefer semantic tokens (text-primary-foreground, bg-background) over raw white/black."},{id:"no-domain-tracking-token",severity:"error",category:"tokens",standard:null,fix:"No package-tracking/domain tokens; use semantic tokens or app theme overrides."},{id:"no-space-xy",severity:"error",category:"tokens",standard:null,fix:"Use <Flex gap> instead of space-x/y-*."},{id:"no-raw-select",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Select> from @godxjp/ui, not a raw <select>."},{id:"no-raw-table",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use the <Table>/<DataTable> family, not a raw <table>."},{id:"no-raw-input",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Input> from @godxjp/ui, not a raw <input>."},{id:"no-raw-textarea",severity:"warn",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Textarea> from @godxjp/ui, not a raw <textarea>."},{id:"no-raw-button",severity:"error",category:"composition",standard:"HTML Living Standard (WHATWG)",fix:"Use <Button> from @godxjp/ui, not a raw <button>."},{id:"card-manual-padding",severity:"error",category:"composition",standard:null,fix:"Wrap the body in <CardContent>; don't hand-roll padding on <Card>."},{id:"card-needs-content",severity:"error",category:"composition",standard:null,fix:"<Card> body must be in <CardContent> (no padding otherwise); flush only for a full-bleed table."},{id:"bare-control-needs-formfield",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.3.1 \xB7 3.3.2 \xB7 @godxjp/ui FormField (cardinal rule 227)",fix:"Wrap a labelled control in <FormField label=\u2026> \u2014 it owns label\u2194control id wiring, aria/error, AND the field rhythm; never pair a bare <Label> with an <Input>."},{id:"formfield-needs-form",severity:"error",category:"composition",standard:"@godxjp/ui Form (gh#998)",fix:'Wrap FormFields in <Form layout="horizontal" labelWidth controlWidth>; a row of fields is <SpaceCompact> or <Form columns>, never a hand-rolled <Flex>. A field component whose whole output is one FormField is exempt.'},{id:"mixed-button-size",severity:"error",category:"composition",standard:null,fix:'Sibling <Button>s under one parent (through fragments, {cond && \u2026}, ternaries, Tooltip wrappers) share ONE size; a missing size is default; icon-sm pairs with sm, icon-xs with xs, icon with default. Never `size="sm"` beside a default Button in one action row.'},{id:"dialog-form-too-big",severity:"error",category:"composition",standard:"@godxjp/ui form placement (gh#998)",fix:"Three or more FormFields in a Dialog body is a page: give the form its own route. Dialogs hold a confirmation or one or two fields; a side Sheet (drawer) may hold a filter or edit form."},{id:"select-width-hint",severity:"warn",category:"composition",standard:"GOV.UK Design System \xB7 text input width",fix:"Size a Select for its content \u2014 `controlWidth` on the FormField, or once on the <Form>."},{id:"manual-field-error",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 3.3.1",fix:"Use <FormField error=\u2026>, not a hand-rolled <p class='text-destructive'>."},{id:"manual-field-helper",severity:"warn",category:"composition",standard:null,fix:"Use <FormField helper=\u2026>, not a hand-rolled helper <p>."},{id:"status-tone-not-variant",severity:"error",category:"api",standard:null,fix:"Badge/Tag/StatCard status uses tone, not variant (variant is structural)."},{id:"value-callback-on-value-change",severity:"error",category:"api",standard:null,fix:"Abstract value components use onValueChange, not onChange."},{id:"icon-button-needs-name",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 4.1.2 \xB7 1.1.1 \xB7 WAI-ARIA 1.2 \xB7 Accessible Name Computation 1.2",fix:"Name <Button size='icon'> with aria-label={t('\u2026')} OR from content \u2014 a <VisuallyHidden>/sr-only child beside the aria-hidden glyph. Text inside an aria-hidden subtree names nothing."},{id:"img-needs-alt",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 1.1.1 \xB7 HTML Living Standard",fix:"Add alt to every <img> (alt='' if decorative); prefer <Avatar>/<AspectRatio>."},{id:"no-positive-tabindex",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.4.3 \xB7 WAI-ARIA APG",fix:"Use tabIndex 0 or -1 only; never positive \u2014 it breaks focus order."},{id:"hand-rolled-close-glyph",severity:"warn",category:"a11y",standard:"WAI-ARIA 1.2 (dialog) \xB7 WCAG 2.2 SC 4.1.2",fix:"Pass onDismiss to <Alert>, or use <Dialog>/<Sheet>'s built-in labelled close \u2014 not a bare \u2715."},{id:"no-hand-rolled-scrollport",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.1.1 (Keyboard) \xB7 WAI-ARIA 1.2 (group) \xB7 Deque axe-core scrollable-region-focusable",fix:'Replace className="overflow-auto / overflow-y-auto / overflow-x-auto / overflow-scroll" on your own element with <ScrollArea label={t("\u2026")} orientation>, which is the tab stop, the role and the localized name \u2014 and withholds all three while there is nothing to scroll. A browser audit only fails a scrollport whose content has NO focusable child, so the same markup is clean or broken depending on the data; this reads the markup instead (gh#825). overflow-hidden is a clipping box, not a scrollport, and is not flagged.'},{id:"no-emoji-in-ui",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"No emoji in product UI; quiet i18n copy + Lucide icon + Badge tone."},{id:"no-emoji-flag",severity:"warn",category:"i18n",standard:"ISO 3166-1 \xB7 ECMA-402 Intl.DisplayNames \xB7 Unicode UTS #51",fix:"Derive country names from Intl.DisplayNames; no emoji flags."},{id:"hardcoded-currency",severity:"warn",category:"i18n",standard:"ISO 4217 \xB7 ECMA-402 Intl.NumberFormat",fix:"Format money with Intl.NumberFormat({ style: 'currency', currency }), not \xA5{amount}."},{id:"raw-intl-date",severity:"warn",category:"i18n",standard:"ISO 8601 \xB7 IANA tz \xB7 ECMA-402 Intl.DateTimeFormat",fix:"Use formatDate from @godxjp/ui/datetime, not hand-built or locale-default dates."},{id:"no-physical-direction",severity:"warn",category:"rtl",standard:"W3C CSS Logical Properties L1 \xB7 WCAG 2.2 (1.3.2)",fix:"Use logical utilities (ms-/me-/ps-/pe-, start-/end-, text-start/end, border-s/e, rounded-s/e)."},{id:"no-em-dash-in-copy",severity:"warn",category:"copy",standard:"@godxjp/ui reference-design typography",fix:"No em-dash (\u2014) in copy; use a middot \xB7 or two calm sentences."},{id:"lucide-icon-needs-size",severity:"warn",category:"composition",standard:"WCAG 2.2 SC 1.4.4 \xB7 @godxjp/ui icon scale (--icon-size-*)",fix:'A lucide glyph outside a sizing context draws at its intrinsic 24px. Render it as <Icon as={Lock} size="sm" tone="muted" /> \u2014 the primitive puts it on the --icon-size-* scale and is aria-hidden unless you pass a label.'},{id:"no-hand-rolled-list",severity:"warn",category:"composition",standard:"WAI-ARIA 1.2 (list / listitem) \xB7 HTML Living Standard (ul/ol/li) \xB7 WCAG 2.2 SC 1.3.1",fix:'Build the list as <Flex as="ul" marker="none" direction="col" gap="none"> with <ListRow as="li"> rows \u2014 not a raw <ul>/<ol> (no gap token), not <div role="list">/<div role="listitem">, and never a wrapper around each row: the divider is :not(:last-child) among SIBLINGS, so a row alone in its own wrapper loses it silently (a consumer lost every divider in a settings menu and a dashboard this way). marker="none" keeps the element, the <li> semantics and the gap, and drops the bullet and the --space-5 indent (gh#714). A deliberate exception \u2014 a drag-and-drop Kanban column, an evidence list inside a TableCell \u2014 takes an ui-audit-disable-line that says so.'},{id:"no-hand-rolled-break-anywhere",severity:"warn",category:"composition",standard:"CSS Text 3 \xA75.5 (overflow-wrap) \xB7 WCAG 2.2 SC 1.4.10 (Reflow)",fix:`Replace className="[overflow-wrap:anywhere] break-words whitespace-normal" (or wrap-anywhere) with <Text break="anywhere">, which emits overflow-wrap: anywhere AND releases a table cell's inherited nowrap, so an email, code or id shrinks its column to the viewport. Not whitespace="pre-wrap": its break-word does not lower min-content, so a table cell stays wide (gh#927).`}];function re(e){return e?oe.filter(t=>t.category===e):oe}var le="node node_modules/@godxjp/ui/scripts/visual-audit.mjs <baseUrl> [route \u2026] (optional peers, TESTED range: playwright >=1.55 <2 [1.61.1] + @axe-core/playwright >=4.10 <5 [4.12.1] + axe-core >=4.10 <5 [4.12.1] + a chromium via `playwright install chromium`; --strict for a CI gate, --format json ALWAYS emits valid JSON with a status of ok|partial|error separating infra errors[] from product findings[], --rules to print this catalog)",se=[{id:"css-layers-missing",severity:"error",category:"layout",standard:"@godxjp/ui styles contract (styles / styles/core are the only entries)",fix:"Import `@godxjp/ui/styles` (or `styles/core` without fonts \u2014 86 KB instead of 367 KB gzip, since the bundled @font-face declarations are most of the CSS, gh#971); never cherry-pick *-layout.css \u2014 a missing layer renders naked menus and unsized Select rows."},{id:"control-height-mismatch",severity:"error",category:"layout",standard:"@godxjp/ui control tier (--control-height) \xB7 Nielsen consistency heuristic",fix:"Every control in one row must share --control-height; replace hand-rolled pills with Avatar/Button/Badge, never restyle a control's height."},{id:"mixed-button-height",severity:"error",category:"layout",standard:"@godxjp/ui Button size (one size per row) \xB7 Nielsen consistency heuristic",fix:"Buttons in one flex row must render at one height (within 0.5px): one `size` per row; icon-sm pairs with sm, icon-xs with xs, icon with default. Runtime twin of the static mixed-button-size rule."},{id:"sibling-card-gap",severity:"error",category:"layout",standard:"@godxjp/ui spacing scale (docs/SPACING.md)",fix:'Adjacent Cards need one space step between them \u2014 <Flex direction="col" gap>, <ResponsiveGrid>, or direct children of PageContainer.'},{id:"row-content-starved",severity:"warn",category:"layout",standard:"WCAG 2.2 SC 1.4.10 reflow",fix:`A sibling (a w-full SelectTrigger) takes the row's width and truncates its neighbours \u2014 give the Select width="auto" or move it out of the row.`},{id:"axe-violations",severity:"warn",category:"a11y",standard:"WCAG 2.2 A/AA \xB7 WAI-ARIA 1.2 (axe-core engine)",fix:"Fix each axe node \u2014 contrast (1.4.3), name/role/value (4.1.2), ARIA, landmarks. Runs on the REAL DOM, catching what static analysis cannot."},{id:"target-size-min",severity:"warn",category:"a11y",standard:"WCAG 2.2 SC 2.5.8 (24\xD724 AA) \xB7 2.5.5 (44\xD744 AAA)",fix:"Interactive targets must be \u226524\xD724 CSS px; size from the --control-height tier."},{id:"oversaturated-accent",severity:"warn",category:"color",standard:"@godxjp/ui reference-design \u6E0B\u307F (OKLCH chroma \u2264 0.18)",fix:"Desaturate brand/primary surfaces (OKLCH chroma \u2264 0.18); read --primary tokens, no raw vivid bars. The accent @godxjp/ui itself ships is exempt (gh#823) \u2014 this finding is always a colour someone chose."},{id:"emoji-rendered",severity:"warn",category:"i18n",standard:"Unicode UTS #51 \xB7 WCAG 2.2 SC 1.1.1",fix:"Remove emoji from rendered product text; quiet i18n copy + Lucide icon + Badge tone."},{id:"alert-controls-misplaced",severity:"warn",category:"layout",standard:"@godxjp/ui Alert anatomy \xB7 WAI-ARIA 1.2 \xB7 WCAG 2.2 SC 4.1.2",fix:"Use <Alert>: one leading tone icon, <Alert.Actions> trailing-right normal width, onDismiss \xD7 top-right, one horizontal row."}];function de(e){return e?se.filter(t=>t.category===e):se}var h={name:"@godxjp/ui-mcp",version:"31.19.0",godxUiCompatibility:"31.19.x",description:"Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 45 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",type:"module",main:"./dist/index.js",module:"./dist/index.js",types:"./dist/index.d.ts",bin:{"godx-ui-mcp":"./dist/index.js"},files:["dist","README.md"],publishConfig:{registry:"https://registry.npmjs.org/",access:"public"},repository:{type:"git",url:"git+https://github.com/godx-jp/godxjp-ui.git",directory:"mcp"},homepage:"https://github.com/godx-jp/godxjp-ui/tree/main/mcp#readme",license:"Apache-2.0",scripts:{build:"tsup",dev:"tsup --watch",start:"node dist/index.js",inspect:"npx @modelcontextprotocol/inspector node dist/index.js","type-check":"tsc --noEmit",test:"vitest run",prepublishOnly:"npm run build"},dependencies:{"@modelcontextprotocol/sdk":"^1.29.0",zod:"^4.4.3"},devDependencies:{"@types/node":"^22.10.0",tsup:"^8.5.1",typescript:"^6.0.3",vitest:"^4.1.6"},keywords:["mcp","model-context-protocol","godxjp","ui","design-system","react","claude","cursor"],author:"GoDX (https://godx.jp)",bugs:{url:"https://github.com/godx-jp/godxjp-ui/issues"}};var H=[{name:"list_skills",description:"List every design/taste skill bundled by this MCP (id + name + whenToUse + section ids). Use FIRST to discover skills; then `get_skill_section` to drill in.",inputSchema:{type:"object",properties:{}}},{name:"list_primitives",description:"List every @godxjp/ui primitive/composite/shell (group + tagline per entry). Optionally filter by group. Then `get_component` for one's full API.",inputSchema:{type:"object",properties:{group:{type:"string",enum:["general","layout","data-display","data-entry","feedback","navigation","composites","shell","providers"]}}}},{name:"list_utilities",description:'List every NON-component public export of @godxjp/ui \u2014 hooks, helper functions and constants (cn, formatDate, formatCurrency, toast, useDebouncedValue, buttonVariants, CHART_COLORS, SHOW_PARENT \u2026). Reach for this before hand-writing a className merger, a date/money formatter, a debounce hook or a chart palette: two products in this org each re-implemented `cn` because it could not be found. Optionally filter by kind. Then `get_component name="<name>"` for its signature, usage and example.',inputSchema:{type:"object",properties:{kind:{type:"string",enum:["hook","function","value"],description:"hook = only legal inside a component body; function = callable anywhere; value = a constant to read."}}}},{name:"list_patterns",description:"List every canonical copy-paste code pattern (signup-form, settings-page, data-table-page, async-data-state, confirm-destructive, \u2026); common aliases resolve too. Use before `get_pattern`.",inputSchema:{type:"object",properties:{}}},{name:"list_anti_ai_tells",description:"List every AI-tell pattern to AVOID (optionally by category). Use to self-audit a design before shipping; then `get_anti_ai_tell` for the fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["visual","layout","copy","interaction","imagery","structure"]}}}},{name:"list_redesign_checks",description:"List the redesign audit checklist (50+ checks; optionally by category). Use when auditing an existing project; then `get_redesign_check` for a symptom's fix.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["typography","color-surface","layout","interactivity","content","components","iconography","code-quality","omissions"]}}}},{name:"list_audit_rules",description:"List the LOCAL static ui-audit rules (scripts/ui-audit.mjs) to run BEFORE any visual review \u2014 each cites the standard it enforces (WCAG/WAI-ARIA/Intl/ISO/IANA/CSS-Logical) + a fix + the run command. Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["tokens","composition","api","a11y","i18n","rtl","copy"]}}}},{name:"list_visual_checks",description:"List the RUNTIME visual-audit checks (scripts/visual-audit.mjs \u2014 Playwright + axe-core) to run against the RUNNING app: contrast/ARIA (axe), target size, rendered-accent chroma, DOM emoji, banner layout. Needs a browser (vs list_audit_rules, static). Optionally by category.",inputSchema:{type:"object",properties:{category:{type:"string",enum:["a11y","color","i18n","layout"]}}}},{name:"get_anti_ai_tell",description:"Fetch ONE anti-AI-tell \u2014 full body + concrete fix. Use after `list_anti_ai_tells`.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Exact tell name from list_anti_ai_tells."}},required:["name"]}},{name:"get_redesign_check",description:"Fetch redesign check(s) matching a symptom snippet. Returns full fix + UI note. Use after `list_redesign_checks`.",inputSchema:{type:"object",properties:{symptom:{type:"string",description:"Fragment of the symptom text (e.g. 'Inter everywhere' / '100vh')."}},required:["symptom"]}},{name:"get_skill_section",description:"Fetch ONE section of ONE skill \u2014 token-efficient. E.g. `skill='soft', section='double-bezel'`. Use after `list_skills` narrowed the relevant skill + section.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Skill id (e.g. 'soft', 'minimalist', 'taste')."},section:{type:"string",description:"Section id within that skill."}},required:["skill","section"]}},{name:"get_component",description:"Full guide for one @godxjp/ui component \u2014 import path, props/types/defaults, HOW to use it (DO/DON'T), WHEN to reach for it (use cases), related components (don't reinvent/confuse), a copy-paste example, story path, and cardinal rules. Use this before hand-rolling anything. Design-token knobs are listed compactly (name+default); pass `verbose:true` for what each token controls.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Component name (e.g. 'Button', 'DataTable')."},verbose:{type:"boolean",description:"Include the full design-token table with a 'what it controls' description per token. Default false (compact token+default only) to save context."}},required:["name"]}},{name:"get_pattern",description:"Full code snippet for one canonical pattern \u2014 copy-paste-ready.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Pattern slug (use list_patterns first)."}},required:["name"]}},{name:"get_rule",description:"Read one cardinal rule from CLAUDE.md (by number) OR all if no number.",inputSchema:{type:"object",properties:{number:{type:"number",description:"Rule number (1-N)."}}}},{name:"get_vocab",description:"Read shared prop-vocabulary type (`SizeProp`, `StatusProp`, `ColorProp`, `LoadingProp`, etc.) OR all if no name.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Vocab type name."}}}},{name:"get_tokens",description:"Read design tokens, optionally filtered by tier category (primitive / semantic / component).",inputSchema:{type:"object",properties:{category:{type:"string",enum:["primitive","semantic","component"]}}}},{name:"list_consumer_skills",description:"List the design skills relevant to an app-dev BUILDING WITH @godxjp/ui (audience consumer/both). Hides core library-maintenance skills. START HERE if you import @godxjp/ui and want guidance (design-to-page, compose-a-screen, taste, \u2026). Returns id + name + whenToUse + section ids.",inputSchema:{type:"object",properties:{}}},{name:"get_consumer_skill",description:"Fetch ONE section of ONE consumer-facing skill. Same as get_skill_section but refuses core-only skills (steers app-devs away from library-maintenance material). Use after list_consumer_skills / route_consumer_task.",inputSchema:{type:"object",properties:{skill:{type:"string",description:"Consumer skill id (e.g. 'design-to-page', 'compose-a-screen')."},section:{type:"string",description:"Section id within that skill."}},required:["skill"]}},{name:"route_consumer_task",description:"Natural-language task \u2192 consumer skill+section pointer. Like route_task but only points to consumer-facing skills (never core library-maintenance). Use FIRST when you're building an app with @godxjp/ui.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"draft_bug_report",description:"When @godxjp/ui ITSELF is at fault (missing token, a primitive lacking the controlled-vocabulary prop, a real a11y/behaviour bug, a wrong catalog example) and you cannot follow a rule \u2014 DON'T fake a workaround. This drafts a detailed GitHub issue body + a copy-paste `gh issue create` command so you can report it. Prints the command only; never runs gh.",inputSchema:{type:"object",properties:{summary:{type:"string",description:"One-line title of the bug / blocked rule."},repro:{type:"string",description:"Minimal steps or code to reproduce."},expected:{type:"string",description:"What SHOULD happen (per the rule/spec)."},actual:{type:"string",description:"What actually happens."},component:{type:"string",description:"Affected component name, if any (links to get_component)."},rule:{type:"number",description:"Cardinal rule number that can't be followed, if any."},version:{type:"string",description:"Installed @godxjp/ui version (e.g. '12.1.0')."},env:{type:"string",description:"Environment (browser/OS/framework), if relevant."}},required:["summary"]}},{name:"check_compatibility",description:"Report whether the @godxjp/ui version installed in the target project matches THIS catalog (which describes one release train). A mismatched minor means the props/tokens/patterns may describe a build they never installed (#140). Pass the installed version (`npm ls @godxjp/ui`); call it at the START of a consumer session.",inputSchema:{type:"object",properties:{version:{type:"string",description:"Installed @godxjp/ui version in the target project, e.g. '16.10.0'. Omit to just read the catalog's own version + compatible range."}}}},{name:"route_task",description:"Natural-language task \u2192 skill+section pointer (e.g. 'design a premium agency hero' \u2192 soft/vibe-archetypes). Use FIRST when you don't know which skill applies.",inputSchema:{type:"object",properties:{task:{type:"string",description:"Describe what you want to build."}},required:["task"]}},{name:"suggest_primitive",description:"Use case \u2192 primitive recommendation. E.g. 'confirm a destructive delete' \u2192 DangerZone pattern + Dialog suggestion.",inputSchema:{type:"object",properties:{use_case:{type:"string"}},required:["use_case"]}},{name:"search_components",description:"Fuzzy-search primitives by name / tagline / prop. Returns ranked matches.",inputSchema:{type:"object",properties:{query:{type:"string"}},required:["query"]}},{name:"get_frame_coverage",description:"Verified preview-contract coverage for a component (issue #163). Answers 'is this state actually PROVEN?' \u2014 returns, per contract dimension (variants, tones, sizes, shapes, density, controlled/uncontrolled ownership, disabled/read-only/loading/empty/error/success, async retry/cancel/offline, responsive viewport matrix, RTL, long/localized content, keyboard/focus, accessible name/description/error, reduced motion / coarse touch), whether an EXECUTED case proves it (covered), whether nothing proves it (UNTESTED), or whether it cannot exist (not-applicable, with a reason). UNTESTED IS NOT A PASS: never infer that a component supports a state because an example renders. Call this before telling a user a component 'supports' anything. Omit `name` for the repo-wide summary and the tracked known gaps.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Public export name (e.g. 'Button', 'DataTable', 'CardFooter'). Omit for the repo-wide coverage summary."}}}},{name:"lint_jsx",description:"Heuristic check of a JSX snippet for common violations \u2014 raw `<button>` / `<input>`, `color='error'` on Tag/Badge, missing aria-label, missing source.code override on stories with cell renderers (rule 34), etc.",inputSchema:{type:"object",properties:{jsx:{type:"string"}},required:["jsx"]}}],Ie=new Set(H.map(e=>e.name));function ze(e=V()){let t=h.godxUiCompatibility??h.version,a=`@godxjp/ui-mcp ${h.version} (catalog for @godxjp/ui ${t})`;if(!e)return a;let o=e.source==="node_modules"?"read from node_modules at answer time":"from GODX_UI_VERSION at launch \u2014 node_modules/@godxjp/ui not resolved";return`${a} \u2014 installed @godxjp/ui ${e.version} (${o})`}function Pe(e=V()){let t=e?L(e.version):null,a=L(h.version);return!e||!t||!a?null:Ue(t,a)>0?`\u26A0\uFE0F SERVER OLDER THAN INSTALLED PACKAGE: this server is @godxjp/ui-mcp ${h.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog is BEHIND the package and may be missing props and components that exist. Do not conclude from this answer that a prop is unavailable. Restart the session so the server relaunches on the installed version (run \`npx @godxjp/ui sync-rules\` first if the project's .mcp.json still pins an older @godxjp/ui-mcp).`:t.major===a.major?null:`\u26A0\uFE0F MAJOR MISMATCH: this server is @godxjp/ui-mcp ${h.version}, the project has @godxjp/ui ${e.version} installed \u2014 this catalog may describe components that do not exist in the installed package. Pin the MCP to @godxjp/ui-mcp@${e.version} (\`npx @godxjp/ui sync-rules\` updates the project's .mcp.json; a registration outside the project needs \`claude mcp remove <key>\`), then restart the agent.`}async function me(e,t){let a=await Le(e,t);if(!Ie.has(e))return a;let o=V(),n=Pe(o);return`${ze(o)}
|
|
4970
4986
|
${n?`${n}
|
|
4971
4987
|
`:""}
|
|
4972
4988
|
${a}`}async function Le(e,t){switch(e){case"list_skills":return Fe();case"list_primitives":return ve(t.group);case"list_utilities":return et(t.kind);case"list_patterns":return qe();case"list_anti_ai_tells":return _e(t.category);case"list_redesign_checks":return $e(t.category);case"list_audit_rules":return Ke(t.category);case"list_visual_checks":return We(t.category);case"get_anti_ai_tell":return Ye(String(t.name??""));case"get_redesign_check":return Xe(String(t.symptom??""));case"get_skill_section":return ye(String(t.skill??""),String(t.section??""));case"get_component":return tt(String(t.name??""),t.verbose===!0);case"get_pattern":return nt(String(t.name??""));case"get_rule":return it(typeof t.number=="number"?t.number:void 0);case"get_vocab":return rt(t.name==null?void 0:String(t.name));case"get_tokens":return st(t.category);case"list_consumer_skills":return Me();case"get_consumer_skill":return Be(String(t.skill??""),String(t.section??""));case"route_consumer_task":return pe(String(t.task??""),{consumerOnly:!0});case"draft_bug_report":return He(t);case"check_compatibility":return fe(t.version==null?void 0:String(t.version));case"route_task":return pe(String(t.task??""));case"suggest_primitive":return lt(String(t.use_case??""));case"search_components":return dt(String(t.query??""));case"get_frame_coverage":return ot(t.name===void 0?void 0:String(t.name));case"lint_jsx":return ct(String(t.jsx??""));default:return`Unknown tool: ${e}`}}function Fe(){let e=`# Available skills (${T.length})
|
|
@@ -5029,7 +5045,7 @@ ${p}
|
|
|
5029
5045
|
${N}
|
|
5030
5046
|
\`\`\`
|
|
5031
5047
|
|
|
5032
|
-
`,d+="_Reminder: don't hand-roll a fake workaround to hide the bug \u2014 file this, then mark any minimal local workaround with `// TODO(godxui#<n>)`._\n",d}function L(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?{major:t[1],minor:t[2],patch:t[3]}:null}function Ue(e,t){for(let a of["major","minor","patch"]){let o=Number(e[a])-Number(t[a]);if(o!==0)return o<0?-1:1}return 0}function ge(){let e=h.godxUiCompatibility;return e?/^(\d+)\.(\d+)\.x$/.exec(e):null}function U(e){let t=L(e),a=ge(),o=h.godxUiCompatibility;return!t||o&&!a?!1:a?t.major===a[1]&&t.minor===a[2]:e.trim()===h.version}function
|
|
5048
|
+
`,d+="_Reminder: don't hand-roll a fake workaround to hide the bug \u2014 file this, then mark any minimal local workaround with `// TODO(godxui#<n>)`._\n",d}function L(e){let t=/^(\d+)\.(\d+)\.(\d+)/.exec(e.trim());return t?{major:t[1],minor:t[2],patch:t[3]}:null}function Ue(e,t){for(let a of["major","minor","patch"]){let o=Number(e[a])-Number(t[a]);if(o!==0)return o<0?-1:1}return 0}function ge(){let e=h.godxUiCompatibility;return e?/^(\d+)\.(\d+)\.x$/.exec(e):null}function U(e){let t=L(e),a=ge(),o=h.godxUiCompatibility;return!t||o&&!a?!1:a?t.major===a[1]&&t.minor===a[2]:e.trim()===h.version}function j(){return process.env.GODX_UI_VERSION?.trim()||void 0}var je=ue("node_modules","@godxjp","ui","package.json"),ce=null;function he(e){try{return Ee(e).mtimeMs}catch{return null}}function Ve(e){let t=Re(e);for(;;){let a=ue(t,je);if(De(a))return a;let o=Ne(t);if(o===t)return null;t=o}}function Ge(){let e=process.cwd(),t=ce;if(t?.cwd===e&&t.path!==null&&he(t.path)===t.mtimeMs)return t.version;let a=Ve(e),o=null;if(a)try{let n=JSON.parse(Oe(a,"utf8"));typeof n.version=="string"&&n.version.trim()&&(o=n.version.trim())}catch{o=null}return ce={cwd:e,path:a,mtimeMs:a?he(a):null,version:o},o}function V(){let e=Ge();if(e)return{version:e,source:"node_modules"};let t=j();return t?{version:t,source:"GODX_UI_VERSION"}:null}function be(e,t){return fe(e)+`
|
|
5033
5049
|
|
|
5034
5050
|
\u26D4 **Catalog withheld for \`${t}\`** \u2014 props, examples, and pattern code are not returned when the installed @godxjp/ui version does not match this MCP catalog. Align versions (see above), restart the MCP, then retry.
|
|
5035
5051
|
`}function fe(e){let t=h.version,a=h.godxUiCompatibility,o=`# @godxjp/ui compatibility
|
|
@@ -5179,7 +5195,7 @@ Not components: these have no props table. Call \`get_component name="<name>"\`
|
|
|
5179
5195
|
| Name | Import from | What it does |
|
|
5180
5196
|
|---|---|---|
|
|
5181
5197
|
`;for(let s of r){let i=s.subpaths.map(l=>`\`@godxjp/ui${l==="."?"":l.slice(1)}\``).join(" \xB7 ");o+=`| \`${s.name}\` | ${i} | ${s.tagline.split(" \u2014 ")[0].split(". ")[0]} |
|
|
5182
|
-
`}}}return o}var we='**FORM RULES** \u2014 enforced by ui-audit (`formfield-needs-form`, `dialog-form-too-big`, `select-width-hint`):\n- Wrap fields in `<Form layout="horizontal" labelWidth controlWidth>` \u2014 never a group of FormFields without it, never a hand-rolled `<Flex>` row of fields.\n- Size each control for its content (GOV.UK text-input width): `controlWidth` per field or once on the Form \u2014 a port ~7rem, a short enum ~10rem.\n- Fields that belong on one row: `<SpaceCompact>` or `<Form columns>`.\n- Three or more fields is a page (own route), not a Dialog.\n- A case these do not cover: open an issue on godx-jp/godxjp-ui, use the nearest valid composition with `// TODO(godxjp-ui#<n>)` \u2014 never hand-roll.\n';function tt(e,t=!1){let a=
|
|
5198
|
+
`}}}return o}var we='**FORM RULES** \u2014 enforced by ui-audit (`formfield-needs-form`, `dialog-form-too-big`, `select-width-hint`):\n- Wrap fields in `<Form layout="horizontal" labelWidth controlWidth>` \u2014 never a group of FormFields without it, never a hand-rolled `<Flex>` row of fields.\n- Size each control for its content (GOV.UK text-input width): `controlWidth` per field or once on the Form \u2014 a port ~7rem, a short enum ~10rem.\n- Fields that belong on one row: `<SpaceCompact>` or `<Form columns>`.\n- Three or more fields is a page (own route), not a Dialog.\n- A case these do not cover: open an issue on godx-jp/godxjp-ui, use the nearest valid composition with `// TODO(godxjp-ui#<n>)` \u2014 never hand-roll.\n';function tt(e,t=!1){let a=j();if(a&&!U(a))return be(a,"get_component");let o=R(e);if(!o){let i=G(e);if(i)return`\`${e}\` ships, and it is documented as part of **${i.name}** \u2014 it has no separate entry because it is a compound sub-part, alias or shim of that component.
|
|
5183
5199
|
|
|
5184
5200
|
Call \`get_component name="${i.name}"\` for its props, usage and example. Do NOT hand-roll \`${e}\`.`;let l=K(e);return l?Ze(l):`Component "${e}" not found \u2014 and it is not a hook or utility either. Use \`list_primitives\` to discover components, or \`list_utilities\` for hooks and helpers.`}let n=`# ${o.name}
|
|
5185
5201
|
|
|
@@ -5285,7 +5301,7 @@ ${we}`),n}var at=new Map(u.dimensions.map(e=>[e.id,e.title]));function ke(e,t){i
|
|
|
5285
5301
|
`:"")+"Call `get_frame_coverage` with no name for the repo-wide summary.\n"}return`# ${a.name} \u2014 contract coverage
|
|
5286
5302
|
|
|
5287
5303
|
**Group:** ${a.group}
|
|
5288
|
-
${ke(a,a.name)}`}function nt(e){let t=
|
|
5304
|
+
${ke(a,a.name)}`}function nt(e){let t=j();if(t&&!U(t))return be(t,"get_pattern");let a=z(e);if(!a){let o=X(e);if(o.length===0)return`Pattern "${e}" not found.`;let n=`Pattern "${e}" not found. Closest:
|
|
5289
5305
|
`;for(let r of o)n+=`- ${r.name} \u2014 ${r.tagline}
|
|
5290
5306
|
`;return n}return`# Pattern: ${a.name}
|
|
5291
5307
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@godxjp/ui-mcp",
|
|
3
|
-
"version": "31.
|
|
4
|
-
"godxUiCompatibility": "31.
|
|
3
|
+
"version": "31.19.0",
|
|
4
|
+
"godxUiCompatibility": "31.19.x",
|
|
5
5
|
"description": "Model Context Protocol server for @godxjp/ui \u2014 gives Claude Code / Codex CLI / Cursor / any MCP-aware agent live access to the component catalog, prop vocabulary, design tokens, 45 cardinal rules, copy-paste-ready patterns, 12 design / taste skills synthesised from Leonxlnx/taste-skill, 20+ anti-AI-tell patterns, and a 50-check redesign audit \u2014 token-efficient (list \u2192 drill-down).",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|