@godxjp/ui 20.2.1 → 21.0.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 (59) hide show
  1. package/dist/components/data-display/chat-bubble.d.ts +30 -0
  2. package/dist/components/data-display/chat-bubble.js +229 -0
  3. package/dist/components/data-display/descriptions.js +4 -1
  4. package/dist/components/data-display/index.d.ts +4 -2
  5. package/dist/components/data-display/index.js +5 -2
  6. package/dist/components/data-display/popover.js +3 -2
  7. package/dist/components/data-display/scroll-area.js +26 -1
  8. package/dist/components/data-display/tree.d.ts +8 -0
  9. package/dist/components/data-display/tree.js +426 -0
  10. package/dist/components/data-entry/chat-composer.d.ts +50 -0
  11. package/dist/components/data-entry/chat-composer.js +163 -0
  12. package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
  13. package/dist/components/data-entry/chat-suggestion.js +285 -0
  14. package/dist/components/data-entry/index.d.ts +4 -0
  15. package/dist/components/data-entry/index.js +4 -0
  16. package/dist/components/data-entry/textarea.js +3 -1
  17. package/dist/components/data-entry/tree-utils.d.ts +10 -48
  18. package/dist/components/data-entry/tree-utils.js +1 -154
  19. package/dist/i18n/messages/en.json +45 -0
  20. package/dist/i18n/messages/ja.json +43 -0
  21. package/dist/i18n/messages/vi.json +44 -0
  22. package/dist/lib/tree.d.ts +53 -0
  23. package/dist/lib/tree.js +155 -0
  24. package/dist/props/components/data-display.prop.d.ts +187 -1
  25. package/dist/props/components/data-entry.prop.d.ts +127 -0
  26. package/dist/props/registry.d.ts +140 -3
  27. package/dist/props/registry.js +202 -3
  28. package/dist/styles/control.css +7 -0
  29. package/dist/styles/data-display-layout.css +309 -55
  30. package/dist/styles/data-entry-layout.css +64 -0
  31. package/dist/styles/shell-layout.css +2 -1
  32. package/dist/tokens/base.css +3 -0
  33. package/dist/tokens/components/chat-bubble.css +36 -0
  34. package/dist/tokens/components/chat-composer.css +19 -0
  35. package/dist/tokens/components/data-display.css +0 -6
  36. package/dist/tokens/components/descriptions.css +4 -0
  37. package/dist/tokens/components/shell.css +1 -0
  38. package/dist/tokens/components/tree.css +27 -0
  39. package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
  40. package/docs/FRAME-COVERAGE-REPORT.md +8 -3
  41. package/docs/data-display/chat-bubble.tsx +397 -0
  42. package/docs/data-display/timeline.tsx +46 -0
  43. package/docs/data-display/tree.tsx +394 -0
  44. package/docs/data-entry/chat-composer.tsx +464 -0
  45. package/docs/data-entry/chat-suggestion.tsx +301 -0
  46. package/docs/roadmap/ai-chat-components.md +207 -0
  47. package/docs/roadmap/antd-parity.md +154 -0
  48. package/docs/roadmap/badge-tag-chip-count.md +172 -0
  49. package/docs/roadmap/list-masonry.md +159 -0
  50. package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
  51. package/docs/roadmap/parity-audit-data-entry.md +344 -0
  52. package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
  53. package/docs/roadmap/parity-backlog.md +79 -0
  54. package/docs/roadmap/tree-components.md +151 -0
  55. package/docs/showcase/table-tree-rows.tsx +4 -4
  56. package/package.json +5 -3
  57. package/dist/components/data-display/tree-list.d.ts +0 -13
  58. package/dist/components/data-display/tree-list.js +0 -26
  59. package/docs/data-display/tree-list.tsx +0 -107
@@ -0,0 +1,285 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useTranslation } from "../../i18n/use-translation.js";
5
+ import { cn } from "../../lib/utils.js";
6
+ import { VisuallyHidden } from "../general/visually-hidden.js";
7
+ import { Popover, PopoverAnchor, PopoverContent } from "../data-display/popover.js";
8
+ import { Command, CommandEmpty, CommandItem, CommandList } from "./command.js";
9
+ function matchTrigger(text, caret, trigger) {
10
+ if (!trigger) return null;
11
+ const before = text.slice(0, caret);
12
+ const start = before.lastIndexOf(trigger);
13
+ if (start === -1) return null;
14
+ const query = before.slice(start + trigger.length);
15
+ if (/\s/.test(query)) return null;
16
+ const preceding = start === 0 ? "" : before[start - 1];
17
+ if (preceding !== "" && !/\s/.test(preceding)) return null;
18
+ return { query };
19
+ }
20
+ function levelItems(items, path) {
21
+ let current = items;
22
+ for (const step of path) {
23
+ const next = current.find((item) => item.value === step)?.children;
24
+ if (!next) return current;
25
+ current = next;
26
+ }
27
+ return current;
28
+ }
29
+ function labelOf(item) {
30
+ return item.label ?? item.value;
31
+ }
32
+ function matches(item, query) {
33
+ if (!query) return true;
34
+ const needle = query.toLocaleLowerCase();
35
+ return labelOf(item).toLocaleLowerCase().includes(needle) || item.value.toLocaleLowerCase().includes(needle) || (item.description?.toLocaleLowerCase().includes(needle) ?? false);
36
+ }
37
+ function ChatSuggestion({
38
+ items,
39
+ onValueChange,
40
+ triggerCharacter = "/",
41
+ open,
42
+ defaultOpen,
43
+ onOpenChange,
44
+ children,
45
+ emptyMessage,
46
+ listLabel: listLabelProp,
47
+ id,
48
+ className
49
+ }) {
50
+ const { t } = useTranslation();
51
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
52
+ const isOpen = open ?? uncontrolledOpen;
53
+ const [query, setQuery] = React.useState("");
54
+ const [path, setPath] = React.useState([]);
55
+ const [activeValue, setActiveValue] = React.useState("");
56
+ const [activeId, setActiveId] = React.useState(void 0);
57
+ const anchorRef = React.useRef(null);
58
+ const contentRef = React.useRef(null);
59
+ const [listId, setListId] = React.useState(void 0);
60
+ const field = React.useCallback(() => anchorRef.current?.querySelector("textarea") ?? null, []);
61
+ const openRef = React.useRef(isOpen);
62
+ openRef.current = isOpen;
63
+ const setOpen = React.useCallback(
64
+ (next) => {
65
+ if (openRef.current === next) return;
66
+ openRef.current = next;
67
+ if (open === void 0) setUncontrolledOpen(next);
68
+ onOpenChange?.(next);
69
+ if (!next) {
70
+ setQuery("");
71
+ setPath([]);
72
+ }
73
+ },
74
+ [open, onOpenChange]
75
+ );
76
+ const visible = React.useMemo(() => {
77
+ return levelItems(items, path).filter((item) => matches(item, query));
78
+ }, [items, path, query]);
79
+ const enabled = React.useMemo(() => visible.filter((item) => !item.disabled), [visible]);
80
+ React.useEffect(() => {
81
+ if (enabled.length === 0) {
82
+ setActiveValue("");
83
+ return;
84
+ }
85
+ setActiveValue(
86
+ (current) => enabled.some((item) => item.value === current) ? current : enabled[0].value
87
+ );
88
+ }, [enabled]);
89
+ React.useEffect(() => {
90
+ if (!isOpen) {
91
+ setListId(void 0);
92
+ setActiveId(void 0);
93
+ return;
94
+ }
95
+ const content = contentRef.current;
96
+ setListId(content?.querySelector("[cmdk-list]")?.id || void 0);
97
+ setActiveId(content?.querySelector('[cmdk-item][aria-selected="true"]')?.id || void 0);
98
+ });
99
+ const close = React.useCallback(() => {
100
+ setOpen(false);
101
+ field()?.focus();
102
+ }, [field, setOpen]);
103
+ const evaluate = React.useCallback(() => {
104
+ const node = field();
105
+ if (!node) return;
106
+ const caret = node.selectionStart ?? node.value.length;
107
+ const hit = matchTrigger(node.value, caret, triggerCharacter);
108
+ if (!hit) {
109
+ setOpen(false);
110
+ return;
111
+ }
112
+ setQuery(hit.query);
113
+ setOpen(true);
114
+ }, [field, setOpen, triggerCharacter]);
115
+ const onTrigger = React.useCallback(
116
+ (next) => {
117
+ if (next === false) {
118
+ setOpen(false);
119
+ return;
120
+ }
121
+ if (next === void 0) {
122
+ setQuery("");
123
+ setOpen(true);
124
+ return;
125
+ }
126
+ window.setTimeout(evaluate, 0);
127
+ },
128
+ [evaluate, setOpen]
129
+ );
130
+ const pick = React.useCallback(
131
+ (item) => {
132
+ if (item.disabled) return;
133
+ if (item.children && item.children.length > 0) {
134
+ setPath((current) => [...current, item.value]);
135
+ setQuery("");
136
+ field()?.focus();
137
+ return;
138
+ }
139
+ onValueChange?.(item.value);
140
+ setOpen(false);
141
+ field()?.focus();
142
+ },
143
+ [field, onValueChange, setOpen]
144
+ );
145
+ const move = React.useCallback(
146
+ (direction) => {
147
+ if (enabled.length === 0) return;
148
+ const index = enabled.findIndex((item) => item.value === activeValue);
149
+ const from = index === -1 ? direction === 1 ? -1 : 0 : index;
150
+ const next = (from + direction + enabled.length) % enabled.length;
151
+ setActiveValue(enabled[next].value);
152
+ },
153
+ [activeValue, enabled]
154
+ );
155
+ const onKeyDown = React.useCallback(
156
+ (event) => {
157
+ if (!openRef.current) {
158
+ if (event.key.startsWith("Arrow") || event.key === "Home" || event.key === "End") {
159
+ window.setTimeout(evaluate, 0);
160
+ }
161
+ return;
162
+ }
163
+ switch (event.key) {
164
+ case "ArrowDown":
165
+ event.preventDefault();
166
+ move(1);
167
+ return;
168
+ case "ArrowUp":
169
+ event.preventDefault();
170
+ move(-1);
171
+ return;
172
+ case "Enter":
173
+ case "Tab": {
174
+ const item = enabled.find((candidate) => candidate.value === activeValue);
175
+ if (!item) return;
176
+ event.preventDefault();
177
+ pick(item);
178
+ return;
179
+ }
180
+ case "Escape":
181
+ event.preventDefault();
182
+ event.stopPropagation();
183
+ close();
184
+ return;
185
+ default:
186
+ if (event.key.startsWith("Arrow") || event.key === "Home" || event.key === "End") {
187
+ window.setTimeout(evaluate, 0);
188
+ }
189
+ }
190
+ },
191
+ [activeValue, close, enabled, evaluate, move, pick]
192
+ );
193
+ const listLabel = listLabelProp ?? t("dataEntry.chatSuggestion.label");
194
+ return /* @__PURE__ */ jsxs(Popover, { open: isOpen, onOpenChange: setOpen, children: [
195
+ /* @__PURE__ */ jsxs(
196
+ PopoverAnchor,
197
+ {
198
+ ref: anchorRef,
199
+ id,
200
+ "data-slot": "chat-suggestion",
201
+ className: cn("block w-full", className),
202
+ onPointerUp: () => window.setTimeout(evaluate, 0),
203
+ onBlurCapture: (event) => {
204
+ const next = event.relatedTarget;
205
+ if (next && anchorRef.current?.contains(next)) return;
206
+ if (openRef.current) setOpen(false);
207
+ },
208
+ children: [
209
+ children({
210
+ onTrigger,
211
+ onKeyDown
212
+ }),
213
+ /* @__PURE__ */ jsx(FieldPopupWiring, { field, open: isOpen, listId, activeId }),
214
+ /* @__PURE__ */ jsx(VisuallyHidden, { role: "status", "aria-live": "polite", children: isOpen ? t("dataEntry.chatSuggestion.count", { count: visible.length }) : "" })
215
+ ]
216
+ }
217
+ ),
218
+ /* @__PURE__ */ jsx(
219
+ PopoverContent,
220
+ {
221
+ ref: contentRef,
222
+ flush: true,
223
+ align: "start",
224
+ "aria-label": listLabel,
225
+ className: "ui-chat-suggestion-panel",
226
+ onOpenAutoFocus: (event) => event.preventDefault(),
227
+ onCloseAutoFocus: (event) => event.preventDefault(),
228
+ children: /* @__PURE__ */ jsx(
229
+ Command,
230
+ {
231
+ value: activeValue,
232
+ onValueChange: setActiveValue,
233
+ shouldFilter: false,
234
+ label: listLabel,
235
+ children: /* @__PURE__ */ jsxs(CommandList, { label: listLabel, children: [
236
+ /* @__PURE__ */ jsx(CommandEmpty, { children: emptyMessage ?? t("dataEntry.chatSuggestion.empty") }),
237
+ visible.map((item) => /* @__PURE__ */ jsxs(
238
+ CommandItem,
239
+ {
240
+ value: item.value,
241
+ disabled: item.disabled,
242
+ onSelect: () => pick(item),
243
+ children: [
244
+ item.icon ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center", "aria-hidden": "true", children: item.icon }) : null,
245
+ /* @__PURE__ */ jsxs("span", { className: "ui-chat-suggestion-item-text", children: [
246
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: labelOf(item) }),
247
+ item.description ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground truncate text-xs", children: item.description }) : null
248
+ ] })
249
+ ]
250
+ },
251
+ item.value
252
+ ))
253
+ ] })
254
+ }
255
+ )
256
+ }
257
+ )
258
+ ] });
259
+ }
260
+ ChatSuggestion.displayName = "ChatSuggestion";
261
+ function FieldPopupWiring({
262
+ field,
263
+ open,
264
+ listId,
265
+ activeId
266
+ }) {
267
+ React.useEffect(() => {
268
+ const node = field();
269
+ if (!node) return;
270
+ node.setAttribute("aria-haspopup", "listbox");
271
+ node.setAttribute("aria-autocomplete", "list");
272
+ if (open && listId) {
273
+ node.setAttribute("aria-controls", listId);
274
+ if (activeId) node.setAttribute("aria-activedescendant", activeId);
275
+ else node.removeAttribute("aria-activedescendant");
276
+ } else {
277
+ node.removeAttribute("aria-controls");
278
+ node.removeAttribute("aria-activedescendant");
279
+ }
280
+ }, [activeId, field, listId, open]);
281
+ return null;
282
+ }
283
+ export {
284
+ ChatSuggestion
285
+ };
@@ -7,6 +7,10 @@ export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScro
7
7
  export { Checkbox } from "./checkbox.js";
8
8
  export { CheckboxGroup } from "./checkbox-group.js";
9
9
  export { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
10
+ export { ChatComposer } from "./chat-composer.js";
11
+ export type { ChatComposerProp, ChatComposerProps, ChatComposerSubmitTypeProp, } from "./chat-composer.js";
12
+ export { ChatSuggestion } from "./chat-suggestion.js";
13
+ export type { ChatSuggestionProp, ChatSuggestionProps, ChatSuggestionItemProp, ChatSuggestionRenderProp, } from "./chat-suggestion.js";
10
14
  export { Textarea } from "./textarea.js";
11
15
  export type { TextareaProps } from "./textarea.js";
12
16
  export { Form, useFormLayout, type FormLayoutContextValue } from "./form.js";
@@ -16,6 +16,8 @@ import {
16
16
  import { Checkbox } from "./checkbox.js";
17
17
  import { CheckboxGroup } from "./checkbox-group.js";
18
18
  import { Radio, RadioGroup, RadioItem, RadioGroupRoot } from "./radio.js";
19
+ import { ChatComposer } from "./chat-composer.js";
20
+ import { ChatSuggestion } from "./chat-suggestion.js";
19
21
  import { Textarea } from "./textarea.js";
20
22
  import { Form, useFormLayout } from "./form.js";
21
23
  import { FormField } from "./form-field.js";
@@ -64,6 +66,8 @@ export {
64
66
  BranchScopePicker,
65
67
  Calendar,
66
68
  Cascader,
69
+ ChatComposer,
70
+ ChatSuggestion,
67
71
  Checkbox,
68
72
  CheckboxGroup,
69
73
  ColorPicker,
@@ -158,11 +158,13 @@ const Textarea = React.forwardRef(
158
158
  }
159
159
  );
160
160
  if (!needsWrapper) return field;
161
+ const placeholderText = typeof props.placeholder === "string" ? props.placeholder : void 0;
162
+ const mirrorText = mirror.length > 0 ? mirror : placeholderText ?? "";
161
163
  return /* @__PURE__ */ jsxs(
162
164
  "span",
163
165
  {
164
166
  "data-slot": "textarea-affix-wrapper",
165
- "data-autogrow-value": growing ? mirror : void 0,
167
+ "data-autogrow-value": growing ? mirrorText : void 0,
166
168
  style: { ...autoGrowVars, ...mirrorInset },
167
169
  className: cn(
168
170
  "relative w-full",
@@ -1,48 +1,10 @@
1
- import type * as React from "react";
2
- /** Normalized tree node — the conventional `treeData` / Cascader `options` shape. */
3
- export type TreeOption = {
4
- value: string;
5
- label: React.ReactNode;
6
- disabled?: boolean;
7
- disableCheckbox?: boolean;
8
- /** When false with `loadData`, shows expand affordance */
9
- isLeaf?: boolean;
10
- children?: TreeOption[];
11
- };
12
- export type TreeFieldNames = {
13
- label?: string;
14
- value?: string;
15
- children?: string;
16
- };
17
- export type NormalizedTreeOption = TreeOption & {
18
- children?: NormalizedTreeOption[];
19
- };
20
- type RawTreeNode = Record<string, unknown>;
21
- export declare function reactNodeText(value: React.ReactNode): string;
22
- export declare function normalizeTreeOptions(nodes: RawTreeNode[] | undefined, fieldNames?: TreeFieldNames): NormalizedTreeOption[];
23
- export declare function getNodeByPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
24
- export declare function getOptionsAtPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
25
- export declare function formatPathLabels(chain: NormalizedTreeOption[], separator?: string): string;
26
- export type TreePath = {
27
- path: string[];
28
- labels: string[];
29
- };
30
- export declare function collectLeafPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
31
- export declare function collectAllPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
32
- export declare function pathKey(path: string[]): string;
33
- export declare function pathsEqual(a: string[], b: string[]): boolean;
34
- export declare function filterTreeOptions(options: NormalizedTreeOption[], query: string, filter?: (query: string, path: NormalizedTreeOption[]) => boolean): TreePath[];
35
- export declare function getDescendantValues(node: NormalizedTreeOption): string[];
36
- export declare function flattenVisibleTree(options: NormalizedTreeOption[], expandedKeys: Set<string>, depth?: number): {
37
- node: NormalizedTreeOption;
38
- depth: number;
39
- hasChildren: boolean;
40
- }[];
41
- export declare function filterVisibleTree(options: NormalizedTreeOption[], query: string): {
42
- node: NormalizedTreeOption;
43
- depth: number;
44
- hasChildren: boolean;
45
- }[];
46
- export declare function collectAllExpandableKeys(options: NormalizedTreeOption[]): string[];
47
- export declare function findNodeByValue(options: NormalizedTreeOption[], value: string): NormalizedTreeOption | undefined;
48
- export {};
1
+ /**
2
+ * RE-EXPORT SHIM — the tree model moved to `src/lib/tree.ts`.
3
+ *
4
+ * `Tree` (data-display) and `TreeSelect` (data-entry) are two SURFACES over ONE model. Forking the
5
+ * traversal would let a page tree and a dropdown tree disagree about what "expanded", "a leaf" or
6
+ * "every descendant" means — so the normalizer, the visible-row flattener and the descendant walk
7
+ * live in `src/lib/`, which both groups may import, and this path stays valid so nothing that
8
+ * already imports `./tree-utils` has to change.
9
+ */
10
+ export * from "../../lib/tree.js";
@@ -1,154 +1 @@
1
- function reactNodeText(value) {
2
- if (value == null || typeof value === "boolean") return "";
3
- if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
4
- return String(value);
5
- }
6
- if (Array.isArray(value)) {
7
- return value.map((item) => reactNodeText(item)).join("");
8
- }
9
- return "";
10
- }
11
- function unknownText(value) {
12
- if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
13
- return String(value);
14
- }
15
- return "";
16
- }
17
- function normalizeTreeOptions(nodes, fieldNames) {
18
- if (!nodes?.length) return [];
19
- const labelKey = fieldNames?.label ?? "label";
20
- const valueKey = fieldNames?.value ?? "value";
21
- const childrenKey = fieldNames?.children ?? "children";
22
- return nodes.map((node) => {
23
- const children = node[childrenKey];
24
- const value = unknownText(node[valueKey]);
25
- const label = node[labelKey];
26
- return {
27
- value,
28
- label: label ?? value,
29
- disabled: Boolean(node.disabled),
30
- disableCheckbox: Boolean(node.disableCheckbox),
31
- isLeaf: node.isLeaf,
32
- children: Array.isArray(children) ? normalizeTreeOptions(children, fieldNames) : void 0
33
- };
34
- });
35
- }
36
- function getNodeByPath(options, path) {
37
- const chain = [];
38
- let level = options;
39
- for (const segment of path) {
40
- const found = level.find((n) => n.value === segment);
41
- if (!found) break;
42
- chain.push(found);
43
- level = found.children ?? [];
44
- }
45
- return chain;
46
- }
47
- function getOptionsAtPath(options, path) {
48
- if (!path.length) return options;
49
- const chain = getNodeByPath(options, path);
50
- return chain.at(-1)?.children ?? [];
51
- }
52
- function formatPathLabels(chain, separator = " / ") {
53
- return chain.map((n) => reactNodeText(n.label)).join(separator);
54
- }
55
- function collectLeafPaths(options, prefix = [], root = options) {
56
- const out = [];
57
- for (const node of options) {
58
- const path = [...prefix, node.value];
59
- const hasChildren = (node.children?.length ?? 0) > 0;
60
- if (!hasChildren || node.isLeaf === true) {
61
- out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
62
- }
63
- if (hasChildren) out.push(...collectLeafPaths(node.children, path, root));
64
- }
65
- return out;
66
- }
67
- function collectAllPaths(options, prefix = [], root = options) {
68
- const out = [];
69
- for (const node of options) {
70
- const path = [...prefix, node.value];
71
- out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
72
- if (node.children?.length) out.push(...collectAllPaths(node.children, path, root));
73
- }
74
- return out;
75
- }
76
- function pathKey(path) {
77
- return path.join("\0");
78
- }
79
- function pathsEqual(a, b) {
80
- return a.length === b.length && a.every((v, i) => v === b[i]);
81
- }
82
- function filterTreeOptions(options, query, filter) {
83
- const q = query.trim().toLowerCase();
84
- if (!q) return [];
85
- const paths = collectLeafPaths(options);
86
- return paths.filter(({ path }) => {
87
- const chain = getNodeByPath(options, path);
88
- if (filter) return filter(query, chain);
89
- return chain.some((n) => reactNodeText(n.label).toLowerCase().includes(q));
90
- });
91
- }
92
- function getDescendantValues(node) {
93
- const values = [node.value];
94
- for (const child of node.children ?? []) values.push(...getDescendantValues(child));
95
- return values;
96
- }
97
- function flattenVisibleTree(options, expandedKeys, depth = 0) {
98
- const out = [];
99
- for (const node of options) {
100
- const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
101
- out.push({ node, depth, hasChildren });
102
- if (hasChildren && expandedKeys.has(node.value)) {
103
- out.push(...flattenVisibleTree(node.children, expandedKeys, depth + 1));
104
- }
105
- }
106
- return out;
107
- }
108
- function filterVisibleTree(options, query) {
109
- const q = query.trim().toLowerCase();
110
- if (!q) return flattenVisibleTree(options, new Set(collectAllExpandableKeys(options)));
111
- function matches(nodes, depth) {
112
- return nodes.flatMap((node) => {
113
- const children = node.isLeaf ? [] : matches(node.children ?? [], depth + 1);
114
- if (!reactNodeText(node.label).toLowerCase().includes(q) && children.length === 0) return [];
115
- return [{ node, depth, hasChildren: children.length > 0 }, ...children];
116
- });
117
- }
118
- return matches(options, 0);
119
- }
120
- function collectAllExpandableKeys(options) {
121
- const keys = [];
122
- for (const node of options) {
123
- if ((node.children?.length ?? 0) > 0 && node.isLeaf !== true) {
124
- keys.push(node.value);
125
- keys.push(...collectAllExpandableKeys(node.children));
126
- }
127
- }
128
- return keys;
129
- }
130
- function findNodeByValue(options, value) {
131
- for (const node of options) {
132
- if (node.value === value) return node;
133
- const nested = node.children ? findNodeByValue(node.children, value) : void 0;
134
- if (nested) return nested;
135
- }
136
- return void 0;
137
- }
138
- export {
139
- collectAllExpandableKeys,
140
- collectAllPaths,
141
- collectLeafPaths,
142
- filterTreeOptions,
143
- filterVisibleTree,
144
- findNodeByValue,
145
- flattenVisibleTree,
146
- formatPathLabels,
147
- getDescendantValues,
148
- getNodeByPath,
149
- getOptionsAtPath,
150
- normalizeTreeOptions,
151
- pathKey,
152
- pathsEqual,
153
- reactNodeText
154
- };
1
+ export * from "../../lib/tree.js";
@@ -162,6 +162,22 @@
162
162
  "error": "Invalid",
163
163
  "validating": "Validating",
164
164
  "submitFailed": "Could not submit the form. Please try again."
165
+ },
166
+ "chatComposer": {
167
+ "label": "Message",
168
+ "placeholder": "Write a message",
169
+ "send": "Send message",
170
+ "cancel": "Stop generating",
171
+ "hintEnter": "Enter to send · Shift + Enter for a new line",
172
+ "hintShiftEnter": "Shift + Enter to send · Enter for a new line"
173
+ },
174
+ "chatSuggestion": {
175
+ "label": "Suggestions",
176
+ "empty": "No suggestions match",
177
+ "count": {
178
+ "one": "{count} suggestion",
179
+ "other": "{count} suggestions"
180
+ }
165
181
  }
166
182
  },
167
183
  "feedback": {
@@ -289,6 +305,13 @@
289
305
  "progress": {
290
306
  "ariaLabel": "Progress",
291
307
  "breakdownSeparator": ", "
308
+ },
309
+ "tree": {
310
+ "expand": "Expand",
311
+ "collapse": "Collapse",
312
+ "loading": "Loading child nodes…",
313
+ "empty": "Nothing to show",
314
+ "selected": "Selected"
292
315
  }
293
316
  },
294
317
  "ui": {
@@ -536,5 +559,27 @@
536
559
  "textExamples": {
537
560
  "preservedText": "Plain text",
538
561
  "sampleText": "First line\n Indented line\n\nLast line"
562
+ },
563
+ "chat": {
564
+ "bubble": {
565
+ "typing": "Typing…",
566
+ "loading": "Loading the message",
567
+ "you": "You",
568
+ "assistant": "Assistant",
569
+ "tone": {
570
+ "info": "Information",
571
+ "success": "Success",
572
+ "warning": "Warning",
573
+ "destructive": "Error"
574
+ }
575
+ },
576
+ "list": {
577
+ "label": "Conversation",
578
+ "jumpToLatest": "Jump to latest",
579
+ "newMessages": {
580
+ "one": "{count} new message",
581
+ "other": "{count} new messages"
582
+ }
583
+ }
539
584
  }
540
585
  }
@@ -159,6 +159,21 @@
159
159
  "error": "入力エラー",
160
160
  "validating": "確認中",
161
161
  "submitFailed": "フォームを送信できませんでした。再試行してください。"
162
+ },
163
+ "chatComposer": {
164
+ "label": "メッセージ",
165
+ "placeholder": "メッセージを入力",
166
+ "send": "メッセージを送信",
167
+ "cancel": "生成を停止",
168
+ "hintEnter": "Enter で送信 · Shift + Enter で改行",
169
+ "hintShiftEnter": "Shift + Enter で送信 · Enter で改行"
170
+ },
171
+ "chatSuggestion": {
172
+ "label": "候補",
173
+ "empty": "一致する候補がありません",
174
+ "count": {
175
+ "other": "候補 {count} 件"
176
+ }
162
177
  }
163
178
  },
164
179
  "feedback": {
@@ -283,6 +298,13 @@
283
298
  "progress": {
284
299
  "ariaLabel": "進捗",
285
300
  "breakdownSeparator": "、"
301
+ },
302
+ "tree": {
303
+ "expand": "展開",
304
+ "collapse": "折りたたむ",
305
+ "loading": "子ノードを読み込み中…",
306
+ "empty": "表示する項目がありません",
307
+ "selected": "選択中"
286
308
  }
287
309
  },
288
310
  "ui": {
@@ -524,5 +546,26 @@
524
546
  "textExamples": {
525
547
  "preservedText": "プレーンテキスト",
526
548
  "sampleText": "最初の行\n 字下げした行\n\n最後の行"
549
+ },
550
+ "chat": {
551
+ "bubble": {
552
+ "typing": "入力中",
553
+ "loading": "メッセージを読み込み中",
554
+ "you": "自分",
555
+ "assistant": "アシスタント",
556
+ "tone": {
557
+ "info": "情報",
558
+ "success": "成功",
559
+ "warning": "警告",
560
+ "destructive": "エラー"
561
+ }
562
+ },
563
+ "list": {
564
+ "label": "会話",
565
+ "jumpToLatest": "最新へ移動",
566
+ "newMessages": {
567
+ "other": "新着 {count} 件"
568
+ }
569
+ }
527
570
  }
528
571
  }