@duskmoon-dev/components 0.4.1 → 0.5.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.
- package/dist/components/chat/Chat.types.d.ts +75 -1
- package/dist/components/chat/ChatComposer.d.ts +4 -0
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.js +1098 -25
- package/dist/generated/version.d.ts +1 -1
- package/dist/index.js +8094 -7666
- package/dist/infrastructure.js +1 -1
- package/dist/styles.css +92 -0
- package/package.json +1 -1
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
// src/components/chat/Chat.tsx
|
|
4
|
-
import { forwardRef } from "react";
|
|
4
|
+
import { forwardRef as forwardRef3 } from "react";
|
|
5
|
+
|
|
6
|
+
// src/components/chat/ChatComposer.tsx
|
|
7
|
+
import {
|
|
8
|
+
forwardRef as forwardRef2,
|
|
9
|
+
useEffect,
|
|
10
|
+
useId,
|
|
11
|
+
useLayoutEffect,
|
|
12
|
+
useRef as useRef2,
|
|
13
|
+
useState as useState2
|
|
14
|
+
} from "react";
|
|
15
|
+
|
|
16
|
+
// src/components/button/Button.tsx
|
|
17
|
+
import React from "react";
|
|
18
|
+
import { createPortal } from "react-dom";
|
|
5
19
|
|
|
6
20
|
// ../../node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
|
|
7
21
|
function r(e) {
|
|
@@ -28,6 +42,1063 @@ function clsx() {
|
|
|
28
42
|
function cn(...inputs) {
|
|
29
43
|
return clsx(inputs);
|
|
30
44
|
}
|
|
45
|
+
// src/classes/button.ts
|
|
46
|
+
var buttonBaseClass = "btn";
|
|
47
|
+
var buttonColorClasses = {
|
|
48
|
+
primary: "btn-primary",
|
|
49
|
+
secondary: "btn-secondary",
|
|
50
|
+
tertiary: "btn-tertiary",
|
|
51
|
+
accent: "btn-accent",
|
|
52
|
+
neutral: "btn-neutral",
|
|
53
|
+
base: "btn-base",
|
|
54
|
+
info: "btn-info",
|
|
55
|
+
success: "btn-success",
|
|
56
|
+
warning: "btn-warning",
|
|
57
|
+
error: "btn-error"
|
|
58
|
+
};
|
|
59
|
+
var buttonAppearanceClasses = {
|
|
60
|
+
filled: "",
|
|
61
|
+
outline: "btn-outline",
|
|
62
|
+
tonal: "btn-tonal",
|
|
63
|
+
ghost: "btn-ghost",
|
|
64
|
+
text: "btn-text"
|
|
65
|
+
};
|
|
66
|
+
var buttonShapeClasses = {
|
|
67
|
+
rect: "",
|
|
68
|
+
circle: "btn-circle btn-icon",
|
|
69
|
+
square: "btn-square"
|
|
70
|
+
};
|
|
71
|
+
var buttonSizeClasses = {
|
|
72
|
+
xs: "btn-xs",
|
|
73
|
+
sm: "btn-sm",
|
|
74
|
+
md: "btn-md",
|
|
75
|
+
lg: "btn-lg"
|
|
76
|
+
};
|
|
77
|
+
var buttonBlockClass = "btn-block";
|
|
78
|
+
var buttonIsLoadingClass = "btn-loading";
|
|
79
|
+
function getButtonClasses({
|
|
80
|
+
color = "primary",
|
|
81
|
+
appearance = "filled",
|
|
82
|
+
shape = "rect",
|
|
83
|
+
size = "md",
|
|
84
|
+
block,
|
|
85
|
+
isLoading,
|
|
86
|
+
className
|
|
87
|
+
}) {
|
|
88
|
+
return cn(buttonBaseClass, buttonColorClasses[color], buttonAppearanceClasses[appearance], buttonShapeClasses[shape], buttonSizeClasses[size], shape === "circle" && size === "xs" && "btn-icon-sm", shape === "circle" && size === "sm" && "btn-icon-sm", shape === "circle" && size === "lg" && "btn-icon-lg", block && buttonBlockClass, isLoading && buttonIsLoadingClass, className);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// src/components/button/Button.tsx
|
|
92
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
93
|
+
function isPromiseLike(value) {
|
|
94
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
95
|
+
}
|
|
96
|
+
function Button({
|
|
97
|
+
color = "primary",
|
|
98
|
+
appearance = "filled",
|
|
99
|
+
shape = "rect",
|
|
100
|
+
size = "md",
|
|
101
|
+
block,
|
|
102
|
+
isLoading,
|
|
103
|
+
leftIcon,
|
|
104
|
+
rightIcon,
|
|
105
|
+
className,
|
|
106
|
+
children,
|
|
107
|
+
disabled,
|
|
108
|
+
ref,
|
|
109
|
+
confirm,
|
|
110
|
+
onClick,
|
|
111
|
+
...props
|
|
112
|
+
}) {
|
|
113
|
+
const buttonRef = React.useRef(null);
|
|
114
|
+
const popoverRef = React.useRef(null);
|
|
115
|
+
const cancelRef = React.useRef(null);
|
|
116
|
+
const approvalRef = React.useRef("idle");
|
|
117
|
+
const pendingRef = React.useRef(false);
|
|
118
|
+
const actionRef = React.useRef(0);
|
|
119
|
+
const operationRef = React.useRef(0);
|
|
120
|
+
const mountedRef = React.useRef(false);
|
|
121
|
+
const [portalTarget, setPortalTarget] = React.useState(null);
|
|
122
|
+
const [confirmLoading, setConfirmLoading] = React.useState(false);
|
|
123
|
+
const [confirmError, setConfirmError] = React.useState(false);
|
|
124
|
+
const generatedId = React.useId().replace(/:/g, "");
|
|
125
|
+
const popoverId = `button-confirm-${generatedId}`;
|
|
126
|
+
const titleId = `${popoverId}-title`;
|
|
127
|
+
const descriptionId = `${popoverId}-description`;
|
|
128
|
+
const anchor = `--${popoverId}-anchor`;
|
|
129
|
+
const hasConfirm = Boolean(confirm);
|
|
130
|
+
const confirmation = confirm && typeof confirm === "object" ? confirm : undefined;
|
|
131
|
+
const title = confirmation ? confirmation.title : "Confirm action";
|
|
132
|
+
const hasTitle = title !== undefined && title !== null && title !== false && title !== "";
|
|
133
|
+
const content = confirmation?.component ?? confirmation?.message ?? confirmation?.description;
|
|
134
|
+
const hasContent = content !== undefined && content !== null && content !== false && content !== "";
|
|
135
|
+
const mergedRef = React.useCallback((node) => {
|
|
136
|
+
buttonRef.current = node;
|
|
137
|
+
if (!node) {
|
|
138
|
+
if (typeof ref === "function")
|
|
139
|
+
ref(null);
|
|
140
|
+
else if (ref)
|
|
141
|
+
ref.current = null;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const cleanup = typeof ref === "function" ? ref(node) : undefined;
|
|
145
|
+
if (ref && typeof ref !== "function")
|
|
146
|
+
ref.current = node;
|
|
147
|
+
return () => {
|
|
148
|
+
buttonRef.current = null;
|
|
149
|
+
if (typeof cleanup === "function")
|
|
150
|
+
cleanup();
|
|
151
|
+
else if (typeof ref === "function")
|
|
152
|
+
ref(null);
|
|
153
|
+
else if (ref)
|
|
154
|
+
ref.current = null;
|
|
155
|
+
};
|
|
156
|
+
}, [ref]);
|
|
157
|
+
React.useEffect(() => {
|
|
158
|
+
mountedRef.current = true;
|
|
159
|
+
return () => {
|
|
160
|
+
mountedRef.current = false;
|
|
161
|
+
operationRef.current += 1;
|
|
162
|
+
};
|
|
163
|
+
}, []);
|
|
164
|
+
React.useEffect(() => {
|
|
165
|
+
operationRef.current += 1;
|
|
166
|
+
approvalRef.current = "idle";
|
|
167
|
+
setConfirmError(false);
|
|
168
|
+
setPortalTarget(hasConfirm ? document.body : null);
|
|
169
|
+
}, [hasConfirm]);
|
|
170
|
+
function closePopover() {
|
|
171
|
+
if (popoverRef.current?.matches(":popover-open")) {
|
|
172
|
+
popoverRef.current.hidePopover();
|
|
173
|
+
}
|
|
174
|
+
buttonRef.current?.focus();
|
|
175
|
+
}
|
|
176
|
+
function handleClick(event) {
|
|
177
|
+
if (!confirm) {
|
|
178
|
+
onClick?.(event);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (approvalRef.current === "native") {
|
|
182
|
+
approvalRef.current = "idle";
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (approvalRef.current === "action") {
|
|
186
|
+
approvalRef.current = "idle";
|
|
187
|
+
let result;
|
|
188
|
+
try {
|
|
189
|
+
result = onClick?.(event);
|
|
190
|
+
} catch {
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
pendingRef.current = false;
|
|
193
|
+
setConfirmError(true);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!isPromiseLike(result)) {
|
|
197
|
+
pendingRef.current = false;
|
|
198
|
+
closePopover();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const userPreventedDefault = event.defaultPrevented;
|
|
202
|
+
event.preventDefault();
|
|
203
|
+
setConfirmLoading(true);
|
|
204
|
+
const action = actionRef.current;
|
|
205
|
+
const operation = operationRef.current;
|
|
206
|
+
Promise.resolve(result).then(() => {
|
|
207
|
+
if (!mountedRef.current || action !== actionRef.current)
|
|
208
|
+
return;
|
|
209
|
+
pendingRef.current = false;
|
|
210
|
+
setConfirmLoading(false);
|
|
211
|
+
if (operation !== operationRef.current)
|
|
212
|
+
return;
|
|
213
|
+
closePopover();
|
|
214
|
+
if (!userPreventedDefault && !buttonRef.current?.disabled) {
|
|
215
|
+
approvalRef.current = "native";
|
|
216
|
+
buttonRef.current?.click();
|
|
217
|
+
approvalRef.current = "idle";
|
|
218
|
+
}
|
|
219
|
+
}, () => {
|
|
220
|
+
if (!mountedRef.current || action !== actionRef.current)
|
|
221
|
+
return;
|
|
222
|
+
pendingRef.current = false;
|
|
223
|
+
setConfirmLoading(false);
|
|
224
|
+
if (operation !== operationRef.current)
|
|
225
|
+
return;
|
|
226
|
+
setConfirmError(true);
|
|
227
|
+
cancelRef.current?.focus();
|
|
228
|
+
});
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
event.preventDefault();
|
|
232
|
+
if (disabled || isLoading || pendingRef.current)
|
|
233
|
+
return;
|
|
234
|
+
if (popoverRef.current?.matches(":popover-open")) {
|
|
235
|
+
closePopover();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
setConfirmError(false);
|
|
239
|
+
popoverRef.current?.showPopover();
|
|
240
|
+
cancelRef.current?.focus();
|
|
241
|
+
}
|
|
242
|
+
function handleConfirm(event) {
|
|
243
|
+
event.preventDefault();
|
|
244
|
+
event.stopPropagation();
|
|
245
|
+
const trigger = buttonRef.current;
|
|
246
|
+
if (disabled || isLoading || pendingRef.current || !trigger || trigger.disabled || !event.currentTarget.matches(":popover-open"))
|
|
247
|
+
return;
|
|
248
|
+
setConfirmError(false);
|
|
249
|
+
pendingRef.current = true;
|
|
250
|
+
actionRef.current += 1;
|
|
251
|
+
approvalRef.current = "action";
|
|
252
|
+
trigger.click();
|
|
253
|
+
if (approvalRef.current === "action") {
|
|
254
|
+
approvalRef.current = "idle";
|
|
255
|
+
pendingRef.current = false;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const classes = getButtonClasses({
|
|
259
|
+
color,
|
|
260
|
+
appearance,
|
|
261
|
+
shape,
|
|
262
|
+
size,
|
|
263
|
+
block,
|
|
264
|
+
isLoading,
|
|
265
|
+
className
|
|
266
|
+
});
|
|
267
|
+
const confirmClasses = getButtonClasses({
|
|
268
|
+
color,
|
|
269
|
+
size: "sm",
|
|
270
|
+
isLoading: confirmLoading
|
|
271
|
+
});
|
|
272
|
+
return /* @__PURE__ */ jsxs(Fragment, {
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ jsxs("button", {
|
|
275
|
+
ref: confirm ? mergedRef : ref,
|
|
276
|
+
className: classes,
|
|
277
|
+
disabled: disabled || isLoading,
|
|
278
|
+
"aria-busy": isLoading,
|
|
279
|
+
"aria-disabled": disabled || isLoading,
|
|
280
|
+
...props,
|
|
281
|
+
style: confirm ? { ...props.style, anchorName: anchor } : props.style,
|
|
282
|
+
"aria-haspopup": confirm ? "dialog" : props["aria-haspopup"],
|
|
283
|
+
"aria-controls": confirm ? popoverId : props["aria-controls"],
|
|
284
|
+
onClick: confirm ? handleClick : onClick,
|
|
285
|
+
children: [
|
|
286
|
+
isLoading && /* @__PURE__ */ jsx("span", {
|
|
287
|
+
className: "btn-spinner"
|
|
288
|
+
}),
|
|
289
|
+
!isLoading && leftIcon && /* @__PURE__ */ jsx("span", {
|
|
290
|
+
className: "btn-icon-left",
|
|
291
|
+
children: leftIcon
|
|
292
|
+
}),
|
|
293
|
+
/* @__PURE__ */ jsx("span", {
|
|
294
|
+
className: "btn-content",
|
|
295
|
+
children
|
|
296
|
+
}),
|
|
297
|
+
!isLoading && rightIcon && /* @__PURE__ */ jsx("span", {
|
|
298
|
+
className: "btn-icon-right",
|
|
299
|
+
children: rightIcon
|
|
300
|
+
})
|
|
301
|
+
]
|
|
302
|
+
}),
|
|
303
|
+
confirm && portalTarget && createPortal(/* @__PURE__ */ jsxs("form", {
|
|
304
|
+
id: popoverId,
|
|
305
|
+
ref: popoverRef,
|
|
306
|
+
popover: "auto",
|
|
307
|
+
role: "dialog",
|
|
308
|
+
"aria-labelledby": hasTitle ? titleId : undefined,
|
|
309
|
+
"aria-label": hasTitle ? undefined : "Confirm action",
|
|
310
|
+
"aria-describedby": hasContent ? descriptionId : undefined,
|
|
311
|
+
className: "popover popover-confirm popover-bottom",
|
|
312
|
+
style: { positionAnchor: anchor },
|
|
313
|
+
onSubmit: handleConfirm,
|
|
314
|
+
onClick: (event) => event.stopPropagation(),
|
|
315
|
+
onToggle: (event) => {
|
|
316
|
+
if (!event.currentTarget.matches(":popover-open")) {
|
|
317
|
+
operationRef.current += 1;
|
|
318
|
+
if (event.currentTarget.contains(document.activeElement) || document.activeElement === document.body) {
|
|
319
|
+
buttonRef.current?.focus();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
children: [
|
|
324
|
+
hasTitle && /* @__PURE__ */ jsx("div", {
|
|
325
|
+
className: "popover-header",
|
|
326
|
+
children: /* @__PURE__ */ jsx("h3", {
|
|
327
|
+
id: titleId,
|
|
328
|
+
className: "popover-title",
|
|
329
|
+
children: title
|
|
330
|
+
})
|
|
331
|
+
}),
|
|
332
|
+
hasContent && /* @__PURE__ */ jsx("div", {
|
|
333
|
+
id: descriptionId,
|
|
334
|
+
className: "popover-body",
|
|
335
|
+
children: content
|
|
336
|
+
}),
|
|
337
|
+
confirmError && /* @__PURE__ */ jsx("div", {
|
|
338
|
+
role: "alert",
|
|
339
|
+
className: "popover-body",
|
|
340
|
+
children: "The action failed. Please try again."
|
|
341
|
+
}),
|
|
342
|
+
/* @__PURE__ */ jsxs("div", {
|
|
343
|
+
className: "popover-footer",
|
|
344
|
+
children: [
|
|
345
|
+
/* @__PURE__ */ jsx("button", {
|
|
346
|
+
ref: cancelRef,
|
|
347
|
+
type: "button",
|
|
348
|
+
className: "btn btn-text btn-sm",
|
|
349
|
+
popoverTarget: popoverId,
|
|
350
|
+
popoverTargetAction: "hide",
|
|
351
|
+
disabled: confirmLoading,
|
|
352
|
+
children: confirmation?.cancelText ?? "Cancel"
|
|
353
|
+
}),
|
|
354
|
+
/* @__PURE__ */ jsx("button", {
|
|
355
|
+
type: "submit",
|
|
356
|
+
className: confirmClasses,
|
|
357
|
+
disabled: disabled || isLoading || confirmLoading,
|
|
358
|
+
"aria-busy": confirmLoading,
|
|
359
|
+
children: confirmation?.confirmText ?? "Confirm"
|
|
360
|
+
})
|
|
361
|
+
]
|
|
362
|
+
})
|
|
363
|
+
]
|
|
364
|
+
}), portalTarget)
|
|
365
|
+
]
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
// src/components/input/Input.tsx
|
|
369
|
+
import {
|
|
370
|
+
forwardRef,
|
|
371
|
+
useImperativeHandle,
|
|
372
|
+
useRef,
|
|
373
|
+
useState
|
|
374
|
+
} from "react";
|
|
375
|
+
|
|
376
|
+
// src/classes/input.ts
|
|
377
|
+
var inputBaseClass = "input";
|
|
378
|
+
var inputWrapperClass = "input-wrapper";
|
|
379
|
+
var inputAffixWrapperClass = "input-affix-wrapper";
|
|
380
|
+
var inputPrefixClass = "input-prefix";
|
|
381
|
+
var inputSuffixClass = "input-suffix";
|
|
382
|
+
var inputClearClass = "input-clear";
|
|
383
|
+
var inputSearchClass = "input-search";
|
|
384
|
+
var inputSearchButtonClass = "input-search-button";
|
|
385
|
+
var inputPasswordClass = "input-password";
|
|
386
|
+
var inputPasswordToggleClass = "input-password-toggle";
|
|
387
|
+
var inputGroupClass = "input-group";
|
|
388
|
+
var textAreaBaseClass = "textarea";
|
|
389
|
+
var textAreaWrapperClass = "textarea-wrapper";
|
|
390
|
+
var textAreaCountClass = "textarea-counter";
|
|
391
|
+
var textAreaCountExceededClass = "textarea-counter-exceeded";
|
|
392
|
+
var textAreaAutoSizeClass = "textarea-auto-resize";
|
|
393
|
+
var inputVariantClasses = {
|
|
394
|
+
filled: "input-filled",
|
|
395
|
+
outlined: "input-outlined"
|
|
396
|
+
};
|
|
397
|
+
var inputSizeClasses = {
|
|
398
|
+
sm: "input-sm",
|
|
399
|
+
md: "input-md",
|
|
400
|
+
lg: "input-lg"
|
|
401
|
+
};
|
|
402
|
+
var inputStatusClasses = {
|
|
403
|
+
error: "input-error",
|
|
404
|
+
success: "input-success"
|
|
405
|
+
};
|
|
406
|
+
var textAreaVariantClasses = {
|
|
407
|
+
filled: "textarea-filled",
|
|
408
|
+
outlined: "textarea-outlined"
|
|
409
|
+
};
|
|
410
|
+
var textAreaSizeClasses = {
|
|
411
|
+
sm: "textarea-sm",
|
|
412
|
+
md: "",
|
|
413
|
+
lg: "textarea-lg"
|
|
414
|
+
};
|
|
415
|
+
var textAreaStatusClasses = {
|
|
416
|
+
error: "textarea-error",
|
|
417
|
+
success: "textarea-success"
|
|
418
|
+
};
|
|
419
|
+
var textAreaResizeClasses = {
|
|
420
|
+
none: "textarea-resize-none",
|
|
421
|
+
both: "textarea-resize-both",
|
|
422
|
+
horizontal: "textarea-resize-horizontal",
|
|
423
|
+
vertical: "textarea-resize-vertical"
|
|
424
|
+
};
|
|
425
|
+
function getInputClasses({
|
|
426
|
+
size = "md",
|
|
427
|
+
status,
|
|
428
|
+
variant = "outlined",
|
|
429
|
+
className
|
|
430
|
+
}) {
|
|
431
|
+
return cn(inputBaseClass, inputVariantClasses[variant], inputSizeClasses[size], status && inputStatusClasses[status], className);
|
|
432
|
+
}
|
|
433
|
+
function getInputWrapperClasses({
|
|
434
|
+
affixed,
|
|
435
|
+
className
|
|
436
|
+
}) {
|
|
437
|
+
return cn(inputWrapperClass, affixed && inputAffixWrapperClass, className);
|
|
438
|
+
}
|
|
439
|
+
function getSearchWrapperClasses({ className }) {
|
|
440
|
+
return cn(inputSearchClass, className);
|
|
441
|
+
}
|
|
442
|
+
function getPasswordWrapperClasses({
|
|
443
|
+
className
|
|
444
|
+
}) {
|
|
445
|
+
return cn(inputPasswordClass, className);
|
|
446
|
+
}
|
|
447
|
+
function getTextAreaClasses({
|
|
448
|
+
size = "md",
|
|
449
|
+
status,
|
|
450
|
+
variant = "outlined",
|
|
451
|
+
resize = "vertical",
|
|
452
|
+
autoSize,
|
|
453
|
+
className
|
|
454
|
+
}) {
|
|
455
|
+
return cn(textAreaBaseClass, textAreaVariantClasses[variant], textAreaSizeClasses[size], status && textAreaStatusClasses[status], textAreaResizeClasses[resize], autoSize && textAreaAutoSizeClass, className);
|
|
456
|
+
}
|
|
457
|
+
function getTextAreaCountClasses({ exceeded }) {
|
|
458
|
+
return cn(textAreaCountClass, exceeded && textAreaCountExceededClass);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/components/input/Input.tsx
|
|
462
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
463
|
+
function toTextValue(value) {
|
|
464
|
+
if (Array.isArray(value)) {
|
|
465
|
+
return value.join(",");
|
|
466
|
+
}
|
|
467
|
+
return value == null ? "" : String(value);
|
|
468
|
+
}
|
|
469
|
+
var InputRoot = forwardRef(({
|
|
470
|
+
allowClear,
|
|
471
|
+
className,
|
|
472
|
+
defaultValue,
|
|
473
|
+
disabled,
|
|
474
|
+
onChange,
|
|
475
|
+
prefix,
|
|
476
|
+
size = "md",
|
|
477
|
+
status,
|
|
478
|
+
suffix,
|
|
479
|
+
type = "text",
|
|
480
|
+
value,
|
|
481
|
+
variant = "outlined",
|
|
482
|
+
...props
|
|
483
|
+
}, ref) => {
|
|
484
|
+
const inputRef = useRef(null);
|
|
485
|
+
const [internalValue, setInternalValue] = useState(() => toTextValue(defaultValue));
|
|
486
|
+
const isControlled = value !== undefined;
|
|
487
|
+
const currentValue = isControlled ? toTextValue(value) : internalValue;
|
|
488
|
+
const hasAffix = prefix != null || suffix != null || allowClear;
|
|
489
|
+
useImperativeHandle(ref, () => inputRef.current);
|
|
490
|
+
function handleChange(event) {
|
|
491
|
+
if (!isControlled) {
|
|
492
|
+
setInternalValue(event.currentTarget.value);
|
|
493
|
+
}
|
|
494
|
+
onChange?.(event);
|
|
495
|
+
}
|
|
496
|
+
function handleClear() {
|
|
497
|
+
const input = inputRef.current;
|
|
498
|
+
if (!input || disabled) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
input.value = "";
|
|
502
|
+
if (!isControlled) {
|
|
503
|
+
setInternalValue("");
|
|
504
|
+
}
|
|
505
|
+
onChange?.({
|
|
506
|
+
currentTarget: input,
|
|
507
|
+
target: input,
|
|
508
|
+
type: "change"
|
|
509
|
+
});
|
|
510
|
+
input.focus();
|
|
511
|
+
}
|
|
512
|
+
const control = /* @__PURE__ */ jsx2("input", {
|
|
513
|
+
...props,
|
|
514
|
+
ref: inputRef,
|
|
515
|
+
type,
|
|
516
|
+
className: getInputClasses({ size, status, variant, className }),
|
|
517
|
+
disabled,
|
|
518
|
+
value: currentValue,
|
|
519
|
+
onChange: handleChange
|
|
520
|
+
});
|
|
521
|
+
if (!hasAffix) {
|
|
522
|
+
return control;
|
|
523
|
+
}
|
|
524
|
+
const showClear = allowClear && !disabled && currentValue.length > 0;
|
|
525
|
+
return /* @__PURE__ */ jsxs2("span", {
|
|
526
|
+
className: getInputWrapperClasses({ affixed: hasAffix }),
|
|
527
|
+
children: [
|
|
528
|
+
prefix != null ? /* @__PURE__ */ jsx2("span", {
|
|
529
|
+
className: inputPrefixClass,
|
|
530
|
+
children: prefix
|
|
531
|
+
}) : null,
|
|
532
|
+
control,
|
|
533
|
+
showClear ? /* @__PURE__ */ jsx2("button", {
|
|
534
|
+
type: "button",
|
|
535
|
+
className: inputClearClass,
|
|
536
|
+
"aria-label": "Clear input",
|
|
537
|
+
onClick: handleClear,
|
|
538
|
+
children: "x"
|
|
539
|
+
}) : null,
|
|
540
|
+
suffix != null ? /* @__PURE__ */ jsx2("span", {
|
|
541
|
+
className: inputSuffixClass,
|
|
542
|
+
children: suffix
|
|
543
|
+
}) : null
|
|
544
|
+
]
|
|
545
|
+
});
|
|
546
|
+
});
|
|
547
|
+
InputRoot.displayName = "Input";
|
|
548
|
+
var Search = forwardRef(({
|
|
549
|
+
className,
|
|
550
|
+
disabled,
|
|
551
|
+
enterButton,
|
|
552
|
+
loading,
|
|
553
|
+
onKeyDown,
|
|
554
|
+
onSearch,
|
|
555
|
+
...props
|
|
556
|
+
}, ref) => {
|
|
557
|
+
const inputRef = useRef(null);
|
|
558
|
+
useImperativeHandle(ref, () => inputRef.current);
|
|
559
|
+
function emitSearch(event) {
|
|
560
|
+
onSearch?.(inputRef.current?.value ?? "", event);
|
|
561
|
+
}
|
|
562
|
+
function handleKeyDown(event) {
|
|
563
|
+
onKeyDown?.(event);
|
|
564
|
+
if (!event.defaultPrevented && event.key === "Enter") {
|
|
565
|
+
emitSearch(event);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const buttonContent = enterButton === true ? "Search" : enterButton ?? "Search";
|
|
569
|
+
return /* @__PURE__ */ jsxs2("span", {
|
|
570
|
+
className: getSearchWrapperClasses({ className }),
|
|
571
|
+
children: [
|
|
572
|
+
/* @__PURE__ */ jsx2(InputRoot, {
|
|
573
|
+
...props,
|
|
574
|
+
ref: inputRef,
|
|
575
|
+
disabled,
|
|
576
|
+
onKeyDown: handleKeyDown
|
|
577
|
+
}),
|
|
578
|
+
enterButton || loading ? /* @__PURE__ */ jsx2("button", {
|
|
579
|
+
type: "button",
|
|
580
|
+
className: inputSearchButtonClass,
|
|
581
|
+
disabled: disabled || loading,
|
|
582
|
+
"aria-busy": loading || undefined,
|
|
583
|
+
onClick: emitSearch,
|
|
584
|
+
children: loading ? "Loading" : buttonContent
|
|
585
|
+
}) : null
|
|
586
|
+
]
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
Search.displayName = "Input.Search";
|
|
590
|
+
var Password = forwardRef(({ className, disabled, visibilityToggle = true, ...props }, ref) => {
|
|
591
|
+
const [visible, setVisible] = useState(false);
|
|
592
|
+
const type = visible ? "text" : "password";
|
|
593
|
+
const toggle = visibilityToggle ? /* @__PURE__ */ jsx2("button", {
|
|
594
|
+
type: "button",
|
|
595
|
+
className: inputPasswordToggleClass,
|
|
596
|
+
disabled,
|
|
597
|
+
"aria-label": visible ? "Hide password" : "Show password",
|
|
598
|
+
onClick: () => {
|
|
599
|
+
setVisible((current) => !current);
|
|
600
|
+
},
|
|
601
|
+
children: visible ? "Hide" : "Show"
|
|
602
|
+
}) : undefined;
|
|
603
|
+
return /* @__PURE__ */ jsx2("span", {
|
|
604
|
+
className: getPasswordWrapperClasses({ className }),
|
|
605
|
+
children: /* @__PURE__ */ jsx2(InputRoot, {
|
|
606
|
+
...props,
|
|
607
|
+
ref,
|
|
608
|
+
disabled,
|
|
609
|
+
type,
|
|
610
|
+
suffix: toggle
|
|
611
|
+
})
|
|
612
|
+
});
|
|
613
|
+
});
|
|
614
|
+
Password.displayName = "Input.Password";
|
|
615
|
+
var TextArea = forwardRef(({
|
|
616
|
+
autoSize,
|
|
617
|
+
className,
|
|
618
|
+
defaultValue,
|
|
619
|
+
maxLength,
|
|
620
|
+
onChange,
|
|
621
|
+
resize = "vertical",
|
|
622
|
+
showCount,
|
|
623
|
+
size = "md",
|
|
624
|
+
status,
|
|
625
|
+
value,
|
|
626
|
+
variant = "outlined",
|
|
627
|
+
...props
|
|
628
|
+
}, ref) => {
|
|
629
|
+
const [internalValue, setInternalValue] = useState(() => toTextValue(defaultValue));
|
|
630
|
+
const isControlled = value !== undefined;
|
|
631
|
+
const currentValue = isControlled ? toTextValue(value) : internalValue;
|
|
632
|
+
const count = currentValue.length;
|
|
633
|
+
const exceeded = maxLength !== undefined && count > maxLength;
|
|
634
|
+
function handleChange(event) {
|
|
635
|
+
if (!isControlled) {
|
|
636
|
+
setInternalValue(event.currentTarget.value);
|
|
637
|
+
}
|
|
638
|
+
onChange?.(event);
|
|
639
|
+
}
|
|
640
|
+
const control = /* @__PURE__ */ jsx2("textarea", {
|
|
641
|
+
...props,
|
|
642
|
+
ref,
|
|
643
|
+
className: getTextAreaClasses({
|
|
644
|
+
size,
|
|
645
|
+
status,
|
|
646
|
+
variant,
|
|
647
|
+
resize,
|
|
648
|
+
autoSize: Boolean(autoSize),
|
|
649
|
+
className
|
|
650
|
+
}),
|
|
651
|
+
maxLength,
|
|
652
|
+
value: currentValue,
|
|
653
|
+
onChange: handleChange
|
|
654
|
+
});
|
|
655
|
+
if (!showCount) {
|
|
656
|
+
return control;
|
|
657
|
+
}
|
|
658
|
+
return /* @__PURE__ */ jsxs2("span", {
|
|
659
|
+
className: textAreaWrapperClass,
|
|
660
|
+
children: [
|
|
661
|
+
control,
|
|
662
|
+
/* @__PURE__ */ jsxs2("span", {
|
|
663
|
+
className: getTextAreaCountClasses({ exceeded }),
|
|
664
|
+
children: [
|
|
665
|
+
count,
|
|
666
|
+
maxLength !== undefined ? ` / ${maxLength}` : null
|
|
667
|
+
]
|
|
668
|
+
})
|
|
669
|
+
]
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
TextArea.displayName = "Input.TextArea";
|
|
673
|
+
var Group = forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsx2("div", {
|
|
674
|
+
...props,
|
|
675
|
+
ref,
|
|
676
|
+
className: cn(inputGroupClass, className),
|
|
677
|
+
children
|
|
678
|
+
}));
|
|
679
|
+
Group.displayName = "Input.Group";
|
|
680
|
+
var Input = Object.assign(InputRoot, {
|
|
681
|
+
Search,
|
|
682
|
+
Password,
|
|
683
|
+
TextArea,
|
|
684
|
+
Group
|
|
685
|
+
});
|
|
686
|
+
// src/components/chat/ChatComposer.tsx
|
|
687
|
+
import { jsx as jsx3, jsxs as jsxs3, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
688
|
+
function AttachmentPreview({ file }) {
|
|
689
|
+
const imageRef = useRef2(null);
|
|
690
|
+
useEffect(() => {
|
|
691
|
+
const image = imageRef.current;
|
|
692
|
+
if (!image || !file.type.startsWith("image/") || !URL.createObjectURL)
|
|
693
|
+
return;
|
|
694
|
+
const objectUrl = URL.createObjectURL(file);
|
|
695
|
+
image.src = objectUrl;
|
|
696
|
+
image.hidden = false;
|
|
697
|
+
return () => {
|
|
698
|
+
image.hidden = true;
|
|
699
|
+
image.removeAttribute("src");
|
|
700
|
+
URL.revokeObjectURL(objectUrl);
|
|
701
|
+
};
|
|
702
|
+
}, [file]);
|
|
703
|
+
return file.type.startsWith("image/") ? /* @__PURE__ */ jsx3("img", {
|
|
704
|
+
ref: imageRef,
|
|
705
|
+
alt: file.name,
|
|
706
|
+
hidden: true
|
|
707
|
+
}) : null;
|
|
708
|
+
}
|
|
709
|
+
function AttachmentContent({
|
|
710
|
+
file,
|
|
711
|
+
renderAttachment,
|
|
712
|
+
context
|
|
713
|
+
}) {
|
|
714
|
+
return renderAttachment(file, context);
|
|
715
|
+
}
|
|
716
|
+
function ExtraActions({
|
|
717
|
+
children,
|
|
718
|
+
context
|
|
719
|
+
}) {
|
|
720
|
+
return typeof children === "function" ? children(context) : children;
|
|
721
|
+
}
|
|
722
|
+
var ChatComposer = forwardRef2(({
|
|
723
|
+
value,
|
|
724
|
+
onValueChange,
|
|
725
|
+
onSubmit,
|
|
726
|
+
attachments = [],
|
|
727
|
+
onAttachmentsChange,
|
|
728
|
+
accept,
|
|
729
|
+
multiple = true,
|
|
730
|
+
allowAttachmentOnly = false,
|
|
731
|
+
disabled = false,
|
|
732
|
+
busy = false,
|
|
733
|
+
generating = false,
|
|
734
|
+
canSubmit = true,
|
|
735
|
+
onStop,
|
|
736
|
+
error,
|
|
737
|
+
onSubmitError,
|
|
738
|
+
clearOnSubmit = true,
|
|
739
|
+
completion,
|
|
740
|
+
minRows = 1,
|
|
741
|
+
maxRows = 6,
|
|
742
|
+
textareaProps,
|
|
743
|
+
attachLabel = "Attach files",
|
|
744
|
+
sendLabel = "Send",
|
|
745
|
+
stopLabel = "Stop",
|
|
746
|
+
renderAttachment,
|
|
747
|
+
children,
|
|
748
|
+
className,
|
|
749
|
+
...props
|
|
750
|
+
}, ref) => {
|
|
751
|
+
const id = useId();
|
|
752
|
+
const editorRef = useRef2(null);
|
|
753
|
+
const pickerRef = useRef2(null);
|
|
754
|
+
const mountedRef = useRef2(false);
|
|
755
|
+
const composingRef = useRef2(false);
|
|
756
|
+
const submittingRef = useRef2(false);
|
|
757
|
+
const revisionRef = useRef2(0);
|
|
758
|
+
const observedValueRef = useRef2(value);
|
|
759
|
+
const entriesRef = useRef2([]);
|
|
760
|
+
const nextEntryRef = useRef2(0);
|
|
761
|
+
const [submitting, setSubmitting] = useState2(false);
|
|
762
|
+
const [submitError, setSubmitError] = useState2();
|
|
763
|
+
const [activeId, setActiveId] = useState2();
|
|
764
|
+
const [dismissed, setDismissed] = useState2(false);
|
|
765
|
+
const latestRef = useRef2({
|
|
766
|
+
value,
|
|
767
|
+
onValueChange,
|
|
768
|
+
attachments,
|
|
769
|
+
onAttachmentsChange,
|
|
770
|
+
onSubmitError,
|
|
771
|
+
clearOnSubmit
|
|
772
|
+
});
|
|
773
|
+
function reconcileEntries(files) {
|
|
774
|
+
const previous = [...entriesRef.current];
|
|
775
|
+
entriesRef.current = files.map((file) => {
|
|
776
|
+
const index = previous.findIndex((entry) => entry.file === file);
|
|
777
|
+
if (index < 0)
|
|
778
|
+
return { file, id: nextEntryRef.current++ };
|
|
779
|
+
return previous.splice(index, 1)[0];
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
useLayoutEffect(() => {
|
|
783
|
+
mountedRef.current = true;
|
|
784
|
+
return () => {
|
|
785
|
+
mountedRef.current = false;
|
|
786
|
+
};
|
|
787
|
+
}, []);
|
|
788
|
+
useLayoutEffect(() => {
|
|
789
|
+
if (observedValueRef.current !== value) {
|
|
790
|
+
revisionRef.current++;
|
|
791
|
+
observedValueRef.current = value;
|
|
792
|
+
}
|
|
793
|
+
reconcileEntries(attachments);
|
|
794
|
+
latestRef.current = {
|
|
795
|
+
value,
|
|
796
|
+
onValueChange,
|
|
797
|
+
attachments,
|
|
798
|
+
onAttachmentsChange,
|
|
799
|
+
onSubmitError,
|
|
800
|
+
clearOnSubmit
|
|
801
|
+
};
|
|
802
|
+
});
|
|
803
|
+
const minimumRows = Math.max(1, Math.floor(minRows));
|
|
804
|
+
const maximumRows = Math.max(minimumRows, Math.floor(maxRows));
|
|
805
|
+
useLayoutEffect(() => {
|
|
806
|
+
const editor = editorRef.current;
|
|
807
|
+
if (!editor)
|
|
808
|
+
return;
|
|
809
|
+
function measure() {
|
|
810
|
+
if (!editor)
|
|
811
|
+
return;
|
|
812
|
+
const style = getComputedStyle(editor);
|
|
813
|
+
const lineHeight = parseFloat(style.lineHeight) || 24;
|
|
814
|
+
const padding = (parseFloat(style.paddingTop) || 0) + (parseFloat(style.paddingBottom) || 0);
|
|
815
|
+
const borders = (parseFloat(style.borderTopWidth) || 0) + (parseFloat(style.borderBottomWidth) || 0);
|
|
816
|
+
const adjustment = style.boxSizing === "border-box" ? padding + borders : 0;
|
|
817
|
+
editor.style.height = "auto";
|
|
818
|
+
const minimum = minimumRows * lineHeight + adjustment;
|
|
819
|
+
const maximum = maximumRows * lineHeight + adjustment;
|
|
820
|
+
const content = editor.scrollHeight - padding + adjustment;
|
|
821
|
+
editor.style.height = `${Math.min(maximum, Math.max(minimum, content))}px`;
|
|
822
|
+
editor.style.overflowY = content > maximum ? "auto" : "hidden";
|
|
823
|
+
}
|
|
824
|
+
measure();
|
|
825
|
+
const observer = typeof ResizeObserver === "undefined" ? undefined : new ResizeObserver(measure);
|
|
826
|
+
observer?.observe(editor);
|
|
827
|
+
window.addEventListener("resize", measure);
|
|
828
|
+
return () => {
|
|
829
|
+
observer?.disconnect();
|
|
830
|
+
window.removeEventListener("resize", measure);
|
|
831
|
+
};
|
|
832
|
+
}, [value, minimumRows, maximumRows, textareaProps?.style]);
|
|
833
|
+
const eligible = canSubmit && !disabled && !busy && !submitting && (value.trim().length > 0 || allowAttachmentOnly && attachments.length > 0);
|
|
834
|
+
const candidates = completion?.items ?? [];
|
|
835
|
+
const enabledCandidates = candidates.filter((item) => !item.disabled);
|
|
836
|
+
const active = enabledCandidates.find((item) => item.id === activeId) ?? enabledCandidates[0];
|
|
837
|
+
const showCompletion = !disabled && !dismissed && candidates.length > 0;
|
|
838
|
+
const visibleError = error ?? submitError;
|
|
839
|
+
const hasError = visibleError !== undefined && visibleError !== null && visibleError !== false;
|
|
840
|
+
function openAttachments() {
|
|
841
|
+
if (!disabled && onAttachmentsChange)
|
|
842
|
+
pickerRef.current?.click();
|
|
843
|
+
}
|
|
844
|
+
function changeAttachments(files) {
|
|
845
|
+
reconcileEntries(files);
|
|
846
|
+
latestRef.current.attachments = files;
|
|
847
|
+
onAttachmentsChange?.(files);
|
|
848
|
+
}
|
|
849
|
+
function removeAttachment(index) {
|
|
850
|
+
if (disabled || !onAttachmentsChange)
|
|
851
|
+
return;
|
|
852
|
+
entriesRef.current.splice(index, 1);
|
|
853
|
+
changeAttachments(attachments.filter((_, fileIndex) => fileIndex !== index));
|
|
854
|
+
}
|
|
855
|
+
function selectCompletion(item) {
|
|
856
|
+
if (!completion || item.disabled || disabled)
|
|
857
|
+
return;
|
|
858
|
+
const editor = editorRef.current;
|
|
859
|
+
completion.onSelect(item, {
|
|
860
|
+
value,
|
|
861
|
+
selectionStart: editor?.selectionStart ?? value.length,
|
|
862
|
+
selectionEnd: editor?.selectionEnd ?? value.length
|
|
863
|
+
});
|
|
864
|
+
setDismissed(true);
|
|
865
|
+
editor?.focus();
|
|
866
|
+
}
|
|
867
|
+
async function submit() {
|
|
868
|
+
if (!eligible || submittingRef.current)
|
|
869
|
+
return;
|
|
870
|
+
submittingRef.current = true;
|
|
871
|
+
setSubmitting(true);
|
|
872
|
+
setSubmitError(undefined);
|
|
873
|
+
const snapshot = { value, files: [...attachments] };
|
|
874
|
+
const submittedRevision = revisionRef.current;
|
|
875
|
+
const submittedEntries = new Set(entriesRef.current.map((entry) => entry.id));
|
|
876
|
+
try {
|
|
877
|
+
const accepted = await onSubmit(snapshot);
|
|
878
|
+
if (!mountedRef.current || accepted === false)
|
|
879
|
+
return;
|
|
880
|
+
const current = latestRef.current;
|
|
881
|
+
if (current.clearOnSubmit) {
|
|
882
|
+
if (revisionRef.current === submittedRevision)
|
|
883
|
+
current.onValueChange("");
|
|
884
|
+
const retained = entriesRef.current.filter((entry) => !submittedEntries.has(entry.id));
|
|
885
|
+
if (retained.length !== entriesRef.current.length) {
|
|
886
|
+
entriesRef.current = retained;
|
|
887
|
+
current.onAttachmentsChange?.(retained.map((entry) => entry.file));
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
} catch (cause) {
|
|
891
|
+
if (!mountedRef.current)
|
|
892
|
+
return;
|
|
893
|
+
setSubmitError("Unable to send message. Please try again.");
|
|
894
|
+
latestRef.current.onSubmitError?.(cause);
|
|
895
|
+
} finally {
|
|
896
|
+
submittingRef.current = false;
|
|
897
|
+
if (mountedRef.current)
|
|
898
|
+
setSubmitting(false);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return /* @__PURE__ */ jsxs3("div", {
|
|
902
|
+
...props,
|
|
903
|
+
ref,
|
|
904
|
+
className: cn("chat-composer", className),
|
|
905
|
+
children: [
|
|
906
|
+
attachments.length > 0 ? /* @__PURE__ */ jsx3("ul", {
|
|
907
|
+
className: "chat-composer-attachments",
|
|
908
|
+
"aria-label": "Attachments",
|
|
909
|
+
children: attachments.map((file, index) => /* @__PURE__ */ jsx3("li", {
|
|
910
|
+
className: "chat-composer-attachment",
|
|
911
|
+
children: renderAttachment ? /* @__PURE__ */ jsx3(AttachmentContent, {
|
|
912
|
+
file,
|
|
913
|
+
renderAttachment,
|
|
914
|
+
context: {
|
|
915
|
+
remove: () => removeAttachment(index),
|
|
916
|
+
disabled: disabled || !onAttachmentsChange
|
|
917
|
+
}
|
|
918
|
+
}) : /* @__PURE__ */ jsxs3(Fragment2, {
|
|
919
|
+
children: [
|
|
920
|
+
/* @__PURE__ */ jsx3(AttachmentPreview, {
|
|
921
|
+
file
|
|
922
|
+
}),
|
|
923
|
+
/* @__PURE__ */ jsx3("span", {
|
|
924
|
+
className: "chat-composer-filename",
|
|
925
|
+
children: file.name
|
|
926
|
+
}),
|
|
927
|
+
onAttachmentsChange ? /* @__PURE__ */ jsx3(Button, {
|
|
928
|
+
type: "button",
|
|
929
|
+
size: "sm",
|
|
930
|
+
color: "base",
|
|
931
|
+
appearance: "ghost",
|
|
932
|
+
disabled,
|
|
933
|
+
"aria-label": `Remove ${file.name}`,
|
|
934
|
+
onClick: () => removeAttachment(index),
|
|
935
|
+
children: "Remove"
|
|
936
|
+
}) : null
|
|
937
|
+
]
|
|
938
|
+
})
|
|
939
|
+
}, index))
|
|
940
|
+
}) : null,
|
|
941
|
+
/* @__PURE__ */ jsx3(Input.TextArea, {
|
|
942
|
+
...textareaProps,
|
|
943
|
+
ref: editorRef,
|
|
944
|
+
className: cn("chat-composer-editor", textareaProps?.className),
|
|
945
|
+
value,
|
|
946
|
+
disabled,
|
|
947
|
+
rows: minimumRows,
|
|
948
|
+
resize: "none",
|
|
949
|
+
"aria-label": textareaProps?.["aria-label"] ?? (textareaProps?.["aria-labelledby"] ? undefined : "Message"),
|
|
950
|
+
"aria-describedby": cn(textareaProps?.["aria-describedby"], hasError && `${id}-error`) || undefined,
|
|
951
|
+
"aria-invalid": hasError ? true : textareaProps?.["aria-invalid"],
|
|
952
|
+
"aria-controls": showCompletion ? `${id}-completions` : undefined,
|
|
953
|
+
"aria-autocomplete": completion ? "list" : undefined,
|
|
954
|
+
"aria-haspopup": completion ? "listbox" : undefined,
|
|
955
|
+
"aria-activedescendant": showCompletion && active ? `${id}-option-${candidates.indexOf(active)}` : undefined,
|
|
956
|
+
onChange: (event) => {
|
|
957
|
+
revisionRef.current++;
|
|
958
|
+
observedValueRef.current = event.currentTarget.value;
|
|
959
|
+
setSubmitError(undefined);
|
|
960
|
+
setDismissed(false);
|
|
961
|
+
setActiveId(undefined);
|
|
962
|
+
onValueChange(event.currentTarget.value);
|
|
963
|
+
},
|
|
964
|
+
onCompositionStart: (event) => {
|
|
965
|
+
composingRef.current = true;
|
|
966
|
+
textareaProps?.onCompositionStart?.(event);
|
|
967
|
+
},
|
|
968
|
+
onCompositionEnd: (event) => {
|
|
969
|
+
composingRef.current = false;
|
|
970
|
+
textareaProps?.onCompositionEnd?.(event);
|
|
971
|
+
},
|
|
972
|
+
onKeyDown: (event) => {
|
|
973
|
+
textareaProps?.onKeyDown?.(event);
|
|
974
|
+
if (event.defaultPrevented || composingRef.current || event.nativeEvent.isComposing || event.keyCode === 229)
|
|
975
|
+
return;
|
|
976
|
+
if (showCompletion) {
|
|
977
|
+
if (event.key === "Escape") {
|
|
978
|
+
event.preventDefault();
|
|
979
|
+
setDismissed(true);
|
|
980
|
+
return;
|
|
981
|
+
}
|
|
982
|
+
if (enabledCandidates.length > 0 && (event.key === "ArrowDown" || event.key === "ArrowUp")) {
|
|
983
|
+
event.preventDefault();
|
|
984
|
+
const position = enabledCandidates.findIndex((item) => item.id === active?.id);
|
|
985
|
+
const direction = event.key === "ArrowDown" ? 1 : -1;
|
|
986
|
+
setActiveId(enabledCandidates[(position + direction + enabledCandidates.length) % enabledCandidates.length].id);
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
if (active && !event.ctrlKey && !event.metaKey && !event.altKey && !event.shiftKey && (event.key === "Enter" || event.key === "Tab")) {
|
|
990
|
+
event.preventDefault();
|
|
991
|
+
selectCompletion(active);
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (event.key === "Enter" && (event.metaKey || event.ctrlKey)) {
|
|
996
|
+
event.preventDefault();
|
|
997
|
+
submit();
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
}),
|
|
1001
|
+
showCompletion ? /* @__PURE__ */ jsx3("ul", {
|
|
1002
|
+
id: `${id}-completions`,
|
|
1003
|
+
role: "listbox",
|
|
1004
|
+
"aria-label": completion?.label ?? "Suggestions",
|
|
1005
|
+
className: "chat-composer-completions",
|
|
1006
|
+
children: candidates.map((item, index) => /* @__PURE__ */ jsxs3("li", {
|
|
1007
|
+
id: `${id}-option-${index}`,
|
|
1008
|
+
role: "option",
|
|
1009
|
+
"aria-selected": item.id === active?.id,
|
|
1010
|
+
"aria-disabled": item.disabled || undefined,
|
|
1011
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1012
|
+
onClick: () => selectCompletion(item),
|
|
1013
|
+
onKeyDown: (event) => {
|
|
1014
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
1015
|
+
event.preventDefault();
|
|
1016
|
+
selectCompletion(item);
|
|
1017
|
+
}
|
|
1018
|
+
},
|
|
1019
|
+
children: [
|
|
1020
|
+
/* @__PURE__ */ jsx3("span", {
|
|
1021
|
+
children: item.label
|
|
1022
|
+
}),
|
|
1023
|
+
item.description ? /* @__PURE__ */ jsx3("span", {
|
|
1024
|
+
className: "chat-composer-description",
|
|
1025
|
+
children: item.description
|
|
1026
|
+
}) : null
|
|
1027
|
+
]
|
|
1028
|
+
}, item.id))
|
|
1029
|
+
}) : null,
|
|
1030
|
+
hasError ? /* @__PURE__ */ jsx3("div", {
|
|
1031
|
+
id: `${id}-error`,
|
|
1032
|
+
role: "alert",
|
|
1033
|
+
className: "chat-composer-error",
|
|
1034
|
+
children: visibleError
|
|
1035
|
+
}) : null,
|
|
1036
|
+
/* @__PURE__ */ jsxs3("div", {
|
|
1037
|
+
className: "chat-composer-actions",
|
|
1038
|
+
children: [
|
|
1039
|
+
onAttachmentsChange ? /* @__PURE__ */ jsxs3(Fragment2, {
|
|
1040
|
+
children: [
|
|
1041
|
+
/* @__PURE__ */ jsx3("input", {
|
|
1042
|
+
ref: pickerRef,
|
|
1043
|
+
type: "file",
|
|
1044
|
+
accept,
|
|
1045
|
+
multiple,
|
|
1046
|
+
disabled,
|
|
1047
|
+
hidden: true,
|
|
1048
|
+
"aria-label": attachLabel,
|
|
1049
|
+
onChange: (event) => {
|
|
1050
|
+
const files = Array.from(event.currentTarget.files ?? []);
|
|
1051
|
+
event.currentTarget.value = "";
|
|
1052
|
+
if (files.length > 0)
|
|
1053
|
+
changeAttachments([...attachments, ...files]);
|
|
1054
|
+
}
|
|
1055
|
+
}),
|
|
1056
|
+
/* @__PURE__ */ jsx3(Button, {
|
|
1057
|
+
type: "button",
|
|
1058
|
+
color: "base",
|
|
1059
|
+
appearance: "ghost",
|
|
1060
|
+
disabled,
|
|
1061
|
+
onClick: openAttachments,
|
|
1062
|
+
children: attachLabel
|
|
1063
|
+
})
|
|
1064
|
+
]
|
|
1065
|
+
}) : null,
|
|
1066
|
+
/* @__PURE__ */ jsx3(ExtraActions, {
|
|
1067
|
+
context: {
|
|
1068
|
+
submit: () => {
|
|
1069
|
+
submit();
|
|
1070
|
+
},
|
|
1071
|
+
submitting,
|
|
1072
|
+
canSubmit: eligible,
|
|
1073
|
+
disabled,
|
|
1074
|
+
generating,
|
|
1075
|
+
openAttachments
|
|
1076
|
+
},
|
|
1077
|
+
children
|
|
1078
|
+
}),
|
|
1079
|
+
generating && onStop ? /* @__PURE__ */ jsx3(Button, {
|
|
1080
|
+
type: "button",
|
|
1081
|
+
disabled,
|
|
1082
|
+
onClick: onStop,
|
|
1083
|
+
children: stopLabel
|
|
1084
|
+
}) : null,
|
|
1085
|
+
/* @__PURE__ */ jsx3(Button, {
|
|
1086
|
+
type: "button",
|
|
1087
|
+
color: "primary",
|
|
1088
|
+
disabled: !eligible,
|
|
1089
|
+
"aria-busy": busy || submitting || undefined,
|
|
1090
|
+
onClick: () => {
|
|
1091
|
+
submit();
|
|
1092
|
+
},
|
|
1093
|
+
children: sendLabel
|
|
1094
|
+
})
|
|
1095
|
+
]
|
|
1096
|
+
})
|
|
1097
|
+
]
|
|
1098
|
+
});
|
|
1099
|
+
});
|
|
1100
|
+
ChatComposer.displayName = "Chat.Composer";
|
|
1101
|
+
|
|
31
1102
|
// src/classes/chat.ts
|
|
32
1103
|
var chatBaseClass = "chat";
|
|
33
1104
|
var chatAvatarClass = "chat-avatar";
|
|
@@ -103,20 +1174,20 @@ function getChatToolClasses({
|
|
|
103
1174
|
}
|
|
104
1175
|
|
|
105
1176
|
// src/components/chat/Chat.tsx
|
|
106
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
107
|
-
var ChatAvatar =
|
|
1177
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1178
|
+
var ChatAvatar = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
108
1179
|
...props,
|
|
109
1180
|
ref,
|
|
110
1181
|
className: cn(chatAvatarClass, className)
|
|
111
1182
|
}));
|
|
112
1183
|
ChatAvatar.displayName = "Chat.Avatar";
|
|
113
|
-
var ChatHeader =
|
|
1184
|
+
var ChatHeader = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
114
1185
|
...props,
|
|
115
1186
|
ref,
|
|
116
1187
|
className: cn(chatHeaderClass, className)
|
|
117
1188
|
}));
|
|
118
1189
|
ChatHeader.displayName = "Chat.Header";
|
|
119
|
-
var ChatBubble =
|
|
1190
|
+
var ChatBubble = forwardRef3(({ color, size = "md", filled, streaming, className, children, ...props }, ref) => /* @__PURE__ */ jsxs4("div", {
|
|
120
1191
|
...props,
|
|
121
1192
|
ref,
|
|
122
1193
|
className: getChatBubbleClasses({
|
|
@@ -128,62 +1199,62 @@ var ChatBubble = forwardRef(({ color, size = "md", filled, streaming, className,
|
|
|
128
1199
|
}),
|
|
129
1200
|
children: [
|
|
130
1201
|
children,
|
|
131
|
-
streaming ? /* @__PURE__ */
|
|
1202
|
+
streaming ? /* @__PURE__ */ jsx4("span", {
|
|
132
1203
|
className: "chat-bubble-content chat-bubble-streaming",
|
|
133
1204
|
"aria-hidden": "true"
|
|
134
1205
|
}) : null
|
|
135
1206
|
]
|
|
136
1207
|
}));
|
|
137
1208
|
ChatBubble.displayName = "Chat.Bubble";
|
|
138
|
-
var ChatFooter =
|
|
1209
|
+
var ChatFooter = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
139
1210
|
...props,
|
|
140
1211
|
ref,
|
|
141
1212
|
className: cn(chatFooterClass, className)
|
|
142
1213
|
}));
|
|
143
1214
|
ChatFooter.displayName = "Chat.Footer";
|
|
144
|
-
var ChatStatus =
|
|
1215
|
+
var ChatStatus = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
145
1216
|
...props,
|
|
146
1217
|
ref,
|
|
147
1218
|
className: cn(chatStatusClass, className)
|
|
148
1219
|
}));
|
|
149
1220
|
ChatStatus.displayName = "Chat.Status";
|
|
150
|
-
var ChatStatusItem =
|
|
1221
|
+
var ChatStatusItem = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("span", {
|
|
151
1222
|
...props,
|
|
152
1223
|
ref,
|
|
153
1224
|
className: cn(chatStatusItemClass, className)
|
|
154
1225
|
}));
|
|
155
1226
|
ChatStatusItem.displayName = "Chat.StatusItem";
|
|
156
|
-
var ChatStatusValue =
|
|
1227
|
+
var ChatStatusValue = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("span", {
|
|
157
1228
|
...props,
|
|
158
1229
|
ref,
|
|
159
1230
|
className: cn(chatStatusValueClass, className)
|
|
160
1231
|
}));
|
|
161
1232
|
ChatStatusValue.displayName = "Chat.StatusValue";
|
|
162
|
-
var ChatActions =
|
|
1233
|
+
var ChatActions = forwardRef3(({ hover, className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
163
1234
|
...props,
|
|
164
1235
|
ref,
|
|
165
1236
|
className: cn(chatActionsClass, hover && chatActionsHoverClass, className)
|
|
166
1237
|
}));
|
|
167
1238
|
ChatActions.displayName = "Chat.Actions";
|
|
168
|
-
var ChatScroll =
|
|
1239
|
+
var ChatScroll = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
169
1240
|
...props,
|
|
170
1241
|
ref,
|
|
171
1242
|
className: cn(chatScrollClass, className)
|
|
172
1243
|
}));
|
|
173
1244
|
ChatScroll.displayName = "Chat.Scroll";
|
|
174
|
-
var ChatScrollBody =
|
|
1245
|
+
var ChatScrollBody = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
175
1246
|
...props,
|
|
176
1247
|
ref,
|
|
177
1248
|
className: cn(chatScrollBodyClass, className)
|
|
178
1249
|
}));
|
|
179
1250
|
ChatScrollBody.displayName = "Chat.ScrollBody";
|
|
180
|
-
var ChatScrollTrack =
|
|
1251
|
+
var ChatScrollTrack = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
181
1252
|
...props,
|
|
182
1253
|
ref,
|
|
183
1254
|
className: cn(chatScrollTrackClass, className)
|
|
184
1255
|
}));
|
|
185
1256
|
ChatScrollTrack.displayName = "Chat.ScrollTrack";
|
|
186
|
-
var ChatScrollIndicator =
|
|
1257
|
+
var ChatScrollIndicator = forwardRef3(({ targetId, timeline, type = "button", className, onClick, ...props }, ref) => /* @__PURE__ */ jsx4("button", {
|
|
187
1258
|
...props,
|
|
188
1259
|
ref,
|
|
189
1260
|
type,
|
|
@@ -205,59 +1276,59 @@ var ChatScrollIndicator = forwardRef(({ targetId, timeline, type = "button", cla
|
|
|
205
1276
|
}
|
|
206
1277
|
}));
|
|
207
1278
|
ChatScrollIndicator.displayName = "Chat.ScrollIndicator";
|
|
208
|
-
var ChatReasoning =
|
|
1279
|
+
var ChatReasoning = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("details", {
|
|
209
1280
|
...props,
|
|
210
1281
|
ref,
|
|
211
1282
|
className: cn(chatReasoningClass, className)
|
|
212
1283
|
}));
|
|
213
1284
|
ChatReasoning.displayName = "Chat.Reasoning";
|
|
214
|
-
var ChatTool =
|
|
1285
|
+
var ChatTool = forwardRef3(({ status, className, ...props }, ref) => /* @__PURE__ */ jsx4("details", {
|
|
215
1286
|
...props,
|
|
216
1287
|
ref,
|
|
217
1288
|
className: getChatToolClasses({ status, className })
|
|
218
1289
|
}));
|
|
219
1290
|
ChatTool.displayName = "Chat.Tool";
|
|
220
|
-
var ChatToolHeader =
|
|
1291
|
+
var ChatToolHeader = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("summary", {
|
|
221
1292
|
...props,
|
|
222
1293
|
ref,
|
|
223
1294
|
className: cn(chatToolHeaderClass, className)
|
|
224
1295
|
}));
|
|
225
1296
|
ChatToolHeader.displayName = "Chat.ToolHeader";
|
|
226
|
-
var ChatToolStatus =
|
|
1297
|
+
var ChatToolStatus = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("span", {
|
|
227
1298
|
...props,
|
|
228
1299
|
ref,
|
|
229
1300
|
className: cn(chatToolStatusClass, className)
|
|
230
1301
|
}));
|
|
231
1302
|
ChatToolStatus.displayName = "Chat.ToolStatus";
|
|
232
|
-
var ChatToolCall =
|
|
1303
|
+
var ChatToolCall = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
233
1304
|
...props,
|
|
234
1305
|
ref,
|
|
235
1306
|
className: cn(chatToolCallClass, className)
|
|
236
1307
|
}));
|
|
237
1308
|
ChatToolCall.displayName = "Chat.ToolCall";
|
|
238
|
-
var ChatToolResult =
|
|
1309
|
+
var ChatToolResult = forwardRef3(({ className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
239
1310
|
...props,
|
|
240
1311
|
ref,
|
|
241
1312
|
className: cn(chatToolResultClass, className)
|
|
242
1313
|
}));
|
|
243
1314
|
ChatToolResult.displayName = "Chat.ToolResult";
|
|
244
|
-
var ChatTyping =
|
|
1315
|
+
var ChatTyping = forwardRef3(({
|
|
245
1316
|
className,
|
|
246
1317
|
role = "status",
|
|
247
1318
|
"aria-label": ariaLabel = "Assistant is typing",
|
|
248
1319
|
...props
|
|
249
|
-
}, ref) => /* @__PURE__ */
|
|
1320
|
+
}, ref) => /* @__PURE__ */ jsx4("span", {
|
|
250
1321
|
...props,
|
|
251
1322
|
ref,
|
|
252
1323
|
role,
|
|
253
1324
|
"aria-label": ariaLabel,
|
|
254
1325
|
className: cn(chatTypingClass, className),
|
|
255
|
-
children: /* @__PURE__ */
|
|
1326
|
+
children: /* @__PURE__ */ jsx4("span", {
|
|
256
1327
|
"aria-hidden": "true"
|
|
257
1328
|
})
|
|
258
1329
|
}));
|
|
259
1330
|
ChatTyping.displayName = "Chat.Typing";
|
|
260
|
-
var ChatRoot =
|
|
1331
|
+
var ChatRoot = forwardRef3(({ placement = "start", timeline, className, ...props }, ref) => /* @__PURE__ */ jsx4("div", {
|
|
261
1332
|
...props,
|
|
262
1333
|
ref,
|
|
263
1334
|
"data-chat-tl": timeline ?? props["data-chat-tl"],
|
|
@@ -265,6 +1336,7 @@ var ChatRoot = forwardRef(({ placement = "start", timeline, className, ...props
|
|
|
265
1336
|
}));
|
|
266
1337
|
ChatRoot.displayName = "Chat";
|
|
267
1338
|
var Chat = Object.assign(ChatRoot, {
|
|
1339
|
+
Composer: ChatComposer,
|
|
268
1340
|
Avatar: ChatAvatar,
|
|
269
1341
|
Header: ChatHeader,
|
|
270
1342
|
Bubble: ChatBubble,
|
|
@@ -290,6 +1362,7 @@ export {
|
|
|
290
1362
|
ChatActions,
|
|
291
1363
|
ChatAvatar,
|
|
292
1364
|
ChatBubble,
|
|
1365
|
+
ChatComposer,
|
|
293
1366
|
ChatFooter,
|
|
294
1367
|
ChatHeader,
|
|
295
1368
|
ChatReasoning,
|