@godxjp/chat 32.0.0 → 32.0.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.
@@ -0,0 +1,24 @@
1
+ import { A as AttachmentsOverflowProp, a as AttachmentsSemanticProp, c as ClassNameProp } from './data-entry.prop-D4jLTqUk.js';
2
+ import * as _godxjp_ui_data_entry from '@godxjp/ui/data-entry';
3
+ import * as React from 'react';
4
+ import { AttachmentsItemProp, AttachmentsPlaceholderProp, AttachmentsRefProp } from '@godxjp/ui/props/components';
5
+ export { AttachmentsItemProp, AttachmentsOverflowProp, AttachmentsPlaceholderProp, AttachmentsProp, AttachmentsProp as AttachmentsProps, AttachmentsRefProp } from '@godxjp/ui/props/components';
6
+
7
+ declare const Attachments: React.ForwardRefExoticComponent<Omit<_godxjp_ui_data_entry.UploadProps, "value" | "defaultValue" | "onValueChange" | "variant" | "showUploadList" | "children" | "onRemove"> & {
8
+ items?: readonly AttachmentsItemProp[];
9
+ onChange?: (info: {
10
+ file: AttachmentsItemProp;
11
+ fileList: AttachmentsItemProp[];
12
+ }) => void;
13
+ onRemove?: (item: AttachmentsItemProp) => boolean | void | Promise<boolean | void>;
14
+ overflow?: AttachmentsOverflowProp;
15
+ placeholder?: AttachmentsPlaceholderProp | ((type: "inline" | "drop") => AttachmentsPlaceholderProp);
16
+ getDropContainer?: (() => HTMLElement | null | undefined) | null;
17
+ classNames?: Partial<Record<AttachmentsSemanticProp, string>>;
18
+ styles?: Partial<Record<AttachmentsSemanticProp, React.CSSProperties>>;
19
+ rootClassName?: ClassNameProp;
20
+ children?: React.ReactElement;
21
+ imageProps?: Record<string, unknown>;
22
+ } & React.RefAttributes<AttachmentsRefProp>>;
23
+
24
+ export { Attachments };
@@ -0,0 +1,8 @@
1
+ "use client";
2
+ "use client";
3
+ import {
4
+ Attachments
5
+ } from "./chunk-PGCRFJHC.js";
6
+ export {
7
+ Attachments
8
+ };
@@ -0,0 +1,33 @@
1
+ import * as React from 'react';
2
+ import { ChatBubbleProp, ChatBubbleListProp } from '@godxjp/ui/props/components';
3
+ export { ChatBubbleListProp, ChatBubblePlacementProp, ChatBubbleProp, ChatBubbleToneProp, ChatBubbleTypingProp, ChatBubbleVariantProp, ChatMessageProp } from '@godxjp/ui/props/components';
4
+
5
+ type ChatBubbleProps = ChatBubbleProp & Omit<React.HTMLAttributes<HTMLElement>, keyof ChatBubbleProp>;
6
+ /**
7
+ * ChatBubble — ONE message in a conversation: an optional author mark, a header naming the turn,
8
+ * the message body, and a footer for timestamps or per-message actions.
9
+ *
10
+ * It is an `<article>` inside the feed's `role="log"`, and it deliberately owns NO live region of
11
+ * its own: one live region per bubble floods a screen reader on every token of a stream. The feed
12
+ * (`ChatBubbleList`) owns the single `aria-live="polite"` region; a bubble only ever says "I am
13
+ * still being produced" through `aria-busy`.
14
+ */
15
+ declare const ChatBubble: React.ForwardRefExoticComponent<ChatBubbleProp & Omit<React.HTMLAttributes<HTMLElement>, keyof ChatBubbleProp> & React.RefAttributes<HTMLElement>>;
16
+ type ChatBubbleListProps = ChatBubbleListProp & Omit<React.HTMLAttributes<HTMLDivElement>, keyof ChatBubbleListProp>;
17
+ /**
18
+ * ChatBubbleList — the message feed.
19
+ *
20
+ * The behaviour that earns this a component rather than a `ScrollArea` + `map` is STICK-TO-BOTTOM:
21
+ * new messages keep the viewport pinned only while the reader is ALREADY at the bottom. The moment
22
+ * they scroll up to re-read something the pin is revoked and stays revoked until they ask for it
23
+ * back — silently yanking a reader to the newest message mid-sentence is a change of context they
24
+ * did not request (WCAG 3.2.5), and it is the classic chat-UI defect. The way back is a real,
25
+ * focusable button that also says how many messages arrived while they were away.
26
+ *
27
+ * GIVE IT A DEFINITE HEIGHT — `className="h-96"`, or a flex/grid parent that hands it a track.
28
+ * The feed scrolls INSIDE itself; with no height it simply grows to its content, nothing ever
29
+ * overflows, and every behaviour above has nothing to act on.
30
+ */
31
+ declare const ChatBubbleList: React.ForwardRefExoticComponent<ChatBubbleListProp & Omit<React.HTMLAttributes<HTMLDivElement>, keyof ChatBubbleListProp> & React.RefAttributes<HTMLDivElement>>;
32
+
33
+ export { ChatBubble, ChatBubbleList, type ChatBubbleListProps, type ChatBubbleProps };
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import {
3
+ ChatBubble,
4
+ ChatBubbleList
5
+ } from "./chunk-XCTRKZCF.js";
6
+ export {
7
+ ChatBubble,
8
+ ChatBubbleList
9
+ };
@@ -0,0 +1,71 @@
1
+ import { V as ValueProp, D as DefaultValueProp, O as OnValueChangeProp, P as PendingProp, b as ChatComposerSubmitTypeProp, f as PlaceholderProp, e as DisabledProp, C as ChatComposerFooterProp, S as SizeProp, d as ControlStatusProp, N as NameProp, I as IdProp, c as ClassNameProp } from './data-entry.prop-D4jLTqUk.js';
2
+ import * as React from 'react';
3
+ export { ChatComposerActionComponents, ChatComposerFooterProp, ChatComposerProp, ChatComposerProp as ChatComposerProps, ChatComposerSubmitTypeProp } from '@godxjp/ui/props/components';
4
+
5
+ /**
6
+ * The accessible-name / description / validation contract that {@link FormField} injects onto its
7
+ * single control child (via `cloneElement`). EVERY form-capable `@godxjp/ui` component accepts
8
+ * these props and forwards them to its **semantic focus target** — the real `<input>` / combobox /
9
+ * trigger the user tabs to, never a presentational wrapper `<div>`.
10
+ */
11
+ interface FieldA11yProps {
12
+ "aria-label"?: string;
13
+ "aria-labelledby"?: string;
14
+ "aria-describedby"?: string;
15
+ "aria-errormessage"?: string;
16
+ "aria-invalid"?: React.AriaAttributes["aria-invalid"];
17
+ "aria-required"?: React.AriaAttributes["aria-required"];
18
+ "data-field"?: string;
19
+ }
20
+
21
+ /**
22
+ * ChatComposer — the message input of a conversation (Ant Design X `Sender`).
23
+ *
24
+ * ## Why a component and not a composition
25
+ *
26
+ * Three behaviours live here and nowhere else, and every app that hand-rolls a composer gets at
27
+ * least one of them wrong:
28
+ *
29
+ * 1. **An IME conversion is not a message.** Between `compositionstart` and `compositionend` the
30
+ * text in the box is a CANDIDATE being converted, and the `Enter` that accepts it belongs to
31
+ * the IME. A composer that reads that `Enter` as "send" makes Japanese and Vietnamese input
32
+ * impossible — the first kanji conversion sends a half-written line. The guard is a ref, not
33
+ * state, because it has to be readable inside the keydown that fires between the two events.
34
+ * 2. **One trailing action at a time.** While a response streams the send button IS the cancel
35
+ * button — the same discipline as the picker trailing-action rule. Rendering both is how a
36
+ * user cancels by aiming for send.
37
+ * 3. **The draft box is the focus target.** `ref`, `id`, `name` and the whole `FormField`
38
+ * label/helper/error contract land on the `<textarea>`, not on the frame, so a composer inside
39
+ * a `FormField` is labelled and described exactly like an `Input`.
40
+ *
41
+ * Everything visible is a real primitive: the draft box is `Textarea` (`variant="borderless"`, so
42
+ * the frame is the only boundary) and every action is a `Button`.
43
+ */
44
+ declare const ChatComposer: React.ForwardRefExoticComponent<Omit<React.HTMLAttributes<HTMLDivElement>, "defaultValue" | "prefix" | "onChange" | "onSubmit" | "onKeyDown"> & FieldA11yProps & {
45
+ value?: ValueProp<string>;
46
+ defaultValue?: DefaultValueProp<string>;
47
+ onValueChange?: OnValueChangeProp<string>;
48
+ onSubmit?: (value: string) => void;
49
+ onCancel?: () => void;
50
+ loading?: PendingProp;
51
+ submitType?: ChatComposerSubmitTypeProp;
52
+ allowEmptySubmit?: boolean;
53
+ placeholder?: PlaceholderProp;
54
+ disabled?: DisabledProp;
55
+ readOnly?: boolean;
56
+ header?: React.ReactNode;
57
+ prefix?: React.ReactNode;
58
+ footer?: ChatComposerFooterProp;
59
+ actions?: React.ReactNode | false;
60
+ size?: SizeProp;
61
+ maxLength?: number;
62
+ status?: ControlStatusProp;
63
+ submitLabel?: string;
64
+ cancelLabel?: string;
65
+ onKeyDown?: React.KeyboardEventHandler<HTMLTextAreaElement>;
66
+ name?: NameProp;
67
+ id?: IdProp;
68
+ className?: ClassNameProp;
69
+ } & React.RefAttributes<HTMLTextAreaElement>>;
70
+
71
+ export { ChatComposer };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import {
3
+ ChatComposer
4
+ } from "./chunk-4K5JWTV5.js";
5
+ export {
6
+ ChatComposer
7
+ };
@@ -0,0 +1,31 @@
1
+ import * as React from 'react';
2
+ import { ChatSuggestionProp } from '@godxjp/ui/props/components';
3
+ export { ChatSuggestionItemProp, ChatSuggestionProp, ChatSuggestionProp as ChatSuggestionProps, ChatSuggestionRenderProp } from '@godxjp/ui/props/components';
4
+
5
+ /**
6
+ * ChatSuggestion — trigger-character autocomplete over a `ChatComposer` (Ant Design X
7
+ * `Suggestion`).
8
+ *
9
+ * ## What it owns, and what it deliberately does not
10
+ *
11
+ * The list is the existing `Command` (cmdk) inside a `Popover`: the listbox/option roles, the
12
+ * active-row bookkeeping and the scroll-into-view all come from a primitive that already ships
13
+ * them, so there is no hand-rolled listbox here. What this component owns is the part `Command`
14
+ * cannot know about — a `<textarea>` it does not render:
15
+ *
16
+ * - **Detecting the trigger at the CARET**, not merely anywhere in the text, so a `/` in the
17
+ * middle of a URL opens nothing and a caret moved out of a token closes the list.
18
+ * - **Driving the list from a box that keeps focus.** Focus never leaves the textarea (the popover
19
+ * is opened with `onOpenAutoFocus` prevented), so arrows/Enter are forwarded through the render
20
+ * prop and the active row is announced through `aria-activedescendant` — the APG combobox
21
+ * pattern, rather than moving focus into the panel and stranding the draft.
22
+ * - **`Escape` costs nothing.** It closes the list, returns focus to the textarea and leaves the
23
+ * typed text exactly as it was; a suggestion list that eats the draft on dismiss is the defect
24
+ * this guards.
25
+ */
26
+ declare function ChatSuggestion({ items, onValueChange, triggerCharacter, open, defaultOpen, onOpenChange, children, emptyMessage, listLabel: listLabelProp, id, className, allowSpaces, terminator, shouldFilter, onQueryChange, }: ChatSuggestionProp): React.JSX.Element;
27
+ declare namespace ChatSuggestion {
28
+ var displayName: string;
29
+ }
30
+
31
+ export { ChatSuggestion };
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import {
3
+ ChatSuggestion
4
+ } from "./chunk-DEYHFJBA.js";
5
+ export {
6
+ ChatSuggestion
7
+ };
@@ -0,0 +1,203 @@
1
+ "use client";
2
+
3
+ // src/chat-composer.tsx
4
+ import * as React from "react";
5
+ import { SendHorizontal, Square } from "lucide-react";
6
+ import { useTranslation } from "@godxjp/ui/i18n";
7
+ import { cn } from "@godxjp/ui/lib/utils";
8
+ import { isApplePlatform } from "@godxjp/ui/lib/utils";
9
+ import { omitFieldA11y, pickFieldA11y, useFieldIdentity } from "@godxjp/ui/data-entry";
10
+ import { Button } from "@godxjp/ui/general";
11
+ import { Textarea } from "@godxjp/ui/data-entry";
12
+ import { controlSurfaceAttrs, resolveAriaInvalid } from "@godxjp/ui/data-entry";
13
+ import { isImeComposing } from "@godxjp/ui/data-entry";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ function isSendable(text) {
16
+ return text.trim().length > 0;
17
+ }
18
+ var ChatComposer = React.forwardRef(
19
+ ({
20
+ className,
21
+ id,
22
+ name,
23
+ value,
24
+ defaultValue,
25
+ onValueChange,
26
+ onSubmit,
27
+ onCancel,
28
+ loading = false,
29
+ submitType = "enter",
30
+ allowEmptySubmit = false,
31
+ placeholder,
32
+ disabled = false,
33
+ readOnly = false,
34
+ header,
35
+ prefix,
36
+ footer,
37
+ actions,
38
+ size,
39
+ maxLength,
40
+ status,
41
+ submitLabel,
42
+ cancelLabel,
43
+ onKeyDown,
44
+ ...props
45
+ }, ref) => {
46
+ const { t } = useTranslation();
47
+ const innerRef = React.useRef(null);
48
+ const setRefs = React.useCallback(
49
+ (node) => {
50
+ innerRef.current = node;
51
+ if (typeof ref === "function") ref(node);
52
+ else if (ref) ref.current = node;
53
+ },
54
+ [ref]
55
+ );
56
+ const [draft, setDraft] = React.useState(() => String(value ?? defaultValue ?? ""));
57
+ React.useEffect(() => {
58
+ if (value !== void 0) setDraft(String(value));
59
+ }, [value]);
60
+ const composing = React.useRef(false);
61
+ const fieldA11y = pickFieldA11y(props);
62
+ const rest = omitFieldA11y(props);
63
+ const identity = useFieldIdentity({ id, name, "data-field": props["data-field"] });
64
+ const surface = controlSurfaceAttrs({ status, size });
65
+ const canSubmit = (allowEmptySubmit || isSendable(draft)) && !disabled && !readOnly && !loading;
66
+ const submit = React.useCallback(() => {
67
+ if (disabled || readOnly || loading) return;
68
+ const text = innerRef.current?.value ?? draft;
69
+ if (isSendable(text)) onSubmit?.(text);
70
+ else if (allowEmptySubmit) onSubmit?.("");
71
+ }, [draft, disabled, readOnly, loading, allowEmptySubmit, onSubmit]);
72
+ const handleKeyDown = (event) => {
73
+ onKeyDown?.(event);
74
+ if (event.defaultPrevented) return;
75
+ if (event.key !== "Enter") return;
76
+ if (composing.current || isImeComposing(event)) return;
77
+ const wantsSend = submitType === "modEnter" ? (
78
+ // ⌘ on Apple platforms, Ctrl everywhere else — the other one is left alone, because
79
+ // Ctrl+Enter on a Mac is not the convention a Mac user reaches for.
80
+ isApplePlatform() ? event.metaKey : event.ctrlKey
81
+ ) : submitType === "enter" ? !event.shiftKey : event.shiftKey;
82
+ if (!wantsSend) return;
83
+ event.preventDefault();
84
+ submit();
85
+ };
86
+ const sendName = submitLabel ?? t("dataEntry.chatComposer.send");
87
+ const cancelName = cancelLabel ?? t("dataEntry.chatComposer.cancel");
88
+ const actionSize = size === "xs" ? "icon-xs" : size === "sm" ? "icon-sm" : size === "lg" ? "icon-lg" : "icon";
89
+ const liveRef = React.useRef({
90
+ actionSize,
91
+ sendName,
92
+ cancelName,
93
+ submit,
94
+ onCancel,
95
+ canSubmit,
96
+ disabled
97
+ });
98
+ liveRef.current = { actionSize, sendName, cancelName, submit, onCancel, canSubmit, disabled };
99
+ const SubmitButton = React.useMemo(
100
+ () => function SubmitButton2(buttonProps) {
101
+ const live = liveRef.current;
102
+ return /* @__PURE__ */ jsx(
103
+ Button,
104
+ {
105
+ type: "button",
106
+ size: live.actionSize,
107
+ "aria-label": live.sendName,
108
+ onClick: live.submit,
109
+ disabled: !live.canSubmit,
110
+ ...buttonProps,
111
+ children: buttonProps.children ?? /* @__PURE__ */ jsx(SendHorizontal, { "aria-hidden": "true" })
112
+ }
113
+ );
114
+ },
115
+ []
116
+ );
117
+ const CancelButton = React.useMemo(
118
+ () => function CancelButton2(buttonProps) {
119
+ const live = liveRef.current;
120
+ return /* @__PURE__ */ jsx(
121
+ Button,
122
+ {
123
+ type: "button",
124
+ size: live.actionSize,
125
+ variant: "secondary",
126
+ "aria-label": live.cancelName,
127
+ onClick: live.onCancel,
128
+ disabled: live.disabled,
129
+ ...buttonProps,
130
+ children: buttonProps.children ?? /* @__PURE__ */ jsx(Square, { "aria-hidden": "true" })
131
+ }
132
+ );
133
+ },
134
+ []
135
+ );
136
+ const actionComponents = React.useMemo(
137
+ () => ({ SubmitButton, CancelButton }),
138
+ [SubmitButton, CancelButton]
139
+ );
140
+ const footerContent = typeof footer === "function" ? footer({ components: actionComponents }) : footer;
141
+ return /* @__PURE__ */ jsxs(
142
+ "div",
143
+ {
144
+ "data-slot": "chat-composer",
145
+ "data-disabled": disabled ? "" : void 0,
146
+ "data-loading": loading ? "" : void 0,
147
+ "data-submit-type": submitType,
148
+ "aria-busy": loading || void 0,
149
+ className: cn("ui-chat-composer ui-control-surface", className),
150
+ ...rest,
151
+ ...surface,
152
+ children: [
153
+ header ? /* @__PURE__ */ jsx("div", { "data-slot": "chat-composer-header", className: "ui-chat-composer-header", children: header }) : null,
154
+ /* @__PURE__ */ jsxs("div", { "data-slot": "chat-composer-row", className: "ui-chat-composer-row", children: [
155
+ prefix ? /* @__PURE__ */ jsx("div", { "data-slot": "chat-composer-prefix", className: "ui-chat-composer-prefix", children: prefix }) : null,
156
+ /* @__PURE__ */ jsx(
157
+ Textarea,
158
+ {
159
+ ref: setRefs,
160
+ id,
161
+ name,
162
+ variant: "borderless",
163
+ autoGrow: true,
164
+ className: "ui-chat-composer-field",
165
+ value,
166
+ defaultValue,
167
+ placeholder: placeholder ?? t("dataEntry.chatComposer.placeholder"),
168
+ disabled,
169
+ readOnly,
170
+ maxLength,
171
+ status,
172
+ "aria-invalid": resolveAriaInvalid(fieldA11y["aria-invalid"], status),
173
+ onValueChange: (next) => {
174
+ if (value === void 0) setDraft(next);
175
+ onValueChange?.(next);
176
+ },
177
+ onKeyDown: handleKeyDown,
178
+ onCompositionStart: () => {
179
+ composing.current = true;
180
+ },
181
+ onCompositionEnd: () => {
182
+ composing.current = false;
183
+ },
184
+ ...fieldA11y,
185
+ ...identity
186
+ }
187
+ ),
188
+ actions === false ? null : /* @__PURE__ */ jsxs("div", { "data-slot": "chat-composer-actions", className: "ui-chat-composer-actions", children: [
189
+ actions,
190
+ loading ? /* @__PURE__ */ jsx(CancelButton, {}) : /* @__PURE__ */ jsx(SubmitButton, {})
191
+ ] })
192
+ ] }),
193
+ footerContent ? /* @__PURE__ */ jsx("div", { "data-slot": "chat-composer-footer", className: "ui-chat-composer-footer", children: footerContent }) : null
194
+ ]
195
+ }
196
+ );
197
+ }
198
+ );
199
+ ChatComposer.displayName = "ChatComposer";
200
+
201
+ export {
202
+ ChatComposer
203
+ };
@@ -0,0 +1,180 @@
1
+ "use client";
2
+
3
+ // src/thought-chain.tsx
4
+ import * as React from "react";
5
+ import { ChevronRight, CircleCheck, CircleMinus, CircleX, Loader2 } from "lucide-react";
6
+ import { useTranslation } from "@godxjp/ui/i18n";
7
+ import { cn } from "@godxjp/ui/lib/utils";
8
+ import { VisuallyHidden } from "@godxjp/ui/general";
9
+ import { Text } from "@godxjp/ui/general";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ var STATUS_GLYPH = {
12
+ loading: /* @__PURE__ */ jsx(Loader2, { "aria-hidden": "true", className: "ui-thought-chain-spinner" }),
13
+ success: /* @__PURE__ */ jsx(CircleCheck, { "aria-hidden": "true" }),
14
+ error: /* @__PURE__ */ jsx(CircleX, { "aria-hidden": "true" }),
15
+ abort: /* @__PURE__ */ jsx(CircleMinus, { "aria-hidden": "true" })
16
+ };
17
+ function StatusMark({
18
+ status,
19
+ fallback
20
+ }) {
21
+ const { t } = useTranslation();
22
+ if (!status) return /* @__PURE__ */ jsx(Fragment, { children: fallback });
23
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
24
+ STATUS_GLYPH[status],
25
+ /* @__PURE__ */ jsx(VisuallyHidden, { children: t(`dataDisplay.thoughtChain.status.${status}`) })
26
+ ] });
27
+ }
28
+ var ThoughtChain = React.forwardRef(
29
+ ({
30
+ items = [],
31
+ defaultExpandedKeys,
32
+ expandedKeys,
33
+ onExpand,
34
+ line = true,
35
+ label,
36
+ id,
37
+ className,
38
+ ...rest
39
+ }, ref) => {
40
+ const { t } = useTranslation();
41
+ const reactId = React.useId();
42
+ const [uncontrolled, setUncontrolled] = React.useState(() => [
43
+ ...defaultExpandedKeys ?? []
44
+ ]);
45
+ const open = expandedKeys ? [...expandedKeys] : uncontrolled;
46
+ const toggle = (key) => {
47
+ const next = open.includes(key) ? open.filter((k) => k !== key) : [...open, key];
48
+ if (!expandedKeys) setUncontrolled(next);
49
+ onExpand?.(next);
50
+ };
51
+ return /* @__PURE__ */ jsx(
52
+ "ol",
53
+ {
54
+ ref,
55
+ id,
56
+ "aria-label": label ?? t("dataDisplay.thoughtChain.label"),
57
+ "data-slot": "thought-chain",
58
+ "data-line": line === false ? "none" : line === true ? "solid" : line,
59
+ className: cn("ui-thought-chain", className),
60
+ ...rest,
61
+ children: items.map((item, index) => {
62
+ const key = item.key ?? `${reactId}-${index}`;
63
+ const bodyId = `${reactId}-body-${index}`;
64
+ const collapsible = Boolean(item.collapsible && item.content);
65
+ const expanded = collapsible ? open.includes(key) : true;
66
+ const glyph = item.icon === false ? null : item.icon ?? index + 1;
67
+ return /* @__PURE__ */ jsxs(
68
+ "li",
69
+ {
70
+ "data-slot": "thought-chain-node",
71
+ "data-status": item.status,
72
+ className: "ui-thought-chain-node",
73
+ children: [
74
+ glyph === null ? null : /* @__PURE__ */ jsx(
75
+ "span",
76
+ {
77
+ "data-slot": "thought-chain-icon",
78
+ "data-status": item.status,
79
+ className: "ui-thought-chain-icon",
80
+ children: /* @__PURE__ */ jsx(StatusMark, { status: item.status, fallback: glyph })
81
+ }
82
+ ),
83
+ /* @__PURE__ */ jsxs("div", { className: "ui-thought-chain-body", children: [
84
+ /* @__PURE__ */ jsxs("div", { className: "ui-thought-chain-header", children: [
85
+ collapsible ? /* @__PURE__ */ jsxs(
86
+ "button",
87
+ {
88
+ type: "button",
89
+ "aria-expanded": expanded,
90
+ "aria-controls": bodyId,
91
+ "data-blink": item.blink ? "" : void 0,
92
+ className: "ui-thought-chain-trigger",
93
+ onClick: () => toggle(key),
94
+ children: [
95
+ /* @__PURE__ */ jsx(
96
+ ChevronRight,
97
+ {
98
+ "aria-hidden": "true",
99
+ "data-expanded": expanded ? "true" : void 0,
100
+ className: "ui-thought-chain-chevron"
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(Text, { weight: "medium", children: item.title })
104
+ ]
105
+ }
106
+ ) : /* @__PURE__ */ jsx(Text, { weight: "medium", "data-blink": item.blink ? "" : void 0, children: item.title }),
107
+ item.description ? /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", children: item.description }) : null
108
+ ] }),
109
+ item.content && (expanded || item.destroyOnHidden === false) ? /* @__PURE__ */ jsx(
110
+ "div",
111
+ {
112
+ id: bodyId,
113
+ hidden: !expanded,
114
+ "data-blink": item.blink ? "" : void 0,
115
+ className: "ui-thought-chain-content",
116
+ children: item.content
117
+ }
118
+ ) : null,
119
+ item.footer ? /* @__PURE__ */ jsx("div", { className: "ui-thought-chain-footer", children: item.footer }) : null
120
+ ] })
121
+ ]
122
+ },
123
+ key
124
+ );
125
+ })
126
+ }
127
+ );
128
+ }
129
+ );
130
+ ThoughtChain.displayName = "ThoughtChain";
131
+ var ThoughtChainItem = React.forwardRef(
132
+ ({
133
+ title,
134
+ description,
135
+ icon,
136
+ status,
137
+ variant = "solid",
138
+ blink,
139
+ disabled,
140
+ onClick,
141
+ id,
142
+ className
143
+ }, ref) => {
144
+ const shared = {
145
+ id,
146
+ "data-slot": "thought-chain-item",
147
+ "data-variant": variant,
148
+ "data-status": status,
149
+ "data-blink": blink ? "" : void 0,
150
+ className: cn("ui-thought-chain-item", className)
151
+ };
152
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
153
+ status || icon ? /* @__PURE__ */ jsx("span", { "data-status": status, className: "ui-thought-chain-icon", children: /* @__PURE__ */ jsx(StatusMark, { status, fallback: icon }) }) : null,
154
+ /* @__PURE__ */ jsxs("span", { className: "ui-thought-chain-item-text", children: [
155
+ title ? /* @__PURE__ */ jsx(Text, { weight: "medium", children: title }) : null,
156
+ description ? /* @__PURE__ */ jsx(Text, { size: "xs", tone: "muted", children: description }) : null
157
+ ] })
158
+ ] });
159
+ if (!onClick) {
160
+ return /* @__PURE__ */ jsx("div", { ref, ...shared, children: inner });
161
+ }
162
+ return /* @__PURE__ */ jsx(
163
+ "button",
164
+ {
165
+ ref,
166
+ type: "button",
167
+ disabled,
168
+ onClick,
169
+ ...shared,
170
+ children: inner
171
+ }
172
+ );
173
+ }
174
+ );
175
+ ThoughtChainItem.displayName = "ThoughtChainItem";
176
+
177
+ export {
178
+ ThoughtChain,
179
+ ThoughtChainItem
180
+ };