@godxjp/ui 31.7.0 → 31.9.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 (46) hide show
  1. package/agent/START-HERE.md +4 -4
  2. package/agent/components/ColorPicker.json +16 -8
  3. package/agent/components/Select.json +22 -0
  4. package/agent/components/SplitPane.json +8 -1
  5. package/agent/components.json +46 -9
  6. package/agent/index.json +4 -4
  7. package/agent/llms.txt +5 -5
  8. package/agent/tokens.json +24 -0
  9. package/dist/components/data-entry/chat-composer.js +11 -4
  10. package/dist/components/data-entry/chat-suggestion.js +2 -0
  11. package/dist/components/data-entry/color-picker.d.ts +2 -2
  12. package/dist/components/data-entry/color-picker.js +150 -37
  13. package/dist/components/data-entry/command-palette.js +2 -1
  14. package/dist/components/data-entry/date-picker.js +2 -0
  15. package/dist/components/data-entry/index.d.ts +1 -1
  16. package/dist/components/data-entry/number-input.js +2 -1
  17. package/dist/components/data-entry/search-select.js +55 -17
  18. package/dist/components/data-entry/select.js +1 -1
  19. package/dist/components/data-entry/time-picker.js +3 -0
  20. package/dist/components/feedback/non-modal-layer.js +2 -1
  21. package/dist/components/general/typography.js +5 -1
  22. package/dist/components/layout/split-pane.d.ts +16 -1
  23. package/dist/components/layout/split-pane.js +7 -2
  24. package/dist/components/navigation/pagination.js +2 -1
  25. package/dist/components/ui/tag-input.js +2 -1
  26. package/dist/contracts/measurement.json +1 -1
  27. package/dist/i18n/messages/en.json +4 -2
  28. package/dist/i18n/messages/ja.json +4 -2
  29. package/dist/i18n/messages/vi.json +4 -2
  30. package/dist/lib/ime.d.ts +12 -0
  31. package/dist/lib/ime.js +7 -0
  32. package/dist/props/components/data-entry.prop.d.ts +66 -1
  33. package/dist/props/components/index.d.ts +1 -1
  34. package/dist/props/registry.d.ts +15 -0
  35. package/dist/props/registry.js +17 -0
  36. package/dist/styles/control.css +79 -0
  37. package/dist/styles/layers.json +1 -1
  38. package/dist/styles/layout.css +45 -0
  39. package/dist/tokens/components/control.css +8 -0
  40. package/docs/data-entry/color-picker.tsx +72 -0
  41. package/docs/data-entry/select.tsx +38 -0
  42. package/docs/i18n/messages/en.json +28 -0
  43. package/docs/i18n/messages/ja.json +28 -0
  44. package/docs/i18n/messages/vi.json +28 -0
  45. package/docs/layout/split-pane.tsx +65 -0
  46. package/package.json +2 -2
@@ -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` 31.7.0.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.9.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,10 +56,10 @@ 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` — 50 cardinal rules. The ones about raw HTML and hardcoded colour are not
58
58
  style advice.
59
- 4. `tokens.json` — 2100 design tokens, each tagged with its `tier`. **If you were handed a
59
+ 4. `tokens.json` — 2104 design tokens, each tagged with its `tier`. **If you were handed a
60
60
  brand, read the 211 `foundation` entries first** — `--primary`, `--background`,
61
61
  `--radius`, `--font-size-base` are the handful everything else derives from. The
62
- 1785 `component` entries are per-part knobs; reach for one only when a role is
62
+ 1789 `component` entries are per-part knobs; reach for one only when a role is
63
63
  right everywhere except one component.
64
64
  5. `anti-ai-tells.json` — 26 shapes that make generated UI look generated, each with the
65
65
  fix. Read before you reach for a gradient hero or a wall of coloured chips.
@@ -145,7 +145,7 @@ has stopped following the brand.
145
145
  |---|---|---|---|
146
146
  | `foundation` | 211 | the seeds — `--primary`, `--background`, `--foreground`, `--radius`, `--font-size-base`, `--shadow-color`. Everything below is derived from these | **yes — this is the main road.** Handed a brand colour, this is where it goes: `:root { --primary: <H> <S>% <L>%; }` (HSL components, no `hsl()` wrapper) |
147
147
  | `semantic` | 104 | named roles that follow the seeds — `--ring`, `--text-link`, `--primary-hover`, `--overlay-background` | only when the seed is right and ONE role must differ. That role then stops following a later brand change |
148
- | `component` | 1785 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
148
+ | `component` | 1789 | per-part knobs, `--{component}-{part}-{property}` | rarely. Most are declared `initial` with the real default at the call site — deliberate, so a scoped override re-resolves instead of freezing at `:root` |
149
149
 
150
150
  A token whose `value` is `initial` is not empty and not broken: `initial` is the guaranteed-invalid
151
151
  value, so the real default is computed where the element paints it. Set it and yours wins.
@@ -1,5 +1,5 @@
1
1
  {
2
- "example": "import { useState } from \"react\";\nimport { ColorPicker, FormField } from \"@godxjp/ui/data-entry\";\n\nexport function BrandColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n\n return (\n <FormField id=\"brand-color\" label=\"Brand color\" className=\"max-w-xs\">\n <ColorPicker\n id=\"brand-color\"\n value={color}\n onValueChange={setColor}\n />\n </FormField>\n );\n}\n\n// Compact swatch-only variant (no hex input)\nexport function SwatchOnly() {\n const [color, setColor] = useState(\"#16a34a\");\n return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;\n}\n\n// Disabled state\nexport function DisabledColor() {\n return <ColorPicker value=\"#6b7280\" disabled />;\n}",
2
+ "example": "import { useState } from \"react\";\nimport { ColorPicker, Form, FormField } from \"@godxjp/ui/data-entry\";\n\nexport function BrandColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n\n return (\n <Form>\n <FormField id=\"brand-color\" label=\"Brand color\">\n <ColorPicker id=\"brand-color\" value={color} onValueChange={setColor} />\n </FormField>\n </Form>\n );\n}\n\n// Compact swatch-only variant (no hex input)\nexport function SwatchOnly() {\n const [color, setColor] = useState(\"#16a34a\");\n return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;\n}\n\n// Fixed palette only (gh#1055): tags pick from presets, never free hex\nexport function TagColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n return (\n <Form>\n <FormField id=\"tag-color\" label=\"Tag colour\">\n <ColorPicker\n id=\"tag-color\"\n value={color}\n onValueChange={setColor}\n presets={[{ label: \"Palette\", colors: [\"#dc2626\", \"#16a34a\", \"#2563eb\", \"#9333ea\"] }]}\n panelRender={(_, { components: { Presets } }) => <Presets />}\n />\n </FormField>\n </Form>\n );\n}\n\n// Disabled state\nexport function DisabledColor() {\n return <ColorPicker value=\"#6b7280\" disabled />;\n}",
3
3
  "group": "data-entry",
4
4
  "importPath": "@godxjp/ui/data-entry",
5
5
  "name": "ColorPicker",
@@ -23,7 +23,7 @@
23
23
  {
24
24
  "defaultValue": "undefined",
25
25
  "description": "Called with the normalized, validated hex string whenever the user commits a new color — via the native swatch picker or by pressing Enter / blurring the hex input. Not called for invalid hex drafts.",
26
- "name": "onChange",
26
+ "name": "onValueChange",
27
27
  "type": "(hex: string) => void"
28
28
  },
29
29
  {
@@ -51,14 +51,21 @@
51
51
  "type": "string"
52
52
  },
53
53
  {
54
- "description": "Fires with the committed hex string.",
55
- "name": "onValueChange",
56
- "type": "(value: string) => void"
54
+ "defaultValue": "undefined",
55
+ "description": "antd `presets` (gh#1055) — named groups of fixed hex swatches under the picker. Each group is collapsible (`defaultOpen`, default true) and is an APG radio group: one tab stop, arrow keys move and select, each swatch is named by its hex and the one equal to `value` is checked. An empty `colors` shows the localized 'Empty' line. Hex only (#rgb/#rrggbb) — antd also accepts colour objects and gradients, which this picker has no model for.",
56
+ "name": "presets",
57
+ "type": "{ label: ReactNode; colors: string[]; defaultOpen?: boolean; key?: Key }[]"
58
+ },
59
+ {
60
+ "defaultValue": "undefined",
61
+ "description": "antd `panelRender` — replaces the picker body. `panel` is the default (swatch + hex row, then presets). PRESETS-ONLY mode (a fixed palette, no free hex): `panelRender={(_, { components: { Presets } }) => <Presets />}`. The hidden `name` input always renders outside the panel.",
62
+ "name": "panelRender",
63
+ "type": "(panel: ReactNode, extra: { components: { Picker: FC; Presets: FC } }) => ReactNode"
57
64
  }
58
65
  ],
59
66
  "related": [
60
67
  "Input — use for plain text/number entry; use ColorPicker when the value is specifically a color hex code and you want a visual swatch.",
61
- "Select / SearchSelect — use for choosing from a fixed palette of named colors (e.g. 'Red', 'Blue'); use ColorPicker for freeform hex color entry."
68
+ "Select / SearchSelect — use for choosing from a list of colour NAMES (e.g. 'Red', 'Blue'); for a fixed palette of visible swatches use ColorPicker `presets` (presets-only via `panelRender`)."
62
69
  ],
63
70
  "rules": [
64
71
  2,
@@ -70,10 +77,11 @@
70
77
  "tagline": "Native color-swatch picker with an optional editable hex input — always pass a valid 3- or 6-digit hex `value`; invalid hex is silently ignored and the previous value is restored.",
71
78
  "usage": [
72
79
  "DO wrap in FormField when a label or validation message is needed — pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v} onValueChange={setV} /></FormField>`.",
73
- "DO use controlled mode (value + onChange) — there is no defaultValue/uncontrolled path; always supply value.",
80
+ "Use it controlled (value + onValueChange) or uncontrolled (defaultValue + onValueChange); both paths commit only validated hex.",
74
81
  "DON'T pass an invalid or empty string to value — the component will flash the invalid color on the preview swatch. Always initialize state to a valid 3- or 6-digit hex (e.g. '#2563eb').",
75
- "The hex Input is a live draft field — onChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onChange firing on every keystroke.",
82
+ "The hex Input is a live draft field — onValueChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onValueChange firing on every keystroke.",
76
83
  "Set showHexInput={false} only for compact/inline contexts (icon pickers, table cells) where space is tight and keyboard hex entry is not needed.",
84
+ "DO use `presets` when the value must come from a fixed palette (tag / label colours) — add `panelRender={(_, { components: { Presets } }) => <Presets />}` to remove free hex entry entirely. NEVER rebuild a palette from a ToggleGroup of swatches.",
77
85
  "NEVER hand-roll a color picker with raw <input type='color'> — always use this component; it normalizes hex, debounces draft state, and respects the design-token control styles."
78
86
  ],
79
87
  "useCases": [
@@ -321,6 +321,28 @@
321
321
  "name": "tokenSeparators",
322
322
  "type": "string[]"
323
323
  },
324
+ {
325
+ "defaultValue": "true",
326
+ "description": "mode=\"tags\" — whether typed text may become a NEW value (gh#1052). false, or a predicate returning false for the text, hides the create row and drops unknown tokenSeparators tokens; values already held stay listed so they can be removed. Not an antd prop.",
327
+ "name": "allowCreate",
328
+ "type": "boolean | ((text: string) => boolean)"
329
+ },
330
+ {
331
+ "description": "mode=\"tags\" — the create row's content (gh#1052). Default is the localised Create “text” / 「text」を作成 / Tạo “text”. The committed value is the typed text either way. antd shows the bare text instead — a documented deviation.",
332
+ "name": "createLabel",
333
+ "type": "(text: string) => React.ReactNode"
334
+ },
335
+ {
336
+ "description": "mode=\"tags\" — fires when a value that NO option carries joins the selection (create row, Enter on it, or a tokenSeparators run), beside onSelect/onValueChange. The way to tell invented text from an option id. Not an antd prop.",
337
+ "name": "onCreate",
338
+ "type": "(text: string) => void"
339
+ },
340
+ {
341
+ "defaultValue": "false",
342
+ "description": "Compare typed text case-sensitively (gh#1053). Default false: the built-in filter, the tags create row and a tokenSeparators run all case-fold, so typing \"bug\" when \"Bug\" exists finds Bug instead of offering a duplicate. antd folds its filter but compares the create row exactly; true restores that. A custom filterOption is unaffected.",
343
+ "name": "caseSensitive",
344
+ "type": "boolean"
345
+ },
324
346
  {
325
347
  "description": "antd `maxTagTextLength` (multiple/tags) — cut each chip's TEXT to this many characters (an ellipsis marks the cut). The value keeps its whole label.",
326
348
  "name": "maxTagTextLength",
@@ -32,6 +32,12 @@
32
32
  "description": "Take the remaining height of the parent instead of growing with the content. Default false keeps the pane content-height, which is right inside a scrolling page. Set it for an app-shell surface whose columns own their own scrolling — a chat transcript with a pinned composer, a full-height table beside a detail rail: the pane and both columns get a DEFINITE height, so overflow inside a column scrolls that column instead of pushing the page taller. The parent still decides how much height there is to fill: inside a flex column, give the wrapper `flex: 1; min-height: 0`.",
33
33
  "name": "fill",
34
34
  "type": "boolean"
35
+ },
36
+ {
37
+ "defaultValue": "\"main-first\"",
38
+ "description": "Which pane comes first when the pane is too narrow to split and the columns stack. `aside-first` puts the aside on top — a detail page whose properties card must not fall below a long comment thread on a phone. It moves the `<aside>` first in the DOM (not a CSS `order` swap), so in the stacked layout reading and Tab order match the screen (WCAG 1.3.2 / 2.4.3); once the pane splits, grid placement keeps main in the leading column and the aside inline-end, geometry identical to the default. No antd equivalent (antd's closest tool is Grid `Col order`, a visual-only reorder).",
39
+ "name": "stackOrder",
40
+ "type": "\"main-first\" | \"aside-first\""
35
41
  }
36
42
  ],
37
43
  "related": [
@@ -48,11 +54,12 @@
48
54
  "usage": [
49
55
  "DO: pass all right-panel content via the `aside` prop — it renders inside a semantic `<aside>` element at a fixed rem width (sm=20rem, md=22rem). The `children` prop fills the main `1fr` column. Both accept any ReactNode.",
50
56
  "DO: reach for `fill` when the pane is an app-shell surface rather than a block in a scrolling page — a chat transcript whose composer stays pinned to the bottom, a full-height table beside a detail rail. It is the supported way to say \"be as tall as what is left\"; a consumer that instead styles the pane's wrapper divs from outside (`[&>*]`, `[&>*>*]`) is targeting this component's internal DOM by position and will break the day another wrapper appears.",
57
+ "DO: set `stackOrder=\"aside-first\"` when the aside is what a phone user needs first — an issue's properties / agent card above its comment thread. DON'T render the aside twice (once above, once beside) or reorder with a CSS `order` override: the prop already puts it first in the DOM, so focus order follows the stacked screen, and restores the side-by-side placement once the pane splits.",
51
58
  "DO: choose `asideWidth=\"sm\"` for compact detail panels (filters, quick stats, key-value summaries) and the default `asideWidth=\"md\"` for richer panels (forms, timelines, long metadata lists).",
52
59
  "DO: wrap SplitPane inside `PageContainer` or `PageContainer.Inset` — SplitPane provides no page padding of its own. It is a grid primitive, not a page scaffold.",
53
60
  "DO: close a collapsible rail with `aside={null}` — a Slack-style thread, a Linear-style detail panel — rather than swapping `<SplitPane aside={<Thread />}>{page}</SplitPane>` for a bare `{page}`. Both look the same on screen; only the prop keeps `children` mounted. The conditional swap changes the DEPTH of `{page}` in the React tree, so React remounts it: measured in a consumer as a message list jumping from scrollTop 400 to the bottom the instant a thread opened, losing the reader's place and any component state below it.",
54
61
  "DON'T: leave `SplitPane` mounted with an empty `aside={<div />}` to avoid that remount — an empty aside still reserves its rail, leaving a blank 20-30rem column. `aside={null}` removes the element AND the column (the pane sets `data-aside=\"closed\"` on itself, so the collapse is one CSS rule, not a call-site width override).",
55
- "DON'T: expect two columns below 48rem OF THE PANE'S OWN WIDTH (64rem for `asideWidth=\"lg\"`). The split is a CSS container query on the pane, not a viewport media query, so a narrow embedded pane on a large screen correctly stays single-column and a wide pane on a small screen still splits. Below the threshold it stacks (main on top, aside below); for layouts that must stay side-by-side at any width use CSS Grid or `ResponsiveGrid`.",
62
+ "DON'T: expect two columns below 48rem OF THE PANE'S OWN WIDTH (64rem for `asideWidth=\"lg\"`). The split is a CSS container query on the pane, not a viewport media query, so a narrow embedded pane on a large screen correctly stays single-column and a wide pane on a small screen still splits. Below the threshold it stacks (main on top, aside below — `stackOrder=\"aside-first\"` flips that); for layouts that must stay side-by-side at any width use CSS Grid or `ResponsiveGrid`.",
56
63
  "DON'T: add a CSS `overflow: hidden` or fixed height on the SplitPane wrapper; both columns carry `min-width: 0` to handle overflow correctly, and the grid uses `minmax(0, 1fr)` — adding external constraints will break the overflow contract.",
57
64
  "DON'T: hand-roll a two-column div layout with flexbox or CSS Grid when SplitPane already ships — that duplicates the responsive breakpoint logic and the semantic `<aside>` element."
58
65
  ],
@@ -2070,6 +2070,12 @@
2070
2070
  "description": "Take the remaining height of the parent instead of growing with the content. Default false keeps the pane content-height, which is right inside a scrolling page. Set it for an app-shell surface whose columns own their own scrolling — a chat transcript with a pinned composer, a full-height table beside a detail rail: the pane and both columns get a DEFINITE height, so overflow inside a column scrolls that column instead of pushing the page taller. The parent still decides how much height there is to fill: inside a flex column, give the wrapper `flex: 1; min-height: 0`.",
2071
2071
  "name": "fill",
2072
2072
  "type": "boolean"
2073
+ },
2074
+ {
2075
+ "defaultValue": "\"main-first\"",
2076
+ "description": "Which pane comes first when the pane is too narrow to split and the columns stack. `aside-first` puts the aside on top — a detail page whose properties card must not fall below a long comment thread on a phone. It moves the `<aside>` first in the DOM (not a CSS `order` swap), so in the stacked layout reading and Tab order match the screen (WCAG 1.3.2 / 2.4.3); once the pane splits, grid placement keeps main in the leading column and the aside inline-end, geometry identical to the default. No antd equivalent (antd's closest tool is Grid `Col order`, a visual-only reorder).",
2077
+ "name": "stackOrder",
2078
+ "type": "\"main-first\" | \"aside-first\""
2073
2079
  }
2074
2080
  ],
2075
2081
  "related": [
@@ -2086,11 +2092,12 @@
2086
2092
  "usage": [
2087
2093
  "DO: pass all right-panel content via the `aside` prop — it renders inside a semantic `<aside>` element at a fixed rem width (sm=20rem, md=22rem). The `children` prop fills the main `1fr` column. Both accept any ReactNode.",
2088
2094
  "DO: reach for `fill` when the pane is an app-shell surface rather than a block in a scrolling page — a chat transcript whose composer stays pinned to the bottom, a full-height table beside a detail rail. It is the supported way to say \"be as tall as what is left\"; a consumer that instead styles the pane's wrapper divs from outside (`[&>*]`, `[&>*>*]`) is targeting this component's internal DOM by position and will break the day another wrapper appears.",
2095
+ "DO: set `stackOrder=\"aside-first\"` when the aside is what a phone user needs first — an issue's properties / agent card above its comment thread. DON'T render the aside twice (once above, once beside) or reorder with a CSS `order` override: the prop already puts it first in the DOM, so focus order follows the stacked screen, and restores the side-by-side placement once the pane splits.",
2089
2096
  "DO: choose `asideWidth=\"sm\"` for compact detail panels (filters, quick stats, key-value summaries) and the default `asideWidth=\"md\"` for richer panels (forms, timelines, long metadata lists).",
2090
2097
  "DO: wrap SplitPane inside `PageContainer` or `PageContainer.Inset` — SplitPane provides no page padding of its own. It is a grid primitive, not a page scaffold.",
2091
2098
  "DO: close a collapsible rail with `aside={null}` — a Slack-style thread, a Linear-style detail panel — rather than swapping `<SplitPane aside={<Thread />}>{page}</SplitPane>` for a bare `{page}`. Both look the same on screen; only the prop keeps `children` mounted. The conditional swap changes the DEPTH of `{page}` in the React tree, so React remounts it: measured in a consumer as a message list jumping from scrollTop 400 to the bottom the instant a thread opened, losing the reader's place and any component state below it.",
2092
2099
  "DON'T: leave `SplitPane` mounted with an empty `aside={<div />}` to avoid that remount — an empty aside still reserves its rail, leaving a blank 20-30rem column. `aside={null}` removes the element AND the column (the pane sets `data-aside=\"closed\"` on itself, so the collapse is one CSS rule, not a call-site width override).",
2093
- "DON'T: expect two columns below 48rem OF THE PANE'S OWN WIDTH (64rem for `asideWidth=\"lg\"`). The split is a CSS container query on the pane, not a viewport media query, so a narrow embedded pane on a large screen correctly stays single-column and a wide pane on a small screen still splits. Below the threshold it stacks (main on top, aside below); for layouts that must stay side-by-side at any width use CSS Grid or `ResponsiveGrid`.",
2100
+ "DON'T: expect two columns below 48rem OF THE PANE'S OWN WIDTH (64rem for `asideWidth=\"lg\"`). The split is a CSS container query on the pane, not a viewport media query, so a narrow embedded pane on a large screen correctly stays single-column and a wide pane on a small screen still splits. Below the threshold it stacks (main on top, aside below — `stackOrder=\"aside-first\"` flips that); for layouts that must stay side-by-side at any width use CSS Grid or `ResponsiveGrid`.",
2094
2101
  "DON'T: add a CSS `overflow: hidden` or fixed height on the SplitPane wrapper; both columns carry `min-width: 0` to handle overflow correctly, and the grid uses `minmax(0, 1fr)` — adding external constraints will break the overflow contract.",
2095
2102
  "DON'T: hand-roll a two-column div layout with flexbox or CSS Grid when SplitPane already ships — that duplicates the responsive breakpoint logic and the semantic `<aside>` element."
2096
2103
  ],
@@ -6502,6 +6509,28 @@
6502
6509
  "name": "tokenSeparators",
6503
6510
  "type": "string[]"
6504
6511
  },
6512
+ {
6513
+ "defaultValue": "true",
6514
+ "description": "mode=\"tags\" — whether typed text may become a NEW value (gh#1052). false, or a predicate returning false for the text, hides the create row and drops unknown tokenSeparators tokens; values already held stay listed so they can be removed. Not an antd prop.",
6515
+ "name": "allowCreate",
6516
+ "type": "boolean | ((text: string) => boolean)"
6517
+ },
6518
+ {
6519
+ "description": "mode=\"tags\" — the create row's content (gh#1052). Default is the localised Create “text” / 「text」を作成 / Tạo “text”. The committed value is the typed text either way. antd shows the bare text instead — a documented deviation.",
6520
+ "name": "createLabel",
6521
+ "type": "(text: string) => React.ReactNode"
6522
+ },
6523
+ {
6524
+ "description": "mode=\"tags\" — fires when a value that NO option carries joins the selection (create row, Enter on it, or a tokenSeparators run), beside onSelect/onValueChange. The way to tell invented text from an option id. Not an antd prop.",
6525
+ "name": "onCreate",
6526
+ "type": "(text: string) => void"
6527
+ },
6528
+ {
6529
+ "defaultValue": "false",
6530
+ "description": "Compare typed text case-sensitively (gh#1053). Default false: the built-in filter, the tags create row and a tokenSeparators run all case-fold, so typing \"bug\" when \"Bug\" exists finds Bug instead of offering a duplicate. antd folds its filter but compares the create row exactly; true restores that. A custom filterOption is unaffected.",
6531
+ "name": "caseSensitive",
6532
+ "type": "boolean"
6533
+ },
6505
6534
  {
6506
6535
  "description": "antd `maxTagTextLength` (multiple/tags) — cut each chip's TEXT to this many characters (an ellipsis marks the cut). The value keeps its whole label.",
6507
6536
  "name": "maxTagTextLength",
@@ -9840,7 +9869,7 @@
9840
9869
  ]
9841
9870
  },
9842
9871
  {
9843
- "example": "import { useState } from \"react\";\nimport { ColorPicker, FormField } from \"@godxjp/ui/data-entry\";\n\nexport function BrandColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n\n return (\n <FormField id=\"brand-color\" label=\"Brand color\" className=\"max-w-xs\">\n <ColorPicker\n id=\"brand-color\"\n value={color}\n onValueChange={setColor}\n />\n </FormField>\n );\n}\n\n// Compact swatch-only variant (no hex input)\nexport function SwatchOnly() {\n const [color, setColor] = useState(\"#16a34a\");\n return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;\n}\n\n// Disabled state\nexport function DisabledColor() {\n return <ColorPicker value=\"#6b7280\" disabled />;\n}",
9872
+ "example": "import { useState } from \"react\";\nimport { ColorPicker, Form, FormField } from \"@godxjp/ui/data-entry\";\n\nexport function BrandColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n\n return (\n <Form>\n <FormField id=\"brand-color\" label=\"Brand color\">\n <ColorPicker id=\"brand-color\" value={color} onValueChange={setColor} />\n </FormField>\n </Form>\n );\n}\n\n// Compact swatch-only variant (no hex input)\nexport function SwatchOnly() {\n const [color, setColor] = useState(\"#16a34a\");\n return <ColorPicker value={color} onValueChange={setColor} showHexInput={false} />;\n}\n\n// Fixed palette only (gh#1055): tags pick from presets, never free hex\nexport function TagColorField() {\n const [color, setColor] = useState(\"#2563eb\");\n return (\n <Form>\n <FormField id=\"tag-color\" label=\"Tag colour\">\n <ColorPicker\n id=\"tag-color\"\n value={color}\n onValueChange={setColor}\n presets={[{ label: \"Palette\", colors: [\"#dc2626\", \"#16a34a\", \"#2563eb\", \"#9333ea\"] }]}\n panelRender={(_, { components: { Presets } }) => <Presets />}\n />\n </FormField>\n </Form>\n );\n}\n\n// Disabled state\nexport function DisabledColor() {\n return <ColorPicker value=\"#6b7280\" disabled />;\n}",
9844
9873
  "group": "data-entry",
9845
9874
  "importPath": "@godxjp/ui/data-entry",
9846
9875
  "name": "ColorPicker",
@@ -9864,7 +9893,7 @@
9864
9893
  {
9865
9894
  "defaultValue": "undefined",
9866
9895
  "description": "Called with the normalized, validated hex string whenever the user commits a new color — via the native swatch picker or by pressing Enter / blurring the hex input. Not called for invalid hex drafts.",
9867
- "name": "onChange",
9896
+ "name": "onValueChange",
9868
9897
  "type": "(hex: string) => void"
9869
9898
  },
9870
9899
  {
@@ -9892,14 +9921,21 @@
9892
9921
  "type": "string"
9893
9922
  },
9894
9923
  {
9895
- "description": "Fires with the committed hex string.",
9896
- "name": "onValueChange",
9897
- "type": "(value: string) => void"
9924
+ "defaultValue": "undefined",
9925
+ "description": "antd `presets` (gh#1055) — named groups of fixed hex swatches under the picker. Each group is collapsible (`defaultOpen`, default true) and is an APG radio group: one tab stop, arrow keys move and select, each swatch is named by its hex and the one equal to `value` is checked. An empty `colors` shows the localized 'Empty' line. Hex only (#rgb/#rrggbb) — antd also accepts colour objects and gradients, which this picker has no model for.",
9926
+ "name": "presets",
9927
+ "type": "{ label: ReactNode; colors: string[]; defaultOpen?: boolean; key?: Key }[]"
9928
+ },
9929
+ {
9930
+ "defaultValue": "undefined",
9931
+ "description": "antd `panelRender` — replaces the picker body. `panel` is the default (swatch + hex row, then presets). PRESETS-ONLY mode (a fixed palette, no free hex): `panelRender={(_, { components: { Presets } }) => <Presets />}`. The hidden `name` input always renders outside the panel.",
9932
+ "name": "panelRender",
9933
+ "type": "(panel: ReactNode, extra: { components: { Picker: FC; Presets: FC } }) => ReactNode"
9898
9934
  }
9899
9935
  ],
9900
9936
  "related": [
9901
9937
  "Input — use for plain text/number entry; use ColorPicker when the value is specifically a color hex code and you want a visual swatch.",
9902
- "Select / SearchSelect — use for choosing from a fixed palette of named colors (e.g. 'Red', 'Blue'); use ColorPicker for freeform hex color entry."
9938
+ "Select / SearchSelect — use for choosing from a list of colour NAMES (e.g. 'Red', 'Blue'); for a fixed palette of visible swatches use ColorPicker `presets` (presets-only via `panelRender`)."
9903
9939
  ],
9904
9940
  "rules": [
9905
9941
  2,
@@ -9911,10 +9947,11 @@
9911
9947
  "tagline": "Native color-swatch picker with an optional editable hex input — always pass a valid 3- or 6-digit hex `value`; invalid hex is silently ignored and the previous value is restored.",
9912
9948
  "usage": [
9913
9949
  "DO wrap in FormField when a label or validation message is needed — pass the same id to both FormField and ColorPicker so htmlFor wires up correctly: `<FormField id='brand' label='Brand color'><ColorPicker id='brand' value={v} onValueChange={setV} /></FormField>`.",
9914
- "DO use controlled mode (value + onChange) — there is no defaultValue/uncontrolled path; always supply value.",
9950
+ "Use it controlled (value + onValueChange) or uncontrolled (defaultValue + onValueChange); both paths commit only validated hex.",
9915
9951
  "DON'T pass an invalid or empty string to value — the component will flash the invalid color on the preview swatch. Always initialize state to a valid 3- or 6-digit hex (e.g. '#2563eb').",
9916
- "The hex Input is a live draft field — onChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onChange firing on every keystroke.",
9952
+ "The hex Input is a live draft field — onValueChange is NOT called until the user presses Enter or blurs; only then is the value validated and the parent notified. Do not rely on onValueChange firing on every keystroke.",
9917
9953
  "Set showHexInput={false} only for compact/inline contexts (icon pickers, table cells) where space is tight and keyboard hex entry is not needed.",
9954
+ "DO use `presets` when the value must come from a fixed palette (tag / label colours) — add `panelRender={(_, { components: { Presets } }) => <Presets />}` to remove free hex entry entirely. NEVER rebuild a palette from a ToggleGroup of swatches.",
9918
9955
  "NEVER hand-roll a color picker with raw <input type='color'> — always use this component; it normalizes hex, debounces draft state, and respects the design-token control styles."
9919
9956
  ],
9920
9957
  "useCases": [
package/agent/index.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "components": 177,
5
5
  "patterns": 21,
6
6
  "rules": 50,
7
- "tokens": 2100,
7
+ "tokens": 2104,
8
8
  "vocabulary": 14
9
9
  },
10
10
  "files": [
@@ -48,19 +48,19 @@
48
48
  "note": "Pin to the tag that matches the @godxjp/ui version you installed. A catalog newer than your package describes props you do not have; older, and it hides props you do.",
49
49
  "read": {
50
50
  "live": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/index.json",
51
- "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.7.0/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.9.0/agent/index.json"
52
52
  },
53
53
  "source": "mcp/src/data — the same data @godxjp/ui-mcp serves — plus the foundation and semantic token tiers, read from src/tokens/*.css",
54
54
  "start": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/START-HERE.md",
55
55
  "tokenTiers": {
56
56
  "component": "per-part knobs, --{component}-{part}-{property}; usually leave these alone",
57
57
  "counts": {
58
- "component": 1785,
58
+ "component": 1789,
59
59
  "foundation": 211,
60
60
  "semantic": 104
61
61
  },
62
62
  "foundation": "the seeds a consumer is invited to set — --primary, --background, --radius",
63
63
  "semantic": "named roles that follow the seeds — --ring, --text-link, --overlay-background"
64
64
  },
65
- "version": "31.7.0"
65
+ "version": "31.9.0"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
- > A Japanese-enterprise React design system: 177 components, 2100 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.7.0.
3
+ > A Japanese-enterprise React design system: 177 components, 2104 design tokens,
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.9.0.
5
5
 
6
6
  If your client can run a process, do not read these files — run the MCP server instead
7
- (`npx @godxjp/ui-mcp@31.7.0`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.9.0`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -18,7 +18,7 @@ that can only fetch URLs.
18
18
  - [components-index.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components-index.json): 47 KB — all 177 components as name, group, tagline, plus `absorbed`: the names that do NOT exist and map to it (`Combobox` → `Select`).
19
19
  - [components/&lt;Name&gt;.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components/Select.json): one file per component (1 KB–36 KB, median 6 KB). Read the index, then fetch only the ones you chose — this is the selective route, and the reason you do not need the blob.
20
20
  - [components.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/components.json): 1.2 MB — every entry in one file. Most URL fetchers truncate a response this size without saying so; prefer the per-component files.
21
- - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 104 `semantic` roles, 1785 `component` knobs.
21
+ - [tokens.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/tokens.json): every design token with its value, the reason it exists, and its `tier` — 211 `foundation` seeds (`--primary`, `--background`, `--radius`: set these when you are handed a brand), 104 `semantic` roles, 1789 `component` knobs.
22
22
  - [vocabulary.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/vocabulary.json): the controlled prop vocabulary — which prop name means what, across every component.
23
23
  - [rules.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/rules.json): 50 cardinal rules.
24
24
  - [anti-ai-tells.json](https://raw.githubusercontent.com/godx-jp/godxjp-ui/main/agent/anti-ai-tells.json): 26 shapes that make generated UI look generated, each with its fix.
@@ -26,7 +26,7 @@ that can only fetch URLs.
26
26
  ## Pinning
27
27
 
28
28
  Every URL above tracks `main`. To pin to the release a project actually installed, swap `main` for
29
- the tag: `.../godx-jp/godxjp-ui/v31.7.0/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.9.0/agent/...`. A catalog that does not match the installed
30
30
  package describes props that are absent, or hides props that are present, and says nothing either way.
31
31
 
32
32
  Pinned catalogs only exist for releases whose tag actually contains `agent/`. If `…/v<version>/agent/index.json` returns 404, that release predates this catalog: read `…/main/…` instead and compare `index.json` → `version` against the package you have, so you at least know which way it drifted.
package/agent/tokens.json CHANGED
@@ -3647,6 +3647,24 @@
3647
3647
  "tier": "component",
3648
3648
  "value": "6.5rem"
3649
3649
  },
3650
+ {
3651
+ "description": "ColorPicker `presets` (gh#1055). The swatch box IS the radio's pointer target, so it sits on the WCAG 2.2 SC 2.5.8 floor (antd's own preset block is 24px too) and never rides --scaling below it. The gap leaves room for the checked ring, which is drawn outside the box.",
3652
+ "name": "--color-picker-preset-size",
3653
+ "tier": "component",
3654
+ "value": "var(--touch-target-min)"
3655
+ },
3656
+ {
3657
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
3658
+ "name": "--color-picker-preset-gap",
3659
+ "tier": "component",
3660
+ "value": "var(--space-2)"
3661
+ },
3662
+ {
3663
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
3664
+ "name": "--color-picker-presets-gap",
3665
+ "tier": "component",
3666
+ "value": "var(--space-1)"
3667
+ },
3650
3668
  {
3651
3669
  "description": "Command / CommandPalette — list height, inner paddings and the palette's own box.",
3652
3670
  "name": "--command-list-max-height",
@@ -3773,6 +3791,12 @@
3773
3791
  "tier": "component",
3774
3792
  "value": "var( --font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)) )"
3775
3793
  },
3794
+ {
3795
+ "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
3796
+ "name": "--color-picker-presets-font-size",
3797
+ "tier": "component",
3798
+ "value": "var( --font-size-xs, calc(var(--font-size-base) / var(--font-size-ratio)) )"
3799
+ },
3776
3800
  {
3777
3801
  "description": "Control primitive tokens: heights, horizontal padding, adjacent control sizes.",
3778
3802
  "name": "--command-group-heading-font-size",
@@ -9,6 +9,7 @@ import { omitFieldA11y, pickFieldA11y, useFieldIdentity } from "../../lib/field-
9
9
  import { Button } from "../general/button.js";
10
10
  import { Textarea } from "./textarea.js";
11
11
  import { controlSurfaceAttrs, resolveAriaInvalid } from "./control-surface.js";
12
+ import { isImeComposing } from "../../lib/ime.js";
12
13
  function isSendable(text) {
13
14
  return text.trim().length > 0;
14
15
  }
@@ -70,9 +71,7 @@ const ChatComposer = React.forwardRef(
70
71
  onKeyDown?.(event);
71
72
  if (event.defaultPrevented) return;
72
73
  if (event.key !== "Enter") return;
73
- if (composing.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) {
74
- return;
75
- }
74
+ if (composing.current || isImeComposing(event)) return;
76
75
  const wantsSend = submitType === "modEnter" ? (
77
76
  // ⌘ on Apple platforms, Ctrl everywhere else — the other one is left alone, because
78
77
  // Ctrl+Enter on a Mac is not the convention a Mac user reaches for.
@@ -85,7 +84,15 @@ const ChatComposer = React.forwardRef(
85
84
  const sendName = submitLabel ?? t("dataEntry.chatComposer.send");
86
85
  const cancelName = cancelLabel ?? t("dataEntry.chatComposer.cancel");
87
86
  const actionSize = size === "xs" ? "icon-xs" : size === "sm" ? "icon-sm" : size === "lg" ? "icon-lg" : "icon";
88
- const liveRef = React.useRef({ actionSize, sendName, cancelName, submit, onCancel, canSubmit, disabled });
87
+ const liveRef = React.useRef({
88
+ actionSize,
89
+ sendName,
90
+ cancelName,
91
+ submit,
92
+ onCancel,
93
+ canSubmit,
94
+ disabled
95
+ });
89
96
  liveRef.current = { actionSize, sendName, cancelName, submit, onCancel, canSubmit, disabled };
90
97
  const SubmitButton = React.useMemo(
91
98
  () => function SubmitButton2(buttonProps) {
@@ -6,6 +6,7 @@ import { cn } from "../../lib/utils.js";
6
6
  import { VisuallyHidden } from "../general/visually-hidden.js";
7
7
  import { Popover, PopoverAnchor, PopoverContent } from "../data-display/popover.js";
8
8
  import { Command, CommandEmpty, CommandItem, CommandList } from "./command.js";
9
+ import { isImeComposing } from "../../lib/ime.js";
9
10
  function matchTrigger(text, caret, trigger) {
10
11
  if (!trigger) return null;
11
12
  const before = text.slice(0, caret);
@@ -154,6 +155,7 @@ function ChatSuggestion({
154
155
  );
155
156
  const onKeyDown = React.useCallback(
156
157
  (event) => {
158
+ if (isImeComposing(event)) return;
157
159
  if (!openRef.current) {
158
160
  if (event.key.startsWith("Arrow") || event.key === "Home" || event.key === "End") {
159
161
  window.setTimeout(evaluate, 0);
@@ -1,4 +1,4 @@
1
1
  import * as React from "react";
2
2
  import type { ColorPickerProp } from "../../props/components/data-entry.prop.js";
3
- export type { ColorPickerProp, ColorPickerProp as ColorPickerProps, } from "../../props/components/data-entry.prop.js";
4
- export declare function ColorPicker({ value: valueProp, defaultValue, onValueChange, disabled, name, className, id, showHexInput, ...ariaProps }: ColorPickerProp): React.JSX.Element;
3
+ export type { ColorPickerProp, ColorPickerProp as ColorPickerProps, ColorPickerPresetProp, ColorPickerPresetProp as ColorPickerPresetProps, } from "../../props/components/data-entry.prop.js";
4
+ export declare function ColorPicker({ value: valueProp, defaultValue, onValueChange, disabled, name, className, id, showHexInput, presets, panelRender, ...ariaProps }: ColorPickerProp): React.JSX.Element;