@godxjp/ui 31.21.1 → 31.22.1

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.1.** 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 a ONE-character trigger, so `https://x` opens nothing. A multi-character trigger (`[[`) opens anywhere, `abc[[` included (gh#1129). After Escape the list stays shut until a new trigger is typed. 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 ⌘ on Apple platforms and Ctrl elsewhere — never both, so Ctrl+O / Ctrl+P keep their editing meaning on a Mac (gh#1129); 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 ⌘ on Apple platforms and Ctrl elsewhere — never both, so Ctrl+O / Ctrl+P keep their editing meaning on a Mac (gh#1129); 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 a ONE-character trigger, so `https://x` opens nothing. A multi-character trigger (`[[`) opens anywhere, `abc[[` included (gh#1129). After Escape the list stays shut until a new trigger is typed. 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.1/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.1"
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.1.
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.1`). 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.1/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,16 +7,19 @@ 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
- return { query };
19
+ if (trigger.length === 1 && preceding !== "" && !/\s/.test(preceding) && preceding.charCodeAt(0) < 128) {
20
+ return null;
21
+ }
22
+ return { query, start };
20
23
  }
21
24
  function levelItems(items, path) {
22
25
  let current = items;
@@ -46,7 +49,11 @@ function ChatSuggestion({
46
49
  emptyMessage,
47
50
  listLabel: listLabelProp,
48
51
  id,
49
- className
52
+ className,
53
+ allowSpaces = false,
54
+ terminator,
55
+ shouldFilter = true,
56
+ onQueryChange
50
57
  }) {
51
58
  const { t } = useTranslation();
52
59
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
@@ -75,8 +82,12 @@ function ChatSuggestion({
75
82
  [open, onOpenChange]
76
83
  );
77
84
  const visible = React.useMemo(() => {
78
- return levelItems(items, path).filter((item) => matches(item, query));
79
- }, [items, path, query]);
85
+ const level = levelItems(items, path);
86
+ return shouldFilter ? level.filter((item) => matches(item, query)) : level;
87
+ }, [items, path, query, shouldFilter]);
88
+ React.useEffect(() => {
89
+ if (isOpen) onQueryChange?.(query);
90
+ }, [isOpen, query, onQueryChange]);
80
91
  const enabled = React.useMemo(() => visible.filter((item) => !item.disabled), [visible]);
81
92
  React.useEffect(() => {
82
93
  if (enabled.length === 0) {
@@ -97,22 +108,33 @@ function ChatSuggestion({
97
108
  setListId(content?.querySelector("[cmdk-list]")?.id || void 0);
98
109
  setActiveId(content?.querySelector('[cmdk-item][aria-selected="true"]')?.id || void 0);
99
110
  });
111
+ const dismissedStartRef = React.useRef(null);
100
112
  const close = React.useCallback(() => {
113
+ const node = field();
114
+ const caret = node ? node.selectionStart ?? node.value.length : 0;
115
+ const hit = node ? matchTrigger(node.value, caret, triggerCharacter, { allowSpaces, terminator }) : null;
116
+ dismissedStartRef.current = hit ? hit.start : null;
101
117
  setOpen(false);
102
118
  field()?.focus();
103
- }, [field, setOpen]);
119
+ }, [field, setOpen, triggerCharacter, allowSpaces, terminator]);
104
120
  const evaluate = React.useCallback(() => {
105
121
  const node = field();
106
122
  if (!node) return;
107
123
  const caret = node.selectionStart ?? node.value.length;
108
- const hit = matchTrigger(node.value, caret, triggerCharacter);
124
+ const hit = matchTrigger(node.value, caret, triggerCharacter, { allowSpaces, terminator });
109
125
  if (!hit) {
126
+ dismissedStartRef.current = null;
127
+ setOpen(false);
128
+ return;
129
+ }
130
+ if (hit.start === dismissedStartRef.current) {
110
131
  setOpen(false);
111
132
  return;
112
133
  }
134
+ dismissedStartRef.current = null;
113
135
  setQuery(hit.query);
114
136
  setOpen(true);
115
- }, [field, setOpen, triggerCharacter]);
137
+ }, [field, setOpen, triggerCharacter, allowSpaces, terminator]);
116
138
  const onTrigger = React.useCallback(
117
139
  (next) => {
118
140
  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,15 @@ 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 — never both, so Ctrl+O keeps its editing meaning on a Mac; `shift` and
84
+ * `alt` must match exactly, so `mod+p` and
85
+ * `mod+shift+p` are different shortcuts. Never fires during IME composition.
86
+ */
87
+ shortcut?: boolean | string;
69
88
  };
70
89
  /**
71
90
  * Unchanged. - **`shouldFilter={false}` (server-side)** — the PALETTE owns it, derived
@@ -12,7 +12,51 @@ import {
12
12
  CommandItem,
13
13
  CommandList
14
14
  } from "./command.js";
15
+ import { isApplePlatform } from "../../lib/platform.js";
15
16
  import { isImeComposing } from "../../lib/ime.js";
17
+ function parseShortcut(shortcut) {
18
+ if (shortcut === false) return null;
19
+ const parts = (shortcut === true ? "mod+k" : shortcut).toLowerCase().split("+").map((p) => p.trim());
20
+ const key = parts.pop() ?? "";
21
+ if (!key) return null;
22
+ return {
23
+ key,
24
+ mod: parts.includes("mod"),
25
+ meta: parts.includes("meta") || parts.includes("cmd"),
26
+ ctrl: parts.includes("ctrl"),
27
+ shift: parts.includes("shift"),
28
+ alt: parts.includes("alt") || parts.includes("option")
29
+ };
30
+ }
31
+ function shortcutMatches(spec, event) {
32
+ const keyMatches = event.key.toLowerCase() === spec.key || /^[a-z]$/.test(spec.key) && event.code === `Key${spec.key.toUpperCase()}`;
33
+ if (!keyMatches) return false;
34
+ if (spec.mod && !(isApplePlatform() ? event.metaKey : event.ctrlKey)) return false;
35
+ if (spec.meta && !event.metaKey) return false;
36
+ if (spec.ctrl && !event.ctrlKey) return false;
37
+ return event.shiftKey === spec.shift && event.altKey === spec.alt;
38
+ }
39
+ function shortcutHint(spec) {
40
+ return [
41
+ spec.meta || spec.mod && isApplePlatform() ? "\u2318" : "",
42
+ spec.ctrl || spec.mod && !isApplePlatform() ? "Ctrl+" : "",
43
+ spec.alt ? "\u2325" : "",
44
+ spec.shift ? "\u21E7" : "",
45
+ spec.key.length === 1 ? spec.key.toUpperCase() : spec.key
46
+ ].join("");
47
+ }
48
+ const modifiersOf = (event) => ({
49
+ metaKey: event.metaKey,
50
+ ctrlKey: event.ctrlKey,
51
+ shiftKey: event.shiftKey,
52
+ altKey: event.altKey
53
+ });
54
+ const NO_MODIFIERS = {
55
+ metaKey: false,
56
+ ctrlKey: false,
57
+ shiftKey: false,
58
+ altKey: false
59
+ };
16
60
  function CommandPalette({
17
61
  groups,
18
62
  labels,
@@ -32,6 +76,8 @@ function CommandPalette({
32
76
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
33
77
  const [uncontrolledSearch, setUncontrolledSearch] = React.useState(defaultSearch);
34
78
  const shortcutRestoreFocusRef = React.useRef(null);
79
+ const modifiersRef = React.useRef(NO_MODIFIERS);
80
+ const shortcutSpec = React.useMemo(() => parseShortcut(shortcut), [shortcut]);
35
81
  const open = controlledOpen ?? uncontrolledOpen;
36
82
  const search = controlledSearch ?? uncontrolledSearch;
37
83
  const setOpen = React.useCallback(
@@ -60,9 +106,9 @@ function CommandPalette({
60
106
  setSearch(defaultSearch);
61
107
  }, [open, search, defaultSearch, setSearch]);
62
108
  React.useEffect(() => {
63
- if (!shortcut) return;
109
+ if (!shortcutSpec) return;
64
110
  const handleKeyDown = (event) => {
65
- if (!isImeComposing(event) && event.key.toLowerCase() === "k" && (event.metaKey || event.ctrlKey)) {
111
+ if (!isImeComposing(event) && shortcutMatches(shortcutSpec, event)) {
66
112
  event.preventDefault();
67
113
  if (!open && document.activeElement instanceof HTMLElement) {
68
114
  shortcutRestoreFocusRef.current = document.activeElement;
@@ -72,12 +118,12 @@ function CommandPalette({
72
118
  };
73
119
  document.addEventListener("keydown", handleKeyDown);
74
120
  return () => document.removeEventListener("keydown", handleKeyDown);
75
- }, [open, setOpen, shortcut]);
121
+ }, [open, setOpen, shortcutSpec]);
76
122
  const hasItems = React.useMemo(() => groups.some((group) => group.items.length > 0), [groups]);
77
123
  const triggerNode = trigger === void 0 ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ui-command-palette-trigger", children: [
78
124
  /* @__PURE__ */ jsx(Search, { "aria-hidden": "true" }),
79
125
  /* @__PURE__ */ jsx("span", { children: labels.open }),
80
- /* @__PURE__ */ jsx("span", { className: "kbd", "aria-hidden": "true", children: "\u2318K" })
126
+ shortcutSpec ? /* @__PURE__ */ jsx("span", { className: "kbd", "aria-hidden": "true", children: shortcutHint(shortcutSpec) }) : null
81
127
  ] }) : trigger;
82
128
  return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
83
129
  triggerNode === null ? null : /* @__PURE__ */ jsx(Dialog.Trigger, { asChild: true, children: triggerNode }),
@@ -103,41 +149,56 @@ function CommandPalette({
103
149
  children: [
104
150
  /* @__PURE__ */ jsx(Dialog.Title, { className: "sr-only", children: labels.title }),
105
151
  /* @__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
- ] }),
152
+ /* @__PURE__ */ jsxs(
153
+ Command,
154
+ {
155
+ label: labels.title,
156
+ shouldFilter,
157
+ onKeyDownCapture: (event) => {
158
+ if (event.key === "Enter") modifiersRef.current = modifiersOf(event);
159
+ },
160
+ onClickCapture: (event) => {
161
+ modifiersRef.current = modifiersOf(event);
162
+ },
163
+ children: [
164
+ /* @__PURE__ */ jsx(
165
+ CommandInput,
166
+ {
167
+ autoFocus: true,
168
+ value: search,
169
+ onValueChange: setSearch,
170
+ placeholder: labels.placeholder,
171
+ "aria-label": labels.placeholder
172
+ }
173
+ ),
174
+ /* @__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: [
175
+ shouldFilter ? /* @__PURE__ */ jsx(CommandEmpty, { children: labels.empty }) : hasItems ? null : (
176
+ // Same node cmdk would have rendered (class + role), decided from `groups`
177
+ // instead of from its scheduled DOM-registration count — see the doc block.
178
+ /* @__PURE__ */ jsx("div", { className: "ui-command-empty", role: "presentation", children: labels.empty })
179
+ ),
180
+ groups.map((group) => /* @__PURE__ */ jsx(CommandGroup, { heading: group.label, children: group.items.map((item) => /* @__PURE__ */ jsxs(
181
+ CommandItem,
182
+ {
183
+ value: item.searchValue ?? `${item.id} ${String(item.label)}`,
184
+ disabled: item.disabled,
185
+ onSelect: () => {
186
+ const modifiers = modifiersRef.current;
187
+ modifiersRef.current = NO_MODIFIERS;
188
+ setOpen(false);
189
+ onSelect(item, modifiers);
190
+ },
191
+ children: [
192
+ /* @__PURE__ */ jsx("span", { className: "ui-command-palette-label", children: item.label }),
193
+ item.meta != null ? /* @__PURE__ */ jsx("span", { className: "ui-command-palette-meta", children: item.meta }) : null
194
+ ]
195
+ },
196
+ item.id
197
+ )) }, group.id))
198
+ ] }) })
199
+ ]
200
+ }
201
+ ),
141
202
  labels.move != null || labels.select != null || labels.close != null ? /* @__PURE__ */ jsxs("div", { className: "ui-command-palette-hints", "aria-hidden": "true", children: [
142
203
  labels.move != null ? /* @__PURE__ */ jsxs("span", { children: [
143
204
  "\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.1",
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.1",
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.1",
4
+ "godxUiMcp": "31.22.1",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",