@godxjp/ui 28.10.0 → 28.13.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 (51) hide show
  1. package/agent/START-HERE.md +2 -2
  2. package/agent/components/AppLauncher.json +10 -0
  3. package/agent/components/Attachments.json +27 -1
  4. package/agent/components/BranchScopePicker.json +10 -0
  5. package/agent/components/Cascader.json +5 -0
  6. package/agent/components/Checkbox.json +6 -0
  7. package/agent/components/CredentialReveal.json +21 -0
  8. package/agent/components/InputOTP.json +35 -0
  9. package/agent/components/PermissionMatrix.json +5 -0
  10. package/agent/components/SearchInput.json +5 -0
  11. package/agent/components/ServiceLauncherCard.json +10 -3
  12. package/agent/components/ServiceRolePanel.json +5 -0
  13. package/agent/components/Switch.json +6 -0
  14. package/agent/components/Tabs.json +10 -0
  15. package/agent/components/TimeRangePicker.json +5 -0
  16. package/agent/components/Transfer.json +5 -0
  17. package/agent/components/Upload.json +5 -0
  18. package/agent/components-index.json +1 -1
  19. package/agent/components.json +170 -4
  20. package/agent/index.json +3 -3
  21. package/agent/llms.txt +4 -4
  22. package/agent/tokens.json +30 -25
  23. package/dist/components/data-display/service-launcher-card.d.ts +19 -0
  24. package/dist/components/data-display/service-launcher-card.js +14 -1
  25. package/dist/components/data-entry/attachments.js +77 -33
  26. package/dist/components/ui/avatar.d.ts +1 -18
  27. package/dist/components/ui/avatar.js +1 -36
  28. package/dist/contracts/measurement.json +1 -1
  29. package/dist/i18n/messages/en.json +0 -530
  30. package/dist/i18n/messages/ja.json +0 -524
  31. package/dist/i18n/messages/vi.json +0 -524
  32. package/dist/lib/image-loading-status.d.ts +25 -0
  33. package/dist/lib/image-loading-status.js +41 -0
  34. package/dist/props/registry.d.ts +5 -1
  35. package/dist/props/registry.js +12 -1
  36. package/dist/styles/card-layout.css +6 -0
  37. package/dist/styles/control.css +2 -2
  38. package/dist/styles/data-entry-layout.css +245 -2
  39. package/dist/tokens/components/attachments.css +18 -9
  40. package/dist/tokens/components/segmented.css +2 -2
  41. package/docs/DESIGN-AUTHORITY.md +51 -0
  42. package/docs/assets/service-mark-rose.svg +6 -0
  43. package/docs/assets/service-mark-teal.svg +5 -0
  44. package/docs/data-display/service-launcher-card.tsx +232 -92
  45. package/docs/i18n/messages/en.json +699 -0
  46. package/docs/i18n/messages/ja.json +693 -0
  47. package/docs/i18n/messages/vi.json +693 -0
  48. package/docs/showcase/marketing-page.tsx +3 -2
  49. package/docs/showcase/table-pagination.tsx +18 -7
  50. package/docs/showcase/theme-customization.tsx +2 -1
  51. package/package.json +4 -3
@@ -3,7 +3,7 @@
3
3
  You are about to write code against a design system you did not author. This file is the whole
4
4
  contract. Read it before you write JSX.
5
5
 
6
- **This catalog describes `@godxjp/ui` 28.10.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 28.13.0.** If the project you are editing has a different
7
7
  version in its `package.json`, read the pinned catalog for THAT version instead
8
8
  (`…/v<their-version>/agent/…`). A catalog newer than the installed package describes props that do
9
9
  not exist yet; older, and it hides props that do. Neither failure announces itself.
@@ -56,7 +56,7 @@ Then ask it: `search_components`, `get_component`, `get_tokens`, `get_rule`, `li
56
56
  its `importPath`, and its examples. Fetch only the handful you picked in step 1.
57
57
  3. `rules.json` — 47 cardinal rules. The ones about raw HTML and hardcoded colour are not
58
58
  style advice.
59
- 4. `tokens.json` — 1684 design tokens. Only when you need a specific knob's name.
59
+ 4. `tokens.json` — 1685 design tokens. Only when you need a specific knob's name.
60
60
  5. `anti-ai-tells.json` — 26 shapes that make generated UI look generated, each with the
61
61
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
62
62
 
@@ -60,6 +60,16 @@
60
60
  "name": "appearance",
61
61
  "type": "\"bar\" | \"icon\""
62
62
  },
63
+ {
64
+ "description": "Which way the panel opens. DERIVED from `appearance` when unset — `bottom` in a bar (the panel drops below the trigger, the only direction that does not cover the bar itself), `right` otherwise, because a rail is vertical and its panel goes beside it. State it when the chrome can be RE-DOCKED: `appearance` says the trigger is NOT in a bar, and it cannot say which way is out — a rail pinned to the top edge is not a bar and still opens downward. Measured without it, an embedded bar trigger at (2,50) put its panel at (12,90), lying over the host application's sidebar. Not used by `responsive=\"fullscreen\"`, which has no side.",
65
+ "name": "side",
66
+ "type": "\"top\" | \"right\" | \"bottom\" | \"left\""
67
+ },
68
+ {
69
+ "description": "Where the panel sits along the `side` edge — the cross-axis half of the same decision, and derived the same way: `end` in a bar (the Workspace shape, flush with the bar's end), `start` otherwise (aligned to the rail trigger's own start). State it alongside `side` when you state either.",
70
+ "name": "align",
71
+ "type": "\"start\" | \"center\" | \"end\""
72
+ },
63
73
  {
64
74
  "description": "Controlled open state.",
65
75
  "name": "open",
@@ -50,6 +50,31 @@
50
50
  "description": "Child mode: visible trigger; upload runs through a hidden input beside it.",
51
51
  "name": "children",
52
52
  "type": "ReactElement"
53
+ },
54
+ {
55
+ "description": "antd Upload `onRemove`, narrowed to the attachment row. RETURNING `false` (or a promise of it) VETOES the removal and the card stays — 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.",
56
+ "name": "onRemove",
57
+ "type": "(item: AttachmentsItemProp) => boolean | void | Promise<boolean | void>"
58
+ },
59
+ {
60
+ "description": "Ant Design X `classNames` — 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 — do not copy it onto another component, and retune through `--attachments-*` instead.",
61
+ "name": "classNames",
62
+ "type": "Partial<Record<AttachmentsSemanticProp, string>>"
63
+ },
64
+ {
65
+ "description": "Ant Design X `styles` — 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.",
66
+ "name": "styles",
67
+ "type": "Partial<Record<AttachmentsSemanticProp, React.CSSProperties>>"
68
+ },
69
+ {
70
+ "description": "Ant Design X `rootClassName` — 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.",
71
+ "name": "rootClassName",
72
+ "type": "string"
73
+ },
74
+ {
75
+ "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.",
76
+ "name": "imageProps",
77
+ "type": "Record<string, unknown>"
53
78
  }
54
79
  ],
55
80
  "related": [
@@ -67,7 +92,8 @@
67
92
  "usage": [
68
93
  "DO keep antd field names on each item (`thumbUrl`, `originFileObj`, `uid`) — an Ant X call site should compile unchanged.",
69
94
  "DO use `ref.select({ accept, multiple })` to open the picker programmatically (Ant X 2.0).",
70
- "DON'T expect `styles`/`classNames` from Ant X — retune through `--attachments-*` tokens."
95
+ "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.",
96
+ "The card is a FIXED box — 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."
71
97
  ],
72
98
  "useCases": [
73
99
  "The attachment tray above a ChatComposer in an assistant surface.",
@@ -63,6 +63,16 @@
63
63
  "description": "Override the localized radio labels (e.g. domain wording like 全店舗).",
64
64
  "name": "allLabel / selectedLabel",
65
65
  "type": "ReactNode"
66
+ },
67
+ {
68
+ "description": "Native form name, forwarded to the MODE radio group — the all / selected choice is what submits under it. The checked branch ids are not native fields; they live in the single `{ mode, branchIds }` value and are yours to serialise.",
69
+ "name": "name",
70
+ "type": "string"
71
+ },
72
+ {
73
+ "description": "DOM id on the picker root, and the SEED for the ids beneath it — the validation message is `${id}-error`, which is what `aria-errormessage` points at. Left out, a `useId`-based id is generated, so the association still holds; set it when a server-rendered page needs those ids to be stable.",
74
+ "name": "id",
75
+ "type": "string"
66
76
  }
67
77
  ],
68
78
  "related": [
@@ -175,6 +175,11 @@
175
175
  "description": "Search query change (antd `showSearch.onSearch`).",
176
176
  "name": "onSearchChange",
177
177
  "type": "(query: string) => void"
178
+ },
179
+ {
180
+ "description": "Accessible name for the COMBOBOX TRIGGER — the element a keyboard user lands on, not the panel. Inside a FormField it is injected for you and you do not pass it; on a bare Cascader (a toolbar scope filter, a compact drilldown with no label row) it is the only name the control has, and cardinal rule 227 requires one. Route it through t(). The rest of the field-a11y contract — aria-labelledby / describedby / errormessage / invalid / required — is accepted on every form-capable component here and is FormField's to wire.",
181
+ "name": "aria-label",
182
+ "type": "string"
178
183
  }
179
184
  ],
180
185
  "related": [
@@ -29,6 +29,12 @@
29
29
  "name": "id",
30
30
  "type": "string"
31
31
  },
32
+ {
33
+ "defaultValue": "false",
34
+ "description": "Keeps its HTML spelling here and becomes react-aria's `isRequired`, so unlike Switch — where the same prop only ANNOUNCES the requirement — this is real constraint validation on the underlying input: an unchecked box blocks native form submission. The consent checkbox is the case it exists for. It does not render an asterisk; the required MARK belongs to FormField's label.",
35
+ "name": "required",
36
+ "type": "boolean"
37
+ },
32
38
  {
33
39
  "description": "antd `<Checkbox>label</Checkbox>` — the INLINE label: box first, label at inline-end on the same line, the same markup as a `Checkbox.Group` option. A real `<label for>`: clicking the text toggles the box and the text is its accessible name. `className` then styles the labelled row.",
34
40
  "name": "children",
@@ -46,12 +46,23 @@
46
46
  "name": "onAcknowledge",
47
47
  "type": "() => void"
48
48
  },
49
+ {
50
+ "description": "Copy on the button `onAcknowledge` creates. Defaults to a localized \"I've saved it\" — override it when the confirmation claims something more specific than having read the secret (\"保管しました\", \"Stored in 1Password\"). Consumer-owned wording: route it through t().",
51
+ "name": "acknowledgeLabel",
52
+ "type": "React.ReactNode"
53
+ },
49
54
  {
50
55
  "defaultValue": "false",
51
56
  "description": "Offer a download-as-file button.",
52
57
  "name": "downloadable",
53
58
  "type": "boolean"
54
59
  },
60
+ {
61
+ "defaultValue": "\"credential.txt\"",
62
+ "description": "Name of the file `downloadable` writes. The default is deliberately anonymous; set it when the user will hold several at once (`api-key-prod.txt`) so the saved files are still telling apart in a downloads folder.",
63
+ "name": "downloadFileName",
64
+ "type": "string"
65
+ },
55
66
  {
56
67
  "defaultValue": "\"md\"",
57
68
  "description": "Action button size tier.",
@@ -63,6 +74,16 @@
63
74
  "description": "Caution banner severity.",
64
75
  "name": "tone",
65
76
  "type": "\"warning\" | \"destructive\" | \"info\""
77
+ },
78
+ {
79
+ "description": "DOM id on the root. Useful here because the surface is usually inside a Dialog: it is what a `aria-describedby` on the dialog, or a deep link back to the issued credential, can point at.",
80
+ "name": "id",
81
+ "type": "string"
82
+ },
83
+ {
84
+ "description": "Accessible name for the credential region when `label` is not set or is a non-string node. `label` is the VISIBLE caption and already names the region when it is a plain string, so reach for this only when the caption is rich content or when the surrounding dialog title is the only thing saying which secret this is.",
85
+ "name": "aria-label",
86
+ "type": "string"
66
87
  }
67
88
  ],
68
89
  "related": [
@@ -70,6 +70,41 @@
70
70
  "description": "Main-axis alignment of the whole code row (groups + separators) inside the field. `center` is the canonical auth challenge. Before this existed, every consumer wrapped the OTP in their own flex-centring div — do not. A service that wants all code fields centred sets `--otp-container-align` once instead.",
71
71
  "name": "align",
72
72
  "type": "\"start\" | \"center\" | \"end\""
73
+ },
74
+ {
75
+ "description": "Fires ONCE the last slot fills, whether the user typed it or pasted the whole code. This is the auto-submit hook: a 2FA challenge with a visible submit button is a step nobody wants, and the alternative — watching `value.length === maxLength` in an effect — re-fires on every re-render. Keep the submit button anyway for the paste-then-correct case.",
76
+ "name": "onComplete",
77
+ "type": "(value: string) => void"
78
+ },
79
+ {
80
+ "description": "Rewrites CLIPBOARD text before it reaches the field. Distinct from `formatter`, which normalises every value: this one only sees a paste, which is where the junk arrives — `\"123 456\"`, `\"code: 123456\"`, a copied SMS line. Note the order the field applies them: `pattern` is matched against the RAW keystroke first, so a pattern must accept what a user actually types, not only what these two produce.",
81
+ "name": "pasteTransformer",
82
+ "type": "(pasted: string) => string"
83
+ },
84
+ {
85
+ "description": "Class on the ROW container `input-otp` renders (the slots' flex parent), which `className` cannot reach — `className` lands on the hidden input, because that is the real field. Prefer `align` and the `--otp-*` tokens; this is the vendored escape hatch underneath them.",
86
+ "name": "containerClassName",
87
+ "type": "string"
88
+ },
89
+ {
90
+ "description": "`input-otp`'s answer to the 1Password / LastPass badge that browsers float over a code field and that covers the last slot. `increase-width` (its default) reserves room so the badge sits beside the row; `none` turns the accommodation off, which is what a row already centred by `align` usually wants. Pure layout — it changes no value and no keyboard behaviour.",
91
+ "name": "pushPasswordManagerStrategy",
92
+ "type": "\"increase-width\" | \"none\""
93
+ },
94
+ {
95
+ "description": "The `<noscript>` stylesheet `input-otp` injects so the slots are still visible with JS disabled. Pass `null` to suppress it — the one real reason being a CSP that forbids inline styles and that `nonce` cannot satisfy. Leave it alone otherwise.",
96
+ "name": "noScriptCSSFallback",
97
+ "type": "string | null"
98
+ },
99
+ {
100
+ "description": "CSP nonce stamped on the stylesheet `input-otp` injects. Required only under a `style-src 'nonce-…'` policy, where the field otherwise renders unstyled and the console reports a blocked inline style. Pass the same nonce the document was served with.",
101
+ "name": "nonce",
102
+ "type": "string"
103
+ },
104
+ {
105
+ "description": "`input-otp`'s headless mode: you draw the entire row from the slot state instead of composing InputOTPGroup / InputOTPSlot. It is mutually exclusive with `children` — the vendor types the two as a union and this component keeps that union. Reaching for it means giving up the slot styling, the group outline and the separator this package owns, so it is the last resort, not a customisation point.",
106
+ "name": "render",
107
+ "type": "(props: InputOTPRenderProps) => React.ReactNode"
73
108
  }
74
109
  ],
75
110
  "related": [
@@ -54,6 +54,11 @@
54
54
  "description": "Accessible table name (localized default).",
55
55
  "name": "label",
56
56
  "type": "string"
57
+ },
58
+ {
59
+ "description": "DOM id on the grid root. Worth setting on a permissions page that also renders a summary or a legend elsewhere: it is the anchor those can point at, and the stable handle for an E2E selector that must not depend on the localized `label`.",
60
+ "name": "id",
61
+ "type": "string"
57
62
  }
58
63
  ],
59
64
  "related": [
@@ -67,6 +67,11 @@
67
67
  "description": "Disable search input and clearing.",
68
68
  "name": "disabled",
69
69
  "type": "boolean"
70
+ },
71
+ {
72
+ "description": "Class on the `<input>` itself. SearchInput renders a WRAPPER (label, icon, clear button, input), so `className` lands on that wrapper and never reaches the field — this is the second handle, for the case where the field and its chrome need different treatment. Layout and colour still belong to tokens; use it for the rare geometry a token cannot reach.",
73
+ "name": "inputClassName",
74
+ "type": "string"
70
75
  }
71
76
  ],
72
77
  "related": [
@@ -1,15 +1,20 @@
1
1
  {
2
- "example": "import { Clock } from \"lucide-react\";\nimport {\n ServiceCatalogCta,\n ServiceLauncherCard,\n ServiceLauncherCardSkeleton,\n} from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { ResponsiveGrid } from \"@godxjp/ui/layout\";\n\n// ResponsiveGrid owns the canonical 3 → 2 → 1 ladder; the page writes no tracks.\n<ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>\n {loading\n ? services.map((s) => <ServiceLauncherCardSkeleton key={s.id} label={t(\"loadingService\")} />)\n : services.map((s) => (\n <ServiceLauncherCard\n key={s.id}\n icon={Clock}\n title={s.name}\n statusLabel={s.accessLabel}\n statusTone={s.accessTone}\n description={s.description}\n metadata={s.hostnameAndPlan}\n disabledReason={s.blockedReason}\n action={\n <Button asChild={s.canLaunch} disabled={!s.canLaunch}>\n {s.canLaunch ? <a href={s.launchUrl}>{t(\"launch\")}</a> : t(\"launch\")}\n </Button>\n }\n />\n ))}\n <ServiceCatalogCta\n title={t(\"addFromCatalog\")}\n action={<Button variant=\"outline\">{t(\"viewCatalog\")}</Button>}\n />\n</ResponsiveGrid>",
2
+ "example": "import { Clock } from \"lucide-react\";\nimport {\n ServiceCatalogCta,\n ServiceLauncherCard,\n ServiceLauncherCardSkeleton,\n} from \"@godxjp/ui/data-display\";\nimport { Button } from \"@godxjp/ui/general\";\nimport { ResponsiveGrid } from \"@godxjp/ui/layout\";\n\n// ResponsiveGrid owns the canonical 3 → 2 → 1 ladder; the page writes no tracks.\n<ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }}>\n {loading\n ? services.map((s) => <ServiceLauncherCardSkeleton key={s.id} label={t(\"loadingService\")} />)\n : services.map((s) => (\n <ServiceLauncherCard\n key={s.id}\n icon={Clock}\n // The uploaded mark when there is one; the tile falls back to `icon` when there is not,\n // and again if the file behind the URL has gone away.\n logo={s.logoUrl ?? undefined}\n title={s.name}\n statusLabel={s.accessLabel}\n statusTone={s.accessTone}\n description={s.description}\n metadata={s.hostnameAndPlan}\n disabledReason={s.blockedReason}\n action={\n <Button asChild={s.canLaunch} disabled={!s.canLaunch}>\n {s.canLaunch ? <a href={s.launchUrl}>{t(\"launch\")}</a> : t(\"launch\")}\n </Button>\n }\n />\n ))}\n <ServiceCatalogCta\n title={t(\"addFromCatalog\")}\n action={<Button variant=\"outline\">{t(\"viewCatalog\")}</Button>}\n />\n</ResponsiveGrid>",
3
3
  "group": "data-display",
4
4
  "importPath": "@godxjp/ui/data-display",
5
5
  "name": "ServiceLauncherCard",
6
6
  "props": [
7
7
  {
8
- "description": "Decorative service glyph rendered in the canonical semantic icon surface.",
8
+ "description": "Decorative glyph for the KIND of service, rendered in the canonical semantic icon surface. Stays required because it is also what `logo` falls back to when an upload is missing or broken.",
9
9
  "name": "icon",
10
10
  "required": true,
11
11
  "type": "LucideIcon"
12
12
  },
13
+ {
14
+ "description": "URL of the service's OWN uploaded mark (the PNG/WebP an administrator uploaded). When it loads it replaces `icon` in the medallion, in the glyph's exact box (--card-service-launcher-icon-glyph-size, object-fit: contain). It is a fallback chain, not a switch: the tile renders `icon` while the URL is in flight and KEEPS rendering `icon` if the URL 404s or is empty — a broken logo never reaches the DOM as an <img>. Decorative (alt=\"\", medallion aria-hidden), so there is no logoAlt prop: the service name is already beside it.",
15
+ "name": "logo",
16
+ "type": "string"
17
+ },
13
18
  {
14
19
  "description": "Real downstream service display name.",
15
20
  "name": "title",
@@ -68,13 +73,15 @@
68
73
  "subParts": [
69
74
  "ServiceLauncherCardSkeleton"
70
75
  ],
71
- "tagline": "Token-owned downstream-service launcher tile with semantic icon, status, metadata, action, disabled reason, matching skeleton, and companion catalog CTA.",
76
+ "tagline": "Token-owned downstream-service launcher tile with an uploaded service logo (falling back to a semantic icon), status, metadata, action, disabled reason, matching skeleton, and companion catalog CTA.",
72
77
  "usage": [
73
78
  "DO provide status, hostname, plan, access state and action from the product's real API contract. ServiceLauncherCard deliberately performs no entitlement or URL inference — it has no href/entitlement/available prop at all.",
74
79
  "DO own the layout with ResponsiveGrid columns={{ sm: 1, md: 2, lg: 3 }} — the canonical 3→2→1 launcher grid. ResponsiveGrid queries its OWN container (40/48/64rem), so never hand-write grid-template-columns or a media query in the page. The shorthand columns={3} also works but widens to 2 columns earlier (40rem).",
75
80
  "DO render ServiceLauncherCard directly as a grid child; it already owns its Card shell, its 36px medallion (--control-height-lg tier) and the canonical internal rhythm.",
76
81
  "DO replace it with ServiceLauncherCardSkeleton while loading (it carries a required `label` and aria-busy, and deliberately opens no live region). Use ServiceCatalogCta as the peer tile only when a real catalog/add route exists.",
77
82
  "DO keep `metadata` to machine identifiers (hostname · plan) — it is the only mono line. Sentences belong in `description` / `disabledReason`.",
83
+ "DO pass BOTH `logo` and `icon` when a service may have an uploaded mark: `logo={s.logoUrl ?? undefined}` with `icon` as the kind glyph. `icon` is not optional and must not be treated as redundant — it is the tile a freshly created service, and a service whose logo file has been deleted, actually shows.",
84
+ "DON'T wrap an <img> in a component to squeeze it through `icon`, and DON'T style the medallion to fit a picture. `logo` sizes the image to the glyph's own token, so a grid mixing logo tiles and glyph tiles keeps one optical weight.",
78
85
  "DON'T recreate launcher geometry with page-local CSS, utility padding, grid tracks, or a hand-built Card hierarchy. Retune it with the --card-service-launcher-* tokens instead.",
79
86
  "DON'T show LIVE, a hostname, subscribed plan, or launch action merely because a service is active in the global catalog."
80
87
  ],
@@ -56,6 +56,11 @@
56
56
  "description": "Forwarded to MasterDetail (localized region labels by default). Never re-derive tracks or breakpoints in the app.",
57
57
  "name": "railWidth / masterViewport / collapseBelow / masterLabel / detailLabel",
58
58
  "type": "MasterDetail geometry + region labels"
59
+ },
60
+ {
61
+ "description": "DOM id on the panel root — the two-region MasterDetail wrapper, not the rail or the detail. It is the handle for a deep link onto the roles panel of a settings page, and for an E2E selector that must survive `masterLabel` being localized.",
62
+ "name": "id",
63
+ "type": "string"
59
64
  }
60
65
  ],
61
66
  "related": [
@@ -47,6 +47,12 @@
47
47
  "name": "id",
48
48
  "type": "string"
49
49
  },
50
+ {
51
+ "defaultValue": "false",
52
+ "description": "ANNOUNCES the requirement; it does not enforce it. react-aria's Switch omits `isRequired`, and a switch is never the target of native constraint validation in this library, so this writes `aria-required=\"true\"` onto the real input and stops there. The form layer (FormField / your schema) still owns whether an unflipped switch blocks submit — pairing this with nothing that validates is how a screen reader ends up promising a check the form never makes.",
53
+ "name": "required",
54
+ "type": "boolean"
55
+ },
50
56
  {
51
57
  "defaultValue": "false",
52
58
  "description": "Disable the toggle.",
@@ -116,6 +116,16 @@
116
116
  "description": "Ant Design `onTabScroll`, fired whenever the trigger strip's own scrollport moves — a swipe, a wheel, or the component re-pinning the active trigger (antd reports its own re-pins too). LOGICAL VALUES instead of antd's `left | right | top | bottom`: two of those four are just the other axis of the same event, and upstream's pair is read off the sign of an inner transform, so in an RTL strip its `left` means the opposite of what it means in LTR. `start`/`end` say the same thing on whichever axis and in whichever direction the strip is written. Only fires for the `items` API, which is the path that owns the strip element.",
117
117
  "name": "onTabScroll",
118
118
  "type": "(info: { direction: \"start\" | \"end\" }) => void"
119
+ },
120
+ {
121
+ "description": "Class on the TRIGGER STRIP (`TabsList`) under the `items` API — the handle that composing the tree manually gives you as `<TabsList className>`. `className` reaches only the root, which holds the strip AND the panels, so anything meant for the bar alone belongs here. Almost always unnecessary: placement, size, centring and the card rail are props and `--tabs-*` tokens.",
122
+ "name": "listClassName",
123
+ "type": "string"
124
+ },
125
+ {
126
+ "description": "Class on EVERY panel (`TabsContent`) under the `items` API. It is written so it can WIN: the joined card body travels to CSS as `data-bodied` on the root rather than as a class, precisely so a consumer class on the panel is not fighting a utility the component already claimed (gh#762). Reach for the `bodied` prop and the `--tabs-panel-*` tokens first — this is for the geometry no token exposes.",
127
+ "name": "contentClassName",
128
+ "type": "string"
119
129
  }
120
130
  ],
121
131
  "related": [
@@ -30,6 +30,11 @@
30
30
  "name": "allowEmpty",
31
31
  "type": "[boolean,boolean]"
32
32
  },
33
+ {
34
+ "description": "A PAIR, one per endpoint — TimePicker's single-string `placeholder` is omitted from this type on purpose, because a range has two empty fields and one string would label both of them the same. Route both through t().",
35
+ "name": "placeholder",
36
+ "type": "[string,string]"
37
+ },
33
38
  {
34
39
  "description": "Native names are name_from and name_to.",
35
40
  "name": "name",
@@ -94,6 +94,11 @@
94
94
  "name": "className",
95
95
  "type": "string"
96
96
  },
97
+ {
98
+ "description": "Lands on the `role=\"group\"` shuttle container, not on any one input — a two-pane shuttle has no single labelable control, so this is what a FormField label points at. FormField injects it; pass it yourself only for a bare Transfer.",
99
+ "name": "id",
100
+ "type": "string"
101
+ },
97
102
  {
98
103
  "description": "Controlled selection state as a tuple: index 0 = keys checked in the source panel, index 1 = keys checked in the target panel. Omit to use internal (uncontrolled) selection state. Must be paired with `onSelectChange` when provided.",
99
104
  "name": "selectedKeys",
@@ -67,6 +67,11 @@
67
67
  "name": "className",
68
68
  "type": "string"
69
69
  },
70
+ {
71
+ "description": "Lands on the native `<input type=\"file\">`, NOT on the wrapper — 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.",
72
+ "name": "id",
73
+ "type": "string"
74
+ },
70
75
  {
71
76
  "description": "Custom button label for variant='button'. Falls back to the i18n 'Upload file' string.",
72
77
  "name": "children",
@@ -224,7 +224,7 @@
224
224
  {
225
225
  "group": "data-display",
226
226
  "name": "ServiceLauncherCard",
227
- "tagline": "Token-owned downstream-service launcher tile with semantic icon, status, metadata, action, disabled reason, matching skeleton, and companion catalog CTA."
227
+ "tagline": "Token-owned downstream-service launcher tile with an uploaded service logo (falling back to a semantic icon), status, metadata, action, disabled reason, matching skeleton, and companion catalog CTA."
228
228
  },
229
229
  {
230
230
  "group": "data-display",