@godxjp/ui-mcp 31.20.1 → 31.21.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.
Files changed (2) hide show
  1. package/dist/index.js +3 -3
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1573,7 +1573,7 @@ export function AccountMapping() {
1573
1573
  showSearch
1574
1574
  />
1575
1575
  );
1576
- }`,storyPath:"data-entry/Transfer.stories.tsx",rules:[23,31]},{name:"Upload",group:"data-entry",tagline:"Drag-and-drop / button / avatar / picture file uploader in six variants \u2014 wire onUpload to your media-service and call collectUploadCommitActions on form submit; supports multipart forms and custom media storage.",props:[{name:"variant",type:'"dropzone" | "button" | "picture-card" | "picture" | "avatar" | "avatar-crop"',defaultValue:'"dropzone"',description:"Controls the visual rendering mode. dropzone = large dashed drop area + file list; button = compact outline button + file list; picture-card = grid of 96\xD796 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged."},{name:"listType",type:'"text" | "picture"',defaultValue:'"picture" for variant="picture", otherwise "text"',description:"HOW THE CHOSEN FILES ARE LISTED \u2014 antd's listType, and an axis of its own: variant decides how files are PICKED, listType decides how the picked ones are DRAWN. text = name, size and actions (the classic dropzone/button row). picture = a leading box on every row: the thumbnail when the item has a previewUrl, otherwise the glyph for its file kind (image / pdf / archive / text / generic), both boxes the same size so a mixed list keeps one row height. The glyph is decorative (aria-hidden) and carries its kind as data-file-kind for theming. antd's picture-card is variant='picture-card' here \u2014 the tile grid IS the picker there, so it is not offered as a listType."},{name:"value",type:"UploadFileItem[]",description:"Controlled list of file items. When provided the component is controlled \u2014 you own the state. Omit to run uncontrolled."},{name:"defaultValue",type:"UploadFileItem[]",description:"Initial list of file items for uncontrolled usage. Ignored once value is provided."},{name:"accept",type:"string",description:'MIME / extension accept string passed to the hidden <input type="file">. avatar/avatar-crop/picture/picture-card default to "image/*"; dropzone and button default to unrestricted.'},{name:"multiple",type:"boolean",description:"Allow multi-file selection. Auto-derived: false when maxCount is 1 (or when variant is avatar/avatar-crop/picture); otherwise true."},{name:"maxCount",type:"number",description:"Hard upper bound on the number of items. avatar/avatar-crop/picture auto-default to 1. Once the limit is reached the add button is hidden (picture-card) or additions are rejected; maxCount=1 replaces the current item."},{name:"maxSizeBytes",type:"number",description:"Files larger than this limit are rejected with localized feedback and onReject."},{name:"disabled",type:"boolean",description:"Disables all interactive surfaces (drop zone, buttons). Visual opacity + pointer-events-none applied."},{name:"removable",type:"boolean",defaultValue:"true",description:"Show the remove/delete control on each item. Set false to make uploads permanent within the session."},{name:"onUpload",type:"(file: File, item: UploadFileItem, context: UploadRequestContext) => Promise<UploadResult>",description:"Called immediately after a file is picked (before form submit). Transitions the item to status='uploading', then 'done' on resolve or 'error' on reject. Wire this to your media-service issue/PUT/complete cycle. If omitted files stay in status='idle' and the raw File object remains in item.file."},{name:"className",type:"string",description:"Extra CSS class applied to the outer wrapper div."},{name:"id",type:"string",description:'Lands on the native `<input type="file">`, NOT on the wrapper \u2014 the hidden input is the semantic focus target, so this is what makes a `<label htmlFor>` (or FormField, which injects it) actually focus the picker. Putting it on the visible dropzone instead is the usual reason a label click does nothing.'},{name:"children",type:"React.ReactNode",description:"Custom button label for variant='button'. Falls back to the i18n 'Upload file' string."},{name:"onValueChange",type:"(items: UploadFileItemProp[]) => void",description:"Fires with the current file list."},{name:"triggerSize",type:'"default" | "md" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg"',description:'`variant="button"` only \u2014 size of the visible trigger, forwarded to Button. An icon size renders it icon-only and moves the label to `aria-label`: a 32px square beside other icon buttons rather than a 147px labelled one that outweighs them.'},{name:"triggerVariant",type:'"default" | "destructive" | "outline" | "dashed" | "secondary" | "ghost" | "link" | "bare"',defaultValue:'"outline"',description:'`variant="button"` only \u2014 visual weight of the visible trigger, forwarded to Button. Default `outline` suits a standalone form field. Pass `ghost` when the trigger sits in a toolbar row beside other icon buttons \u2014 inside a chat composer, say \u2014 where a bordered square reads as the odd one out.'},{name:"triggerIcon",type:"React.ComponentType<React.SVGProps<SVGSVGElement> & React.RefAttributes<SVGSVGElement>>",defaultValue:"the lucide upload arrow",description:'`variant="button"` only \u2014 the glyph the trigger draws. Pass the COMPONENT (`triggerIcon={Plus}`), not an element, exactly as `Icon`\'s `as` takes it; every lucide icon qualifies. It exists because the glyph was hard-coded and `triggerVariant` only moves emphasis, so a "create new" action that HAPPENS to upload could not carry a plus and had to announce itself as an upload (gh#734). The library still owns the class, the label spacing and the `aria-hidden`, so a swapped glyph renders at the same `--upload-row-icon-size` and never reaches the accessible name \u2014 the trigger\'s metrics cannot drift with the icon.'},{name:"readOnly",type:"boolean",description:"Displays existing files, blocks changes, preserves staged form data."},{name:"directory",type:"boolean",description:"Select a folder; each item preserves relativePath."},{name:"pastable",type:"boolean",description:"Paste clipboard files while focus is inside this Upload; text paste is untouched."},{name:"openFileDialogOnClick",type:"boolean",description:"Defaults true. Disable native dialog activation for drop-only surfaces."},{name:"name",type:"string",description:"Multipart file field name; named staged files also join native FormData."},{name:"action",type:"string | ((file: File) => string | Promise<string>)",description:"Multipart upload URL; onUpload takes precedence."},{name:"method",type:'"POST" | "PUT" | "PATCH"',description:"Request method, default POST."},{name:"headers",type:"Record<string, string>",description:"Request headers such as CSRF tokens; multipart boundaries remain browser-owned."},{name:"data",type:"Record<string, string | Blob> | ((file: File) => Record<string, string | Blob> | Promise<Record<string, string | Blob>>)",description:"Additional multipart fields, optionally resolved per file."},{name:"withCredentials",type:"boolean",description:"Send credentials with the default transport."},{name:"beforeUpload",type:"(file: File, files: File[]) => boolean | File | Blob | typeof UPLOAD_LIST_IGNORE | Promise<boolean | File | Blob | typeof UPLOAD_LIST_IGNORE>",description:"Validate or transform before upload; false stages manually; UPLOAD_LIST_IGNORE excludes. Rejections are reported."},{name:"onReject",type:"(rejection: UploadRejection) => void",description:"Reports accept, size, count, or preflight rejection."},{name:"onRemove",type:"(item: UploadFileItem) => boolean | void | Promise<boolean | void>",description:"Returning false or rejecting vetoes removal. Accepted removal aborts active upload."},{name:"onPreview",type:"(item: UploadFileItem) => void",description:"Preview action callback."},{name:"onDownload",type:"(item: UploadFileItem) => void",description:"Download action callback."},{name:"previewFile",type:"(file: File) => Promise<string>",description:"Asynchronously generate a custom thumbnail."},{name:"onDrop",type:"React.DragEventHandler<HTMLElement>",description:"Observe drop events."},{name:"showUploadList",type:"boolean",description:"Show the default file list, default true."},{name:"itemRender",type:"(node: React.ReactElement, item: UploadFileItem, items: UploadFileItem[], actions: UploadItemActions) => React.ReactNode",description:"Customize a file row while preserving its default actions."}],usage:["DO provide onUpload to auto-upload on pick. The callback must return { mediaId, previewUrl? } \u2014 the component transitions item.status through uploading \u2192 done/error automatically. Without onUpload the File object sits in item.file until you manually process it.","DO call collectUploadCommitActions(items) on form submit to get { deleteMediaIds, promoteMediaIds } for your media-service. For multipart endpoints submit File objects via FormData; never submit blob URLs as persisted media.","DO use createUploadItem(file) to build UploadFileItem objects when pre-populating value from server data (e.g. edit forms). Set status='done' and mediaId on existing server media so the draft/undo machinery tracks them correctly.","For native multipart or Inertia Form submissions, set name: staged local files are appended during the formdata event. Completed media uploads use collectUploadCommitActions instead.","Avatar/picture variants (maxCount=1) use internal soft-delete draft logic: removing an item marks it pendingDelete so the user can undo before committing. On form submit, collectUploadCommitActions converts pendingDelete \u2192 deleteMediaIds and done mediaIds \u2192 promoteMediaIds.","For avatar-crop: a crop dialog opens after pick. The cropped Blob is staged as a new UploadFileItem. The original file never enters the list \u2014 only the cropped version is passed to onUpload.","For a drawer or panel listing MIXED attachments (.png beside .json and .txt), keep variant='dropzone' and set listType='picture': the image rows draw their previewUrl as a thumbnail and every other row draws the glyph for its kind, on one row height. Do NOT switch to variant='picture' to get thumbnails \u2014 that variant also changes the picker, the default accept to image/* and maxCount to 1."],useCases:["Profile / user avatar editor: use variant='avatar-crop' so users can crop the image before upload; wire onUpload to your media-service; call collectUploadCommitActions on profile form submit to promote or delete.","Invoice / document attachment list: use variant='dropzone' with accept='.pdf,.xlsx' and maxSizeBytes to let accountants drag-drop supporting documents; show the file list with status indicators below the drop zone.","Product gallery (multiple images): use variant='picture-card' with maxCount to display a grid of thumbnails; each item gets an individual remove \u2715 button; collectUploadCommitActions on product save.","Single cover-image picker on a content form: use variant='picture' with maxCount=1 to show a preview rectangle with change/remove controls and undo-delete support.","CSV / bulk-import button in an admin table header: use variant='button' with accept='.csv' and custom children label ('Import CSV') to keep the UI compact; process item.file in the onChange handler.","Inline document replacement on an accounting record (replace, not append): use variant='avatar' (single-slot logic) or picture; onUpload returns the new mediaId; collectUploadCommitActions delivers replacesMediaId \u2192 deleteMediaIds."],related:["Input (type='file') \u2014 never hand-roll a raw file input; use Upload instead. Upload provides drag-drop, preview, upload lifecycle, and soft-delete draft.","Avatar (display-only) \u2014 the godx-ui Avatar component renders a user's existing image; use Upload variant='avatar' or 'avatar-crop' when you need the user to change it.","DataTable \u2014 unrelated to Upload but both appear together in bulk-import flows: Upload (button variant) triggers the import, DataTable shows the result."],example:`import { useState } from "react";
1576
+ }`,storyPath:"data-entry/Transfer.stories.tsx",rules:[23,31]},{name:"Upload",group:"data-entry",tagline:"Drag-and-drop / button / avatar / picture file uploader in six variants \u2014 wire onUpload to your media-service and call collectUploadCommitActions on form submit; supports multipart forms and custom media storage.",props:[{name:"variant",type:'"dropzone" | "button" | "picture-card" | "picture" | "avatar" | "avatar-crop"',defaultValue:'"dropzone"',description:"Controls the visual rendering mode. dropzone = large dashed drop area + file list; button = compact outline button + file list; picture-card = grid of 96\xD796 image thumbnails; picture = single image preview with change/remove actions; avatar = circular single-image picker; avatar-crop = avatar with an in-dialog crop step before the item is staged."},{name:"listType",type:'"text" | "picture"',defaultValue:'"picture" for variant="picture", otherwise "text"',description:"HOW THE CHOSEN FILES ARE LISTED \u2014 antd's listType, and an axis of its own: variant decides how files are PICKED, listType decides how the picked ones are DRAWN. text = name, size and actions (the classic dropzone/button row). picture = a leading box on every row: the thumbnail when the item has a previewUrl, otherwise the glyph for its file kind (image / pdf / archive / text / generic), both boxes the same size so a mixed list keeps one row height. The glyph is decorative (aria-hidden) and carries its kind as data-file-kind for theming. antd's picture-card is variant='picture-card' here \u2014 the tile grid IS the picker there, so it is not offered as a listType."},{name:"value",type:"UploadFileItem[]",description:"Controlled list of file items. When provided the component is controlled \u2014 you own the state. Omit to run uncontrolled."},{name:"defaultValue",type:"UploadFileItem[]",description:"Initial list of file items for uncontrolled usage. Ignored once value is provided."},{name:"accept",type:"string",description:'MIME / extension accept string passed to the hidden <input type="file">. avatar/avatar-crop/picture/picture-card default to "image/*"; dropzone and button default to unrestricted.'},{name:"multiple",type:"boolean",description:"Allow multi-file selection. Auto-derived: false when maxCount is 1 (or when variant is avatar/avatar-crop/picture); otherwise true."},{name:"maxCount",type:"number",description:"Hard upper bound on the number of items. avatar/avatar-crop/picture auto-default to 1. Once the limit is reached the add button is hidden (picture-card) or additions are rejected; maxCount=1 replaces the current item."},{name:"maxSizeBytes",type:"number",description:"Files larger than this limit are rejected with localized feedback and onReject."},{name:"disabled",type:"boolean",description:"Disables all interactive surfaces (drop zone, buttons). Visual opacity + pointer-events-none applied."},{name:"removable",type:"boolean",defaultValue:"true",description:"Show the remove/delete control on each item. Set false to make uploads permanent within the session."},{name:"onUpload",type:"(file: File, item: UploadFileItem, context: UploadRequestContext) => Promise<UploadResult>",description:"Called immediately after a file is picked (before form submit). Transitions the item to status='uploading', then 'done' on resolve or 'error' on reject. Wire this to your media-service issue/PUT/complete cycle. If omitted files stay in status='idle' and the raw File object remains in item.file."},{name:"className",type:"string",description:"Extra CSS class applied to the outer wrapper div."},{name:"id",type:"string",description:'Lands on the native `<input type="file">`, NOT on the wrapper \u2014 the hidden input is the semantic focus target, so this is what makes a `<label htmlFor>` (or FormField, which injects it) actually focus the picker. Putting it on the visible dropzone instead is the usual reason a label click does nothing.'},{name:"children",type:"React.ReactNode",description:"Custom button label for variant='button'. Falls back to the i18n 'Upload file' string."},{name:"onValueChange",type:"(items: UploadFileItemProp[]) => void",description:"Fires with the current file list. If it (or `previewFile` / `onRemove`) THROWS, Upload reports the error through `globalThis.reportError` \u2014 the console and the window `error` event \u2014 and shows a message in the upload area; it is never swallowed (gh#1123)."},{name:"triggerSize",type:'"default" | "md" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg"',description:'`variant="button"` only \u2014 size of the visible trigger, forwarded to Button. An icon size renders it icon-only and moves the label to `aria-label`: a 32px square beside other icon buttons rather than a 147px labelled one that outweighs them.'},{name:"triggerVariant",type:'"default" | "destructive" | "outline" | "dashed" | "secondary" | "ghost" | "link" | "bare"',defaultValue:'"outline"',description:'`variant="button"` only \u2014 visual weight of the visible trigger, forwarded to Button. Default `outline` suits a standalone form field. Pass `ghost` when the trigger sits in a toolbar row beside other icon buttons \u2014 inside a chat composer, say \u2014 where a bordered square reads as the odd one out.'},{name:"triggerIcon",type:"React.ComponentType<React.SVGProps<SVGSVGElement> & React.RefAttributes<SVGSVGElement>>",defaultValue:"the lucide upload arrow",description:'`variant="button"` only \u2014 the glyph the trigger draws. Pass the COMPONENT (`triggerIcon={Plus}`), not an element, exactly as `Icon`\'s `as` takes it; every lucide icon qualifies. It exists because the glyph was hard-coded and `triggerVariant` only moves emphasis, so a "create new" action that HAPPENS to upload could not carry a plus and had to announce itself as an upload (gh#734). The library still owns the class, the label spacing and the `aria-hidden`, so a swapped glyph renders at the same `--upload-row-icon-size` and never reaches the accessible name \u2014 the trigger\'s metrics cannot drift with the icon.'},{name:"readOnly",type:"boolean",description:"Displays existing files, blocks changes, preserves staged form data."},{name:"directory",type:"boolean",description:"Select a folder; each item preserves relativePath."},{name:"pastable",type:"boolean",description:"Paste clipboard files while focus is inside this Upload; text paste is untouched."},{name:"openFileDialogOnClick",type:"boolean",description:"Defaults true. Disable native dialog activation for drop-only surfaces."},{name:"name",type:"string",description:"Multipart file field name; named staged files also join native FormData."},{name:"action",type:"string | ((file: File) => string | Promise<string>)",description:"Multipart upload URL; onUpload takes precedence."},{name:"method",type:'"POST" | "PUT" | "PATCH"',description:"Request method, default POST."},{name:"headers",type:"Record<string, string>",description:"Request headers such as CSRF tokens; multipart boundaries remain browser-owned."},{name:"data",type:"Record<string, string | Blob> | ((file: File) => Record<string, string | Blob> | Promise<Record<string, string | Blob>>)",description:"Additional multipart fields, optionally resolved per file."},{name:"withCredentials",type:"boolean",description:"Send credentials with the default transport."},{name:"beforeUpload",type:"(file: File, files: File[]) => boolean | File | Blob | typeof UPLOAD_LIST_IGNORE | Promise<boolean | File | Blob | typeof UPLOAD_LIST_IGNORE>",description:"Validate or transform before upload; false stages manually; UPLOAD_LIST_IGNORE excludes. Rejections are reported."},{name:"onReject",type:"(rejection: UploadRejection) => void",description:"Reports accept, size, count, or preflight rejection."},{name:"onRemove",type:"(item: UploadFileItem) => boolean | void | Promise<boolean | void>",description:"Returning false or rejecting vetoes removal. Accepted removal aborts active upload."},{name:"onPreview",type:"(item: UploadFileItem) => void",description:"Preview action callback."},{name:"onDownload",type:"(item: UploadFileItem) => void",description:"Download action callback."},{name:"previewFile",type:"(file: File) => Promise<string>",description:"Asynchronously generate a custom thumbnail."},{name:"onDrop",type:"React.DragEventHandler<HTMLElement>",description:"Observe drop events."},{name:"showUploadList",type:"boolean",description:"Show the default file list, default true."},{name:"itemRender",type:"(node: React.ReactElement, item: UploadFileItem, items: UploadFileItem[], actions: UploadItemActions) => React.ReactNode",description:"Customize a file row while preserving its default actions."}],usage:["DO provide onUpload to auto-upload on pick. The callback must return { mediaId, previewUrl? } \u2014 the component transitions item.status through uploading \u2192 done/error automatically. Without onUpload the File object sits in item.file until you manually process it.","DO call collectUploadCommitActions(items) on form submit to get { deleteMediaIds, promoteMediaIds } for your media-service. For multipart endpoints submit File objects via FormData; never submit blob URLs as persisted media.","DO use createUploadItem(file) to build UploadFileItem objects when pre-populating value from server data (e.g. edit forms). Set status='done' and mediaId on existing server media so the draft/undo machinery tracks them correctly.","For native multipart or Inertia Form submissions, set name: staged local files are appended during the formdata event. Completed media uploads use collectUploadCommitActions instead.","Avatar/picture variants (maxCount=1) use internal soft-delete draft logic: removing an item marks it pendingDelete so the user can undo before committing. On form submit, collectUploadCommitActions converts pendingDelete \u2192 deleteMediaIds and done mediaIds \u2192 promoteMediaIds.","For avatar-crop: a crop dialog opens after pick. The cropped Blob is staged as a new UploadFileItem. The original file never enters the list \u2014 only the cropped version is passed to onUpload.","For a drawer or panel listing MIXED attachments (.png beside .json and .txt), keep variant='dropzone' and set listType='picture': the image rows draw their previewUrl as a thumbnail and every other row draws the glyph for its kind, on one row height. Do NOT switch to variant='picture' to get thumbnails \u2014 that variant also changes the picker, the default accept to image/* and maxCount to 1."],useCases:["Profile / user avatar editor: use variant='avatar-crop' so users can crop the image before upload; wire onUpload to your media-service; call collectUploadCommitActions on profile form submit to promote or delete.","Invoice / document attachment list: use variant='dropzone' with accept='.pdf,.xlsx' and maxSizeBytes to let accountants drag-drop supporting documents; show the file list with status indicators below the drop zone.","Product gallery (multiple images): use variant='picture-card' with maxCount to display a grid of thumbnails; each item gets an individual remove \u2715 button; collectUploadCommitActions on product save.","Single cover-image picker on a content form: use variant='picture' with maxCount=1 to show a preview rectangle with change/remove controls and undo-delete support.","CSV / bulk-import button in an admin table header: use variant='button' with accept='.csv' and custom children label ('Import CSV') to keep the UI compact; process item.file in the onChange handler.","Inline document replacement on an accounting record (replace, not append): use variant='avatar' (single-slot logic) or picture; onUpload returns the new mediaId; collectUploadCommitActions delivers replacesMediaId \u2192 deleteMediaIds."],related:["Input (type='file') \u2014 never hand-roll a raw file input; use Upload instead. Upload provides drag-drop, preview, upload lifecycle, and soft-delete draft.","Avatar (display-only) \u2014 the godx-ui Avatar component renders a user's existing image; use Upload variant='avatar' or 'avatar-crop' when you need the user to change it.","DataTable \u2014 unrelated to Upload but both appear together in bulk-import flows: Upload (button variant) triggers the import, DataTable shows the result."],example:`import { useState } from "react";
1577
1577
  import { Upload, type UploadFileItem, collectUploadCommitActions } from "@godxjp/ui/data-entry";
1578
1578
 
1579
1579
  // Example: avatar picker with server upload
@@ -2598,7 +2598,7 @@ const messages: ChatMessageProp[] = [
2598
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(`
2599
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(`
2600
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(`
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(`
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?, download? }",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. `download` adds a Download action to the preview toolbar (gh#1122): `true` = a link to the picture on show (`<a href download>`; a cross-origin URL opens instead), a function `({ src, index }) => void` = a button for a signed URL or a blob. The same key works on `ImagePreviewGroup preview`."},{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(`
2602
2602
  `),docPath:"data-display/image.tsx",storyPath:"data-display/Image.stories.tsx",rules:[2,6,23,44,45]},{name:"Attachments",group:"data-entry",tagline:"The chat-surface attachment collection (Ant Design X Attachments): file cards, inline placeholder, optional full-screen drop target, and ref.select/ref.upload \u2014 inherits antd Upload props but names the list `items`.",props:[{name:"items",type:"AttachmentsItemProp[]",description:"Controlled attachment list. Ant Design X `items` (= antd Upload `fileList`)."},{name:"onChange",type:"(info: { file: AttachmentsItemProp; fileList: AttachmentsItemProp[] }) => void",description:"antd Upload `onChange` \u2014 NOT `onValueChange`."},{name:"overflow",type:'"wrap" | "scrollX" | "scrollY"',description:"Ant Design X `overflow`."},{name:"placeholder",type:"AttachmentsPlaceholderProp | ((type) => AttachmentsPlaceholderProp)",description:"Empty-state copy for inline and drop surfaces."},{name:"getDropContainer",type:"() => HTMLElement | null",description:"Host for a full-screen drop overlay."},{name:"maxCount",type:"number",description:"Maximum files (antd Upload `maxCount`)."},{name:"disabled",type:"boolean",defaultValue:"false",description:"Blocks selection and drop; the control stays focusable so a reader can still reach it."},{name:"accept",type:"string",description:"Forwarded to the hidden file input."},{name:"children",type:"ReactElement",description:"Child mode: visible trigger; upload runs through a hidden input beside it."},{name:"onRemove",type:"(item: AttachmentsItemProp) => boolean | void | Promise<boolean | void>",description:"antd Upload `onRemove`, narrowed to the attachment row. RETURNING `false` (or a promise of it) VETOES the removal and the card stays \u2014 anything else, including `undefined`, lets it go. That is how you gate a removal behind a confirm dialog without owning `items` yourself. It is awaited, so an async guard works."},{name:"classNames",type:"Partial<Record<AttachmentsSemanticProp, string>>",description:"Ant Design X `classNames` \u2014 per-part classes (root, list, card, file, upload, placeholder). DECLARED AND FORWARDED, and the only semantic part map in this package: docs/DESIGN-AUTHORITY.md rules that antd's `classNames`/`styles` maps are NOT adopted because this library answers that layer with tokens (cardinal rule #45), and Attachments is the one component that carries them anyway. Recorded there as a contradiction, not a pattern \u2014 do not copy it onto another component, and retune through `--attachments-*` instead."},{name:"styles",type:"Partial<Record<AttachmentsSemanticProp, React.CSSProperties>>",description:"Ant Design X `styles` \u2014 the same part map as `classNames`, as inline styles, and under the same standing ruling against it. Inline styles beat every stylesheet rule, so this is the one handle in the package that can take a part off the design system entirely. The `--attachments-*` tokens are the supported route."},{name:"rootClassName",type:"string",description:"Ant Design X `rootClassName` \u2014 the outermost node. It is not a duplicate of `className`: in the full-screen-drop mode (`getDropContainer`) the outermost node is the OVERLAY rather than the inline tray, which is why antd separates the two. Both are applied."},{name:"imageProps",type:"Record<string, unknown>",description:"ACCEPTED AND INERT. Ant Design X forwards it to its own Image preview; this package has no Image primitive yet, so the prop exists only so an Ant X call site type-checks, and passing it changes nothing on screen. Do not reach for it expecting a preview knob."}],usage:["DO keep antd field names on each item (`thumbUrl`, `originFileObj`, `uid`) \u2014 an Ant X call site should compile unchanged.","DO use `ref.select({ accept, multiple })` to open the picker programmatically (Ant X 2.0).","Ant X's `classNames` / `styles` / `rootClassName` ARE declared and forwarded, although docs/DESIGN-AUTHORITY.md rules that antd's semantic part maps are not adopted here. The older note in this slot said not to expect them at all; the type has never agreed with it, so an agent reading only the catalog was told the opposite of what autocomplete offered. Retune through the `--attachments-*` tokens, and treat the maps as a recorded contradiction on this one component rather than a pattern to reuse.","The card is a FIXED box \u2014 268x68 (Ant X's own), from `--attachments-card-size` (inline) and `--attachments-card-block-size`. The block size is also the `+` tile's square and the `overflow=\"scrollY\"` one-row viewport, so retune it once and all three follow. The file input is `sr-only`: never style it visible."],useCases:["The attachment tray above a ChatComposer in an assistant surface.","A Sender.Header slot showing picked files before send."],related:["Upload","ChatComposer","ChatBubbleList"],example:`import { Attachments } from "@godxjp/ui/data-entry";
2603
2603
 
2604
2604
  <Attachments
@@ -4982,7 +4982,7 @@ A block with no reason is IGNORED and the finding stands. An unclosed block runs
4982
4982
  The class-shaped rules (gap-*/p-*/m-*, bg-<palette>-*, w-[\u2026], pr-*, dark:*) only read class
4983
4983
  expressions \u2014 a className/class attribute, a class-named binding (\`baseClass\`, \`statusStyles\`,
4984
4984
  \`badgeVariants\`) or a cn()/clsx()/cva() call \u2014 so prose that merely spells a utility is not a
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.20.1",godxUiCompatibility:"31.20.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)}
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.21.0",godxUiCompatibility:"31.21.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)}
4986
4986
  ${n?`${n}
4987
4987
  `:""}
4988
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})
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui-mcp",
3
- "version": "31.20.1",
4
- "godxUiCompatibility": "31.20.x",
3
+ "version": "31.21.0",
4
+ "godxUiCompatibility": "31.21.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",