@godxjp/ui 31.21.1 → 31.22.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.
@@ -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.21.1.** If the project you are editing has a different
6
+ **This catalog describes `@godxjp/ui` 31.22.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.
@@ -18,10 +18,32 @@
18
18
  },
19
19
  {
20
20
  "defaultValue": "\"/\"",
21
- "description": "The character that opens the list when typed at a word boundary (use \"@\" for a mention list).",
21
+ "description": "The character(s) that open the list when typed at a boundary — the start, whitespace, or any non-ASCII character, so `[[` opens right after Japanese text (gh#1127); an ASCII letter still blocks it, so `https://x` opens nothing. Use \"@\" for mentions, \"[[\" for a link picker.",
22
22
  "name": "triggerCharacter",
23
23
  "type": "string"
24
24
  },
25
+ {
26
+ "defaultValue": "false",
27
+ "description": "Spaces may appear inside the query (a `[[` link picker searching \"Meeting notes\"); a line break still ends it (gh#1127).",
28
+ "name": "allowSpaces",
29
+ "type": "boolean"
30
+ },
31
+ {
32
+ "description": "Typing this inside the query finishes the token and closes the list, e.g. `]]` (gh#1127).",
33
+ "name": "terminator",
34
+ "type": "string"
35
+ },
36
+ {
37
+ "defaultValue": "true",
38
+ "description": "`false` when the host answers the query itself (async, via `onQueryChange`) and `items` already holds the matches — they are shown as given, not filtered again (gh#1127).",
39
+ "name": "shouldFilter",
40
+ "type": "boolean"
41
+ },
42
+ {
43
+ "description": "The query typed after the trigger, on every change while the list is open — the seam for server-side search (gh#1127).",
44
+ "name": "onQueryChange",
45
+ "type": "(query: string) => void"
46
+ },
25
47
  {
26
48
  "description": "Controlled open state of the list.",
27
49
  "name": "open",
@@ -17,10 +17,16 @@
17
17
  "type": "CommandPaletteLabels"
18
18
  },
19
19
  {
20
- "description": "Consumer-owned selection handler.",
20
+ "description": "Consumer-owned selection handler. The second argument carries the modifier keys held on the Enter or click that chose the item — build \"open in split / new tab\" on ⌘/Ctrl/Shift (gh#1126).",
21
21
  "name": "onSelect",
22
22
  "required": true,
23
- "type": "(item: CommandPaletteItem) => void"
23
+ "type": "(item: CommandPaletteItem, modifiers: { metaKey, ctrlKey, shiftKey, altKey }) => void"
24
+ },
25
+ {
26
+ "defaultValue": "true",
27
+ "description": "The global toggle shortcut. `true` = ⌘K / Ctrl+K, `false` = none, or a combo string — `\"mod+o\"`, `\"mod+p\"`, `\"mod+shift+p\"` — so a quick switcher and a command palette can live side by side. `mod` is ⌘ or Ctrl; Shift / Alt must match exactly. Never fires during IME composition; the default trigger shows the combo (gh#1126).",
28
+ "name": "shortcut",
29
+ "type": "boolean | string"
24
30
  },
25
31
  {
26
32
  "description": "Controlled open state.",
@@ -14147,10 +14147,16 @@
14147
14147
  "type": "CommandPaletteLabels"
14148
14148
  },
14149
14149
  {
14150
- "description": "Consumer-owned selection handler.",
14150
+ "description": "Consumer-owned selection handler. The second argument carries the modifier keys held on the Enter or click that chose the item — build \"open in split / new tab\" on ⌘/Ctrl/Shift (gh#1126).",
14151
14151
  "name": "onSelect",
14152
14152
  "required": true,
14153
- "type": "(item: CommandPaletteItem) => void"
14153
+ "type": "(item: CommandPaletteItem, modifiers: { metaKey, ctrlKey, shiftKey, altKey }) => void"
14154
+ },
14155
+ {
14156
+ "defaultValue": "true",
14157
+ "description": "The global toggle shortcut. `true` = ⌘K / Ctrl+K, `false` = none, or a combo string — `\"mod+o\"`, `\"mod+p\"`, `\"mod+shift+p\"` — so a quick switcher and a command palette can live side by side. `mod` is ⌘ or Ctrl; Shift / Alt must match exactly. Never fires during IME composition; the default trigger shows the combo (gh#1126).",
14158
+ "name": "shortcut",
14159
+ "type": "boolean | string"
14154
14160
  },
14155
14161
  {
14156
14162
  "description": "Controlled open state.",
@@ -15477,10 +15483,32 @@
15477
15483
  },
15478
15484
  {
15479
15485
  "defaultValue": "\"/\"",
15480
- "description": "The character that opens the list when typed at a word boundary (use \"@\" for a mention list).",
15486
+ "description": "The character(s) that open the list when typed at a boundary — the start, whitespace, or any non-ASCII character, so `[[` opens right after Japanese text (gh#1127); an ASCII letter still blocks it, so `https://x` opens nothing. Use \"@\" for mentions, \"[[\" for a link picker.",
15481
15487
  "name": "triggerCharacter",
15482
15488
  "type": "string"
15483
15489
  },
15490
+ {
15491
+ "defaultValue": "false",
15492
+ "description": "Spaces may appear inside the query (a `[[` link picker searching \"Meeting notes\"); a line break still ends it (gh#1127).",
15493
+ "name": "allowSpaces",
15494
+ "type": "boolean"
15495
+ },
15496
+ {
15497
+ "description": "Typing this inside the query finishes the token and closes the list, e.g. `]]` (gh#1127).",
15498
+ "name": "terminator",
15499
+ "type": "string"
15500
+ },
15501
+ {
15502
+ "defaultValue": "true",
15503
+ "description": "`false` when the host answers the query itself (async, via `onQueryChange`) and `items` already holds the matches — they are shown as given, not filtered again (gh#1127).",
15504
+ "name": "shouldFilter",
15505
+ "type": "boolean"
15506
+ },
15507
+ {
15508
+ "description": "The query typed after the trigger, on every change while the list is open — the seam for server-side search (gh#1127).",
15509
+ "name": "onQueryChange",
15510
+ "type": "(query: string) => void"
15511
+ },
15484
15512
  {
15485
15513
  "description": "Controlled open state of the list.",
15486
15514
  "name": "open",
package/agent/index.json CHANGED
@@ -48,7 +48,7 @@
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.21.1/agent/index.json"
51
+ "pinned": "https://raw.githubusercontent.com/godx-jp/godxjp-ui/v31.22.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",
@@ -62,5 +62,5 @@
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.21.1"
65
+ "version": "31.22.0"
66
66
  }
package/agent/llms.txt CHANGED
@@ -1,10 +1,10 @@
1
1
  # @godxjp/ui
2
2
 
3
3
  > A Japanese-enterprise React design system: 181 components, 2129 design tokens,
4
- > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.21.1.
4
+ > 50 cardinal rules. This file is the entry point for AI agents. Catalog version 31.22.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.21.1`). It is searchable and version-locked. These files exist for agents
7
+ (`npx @godxjp/ui-mcp@31.22.0`). It is searchable and version-locked. These files exist for agents
8
8
  that can only fetch URLs.
9
9
 
10
10
  ## Start
@@ -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.21.1/agent/...`. A catalog that does not match the installed
29
+ the tag: `.../godx-jp/godxjp-ui/v31.22.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.
@@ -22,7 +22,7 @@ export type { ChatSuggestionProp, ChatSuggestionProp as ChatSuggestionProps, Cha
22
22
  * typed text exactly as it was; a suggestion list that eats the draft on dismiss is the defect
23
23
  * this guards.
24
24
  */
25
- export declare function ChatSuggestion({ items, onValueChange, triggerCharacter, open, defaultOpen, onOpenChange, children, emptyMessage, listLabel: listLabelProp, id, className, }: ChatSuggestionProp): React.JSX.Element;
25
+ export declare function ChatSuggestion({ items, onValueChange, triggerCharacter, open, defaultOpen, onOpenChange, children, emptyMessage, listLabel: listLabelProp, id, className, allowSpaces, terminator, shouldFilter, onQueryChange, }: ChatSuggestionProp): React.JSX.Element;
26
26
  export declare namespace ChatSuggestion {
27
27
  var displayName: string;
28
28
  }
@@ -7,15 +7,16 @@ 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
9
  import { isImeComposing } from "../../lib/ime.js";
10
- function matchTrigger(text, caret, trigger) {
10
+ function matchTrigger(text, caret, trigger, { allowSpaces = false, terminator } = {}) {
11
11
  if (!trigger) return null;
12
12
  const before = text.slice(0, caret);
13
13
  const start = before.lastIndexOf(trigger);
14
14
  if (start === -1) return null;
15
15
  const query = before.slice(start + trigger.length);
16
- if (/\s/.test(query)) return null;
16
+ if (allowSpaces ? /\n/.test(query) : /\s/.test(query)) return null;
17
+ if (terminator && query.includes(terminator)) return null;
17
18
  const preceding = start === 0 ? "" : before[start - 1];
18
- if (preceding !== "" && !/\s/.test(preceding)) return null;
19
+ if (preceding !== "" && !/\s/.test(preceding) && preceding.charCodeAt(0) < 128) return null;
19
20
  return { query };
20
21
  }
21
22
  function levelItems(items, path) {
@@ -46,7 +47,11 @@ function ChatSuggestion({
46
47
  emptyMessage,
47
48
  listLabel: listLabelProp,
48
49
  id,
49
- className
50
+ className,
51
+ allowSpaces = false,
52
+ terminator,
53
+ shouldFilter = true,
54
+ onQueryChange
50
55
  }) {
51
56
  const { t } = useTranslation();
52
57
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
@@ -75,8 +80,12 @@ function ChatSuggestion({
75
80
  [open, onOpenChange]
76
81
  );
77
82
  const visible = React.useMemo(() => {
78
- return levelItems(items, path).filter((item) => matches(item, query));
79
- }, [items, path, query]);
83
+ const level = levelItems(items, path);
84
+ return shouldFilter ? level.filter((item) => matches(item, query)) : level;
85
+ }, [items, path, query, shouldFilter]);
86
+ React.useEffect(() => {
87
+ if (isOpen) onQueryChange?.(query);
88
+ }, [isOpen, query, onQueryChange]);
80
89
  const enabled = React.useMemo(() => visible.filter((item) => !item.disabled), [visible]);
81
90
  React.useEffect(() => {
82
91
  if (enabled.length === 0) {
@@ -105,14 +114,14 @@ function ChatSuggestion({
105
114
  const node = field();
106
115
  if (!node) return;
107
116
  const caret = node.selectionStart ?? node.value.length;
108
- const hit = matchTrigger(node.value, caret, triggerCharacter);
117
+ const hit = matchTrigger(node.value, caret, triggerCharacter, { allowSpaces, terminator });
109
118
  if (!hit) {
110
119
  setOpen(false);
111
120
  return;
112
121
  }
113
122
  setQuery(hit.query);
114
123
  setOpen(true);
115
- }, [field, setOpen, triggerCharacter]);
124
+ }, [field, setOpen, triggerCharacter, allowSpaces, terminator]);
116
125
  const onTrigger = React.useCallback(
117
126
  (next) => {
118
127
  if (next === false) {
@@ -22,10 +22,21 @@ export type CommandPaletteLabels = {
22
22
  select?: React.ReactNode;
23
23
  close?: React.ReactNode;
24
24
  };
25
+ /** The modifier keys held while an item was chosen (gh#1126). */
26
+ export type CommandPaletteSelectModifiers = {
27
+ metaKey: boolean;
28
+ ctrlKey: boolean;
29
+ shiftKey: boolean;
30
+ altKey: boolean;
31
+ };
25
32
  export type CommandPaletteProps = {
26
33
  groups: CommandPaletteGroup[];
27
34
  labels: CommandPaletteLabels;
28
- onSelect: (item: CommandPaletteItem) => void;
35
+ /**
36
+ * Fires with the chosen item and the modifier keys held while choosing it (gh#1126) — Enter or a
37
+ * click with ⌘/Ctrl/Shift/Alt — so a host can "open in split" or "open in a new tab".
38
+ */
39
+ onSelect: (item: CommandPaletteItem, modifiers: CommandPaletteSelectModifiers) => void;
29
40
  open?: boolean;
30
41
  defaultOpen?: boolean;
31
42
  onOpenChange?: (open: boolean) => void;
@@ -65,7 +76,14 @@ export type CommandPaletteProps = {
65
76
  * better handler than a consumer's own. A topbar with no width to spare should be able to say so.
66
77
  */
67
78
  trigger?: React.ReactNode;
68
- shortcut?: boolean;
79
+ /**
80
+ * The global shortcut that toggles the palette (gh#1126). `true` (default) is ⌘K / Ctrl+K;
81
+ * `false` binds none; a combo string — `"mod+o"`, `"mod+p"`, `"mod+shift+p"` — binds that, so two
82
+ * palettes (a quick switcher and a command palette) can live side by side. `mod` is ⌘ on Apple
83
+ * and Ctrl elsewhere (either is accepted); `shift` and `alt` must match exactly, so `mod+p` and
84
+ * `mod+shift+p` are different shortcuts. Never fires during IME composition.
85
+ */
86
+ shortcut?: boolean | string;
69
87
  };
70
88
  /**
71
89
  * Unchanged. - **`shouldFilter={false}` (server-side)** — the PALETTE owns it, derived
@@ -13,6 +13,49 @@ import {
13
13
  CommandList
14
14
  } from "./command.js";
15
15
  import { isImeComposing } from "../../lib/ime.js";
16
+ function parseShortcut(shortcut) {
17
+ if (shortcut === false) return null;
18
+ const parts = (shortcut === true ? "mod+k" : shortcut).toLowerCase().split("+").map((p) => p.trim());
19
+ const key = parts.pop() ?? "";
20
+ if (!key) return null;
21
+ return {
22
+ key,
23
+ mod: parts.includes("mod"),
24
+ meta: parts.includes("meta") || parts.includes("cmd"),
25
+ ctrl: parts.includes("ctrl"),
26
+ shift: parts.includes("shift"),
27
+ alt: parts.includes("alt") || parts.includes("option")
28
+ };
29
+ }
30
+ function shortcutMatches(spec, event) {
31
+ const keyMatches = event.key.toLowerCase() === spec.key || /^[a-z]$/.test(spec.key) && event.code === `Key${spec.key.toUpperCase()}`;
32
+ if (!keyMatches) return false;
33
+ if (spec.mod && !(event.metaKey || event.ctrlKey)) return false;
34
+ if (spec.meta && !event.metaKey) return false;
35
+ if (spec.ctrl && !event.ctrlKey) return false;
36
+ return event.shiftKey === spec.shift && event.altKey === spec.alt;
37
+ }
38
+ function shortcutHint(spec) {
39
+ return [
40
+ spec.mod || spec.meta ? "\u2318" : "",
41
+ spec.ctrl ? "Ctrl+" : "",
42
+ spec.alt ? "\u2325" : "",
43
+ spec.shift ? "\u21E7" : "",
44
+ spec.key.length === 1 ? spec.key.toUpperCase() : spec.key
45
+ ].join("");
46
+ }
47
+ const modifiersOf = (event) => ({
48
+ metaKey: event.metaKey,
49
+ ctrlKey: event.ctrlKey,
50
+ shiftKey: event.shiftKey,
51
+ altKey: event.altKey
52
+ });
53
+ const NO_MODIFIERS = {
54
+ metaKey: false,
55
+ ctrlKey: false,
56
+ shiftKey: false,
57
+ altKey: false
58
+ };
16
59
  function CommandPalette({
17
60
  groups,
18
61
  labels,
@@ -32,6 +75,8 @@ function CommandPalette({
32
75
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
33
76
  const [uncontrolledSearch, setUncontrolledSearch] = React.useState(defaultSearch);
34
77
  const shortcutRestoreFocusRef = React.useRef(null);
78
+ const modifiersRef = React.useRef(NO_MODIFIERS);
79
+ const shortcutSpec = React.useMemo(() => parseShortcut(shortcut), [shortcut]);
35
80
  const open = controlledOpen ?? uncontrolledOpen;
36
81
  const search = controlledSearch ?? uncontrolledSearch;
37
82
  const setOpen = React.useCallback(
@@ -60,9 +105,9 @@ function CommandPalette({
60
105
  setSearch(defaultSearch);
61
106
  }, [open, search, defaultSearch, setSearch]);
62
107
  React.useEffect(() => {
63
- if (!shortcut) return;
108
+ if (!shortcutSpec) return;
64
109
  const handleKeyDown = (event) => {
65
- if (!isImeComposing(event) && event.key.toLowerCase() === "k" && (event.metaKey || event.ctrlKey)) {
110
+ if (!isImeComposing(event) && shortcutMatches(shortcutSpec, event)) {
66
111
  event.preventDefault();
67
112
  if (!open && document.activeElement instanceof HTMLElement) {
68
113
  shortcutRestoreFocusRef.current = document.activeElement;
@@ -72,12 +117,12 @@ function CommandPalette({
72
117
  };
73
118
  document.addEventListener("keydown", handleKeyDown);
74
119
  return () => document.removeEventListener("keydown", handleKeyDown);
75
- }, [open, setOpen, shortcut]);
120
+ }, [open, setOpen, shortcutSpec]);
76
121
  const hasItems = React.useMemo(() => groups.some((group) => group.items.length > 0), [groups]);
77
122
  const triggerNode = trigger === void 0 ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ui-command-palette-trigger", children: [
78
123
  /* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
79
124
  /* @__PURE__ */ jsx("span", { children: labels.open }),
80
- /* @__PURE__ */ jsx("span", { className: "kbd", "aria-hidden": "true", children: "\u2318K" })
125
+ shortcutSpec ? /* @__PURE__ */ jsx("span", { className: "kbd", "aria-hidden": "true", children: shortcutHint(shortcutSpec) }) : null
81
126
  ] }) : trigger;
82
127
  return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
83
128
  triggerNode === null ? null : /* @__PURE__ */ jsx(Dialog.Trigger, { asChild: true, children: triggerNode }),
@@ -103,41 +148,56 @@ function CommandPalette({
103
148
  children: [
104
149
  /* @__PURE__ */ jsx(Dialog.Title, { className: "sr-only", children: labels.title }),
105
150
  /* @__PURE__ */ jsx(Dialog.Description, { id: "ui-command-palette-description", className: "sr-only", children: labels.description }),
106
- /* @__PURE__ */ jsxs(Command, { label: labels.title, shouldFilter, children: [
107
- /* @__PURE__ */ jsx(
108
- CommandInput,
109
- {
110
- autoFocus: true,
111
- value: search,
112
- onValueChange: setSearch,
113
- placeholder: labels.placeholder,
114
- "aria-label": labels.placeholder
115
- }
116
- ),
117
- /* @__PURE__ */ jsx(CommandList, { "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "alert", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
118
- shouldFilter ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : hasItems ? null : (
119
- // Same node cmdk would have rendered (class + role), decided from `groups`
120
- // instead of from its scheduled DOM-registration count — see the doc block.
121
- /* @__PURE__ */ jsx("div", { className: "ui-command-empty", role: "presentation", children: labels.empty })
122
- ),
123
- groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
124
- CommandItem,
125
- {
126
- value: item.searchValue ?? `${item.id} ${String(item.label)}`,
127
- disabled: item.disabled,
128
- onSelect: () => {
129
- setOpen(false);
130
- onSelect(item);
131
- },
132
- children: [
133
- /* @__PURE__ */ jsx("span", { className: "ui-command-palette-label", children: item.label }),
134
- item.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-command-palette-meta", children: item.meta }) : null
135
- ]
136
- },
137
- item.id
138
- )) }, group.id))
139
- ] }) })
140
- ] }),
151
+ /* @__PURE__ */ jsxs(
152
+ Command,
153
+ {
154
+ label: labels.title,
155
+ shouldFilter,
156
+ onKeyDownCapture: (event) => {
157
+ if (event.key === "Enter") modifiersRef.current = modifiersOf(event);
158
+ },
159
+ onClickCapture: (event) => {
160
+ modifiersRef.current = modifiersOf(event);
161
+ },
162
+ children: [
163
+ /* @__PURE__ */ jsx(
164
+ CommandInput,
165
+ {
166
+ autoFocus: true,
167
+ value: search,
168
+ onValueChange: setSearch,
169
+ placeholder: labels.placeholder,
170
+ "aria-label": labels.placeholder
171
+ }
172
+ ),
173
+ /* @__PURE__ */ jsx(CommandList, { "aria-busy": loading, children: loading ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "status", children: labels.loading }) : error ? /* @__PURE__ */ jsx("div", { className: "ui-command-palette-state", role: "alert", children: error }) : /* @__PURE__ */ jsxs(Fragment, { children: [
174
+ shouldFilter ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : hasItems ? null : (
175
+ // Same node cmdk would have rendered (class + role), decided from `groups`
176
+ // instead of from its scheduled DOM-registration count — see the doc block.
177
+ /* @__PURE__ */ jsx("div", { className: "ui-command-empty", role: "presentation", children: labels.empty })
178
+ ),
179
+ groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
180
+ CommandItem,
181
+ {
182
+ value: item.searchValue ?? `${item.id} ${String(item.label)}`,
183
+ disabled: item.disabled,
184
+ onSelect: () => {
185
+ const modifiers = modifiersRef.current;
186
+ modifiersRef.current = NO_MODIFIERS;
187
+ setOpen(false);
188
+ onSelect(item, modifiers);
189
+ },
190
+ children: [
191
+ /* @__PURE__ */ jsx("span", { className: "ui-command-palette-label", children: item.label }),
192
+ item.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-command-palette-meta", children: item.meta }) : null
193
+ ]
194
+ },
195
+ item.id
196
+ )) }, group.id))
197
+ ] }) })
198
+ ]
199
+ }
200
+ ),
141
201
  labels.move != null || labels.select != null || labels.close != null ? /* @__PURE__ */ jsxs("div", { className: "ui-command-palette-hints", "aria-hidden": "true", children: [
142
202
  labels.move != null ? /* @__PURE__ */ jsxs("span", { children: [
143
203
  "\u2191\u2193 ",
@@ -54,7 +54,7 @@ export type { TransferProps, TransferItemProp } from "./transfer.js";
54
54
  export { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "./command.js";
55
55
  export type { CommandProps } from "./command.js";
56
56
  export { CommandPalette } from "./command-palette.js";
57
- export type { CommandPaletteGroup, CommandPaletteItem, CommandPaletteLabels, CommandPaletteProps, } from "./command-palette.js";
57
+ export type { CommandPaletteGroup, CommandPaletteItem, CommandPaletteLabels, CommandPaletteProps, CommandPaletteSelectModifiers, } from "./command-palette.js";
58
58
  export { PasswordInput } from "./password-input.js";
59
59
  export type { PasswordInputProps, PasswordVisibilityToggleProp } from "./password-input.js";
60
60
  export { PasswordStrength, usePasswordStrength } from "./password-strength.js";
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "31.21.1",
3
+ "version": "31.22.0",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,
@@ -2253,8 +2253,22 @@ export type ChatSuggestionProp = {
2253
2253
  items: readonly ChatSuggestionItemProp[];
2254
2254
  /** Fires with the picked row's `value`. The caller owns what that does to the draft text. */
2255
2255
  onValueChange?: OnValueChangeProp<string>;
2256
- /** The character that opens the list when typed at a word boundary. Default `/`. */
2256
+ /**
2257
+ * The character(s) that open the list when typed at a boundary — the start, whitespace, or any
2258
+ * non-ASCII character (so `[[` opens right after Japanese text). Default `/`.
2259
+ */
2257
2260
  triggerCharacter?: string;
2261
+ /** Spaces may appear inside the query (a `[[` link picker); a line break still ends it (gh#1127). */
2262
+ allowSpaces?: boolean;
2263
+ /** Typing this inside the query finishes the token and closes the list, e.g. `]]` (gh#1127). */
2264
+ terminator?: string;
2265
+ /**
2266
+ * Whether the list filters `items` against the query itself. `false` when the host answers the
2267
+ * query (async, via `onQueryChange`) and `items` already holds the matches (gh#1127). Default `true`.
2268
+ */
2269
+ shouldFilter?: boolean;
2270
+ /** The query typed after the trigger, on every change while the list is open (gh#1127). */
2271
+ onQueryChange?: (query: string) => void;
2258
2272
  /** Controlled open state. */
2259
2273
  open?: OpenProp;
2260
2274
  /** Uncontrolled initial open state. */
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.21.1",
2
+ "version": "31.22.0",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "31.21.1",
4
- "godxUiMcp": "31.21.1",
3
+ "version": "31.22.0",
4
+ "godxUiMcp": "31.22.0",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",