@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
@@ -159,6 +159,22 @@
159
159
  "error": "Không hợp lệ",
160
160
  "validating": "Đang kiểm tra",
161
161
  "submitFailed": "Không thể gửi biểu mẫu. Vui lòng thử lại."
162
+ },
163
+ "chatComposer": {
164
+ "label": "Tin nhắn",
165
+ "placeholder": "Nhập tin nhắn",
166
+ "send": "Gửi tin nhắn",
167
+ "cancel": "Dừng tạo câu trả lời",
168
+ "hintEnter": "Enter để gửi · Shift + Enter để xuống dòng",
169
+ "hintShiftEnter": "Shift + Enter để gửi · Enter để xuống dòng"
170
+ },
171
+ "chatSuggestion": {
172
+ "label": "Gợi ý",
173
+ "empty": "Không có gợi ý phù hợp",
174
+ "count": {
175
+ "one": "{count} gợi ý",
176
+ "other": "{count} gợi ý"
177
+ }
162
178
  }
163
179
  },
164
180
  "feedback": {
@@ -283,6 +299,13 @@
283
299
  "progress": {
284
300
  "ariaLabel": "Tiến độ",
285
301
  "breakdownSeparator": ", "
302
+ },
303
+ "tree": {
304
+ "expand": "Mở rộng",
305
+ "collapse": "Thu gọn",
306
+ "loading": "Đang tải nút con…",
307
+ "empty": "Không có gì để hiển thị",
308
+ "selected": "Đã chọn"
286
309
  }
287
310
  },
288
311
  "ui": {
@@ -524,5 +547,26 @@
524
547
  "textExamples": {
525
548
  "preservedText": "Văn bản thuần",
526
549
  "sampleText": "Dòng đầu\n Dòng thụt vào\n\nDòng cuối"
550
+ },
551
+ "chat": {
552
+ "bubble": {
553
+ "typing": "Đang nhập…",
554
+ "loading": "Đang tải tin nhắn",
555
+ "you": "Bạn",
556
+ "assistant": "Trợ lý",
557
+ "tone": {
558
+ "info": "Thông tin",
559
+ "success": "Thành công",
560
+ "warning": "Cảnh báo",
561
+ "destructive": "Lỗi"
562
+ }
563
+ },
564
+ "list": {
565
+ "label": "Cuộc trò chuyện",
566
+ "jumpToLatest": "Đến tin mới nhất",
567
+ "newMessages": {
568
+ "other": "{count} tin nhắn mới"
569
+ }
570
+ }
527
571
  }
528
572
  }
@@ -0,0 +1,53 @@
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
+ /**
11
+ * Per-node glyph, drawn by `Tree` when `showIcon` is on. Carried through the normalizer so the
12
+ * page tree and the dropdown tree keep ONE node shape; `TreeSelect` simply never reads it.
13
+ */
14
+ icon?: React.ReactNode;
15
+ children?: TreeOption[];
16
+ };
17
+ export type TreeFieldNames = {
18
+ label?: string;
19
+ value?: string;
20
+ children?: string;
21
+ };
22
+ export type NormalizedTreeOption = TreeOption & {
23
+ children?: NormalizedTreeOption[];
24
+ };
25
+ type RawTreeNode = Record<string, unknown>;
26
+ export declare function reactNodeText(value: React.ReactNode): string;
27
+ export declare function normalizeTreeOptions(nodes: RawTreeNode[] | undefined, fieldNames?: TreeFieldNames): NormalizedTreeOption[];
28
+ export declare function getNodeByPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
29
+ export declare function getOptionsAtPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
30
+ export declare function formatPathLabels(chain: NormalizedTreeOption[], separator?: string): string;
31
+ export type TreePath = {
32
+ path: string[];
33
+ labels: string[];
34
+ };
35
+ export declare function collectLeafPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
36
+ export declare function collectAllPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
37
+ export declare function pathKey(path: string[]): string;
38
+ export declare function pathsEqual(a: string[], b: string[]): boolean;
39
+ export declare function filterTreeOptions(options: NormalizedTreeOption[], query: string, filter?: (query: string, path: NormalizedTreeOption[]) => boolean): TreePath[];
40
+ export declare function getDescendantValues(node: NormalizedTreeOption): string[];
41
+ export declare function flattenVisibleTree(options: NormalizedTreeOption[], expandedKeys: Set<string>, depth?: number): {
42
+ node: NormalizedTreeOption;
43
+ depth: number;
44
+ hasChildren: boolean;
45
+ }[];
46
+ export declare function filterVisibleTree(options: NormalizedTreeOption[], query: string): {
47
+ node: NormalizedTreeOption;
48
+ depth: number;
49
+ hasChildren: boolean;
50
+ }[];
51
+ export declare function collectAllExpandableKeys(options: NormalizedTreeOption[]): string[];
52
+ export declare function findNodeByValue(options: NormalizedTreeOption[], value: string): NormalizedTreeOption | undefined;
53
+ export {};
@@ -0,0 +1,155 @@
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
+ icon: node.icon,
33
+ children: Array.isArray(children) ? normalizeTreeOptions(children, fieldNames) : void 0
34
+ };
35
+ });
36
+ }
37
+ function getNodeByPath(options, path) {
38
+ const chain = [];
39
+ let level = options;
40
+ for (const segment of path) {
41
+ const found = level.find((n) => n.value === segment);
42
+ if (!found) break;
43
+ chain.push(found);
44
+ level = found.children ?? [];
45
+ }
46
+ return chain;
47
+ }
48
+ function getOptionsAtPath(options, path) {
49
+ if (!path.length) return options;
50
+ const chain = getNodeByPath(options, path);
51
+ return chain.at(-1)?.children ?? [];
52
+ }
53
+ function formatPathLabels(chain, separator = " / ") {
54
+ return chain.map((n) => reactNodeText(n.label)).join(separator);
55
+ }
56
+ function collectLeafPaths(options, prefix = [], root = options) {
57
+ const out = [];
58
+ for (const node of options) {
59
+ const path = [...prefix, node.value];
60
+ const hasChildren = (node.children?.length ?? 0) > 0;
61
+ if (!hasChildren || node.isLeaf === true) {
62
+ out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
63
+ }
64
+ if (hasChildren) out.push(...collectLeafPaths(node.children, path, root));
65
+ }
66
+ return out;
67
+ }
68
+ function collectAllPaths(options, prefix = [], root = options) {
69
+ const out = [];
70
+ for (const node of options) {
71
+ const path = [...prefix, node.value];
72
+ out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
73
+ if (node.children?.length) out.push(...collectAllPaths(node.children, path, root));
74
+ }
75
+ return out;
76
+ }
77
+ function pathKey(path) {
78
+ return path.join("\0");
79
+ }
80
+ function pathsEqual(a, b) {
81
+ return a.length === b.length && a.every((v, i) => v === b[i]);
82
+ }
83
+ function filterTreeOptions(options, query, filter) {
84
+ const q = query.trim().toLowerCase();
85
+ if (!q) return [];
86
+ const paths = collectLeafPaths(options);
87
+ return paths.filter(({ path }) => {
88
+ const chain = getNodeByPath(options, path);
89
+ if (filter) return filter(query, chain);
90
+ return chain.some((n) => reactNodeText(n.label).toLowerCase().includes(q));
91
+ });
92
+ }
93
+ function getDescendantValues(node) {
94
+ const values = [node.value];
95
+ for (const child of node.children ?? []) values.push(...getDescendantValues(child));
96
+ return values;
97
+ }
98
+ function flattenVisibleTree(options, expandedKeys, depth = 0) {
99
+ const out = [];
100
+ for (const node of options) {
101
+ const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
102
+ out.push({ node, depth, hasChildren });
103
+ if (hasChildren && expandedKeys.has(node.value)) {
104
+ out.push(...flattenVisibleTree(node.children, expandedKeys, depth + 1));
105
+ }
106
+ }
107
+ return out;
108
+ }
109
+ function filterVisibleTree(options, query) {
110
+ const q = query.trim().toLowerCase();
111
+ if (!q) return flattenVisibleTree(options, new Set(collectAllExpandableKeys(options)));
112
+ function matches(nodes, depth) {
113
+ return nodes.flatMap((node) => {
114
+ const children = node.isLeaf ? [] : matches(node.children ?? [], depth + 1);
115
+ if (!reactNodeText(node.label).toLowerCase().includes(q) && children.length === 0) return [];
116
+ return [{ node, depth, hasChildren: children.length > 0 }, ...children];
117
+ });
118
+ }
119
+ return matches(options, 0);
120
+ }
121
+ function collectAllExpandableKeys(options) {
122
+ const keys = [];
123
+ for (const node of options) {
124
+ if ((node.children?.length ?? 0) > 0 && node.isLeaf !== true) {
125
+ keys.push(node.value);
126
+ keys.push(...collectAllExpandableKeys(node.children));
127
+ }
128
+ }
129
+ return keys;
130
+ }
131
+ function findNodeByValue(options, value) {
132
+ for (const node of options) {
133
+ if (node.value === value) return node;
134
+ const nested = node.children ? findNodeByValue(node.children, value) : void 0;
135
+ if (nested) return nested;
136
+ }
137
+ return void 0;
138
+ }
139
+ export {
140
+ collectAllExpandableKeys,
141
+ collectAllPaths,
142
+ collectLeafPaths,
143
+ filterTreeOptions,
144
+ filterVisibleTree,
145
+ findNodeByValue,
146
+ flattenVisibleTree,
147
+ formatPathLabels,
148
+ getDescendantValues,
149
+ getNodeByPath,
150
+ getOptionsAtPath,
151
+ normalizeTreeOptions,
152
+ pathKey,
153
+ pathsEqual,
154
+ reactNodeText
155
+ };
@@ -28,7 +28,8 @@ export type ProseProp = {
28
28
  className?: ClassNameProp;
29
29
  children?: ChildrenProp;
30
30
  };
31
- import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ColumnDefProp, GetRowIdProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, ToneProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp } from "../vocabulary/index.js";
31
+ import type { ActionProp, ClassNameProp, DescriptionProp, IconProp, TitleProp, ColumnDefProp, GetRowIdProp, OnRowClickProp, OnSelectChangeProp, OnSortChangeProp, OnTableDensityChangeProp, SelectedIdsProp, SortStateProp, TableDensityProp, TablePresetProp, BreakpointProp, DensityProp, ChildrenProp, PendingProp, ToneProp, AvatarShapeProp, HeadingLevelProp, HandlerProp, SizeProp, LabelProp, IdProp, DescriptionsLayoutProp, DescriptionsColumnProp, DescriptionsSpanProp, DescriptionsItemsProp, SortDirectionProp, OnColumnFilterChangeProp, OnRowProp, TableExpandableProp, TableRowSelectionProp, TableScrollProp, TableStickyProp, TableSummaryProp, DisabledProp, ValueProp, DefaultValueProp, OnValueChangeProp } from "../vocabulary/index.js";
32
+ import type { TreeFieldNamesProp, TreeOptionProp } from "./data-entry.prop.js";
32
33
  /**
33
34
  * One key in a `Legend`: a tone, and the words that tone stands for.
34
35
  *
@@ -493,3 +494,188 @@ export type TimelineGridProp = {
493
494
  className?: ClassNameProp;
494
495
  id?: IdProp;
495
496
  };
497
+ /**
498
+ * @see Tree — one node of the standalone, on-page tree view.
499
+ *
500
+ * Structurally the same node `TreeSelect`/`Cascader` take (one tree model, two surfaces — the
501
+ * normalizer lives in `src/lib/tree.ts`), plus the per-node glyph the ON-PAGE surface needs and a
502
+ * dropdown does not.
503
+ */
504
+ export type TreeNodeProp = TreeOptionProp & {
505
+ /** Glyph drawn before the label when `showIcon` is on. Decorative — the label carries the name. */
506
+ icon?: React.ReactNode;
507
+ children?: TreeNodeProp[];
508
+ };
509
+ /**
510
+ * @see Tree — the WAI-ARIA APG "Tree View" on a page (Ant's `Tree` / `DirectoryTree`).
511
+ *
512
+ * `TreeSelect` is the same hierarchy inside a Popover. A flat indented list only LOOKS like a tree
513
+ * (that was `TreeList`, removed in 21.0.0 with `Tree` as its replacement), so reach for `Tree`
514
+ * whenever nodes expand, collapse, or are navigated by keyboard.
515
+ *
516
+ * Ant's key-shaped names are mapped onto this package's controlled vocabulary and NOT re-spelled:
517
+ * selection is `value`/`defaultValue`/`onValueChange`, expansion is
518
+ * `expandedValues`/`defaultExpandedValues`/`onExpandedValuesChange`, checks are
519
+ * `checkedValues`/`defaultCheckedValues`/`onCheckedValuesChange`.
520
+ * @see docs/roadmap/tree-components.md §3
521
+ */
522
+ export type TreeProp = {
523
+ /** The hierarchy (antd `treeData`). Use `fieldNames` to remap keys off an API response. */
524
+ treeData: readonly TreeNodeProp[];
525
+ /** Remap the data's own key names (antd `fieldNames`). */
526
+ fieldNames?: TreeFieldNamesProp;
527
+ /** Controlled selection. `string` while single, `string[]` once `multiple` is on. */
528
+ value?: ValueProp<string | string[]>;
529
+ /** Uncontrolled initial selection (controlled-triad rule). */
530
+ defaultValue?: DefaultValueProp<string | string[]>;
531
+ /** Selection change. Emits `string | undefined` while single, `string[]` once `multiple` is on. */
532
+ onValueChange?: OnValueChangeProp<string | string[] | undefined>;
533
+ /** Allow more than one selected node (antd `multiple`). Also sets `aria-multiselectable`. */
534
+ multiple?: boolean;
535
+ /** Draw a checkbox on every node (antd `checkable`). Checks are a SEPARATE axis from selection. */
536
+ checkable?: boolean;
537
+ /** Parent and child checks are independent (antd `checkStrictly`) — no cascade, no `mixed`. */
538
+ checkStrictly?: boolean;
539
+ /** Controlled checked nodes (antd `checkedKeys`). */
540
+ checkedValues?: readonly string[];
541
+ /** Uncontrolled initial checked nodes (antd `defaultCheckedKeys`). */
542
+ defaultCheckedValues?: readonly string[];
543
+ /** Checked-set change (antd `onCheck`). Fires for controlled and uncontrolled trees alike. */
544
+ onCheckedValuesChange?: (values: string[]) => void;
545
+ /** Controlled expanded branches (antd `expandedKeys`). */
546
+ expandedValues?: readonly string[];
547
+ /** Uncontrolled initial expanded branches (antd `defaultExpandedKeys`). */
548
+ defaultExpandedValues?: readonly string[];
549
+ /** Expansion change (antd `onExpand`). Fires for controlled and uncontrolled trees alike. */
550
+ onExpandedValuesChange?: (values: string[]) => void;
551
+ /** Start with every branch open (antd `defaultExpandAll`). Seeded once, never re-applied. */
552
+ defaultExpandAll?: boolean;
553
+ /**
554
+ * Lazy children (antd `loadData`). Called ONCE per node the first time a branch with no
555
+ * `children` and `isLeaf !== true` is expanded; push the fetched children into `treeData`.
556
+ */
557
+ loadData?: (node: TreeNodeProp) => void | Promise<void>;
558
+ /** Render a node's title (antd `titleRender`). */
559
+ titleRender?: (node: TreeNodeProp) => React.ReactNode;
560
+ /** Draw the connector rails between a parent and its children (antd `showLine`). */
561
+ showLine?: boolean;
562
+ /** Draw each node's `icon` (antd `showIcon`). `variant="directory"` supplies folder/file glyphs. */
563
+ showIcon?: boolean;
564
+ /** `directory` is antd's `<DirectoryTree>`: folder/file glyphs and a full-row selected band. */
565
+ variant?: "default" | "directory";
566
+ /** Row height tier — the shared `--control-height` ladder. Default `md`. */
567
+ size?: SizeProp;
568
+ /** Disable the whole tree: nothing selects, checks or expands; nodes stay readable. */
569
+ disabled?: DisabledProp;
570
+ className?: ClassNameProp;
571
+ id?: IdProp;
572
+ /** Accessible name of the `role="tree"` container. Required unless `aria-labelledby` is given. */
573
+ "aria-label"?: string;
574
+ "aria-labelledby"?: string;
575
+ };
576
+ /**
577
+ * @see ChatBubble — which side of the conversation the message sits on. LOGICAL, not physical:
578
+ * `start` is the inline start of the reading direction (left in `ltr`, right in `rtl`), so a feed
579
+ * flips correctly under `dir="rtl"` with no per-locale branch. `start` = the other party,
580
+ * `end` = the reader's own messages, which is the convention every chat client shares.
581
+ */
582
+ export type ChatBubblePlacementProp = "start" | "end";
583
+ /**
584
+ * @see ChatBubble — STRUCTURAL treatment of the message body, orthogonal to `tone` (which owns
585
+ * colour). Ant Design X's `shadow` is deliberately absent: this design system is a 1px-border
586
+ * system with no drop shadows (docs/TOKENS.md).
587
+ *
588
+ * - `filled` (default) — the quiet `--chat-bubble-background` wash.
589
+ * - `outlined` — transparent body inside a 1px `--chat-bubble-border-color` edge.
590
+ * - `borderless` — no fill and no edge; long assistant prose that should read as page copy.
591
+ */
592
+ export type ChatBubbleVariantProp = "filled" | "borderless" | "outlined";
593
+ /**
594
+ * @see ChatBubble — status intent for a message that is not ordinary conversation (a failed send,
595
+ * a rate-limit warning, a tool result). A subset of `ToneProp`: `muted` and `neutral` are not
596
+ * offered because a bubble is already the quiet surface, so they would name no distinct state.
597
+ *
598
+ * Never colour alone (WCAG 1.4.1): a toned bubble also renders a localized `sr-only` tone word.
599
+ */
600
+ export type ChatBubbleToneProp = Extract<ToneProp, "default" | "info" | "success" | "warning" | "destructive">;
601
+ /**
602
+ * @see ChatBubble — the streaming type-on animation. `true` takes the defaults (1 character every
603
+ * 50ms); the object form retunes them per instance.
604
+ *
605
+ * It animates only when `children` is a plain string — a `ReactNode` has no character count to
606
+ * reveal — and it is DROPPED ENTIRELY under `prefers-reduced-motion: reduce`, which renders the
607
+ * full text immediately (WCAG 2.2 SC 2.3.3).
608
+ */
609
+ export type ChatBubbleTypingProp = boolean | {
610
+ step?: number;
611
+ interval?: number;
612
+ };
613
+ /** @see ChatBubble — one message in a conversation. */
614
+ export type ChatBubbleProp = {
615
+ /** The message. A `ReactNode`; pass a plain string to make `typing` animatable. */
616
+ children?: ChildrenProp;
617
+ /** Which side of the feed the message sits on. Default `start`. */
618
+ placement?: ChatBubblePlacementProp;
619
+ /** Structural treatment of the body. Default `filled`. */
620
+ variant?: ChatBubbleVariantProp;
621
+ /**
622
+ * The author's mark — a real `<Avatar>` node, never a styled div. It is decorative when the
623
+ * `header` already names the author, so mark it `aria-hidden` at the call site.
624
+ */
625
+ avatar?: ChildrenProp;
626
+ /**
627
+ * Line above the body — the author, and anything that identifies the turn. When it is present
628
+ * the bubble takes its ACCESSIBLE NAME from it, so keep it text.
629
+ */
630
+ header?: ChildrenProp;
631
+ /** Line below the body — timestamps, per-message actions, token counts. */
632
+ footer?: ChildrenProp;
633
+ /** The reply has been requested and has not arrived: renders `Skeleton`, sets `aria-busy`. */
634
+ loading?: PendingProp;
635
+ /** Stream the text in character by character. Honours `prefers-reduced-motion`. */
636
+ typing?: ChatBubbleTypingProp;
637
+ /** Type step and inner inset. Default `md`. */
638
+ size?: SizeProp;
639
+ /** Status intent. Default `default`. */
640
+ tone?: ChatBubbleToneProp;
641
+ className?: ClassNameProp;
642
+ id?: IdProp;
643
+ };
644
+ /** @see ChatBubbleList — one entry of the feed: a `ChatBubble`'s own props plus its identity. */
645
+ export type ChatMessageProp = Omit<ChatBubbleProp, "children" | "id"> & {
646
+ /** Stable message id. Also the React key and the rendered `<article>`'s DOM id. */
647
+ id: IdProp;
648
+ /** Key into the list's `roles` map — `"user"`, `"assistant"`, `"system"`, or your own. */
649
+ role?: string;
650
+ /** The message body. Named `content` here because `children` is not a data field. */
651
+ content?: ChildrenProp;
652
+ };
653
+ /**
654
+ * @see ChatBubbleList — the message feed.
655
+ *
656
+ * It scrolls inside itself, so it needs a DEFINITE height from the caller (`className="h-96"`, or
657
+ * a flex/grid parent that gives it a track). Without one it grows to its content and nothing ever
658
+ * overflows, which silently disables `autoScroll` and the jump-to-latest affordance.
659
+ */
660
+ export type ChatBubbleListProp = {
661
+ /** Messages in conversation order, oldest first. */
662
+ items: readonly ChatMessageProp[];
663
+ /**
664
+ * Per-role bubble defaults, merged UNDER each message's own props. This is what keeps a feed
665
+ * consistent: `{ assistant: { placement: "start", variant: "filled" }, user: { placement:
666
+ * "end" } }` is written once instead of on every message.
667
+ */
668
+ roles?: Record<string, Partial<ChatBubbleProp>>;
669
+ /**
670
+ * Keep the newest message in view while the reader is already at the bottom. Default `true`.
671
+ *
672
+ * It is NOT "scroll to the bottom when content arrives": the moment the reader scrolls up the
673
+ * pin is revoked and a focusable "jump to latest" affordance appears instead, because yanking a
674
+ * reader back mid-sentence is a change of context they did not request (WCAG 3.2.5).
675
+ */
676
+ autoScroll?: boolean;
677
+ /** Accessible name of the feed. A plain string — it lands on `aria-label`. Defaults via `t()`. */
678
+ label?: Extract<LabelProp, string>;
679
+ className?: ClassNameProp;
680
+ id?: IdProp;
681
+ };
@@ -1376,3 +1376,130 @@ export type BranchScopePickerProp = FieldA11yProps & {
1376
1376
  id?: IdProp;
1377
1377
  className?: ClassNameProp;
1378
1378
  };
1379
+ /**
1380
+ * Which keystroke SENDS and which one breaks the line — Ant Design X `Sender.submitType`.
1381
+ *
1382
+ * `enter` (default) is the chat convention: `Enter` sends, `Shift+Enter` inserts a newline.
1383
+ * `shiftEnter` is the inverse, for composers that hold long, deliberately multi-line drafts.
1384
+ * Neither ever fires while an IME conversion is in flight.
1385
+ * @see ChatComposer
1386
+ */
1387
+ export type ChatComposerSubmitTypeProp = "enter" | "shiftEnter";
1388
+ /**
1389
+ * @see ChatComposer — the message input of a conversation (Ant Design X `Sender`; the industry
1390
+ * calls the control a *composer*, so that is what it is named).
1391
+ *
1392
+ * Built on the real `Textarea` (auto-growing between `--chat-composer-min-height` and
1393
+ * `--chat-composer-max-height`, both derived from the `--control-height` tier) plus real
1394
+ * `Button`s. The `<textarea>` is the semantic focus target: `ref`, `id`, `name` and the whole
1395
+ * `FormField` label/helper/error contract land on it, so a ChatComposer inside a `FormField`
1396
+ * behaves exactly like an `Input` does.
1397
+ */
1398
+ export type ChatComposerProp = Omit<React.HTMLAttributes<HTMLDivElement>, "onSubmit" | "defaultValue" | "prefix" | "onChange" | "onKeyDown"> & FieldA11yProps & {
1399
+ /** Controlled draft text. Pair with `onValueChange` or the box freezes. */
1400
+ value?: ValueProp<string>;
1401
+ /** Uncontrolled initial draft text. */
1402
+ defaultValue?: DefaultValueProp<string>;
1403
+ /** Draft-text change handler — fires on every keystroke, including during an IME conversion. */
1404
+ onValueChange?: OnValueChangeProp<string>;
1405
+ /**
1406
+ * Send the draft. Receives the text as typed; never fires for an empty or whitespace-only
1407
+ * draft, and never while `loading`, `disabled` or `readOnly`.
1408
+ */
1409
+ onSubmit?: (value: string) => void;
1410
+ /** Stop the in-flight response. Only reachable while `loading`. */
1411
+ onCancel?: () => void;
1412
+ /**
1413
+ * A response is streaming. The trailing action becomes CANCEL — exactly one trailing action
1414
+ * exists at a time, the same discipline as the picker trailing-action rule.
1415
+ */
1416
+ loading?: PendingProp;
1417
+ /** Which keystroke sends and which breaks the line. Default `enter`. */
1418
+ submitType?: ChatComposerSubmitTypeProp;
1419
+ /** Empty-state text of the draft box; pass it through `t()` at the call site. */
1420
+ placeholder?: PlaceholderProp;
1421
+ /** Disable the whole composer (draft box and every action). */
1422
+ disabled?: DisabledProp;
1423
+ /** Show the draft without allowing an edit; still focusable and selectable. */
1424
+ readOnly?: boolean;
1425
+ /** Slot ABOVE the draft row — attachments, a reply-to banner, a model picker. */
1426
+ header?: React.ReactNode;
1427
+ /** Slot at the inline START of the draft row — an attach button, an avatar. */
1428
+ prefix?: React.ReactNode;
1429
+ /** Slot BELOW the draft row — a hint line, a token counter. */
1430
+ footer?: React.ReactNode;
1431
+ /** Extra trailing actions, rendered BEFORE the send/cancel action. */
1432
+ actions?: React.ReactNode;
1433
+ /** Control height tier on the shared `--control-height` ladder. Default `md`. */
1434
+ size?: SizeProp;
1435
+ /** Hard ceiling on the draft length, forwarded to the textarea. */
1436
+ maxLength?: number;
1437
+ /** Validation state the composer paints. `error` also reports `aria-invalid`. */
1438
+ status?: ControlStatusProp;
1439
+ /** Accessible name override for the send action (localized default otherwise). */
1440
+ submitLabel?: string;
1441
+ /** Accessible name override for the cancel action (localized default otherwise). */
1442
+ cancelLabel?: string;
1443
+ /** Keydown on the draft box — how `ChatSuggestion` drives its list from the composer. */
1444
+ onKeyDown?: React.KeyboardEventHandler<HTMLTextAreaElement>;
1445
+ name?: NameProp;
1446
+ id?: IdProp;
1447
+ className?: ClassNameProp;
1448
+ };
1449
+ /** @see ChatSuggestion — one row of the trigger-character list. One level of `children` is honoured. */
1450
+ export type ChatSuggestionItemProp = {
1451
+ /** Stable value handed to `onValueChange`, and the search haystack when there is no `label`. */
1452
+ value: string;
1453
+ /** Human row label. Falls back to `value`. */
1454
+ label?: string;
1455
+ /** Secondary line under the label. */
1456
+ description?: string;
1457
+ /** Decorative leading node (an icon). */
1458
+ icon?: React.ReactNode;
1459
+ /** Keep the row visible but unselectable. */
1460
+ disabled?: DisabledProp;
1461
+ /** One level of sub-rows; picking the parent drills into them instead of emitting a value. */
1462
+ children?: readonly ChatSuggestionItemProp[];
1463
+ };
1464
+ /** The render-prop bag `ChatSuggestion` hands to the composer it wraps. */
1465
+ export type ChatSuggestionRenderProp = {
1466
+ /**
1467
+ * Re-read the caret and decide whether the list belongs open — call it from the composer's
1468
+ * `onValueChange`. `false` force-closes; no argument force-opens with an empty query.
1469
+ */
1470
+ onTrigger: (value?: string | false) => void;
1471
+ /** Forward to the composer's `onKeyDown`: arrows move, Enter/Tab pick, Escape closes. */
1472
+ onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement>;
1473
+ };
1474
+ /**
1475
+ * @see ChatSuggestion — trigger-character autocomplete over a `ChatComposer` (Ant Design X
1476
+ * `Suggestion`).
1477
+ *
1478
+ * The list itself is the existing `Command` (cmdk) inside a `Popover`, so the listbox ARIA comes
1479
+ * from a primitive that already ships it. What this component owns is the part `Command` does not:
1480
+ * spotting the trigger character at the caret in a textarea, tracking the query as the caret
1481
+ * moves, and closing on `Escape` / blur / a word break — `Escape` returns focus to the textarea
1482
+ * and leaves the typed text exactly as it was.
1483
+ */
1484
+ export type ChatSuggestionProp = {
1485
+ /** The rows to offer. One level of `children` is honoured. */
1486
+ items: readonly ChatSuggestionItemProp[];
1487
+ /** Fires with the picked row's `value`. The caller owns what that does to the draft text. */
1488
+ onValueChange?: OnValueChangeProp<string>;
1489
+ /** The character that opens the list when typed at a word boundary. Default `/`. */
1490
+ triggerCharacter?: string;
1491
+ /** Controlled open state. */
1492
+ open?: OpenProp;
1493
+ /** Uncontrolled initial open state. */
1494
+ defaultOpen?: DefaultOpenProp;
1495
+ /** Open-state change handler. */
1496
+ onOpenChange?: OnOpenChangeProp;
1497
+ /** The composer to wrap — receives `{ onTrigger, onKeyDown }`. */
1498
+ children: (props: ChatSuggestionRenderProp) => React.ReactNode;
1499
+ /** Text shown when the query matches nothing (localized default otherwise). */
1500
+ emptyMessage?: EmptyMessageProp;
1501
+ /** Accessible name of the suggestion listbox — a plain string (localized default otherwise). */
1502
+ listLabel?: string;
1503
+ id?: IdProp;
1504
+ className?: ClassNameProp;
1505
+ };