@invergent/agent-chat-react 1.6.9 → 1.7.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.
- package/dist/index.cjs +667 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -3
- package/dist/index.d.ts +62 -3
- package/dist/index.js +665 -205
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -4450,11 +4450,72 @@ var ChatMessage = react.memo(function ChatMessage2({
|
|
|
4450
4450
|
className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
|
|
4451
4451
|
},
|
|
4452
4452
|
i
|
|
4453
|
+
)) }),
|
|
4454
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.attachments.map((a, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4455
|
+
AttachmentChip,
|
|
4456
|
+
{
|
|
4457
|
+
attachment: a,
|
|
4458
|
+
onSelect: onFileSelect
|
|
4459
|
+
},
|
|
4460
|
+
a.path ?? `pending-${i}`
|
|
4453
4461
|
)) })
|
|
4454
4462
|
] }) });
|
|
4455
4463
|
}
|
|
4456
4464
|
return /* @__PURE__ */ jsxRuntime.jsx(AssistantMessage, { message, isLast, onFileSelect });
|
|
4457
4465
|
});
|
|
4466
|
+
function iconForMime(mime) {
|
|
4467
|
+
if (!mime) return lucideReact.File;
|
|
4468
|
+
if (mime.startsWith("image/")) return lucideReact.FileImage;
|
|
4469
|
+
if (mime.startsWith("audio/")) return lucideReact.FileAudio;
|
|
4470
|
+
if (mime.startsWith("video/")) return lucideReact.FileVideo;
|
|
4471
|
+
if (mime === "application/pdf") return lucideReact.FileText;
|
|
4472
|
+
if (mime.startsWith("text/")) return lucideReact.FileText;
|
|
4473
|
+
if (mime === "application/json" || mime === "application/xml" || mime.endsWith("+xml") || mime.endsWith("+json") || mime.includes("javascript") || mime.includes("typescript")) {
|
|
4474
|
+
return lucideReact.FileCode;
|
|
4475
|
+
}
|
|
4476
|
+
if (mime === "application/zip" || mime === "application/x-7z-compressed" || mime === "application/x-tar" || mime === "application/gzip" || mime === "application/x-rar-compressed") {
|
|
4477
|
+
return lucideReact.FileArchive;
|
|
4478
|
+
}
|
|
4479
|
+
if (mime === "text/csv" || mime === "application/vnd.ms-excel" || mime.includes("spreadsheet")) {
|
|
4480
|
+
return lucideReact.FileSpreadsheet;
|
|
4481
|
+
}
|
|
4482
|
+
return lucideReact.File;
|
|
4483
|
+
}
|
|
4484
|
+
function formatBytes(n) {
|
|
4485
|
+
if (n == null || !Number.isFinite(n) || n < 0) return "";
|
|
4486
|
+
for (const [unit, divisor] of [
|
|
4487
|
+
["GB", 1e9],
|
|
4488
|
+
["MB", 1e6],
|
|
4489
|
+
["KB", 1e3]
|
|
4490
|
+
]) {
|
|
4491
|
+
if (n >= divisor) return `${(n / divisor).toFixed(1)} ${unit}`;
|
|
4492
|
+
}
|
|
4493
|
+
return `${n} B`;
|
|
4494
|
+
}
|
|
4495
|
+
function AttachmentChip({
|
|
4496
|
+
attachment,
|
|
4497
|
+
onSelect
|
|
4498
|
+
}) {
|
|
4499
|
+
const Icon = iconForMime(attachment.mimeType);
|
|
4500
|
+
const clickable = !!attachment.path && !!onSelect;
|
|
4501
|
+
const sizeLabel = formatBytes(attachment.size);
|
|
4502
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4503
|
+
"button",
|
|
4504
|
+
{
|
|
4505
|
+
type: "button",
|
|
4506
|
+
onClick: clickable ? () => onSelect(attachment.path) : void 0,
|
|
4507
|
+
disabled: !clickable,
|
|
4508
|
+
title: attachment.filename,
|
|
4509
|
+
"aria-label": clickable ? `Open ${attachment.filename}` : `${attachment.filename} (uploading)`,
|
|
4510
|
+
className: "inline-flex items-center gap-2 rounded-md border border-border bg-muted/40 px-2 py-1 text-xs max-w-[16rem] " + (clickable ? "hover:bg-muted cursor-pointer" : "opacity-60 cursor-default"),
|
|
4511
|
+
children: [
|
|
4512
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-3.5 shrink-0" }),
|
|
4513
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: attachment.filename }),
|
|
4514
|
+
sizeLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground shrink-0", children: sizeLabel })
|
|
4515
|
+
]
|
|
4516
|
+
}
|
|
4517
|
+
);
|
|
4518
|
+
}
|
|
4458
4519
|
function AssistantMessage({
|
|
4459
4520
|
message,
|
|
4460
4521
|
isLast,
|
|
@@ -4601,6 +4662,25 @@ function Command({
|
|
|
4601
4662
|
}
|
|
4602
4663
|
);
|
|
4603
4664
|
}
|
|
4665
|
+
function CommandInput({
|
|
4666
|
+
className,
|
|
4667
|
+
...props
|
|
4668
|
+
}) {
|
|
4669
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "command-input-wrapper", className: "p-1", children: /* @__PURE__ */ jsxRuntime.jsxs(InputGroup, { className: "border-transparent border-b-input bg-transparent px-3", children: [
|
|
4670
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4671
|
+
cmdk.Command.Input,
|
|
4672
|
+
{
|
|
4673
|
+
"data-slot": "command-input",
|
|
4674
|
+
className: cn(
|
|
4675
|
+
"w-full px-2 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
|
4676
|
+
className
|
|
4677
|
+
),
|
|
4678
|
+
...props
|
|
4679
|
+
}
|
|
4680
|
+
),
|
|
4681
|
+
/* @__PURE__ */ jsxRuntime.jsx(InputGroupAddon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "size-3.5 shrink-0 opacity-50" }) })
|
|
4682
|
+
] }) });
|
|
4683
|
+
}
|
|
4604
4684
|
function CommandList({
|
|
4605
4685
|
className,
|
|
4606
4686
|
...props
|
|
@@ -4646,6 +4726,19 @@ function CommandGroup({
|
|
|
4646
4726
|
}
|
|
4647
4727
|
);
|
|
4648
4728
|
}
|
|
4729
|
+
function CommandSeparator({
|
|
4730
|
+
className,
|
|
4731
|
+
...props
|
|
4732
|
+
}) {
|
|
4733
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4734
|
+
cmdk.Command.Separator,
|
|
4735
|
+
{
|
|
4736
|
+
"data-slot": "command-separator",
|
|
4737
|
+
className: cn("-mx-1.5 my-1.5 h-px bg-border/50", className),
|
|
4738
|
+
...props
|
|
4739
|
+
}
|
|
4740
|
+
);
|
|
4741
|
+
}
|
|
4649
4742
|
function CommandItem({
|
|
4650
4743
|
className,
|
|
4651
4744
|
children,
|
|
@@ -4827,7 +4920,8 @@ var PromptInputProvider = ({
|
|
|
4827
4920
|
id: nanoid.nanoid(),
|
|
4828
4921
|
mediaType: file.type,
|
|
4829
4922
|
type: "file",
|
|
4830
|
-
url: URL.createObjectURL(file)
|
|
4923
|
+
url: URL.createObjectURL(file),
|
|
4924
|
+
file
|
|
4831
4925
|
}))
|
|
4832
4926
|
]);
|
|
4833
4927
|
}, []);
|
|
@@ -5009,7 +5103,8 @@ var PromptInput = ({
|
|
|
5009
5103
|
id: nanoid.nanoid(),
|
|
5010
5104
|
mediaType: file.type,
|
|
5011
5105
|
type: "file",
|
|
5012
|
-
url: URL.createObjectURL(file)
|
|
5106
|
+
url: URL.createObjectURL(file),
|
|
5107
|
+
file
|
|
5013
5108
|
});
|
|
5014
5109
|
}
|
|
5015
5110
|
return [...prev, ...next];
|
|
@@ -5209,15 +5304,17 @@ var PromptInput = ({
|
|
|
5209
5304
|
}
|
|
5210
5305
|
try {
|
|
5211
5306
|
const convertedFiles = await Promise.all(
|
|
5212
|
-
files.map(async (
|
|
5213
|
-
|
|
5214
|
-
|
|
5307
|
+
files.map(async (entry) => {
|
|
5308
|
+
const isBlob = entry.url?.startsWith("blob:");
|
|
5309
|
+
const isImage = entry.mediaType?.startsWith("image/");
|
|
5310
|
+
if (isBlob && isImage && entry.url) {
|
|
5311
|
+
const dataUrl = await convertBlobUrlToDataUrl(entry.url);
|
|
5215
5312
|
return {
|
|
5216
|
-
...
|
|
5217
|
-
url: dataUrl ??
|
|
5313
|
+
...entry,
|
|
5314
|
+
url: dataUrl ?? entry.url
|
|
5218
5315
|
};
|
|
5219
5316
|
}
|
|
5220
|
-
return
|
|
5317
|
+
return entry;
|
|
5221
5318
|
})
|
|
5222
5319
|
);
|
|
5223
5320
|
const result = onSubmit({ files: convertedFiles, text }, event);
|
|
@@ -5526,7 +5623,7 @@ var Context = ({
|
|
|
5526
5623
|
() => ({ maxTokens, modelId, usage, usedTokens }),
|
|
5527
5624
|
[maxTokens, modelId, usage, usedTokens]
|
|
5528
5625
|
);
|
|
5529
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(HoverCard, { closeDelay:
|
|
5626
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxRuntime.jsx(HoverCard, { closeDelay: 100, openDelay: 0, ...props }) });
|
|
5530
5627
|
};
|
|
5531
5628
|
var ContextIcon = () => {
|
|
5532
5629
|
const { usedTokens, maxTokens } = useContextValue();
|
|
@@ -5767,6 +5864,128 @@ var ContextCacheUsage = ({
|
|
|
5767
5864
|
);
|
|
5768
5865
|
};
|
|
5769
5866
|
|
|
5867
|
+
// src/components/ui/item.tsx
|
|
5868
|
+
init_utils();
|
|
5869
|
+
var itemVariants = classVarianceAuthority.cva(
|
|
5870
|
+
"group/item flex w-full flex-wrap items-center rounded-none border text-sm transition-colors duration-100 outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 [a]:transition-colors [a]:hover:bg-muted",
|
|
5871
|
+
{
|
|
5872
|
+
variants: {
|
|
5873
|
+
variant: {
|
|
5874
|
+
default: "border-transparent",
|
|
5875
|
+
outline: "border-border",
|
|
5876
|
+
muted: "border-transparent bg-muted/50"
|
|
5877
|
+
},
|
|
5878
|
+
size: {
|
|
5879
|
+
default: "gap-3.5 px-4 py-3.5",
|
|
5880
|
+
sm: "gap-3.5 px-3.5 py-3",
|
|
5881
|
+
xs: "gap-2.5 px-3 py-2.5 in-data-[slot=dropdown-menu-content]:p-0"
|
|
5882
|
+
}
|
|
5883
|
+
},
|
|
5884
|
+
defaultVariants: {
|
|
5885
|
+
variant: "default",
|
|
5886
|
+
size: "default"
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
);
|
|
5890
|
+
function Item({
|
|
5891
|
+
className,
|
|
5892
|
+
variant = "default",
|
|
5893
|
+
size = "default",
|
|
5894
|
+
asChild = false,
|
|
5895
|
+
...props
|
|
5896
|
+
}) {
|
|
5897
|
+
const Comp = asChild ? radixUi.Slot.Root : "div";
|
|
5898
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5899
|
+
Comp,
|
|
5900
|
+
{
|
|
5901
|
+
"data-slot": "item",
|
|
5902
|
+
"data-variant": variant,
|
|
5903
|
+
"data-size": size,
|
|
5904
|
+
className: cn(itemVariants({ variant, size, className })),
|
|
5905
|
+
...props
|
|
5906
|
+
}
|
|
5907
|
+
);
|
|
5908
|
+
}
|
|
5909
|
+
var itemMediaVariants = classVarianceAuthority.cva(
|
|
5910
|
+
"flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none",
|
|
5911
|
+
{
|
|
5912
|
+
variants: {
|
|
5913
|
+
variant: {
|
|
5914
|
+
default: "bg-transparent",
|
|
5915
|
+
icon: "[&_svg:not([class*='size-'])]:size-4",
|
|
5916
|
+
image: "size-10 overflow-hidden rounded-none group-data-[size=sm]/item:size-8 group-data-[size=xs]/item:size-6 group-data-[size=xs]/item:rounded-none [&_img]:size-full [&_img]:object-cover"
|
|
5917
|
+
}
|
|
5918
|
+
},
|
|
5919
|
+
defaultVariants: {
|
|
5920
|
+
variant: "default"
|
|
5921
|
+
}
|
|
5922
|
+
}
|
|
5923
|
+
);
|
|
5924
|
+
function ItemMedia({
|
|
5925
|
+
className,
|
|
5926
|
+
variant = "default",
|
|
5927
|
+
...props
|
|
5928
|
+
}) {
|
|
5929
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5930
|
+
"div",
|
|
5931
|
+
{
|
|
5932
|
+
"data-slot": "item-media",
|
|
5933
|
+
"data-variant": variant,
|
|
5934
|
+
className: cn(itemMediaVariants({ variant, className })),
|
|
5935
|
+
...props
|
|
5936
|
+
}
|
|
5937
|
+
);
|
|
5938
|
+
}
|
|
5939
|
+
function ItemContent({ className, ...props }) {
|
|
5940
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5941
|
+
"div",
|
|
5942
|
+
{
|
|
5943
|
+
"data-slot": "item-content",
|
|
5944
|
+
className: cn(
|
|
5945
|
+
"flex flex-1 flex-col gap-1 group-data-[size=xs]/item:gap-0.5 [&+[data-slot=item-content]]:flex-none",
|
|
5946
|
+
className
|
|
5947
|
+
),
|
|
5948
|
+
...props
|
|
5949
|
+
}
|
|
5950
|
+
);
|
|
5951
|
+
}
|
|
5952
|
+
function ItemTitle({ className, ...props }) {
|
|
5953
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5954
|
+
"div",
|
|
5955
|
+
{
|
|
5956
|
+
"data-slot": "item-title",
|
|
5957
|
+
className: cn(
|
|
5958
|
+
"line-clamp-1 flex w-fit items-center gap-2 text-xs leading-snug font-semibold uppercase underline-offset-4",
|
|
5959
|
+
className
|
|
5960
|
+
),
|
|
5961
|
+
...props
|
|
5962
|
+
}
|
|
5963
|
+
);
|
|
5964
|
+
}
|
|
5965
|
+
function ItemDescription({ className, ...props }) {
|
|
5966
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5967
|
+
"p",
|
|
5968
|
+
{
|
|
5969
|
+
"data-slot": "item-description",
|
|
5970
|
+
className: cn(
|
|
5971
|
+
"line-clamp-2 text-left text-sm leading-relaxed font-normal text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
|
|
5972
|
+
className
|
|
5973
|
+
),
|
|
5974
|
+
...props
|
|
5975
|
+
}
|
|
5976
|
+
);
|
|
5977
|
+
}
|
|
5978
|
+
function ItemActions({ className, ...props }) {
|
|
5979
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5980
|
+
"div",
|
|
5981
|
+
{
|
|
5982
|
+
"data-slot": "item-actions",
|
|
5983
|
+
className: cn("flex items-center gap-2", className),
|
|
5984
|
+
...props
|
|
5985
|
+
}
|
|
5986
|
+
);
|
|
5987
|
+
}
|
|
5988
|
+
|
|
5770
5989
|
// src/components/ui/popover.tsx
|
|
5771
5990
|
init_utils();
|
|
5772
5991
|
function Popover({
|
|
@@ -5802,36 +6021,82 @@ function PopoverAnchor({
|
|
|
5802
6021
|
function ChatComposer(props) {
|
|
5803
6022
|
return /* @__PURE__ */ jsxRuntime.jsx(PromptInputProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ChatComposerInner, { ...props }) });
|
|
5804
6023
|
}
|
|
6024
|
+
function iconForMime2(mime) {
|
|
6025
|
+
if (!mime) return lucideReact.File;
|
|
6026
|
+
if (mime.startsWith("audio/")) return lucideReact.FileAudio;
|
|
6027
|
+
if (mime.startsWith("video/")) return lucideReact.FileVideo;
|
|
6028
|
+
if (mime === "application/pdf" || mime.startsWith("text/")) return lucideReact.FileText;
|
|
6029
|
+
if (mime === "application/json" || mime === "application/xml" || mime.endsWith("+xml") || mime.endsWith("+json") || mime.includes("javascript") || mime.includes("typescript")) {
|
|
6030
|
+
return lucideReact.FileCode;
|
|
6031
|
+
}
|
|
6032
|
+
if (mime === "application/zip" || mime === "application/x-7z-compressed" || mime === "application/x-tar" || mime === "application/gzip" || mime === "application/x-rar-compressed") {
|
|
6033
|
+
return lucideReact.FileArchive;
|
|
6034
|
+
}
|
|
6035
|
+
if (mime === "text/csv" || mime === "application/vnd.ms-excel" || mime.includes("spreadsheet")) {
|
|
6036
|
+
return lucideReact.FileSpreadsheet;
|
|
6037
|
+
}
|
|
6038
|
+
return lucideReact.File;
|
|
6039
|
+
}
|
|
6040
|
+
function formatBytes2(n) {
|
|
6041
|
+
if (n == null || !Number.isFinite(n) || n < 0) return "";
|
|
6042
|
+
for (const [unit, divisor] of [
|
|
6043
|
+
["GB", 1e9],
|
|
6044
|
+
["MB", 1e6],
|
|
6045
|
+
["KB", 1e3]
|
|
6046
|
+
]) {
|
|
6047
|
+
if (n >= divisor) return `${(n / divisor).toFixed(1)} ${unit}`;
|
|
6048
|
+
}
|
|
6049
|
+
return `${n} B`;
|
|
6050
|
+
}
|
|
5805
6051
|
function AttachmentPreviewStrip() {
|
|
5806
6052
|
const attachments = useProviderAttachments();
|
|
5807
6053
|
if (attachments.files.length === 0) return null;
|
|
5808
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 px-3 pt-2 pb-1 flex-wrap", children: attachments.files.map((file) =>
|
|
5809
|
-
file.mediaType?.startsWith("image/") && file.url
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
className: "h-16 w-16 rounded-lg border border-border object-cover"
|
|
5815
|
-
}
|
|
5816
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-16 w-16 rounded-lg border border-border bg-muted flex items-center justify-center text-[10px] text-muted-foreground truncate px-1", children: file.filename }),
|
|
5817
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5818
|
-
"button",
|
|
6054
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 px-3 pt-2 pb-1 flex-wrap", children: attachments.files.map((file) => {
|
|
6055
|
+
const isImage = file.mediaType?.startsWith("image/") && !!file.url;
|
|
6056
|
+
const sizeLabel = formatBytes2(file.file?.size);
|
|
6057
|
+
const Icon = iconForMime2(file.mediaType);
|
|
6058
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6059
|
+
Item,
|
|
5819
6060
|
{
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
className: "
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
6061
|
+
variant: "outline",
|
|
6062
|
+
size: "xs",
|
|
6063
|
+
className: "group w-auto max-w-[18rem] rounded-md",
|
|
6064
|
+
title: file.filename,
|
|
6065
|
+
children: [
|
|
6066
|
+
/* @__PURE__ */ jsxRuntime.jsx(ItemMedia, { variant: isImage ? "image" : "icon", children: isImage ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: file.url, alt: file.filename }) : /* @__PURE__ */ jsxRuntime.jsx(Icon, {}) }),
|
|
6067
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ItemContent, { className: "min-w-0", children: [
|
|
6068
|
+
/* @__PURE__ */ jsxRuntime.jsx(ItemTitle, { className: "block w-full truncate normal-case font-medium text-foreground", children: file.filename }),
|
|
6069
|
+
sizeLabel && /* @__PURE__ */ jsxRuntime.jsx(ItemDescription, { className: "text-xs", children: sizeLabel })
|
|
6070
|
+
] }),
|
|
6071
|
+
/* @__PURE__ */ jsxRuntime.jsx(ItemActions, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6072
|
+
"button",
|
|
6073
|
+
{
|
|
6074
|
+
type: "button",
|
|
6075
|
+
onClick: () => attachments.remove(file.id),
|
|
6076
|
+
"aria-label": `Remove ${file.filename}`,
|
|
6077
|
+
className: "hidden group-hover:flex items-center justify-center w-4 h-4 rounded-full bg-destructive text-destructive-foreground text-[10px]",
|
|
6078
|
+
children: "\xD7"
|
|
6079
|
+
}
|
|
6080
|
+
) })
|
|
6081
|
+
]
|
|
6082
|
+
},
|
|
6083
|
+
file.id
|
|
6084
|
+
);
|
|
6085
|
+
}) });
|
|
5827
6086
|
}
|
|
6087
|
+
var MAX_IMAGES_PER_MESSAGE = 5;
|
|
6088
|
+
var MAX_IMAGE_BYTES = 2e7;
|
|
6089
|
+
var MAX_ATTACHMENTS_PER_MESSAGE = 10;
|
|
6090
|
+
var MAX_ATTACHMENT_BYTES = 5e7;
|
|
6091
|
+
var MAX_ATTACHMENTS_TOTAL_BYTES = 2e8;
|
|
5828
6092
|
function ChatComposerInner({
|
|
5829
6093
|
onSend,
|
|
5830
6094
|
onStop,
|
|
5831
6095
|
isRunning,
|
|
5832
6096
|
disabled = false,
|
|
5833
6097
|
disabledReason,
|
|
5834
|
-
tokenUsage
|
|
6098
|
+
tokenUsage,
|
|
6099
|
+
onComposerError
|
|
5835
6100
|
}) {
|
|
5836
6101
|
const { adapter } = useAgentChatAdapterContext();
|
|
5837
6102
|
const { textInput } = usePromptInputController();
|
|
@@ -5856,8 +6121,8 @@ function ChatComposerInner({
|
|
|
5856
6121
|
setWasClosed(true);
|
|
5857
6122
|
}
|
|
5858
6123
|
}, [adapter, showSlashMenu, wasClosed]);
|
|
5859
|
-
const
|
|
5860
|
-
|
|
6124
|
+
const builtinCommands = react.useMemo(
|
|
6125
|
+
() => [
|
|
5861
6126
|
{ value: "/clear", label: "/clear", description: "Clear conversation" },
|
|
5862
6127
|
{ value: "/compress", label: "/compress", description: "Compress context" },
|
|
5863
6128
|
{ value: "/goal", label: "/goal", description: "Define an outcome goal" },
|
|
@@ -5868,195 +6133,307 @@ function ChatComposerInner({
|
|
|
5868
6133
|
{ value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
|
|
5869
6134
|
{ value: "/loop list", label: "/loop list", description: "List active loops" },
|
|
5870
6135
|
{ value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
|
|
5871
|
-
]
|
|
5872
|
-
|
|
5873
|
-
}, [adapterCommands]);
|
|
5874
|
-
const searchQuery = showSlashMenu ? textInput.value.slice(1).toLowerCase() : "";
|
|
5875
|
-
const filteredCommands = react.useMemo(
|
|
5876
|
-
() => slashCommands.filter(
|
|
5877
|
-
(cmd) => cmd.value.slice(1).toLowerCase().includes(searchQuery) || cmd.description.toLowerCase().includes(searchQuery)
|
|
5878
|
-
),
|
|
5879
|
-
[slashCommands, searchQuery]
|
|
6136
|
+
],
|
|
6137
|
+
[]
|
|
5880
6138
|
);
|
|
5881
|
-
const
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
(
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
6139
|
+
const searchQuery = showSlashMenu ? textInput.value.slice(1) : "";
|
|
6140
|
+
const menuOpen = showSlashMenu;
|
|
6141
|
+
const textareaRef = react.useRef(null);
|
|
6142
|
+
const focusTextareaAtEnd = react.useCallback(() => {
|
|
6143
|
+
requestAnimationFrame(() => {
|
|
6144
|
+
const textarea = textareaRef.current;
|
|
6145
|
+
if (!textarea) return;
|
|
6146
|
+
textarea.focus();
|
|
6147
|
+
const end = textarea.value.length;
|
|
6148
|
+
textarea.setSelectionRange(end, end);
|
|
6149
|
+
});
|
|
6150
|
+
}, []);
|
|
5891
6151
|
const handleCommandSelect = react.useCallback(
|
|
5892
6152
|
(commandValue) => {
|
|
5893
6153
|
textInput.setInput(commandValue + " ");
|
|
5894
6154
|
setButtonMenuOpen(false);
|
|
5895
6155
|
setMenuDismissed(true);
|
|
6156
|
+
focusTextareaAtEnd();
|
|
5896
6157
|
},
|
|
5897
|
-
[textInput]
|
|
6158
|
+
[textInput, focusTextareaAtEnd]
|
|
5898
6159
|
);
|
|
5899
|
-
const
|
|
5900
|
-
(
|
|
5901
|
-
|
|
5902
|
-
if (e.key === "ArrowDown") {
|
|
5903
|
-
e.preventDefault();
|
|
5904
|
-
setSelectedIndex((i) => Math.min(i + 1, filteredCommands.length - 1));
|
|
5905
|
-
} else if (e.key === "ArrowUp") {
|
|
5906
|
-
e.preventDefault();
|
|
5907
|
-
setSelectedIndex((i) => Math.max(i - 1, 0));
|
|
5908
|
-
} else if (e.key === "Enter") {
|
|
5909
|
-
e.preventDefault();
|
|
5910
|
-
const command = filteredCommands[selectedIndex];
|
|
5911
|
-
if (command) handleCommandSelect(command.value);
|
|
5912
|
-
} else if (e.key === "Escape") {
|
|
5913
|
-
e.preventDefault();
|
|
5914
|
-
textInput.setInput("");
|
|
5915
|
-
setButtonMenuOpen(false);
|
|
5916
|
-
setMenuDismissed(true);
|
|
5917
|
-
} else if (e.key === "Tab") {
|
|
5918
|
-
e.preventDefault();
|
|
5919
|
-
const command = filteredCommands[selectedIndex];
|
|
5920
|
-
if (command) handleCommandSelect(command.value);
|
|
5921
|
-
}
|
|
6160
|
+
const handleSearchChange = react.useCallback(
|
|
6161
|
+
(next) => {
|
|
6162
|
+
textInput.setInput("/" + next);
|
|
5922
6163
|
},
|
|
5923
|
-
[
|
|
6164
|
+
[textInput]
|
|
5924
6165
|
);
|
|
5925
6166
|
const handleSubmit = react.useCallback(
|
|
5926
6167
|
async (message) => {
|
|
5927
6168
|
const text = message.text.trim();
|
|
5928
6169
|
if (!text || disabled) return;
|
|
5929
6170
|
const images = [];
|
|
6171
|
+
const pending = [];
|
|
5930
6172
|
for (const file of message.files) {
|
|
5931
6173
|
if (file.mediaType?.startsWith("image/") && file.url) {
|
|
5932
6174
|
images.push({ data: file.url, mimeType: file.mediaType });
|
|
6175
|
+
continue;
|
|
6176
|
+
}
|
|
6177
|
+
if (!file.file) {
|
|
6178
|
+
continue;
|
|
5933
6179
|
}
|
|
6180
|
+
pending.push({
|
|
6181
|
+
file: file.file,
|
|
6182
|
+
filename: file.filename ?? file.file.name,
|
|
6183
|
+
mimeType: file.mediaType ?? file.file.type ?? void 0,
|
|
6184
|
+
size: file.file.size
|
|
6185
|
+
});
|
|
6186
|
+
}
|
|
6187
|
+
if (images.length > MAX_IMAGES_PER_MESSAGE) {
|
|
6188
|
+
onComposerError?.({
|
|
6189
|
+
code: "max_images",
|
|
6190
|
+
message: `Maximum ${MAX_IMAGES_PER_MESSAGE} images per message.`
|
|
6191
|
+
});
|
|
6192
|
+
return;
|
|
6193
|
+
}
|
|
6194
|
+
if (pending.length > MAX_ATTACHMENTS_PER_MESSAGE) {
|
|
6195
|
+
onComposerError?.({
|
|
6196
|
+
code: "max_attachments",
|
|
6197
|
+
message: `Maximum ${MAX_ATTACHMENTS_PER_MESSAGE} attachments per message.`
|
|
6198
|
+
});
|
|
6199
|
+
return;
|
|
6200
|
+
}
|
|
6201
|
+
for (const img of images) {
|
|
6202
|
+
if (img.data.length > Math.ceil(MAX_IMAGE_BYTES * 4 / 3)) {
|
|
6203
|
+
onComposerError?.({
|
|
6204
|
+
code: "max_image_size",
|
|
6205
|
+
message: `Image exceeds ${MAX_IMAGE_BYTES / 1e6} MB.`
|
|
6206
|
+
});
|
|
6207
|
+
return;
|
|
6208
|
+
}
|
|
6209
|
+
}
|
|
6210
|
+
let totalAttachmentBytes = 0;
|
|
6211
|
+
for (const a of pending) {
|
|
6212
|
+
if (a.file.size > MAX_ATTACHMENT_BYTES) {
|
|
6213
|
+
onComposerError?.({
|
|
6214
|
+
code: "max_attachment_size",
|
|
6215
|
+
message: `"${a.filename}" exceeds ${MAX_ATTACHMENT_BYTES / 1e6} MB.`
|
|
6216
|
+
});
|
|
6217
|
+
return;
|
|
6218
|
+
}
|
|
6219
|
+
totalAttachmentBytes += a.file.size;
|
|
6220
|
+
}
|
|
6221
|
+
if (totalAttachmentBytes > MAX_ATTACHMENTS_TOTAL_BYTES) {
|
|
6222
|
+
onComposerError?.({
|
|
6223
|
+
code: "max_attachments_total",
|
|
6224
|
+
message: `Attachments exceed ${MAX_ATTACHMENTS_TOTAL_BYTES / 1e6} MB total for this message.`
|
|
6225
|
+
});
|
|
6226
|
+
return;
|
|
5934
6227
|
}
|
|
5935
6228
|
if (isRunning) {
|
|
5936
6229
|
await onStop();
|
|
5937
6230
|
}
|
|
5938
|
-
await onSend(
|
|
6231
|
+
await onSend(
|
|
6232
|
+
text,
|
|
6233
|
+
images.length > 0 ? images : void 0,
|
|
6234
|
+
pending.length > 0 ? pending : void 0
|
|
6235
|
+
);
|
|
5939
6236
|
},
|
|
5940
|
-
[onSend, onStop, isRunning, disabled]
|
|
6237
|
+
[onSend, onStop, onComposerError, isRunning, disabled]
|
|
5941
6238
|
);
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
}
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
6239
|
+
const handlePromptInputError = react.useCallback(
|
|
6240
|
+
(err) => {
|
|
6241
|
+
onComposerError?.({ code: err.code, message: err.message });
|
|
6242
|
+
},
|
|
6243
|
+
[onComposerError]
|
|
6244
|
+
);
|
|
6245
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6246
|
+
Popover,
|
|
6247
|
+
{
|
|
6248
|
+
open: menuOpen,
|
|
6249
|
+
onOpenChange: (open) => {
|
|
6250
|
+
if (!open) {
|
|
6251
|
+
setButtonMenuOpen(false);
|
|
6252
|
+
setMenuDismissed(true);
|
|
5955
6253
|
}
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
/* @__PURE__ */ jsxRuntime.
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
5974
|
-
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
modelId: tokenUsage.model,
|
|
5984
|
-
usage: {
|
|
5985
|
-
inputTokens: tokenUsage.inputTokens,
|
|
5986
|
-
outputTokens: tokenUsage.outputTokens,
|
|
5987
|
-
reasoningTokens: tokenUsage.reasoningTokens,
|
|
5988
|
-
cachedInputTokens: tokenUsage.cachedInputTokens,
|
|
5989
|
-
totalTokens: tokenUsage.totalTokens,
|
|
5990
|
-
inputTokenDetails: void 0,
|
|
5991
|
-
outputTokenDetails: void 0
|
|
5992
|
-
},
|
|
5993
|
-
children: [
|
|
5994
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextTrigger, {}),
|
|
5995
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ContextContent, { children: [
|
|
5996
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextContentHeader, {}),
|
|
5997
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5998
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
5999
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
6000
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
6001
|
-
] }) }),
|
|
6002
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6003
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
6004
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
6005
|
-
] }) }),
|
|
6006
|
-
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6007
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
6008
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
6009
|
-
] }) }),
|
|
6010
|
-
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6011
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
6012
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
6013
|
-
] }) })
|
|
6014
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
6015
|
-
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-3", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6254
|
+
},
|
|
6255
|
+
children: [
|
|
6256
|
+
/* @__PURE__ */ jsxRuntime.jsx(AttachmentPreviewStrip, {}),
|
|
6257
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6258
|
+
PromptInput,
|
|
6259
|
+
{
|
|
6260
|
+
onSubmit: handleSubmit,
|
|
6261
|
+
multiple: true,
|
|
6262
|
+
maxFiles: MAX_IMAGES_PER_MESSAGE + MAX_ATTACHMENTS_PER_MESSAGE,
|
|
6263
|
+
maxFileSize: MAX_ATTACHMENT_BYTES,
|
|
6264
|
+
onError: handlePromptInputError,
|
|
6265
|
+
children: [
|
|
6266
|
+
/* @__PURE__ */ jsxRuntime.jsx(PromptInputBody, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6267
|
+
PromptInputTextarea,
|
|
6268
|
+
{
|
|
6269
|
+
ref: textareaRef,
|
|
6270
|
+
placeholder: disabled ? disabledReason ?? "Session disabled" : "Send a message...",
|
|
6271
|
+
disabled
|
|
6272
|
+
}
|
|
6273
|
+
) }),
|
|
6274
|
+
/* @__PURE__ */ jsxRuntime.jsxs(PromptInputFooter, { children: [
|
|
6275
|
+
/* @__PURE__ */ jsxRuntime.jsxs(PromptInputTools, { children: [
|
|
6276
|
+
/* @__PURE__ */ jsxRuntime.jsxs(PromptInputActionMenu, { children: [
|
|
6277
|
+
/* @__PURE__ */ jsxRuntime.jsx(PromptInputActionMenuTrigger, {}),
|
|
6278
|
+
/* @__PURE__ */ jsxRuntime.jsx(PromptInputActionMenuContent, { children: /* @__PURE__ */ jsxRuntime.jsx(PromptInputActionAddAttachments, {}) })
|
|
6279
|
+
] }),
|
|
6280
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6016
6281
|
Button,
|
|
6017
6282
|
{
|
|
6018
6283
|
type: "button",
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6284
|
+
variant: "ghost",
|
|
6285
|
+
size: "icon-xs",
|
|
6286
|
+
className: "text-muted-foreground",
|
|
6287
|
+
onClick: (e) => {
|
|
6288
|
+
e.preventDefault();
|
|
6289
|
+
setButtonMenuOpen((v) => !v);
|
|
6290
|
+
},
|
|
6291
|
+
"aria-label": "Slash commands",
|
|
6292
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-bold", children: "/" })
|
|
6022
6293
|
}
|
|
6023
|
-
)
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6051
|
-
|
|
6294
|
+
),
|
|
6295
|
+
tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6296
|
+
Context,
|
|
6297
|
+
{
|
|
6298
|
+
usedTokens: tokenUsage.totalTokens,
|
|
6299
|
+
maxTokens: tokenUsage.contextWindow,
|
|
6300
|
+
modelId: tokenUsage.model,
|
|
6301
|
+
usage: {
|
|
6302
|
+
inputTokens: tokenUsage.inputTokens,
|
|
6303
|
+
outputTokens: tokenUsage.outputTokens,
|
|
6304
|
+
reasoningTokens: tokenUsage.reasoningTokens,
|
|
6305
|
+
cachedInputTokens: tokenUsage.cachedInputTokens,
|
|
6306
|
+
totalTokens: tokenUsage.totalTokens,
|
|
6307
|
+
inputTokenDetails: void 0,
|
|
6308
|
+
outputTokenDetails: void 0
|
|
6309
|
+
},
|
|
6310
|
+
children: [
|
|
6311
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextTrigger, {}),
|
|
6312
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ContextContent, { children: [
|
|
6313
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextContentHeader, {}),
|
|
6314
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6315
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6316
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
6317
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
6318
|
+
] }) }),
|
|
6319
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6320
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
6321
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
6322
|
+
] }) }),
|
|
6323
|
+
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6324
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
6325
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
6326
|
+
] }) }),
|
|
6327
|
+
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
6328
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
6329
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
6330
|
+
] }) })
|
|
6331
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
6332
|
+
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
6333
|
+
Button,
|
|
6334
|
+
{
|
|
6335
|
+
type: "button",
|
|
6336
|
+
size: "xs",
|
|
6337
|
+
onClick: () => onSend("/compress"),
|
|
6338
|
+
children: "Compress"
|
|
6339
|
+
}
|
|
6340
|
+
) })
|
|
6341
|
+
] })
|
|
6342
|
+
]
|
|
6343
|
+
}
|
|
6344
|
+
)
|
|
6345
|
+
] }),
|
|
6346
|
+
/* @__PURE__ */ jsxRuntime.jsx(PromptInputSubmit, { status, onStop })
|
|
6347
|
+
] })
|
|
6348
|
+
]
|
|
6349
|
+
}
|
|
6350
|
+
) }),
|
|
6351
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6352
|
+
PopoverContent,
|
|
6353
|
+
{
|
|
6354
|
+
side: "top",
|
|
6355
|
+
align: "start",
|
|
6356
|
+
className: "overflow-hidden p-0",
|
|
6357
|
+
style: { width: "var(--radix-popover-trigger-width)" },
|
|
6358
|
+
onCloseAutoFocus: (e) => e.preventDefault(),
|
|
6359
|
+
onEscapeKeyDown: () => {
|
|
6360
|
+
focusTextareaAtEnd();
|
|
6052
6361
|
},
|
|
6053
|
-
|
|
6054
|
-
|
|
6055
|
-
|
|
6056
|
-
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6362
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(Command, { children: [
|
|
6363
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6364
|
+
CommandInput,
|
|
6365
|
+
{
|
|
6366
|
+
placeholder: "Type a command or search...",
|
|
6367
|
+
value: searchQuery,
|
|
6368
|
+
onValueChange: handleSearchChange
|
|
6369
|
+
}
|
|
6370
|
+
),
|
|
6371
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CommandList, { children: [
|
|
6372
|
+
/* @__PURE__ */ jsxRuntime.jsx(CommandEmpty, { children: "No commands found." }),
|
|
6373
|
+
builtinCommands.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { heading: "Built-in commands", children: builtinCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6374
|
+
CommandItem,
|
|
6375
|
+
{
|
|
6376
|
+
value: cmd.value,
|
|
6377
|
+
keywords: [cmd.description],
|
|
6378
|
+
onSelect: () => handleCommandSelect(cmd.value),
|
|
6379
|
+
className: "grid grid-cols-[12rem_1fr] items-baseline gap-3 [&_svg]:hidden",
|
|
6380
|
+
children: [
|
|
6381
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6382
|
+
"span",
|
|
6383
|
+
{
|
|
6384
|
+
className: "font-mono text-foreground truncate",
|
|
6385
|
+
title: cmd.label,
|
|
6386
|
+
children: cmd.label
|
|
6387
|
+
}
|
|
6388
|
+
),
|
|
6389
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6390
|
+
"span",
|
|
6391
|
+
{
|
|
6392
|
+
className: "min-w-0 truncate text-xs text-muted-foreground",
|
|
6393
|
+
title: cmd.description,
|
|
6394
|
+
children: cmd.description
|
|
6395
|
+
}
|
|
6396
|
+
)
|
|
6397
|
+
]
|
|
6398
|
+
},
|
|
6399
|
+
cmd.value
|
|
6400
|
+
)) }),
|
|
6401
|
+
builtinCommands.length > 0 && adapterCommands.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(CommandSeparator, {}),
|
|
6402
|
+
adapterCommands.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { heading: "Skills", children: adapterCommands.map((cmd) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6403
|
+
CommandItem,
|
|
6404
|
+
{
|
|
6405
|
+
value: cmd.value,
|
|
6406
|
+
keywords: [cmd.description],
|
|
6407
|
+
onSelect: () => handleCommandSelect(cmd.value),
|
|
6408
|
+
className: "grid grid-cols-[12rem_1fr] items-baseline gap-3 [&_svg]:hidden",
|
|
6409
|
+
children: [
|
|
6410
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6411
|
+
"span",
|
|
6412
|
+
{
|
|
6413
|
+
className: "font-mono text-foreground truncate",
|
|
6414
|
+
title: cmd.label,
|
|
6415
|
+
children: cmd.label
|
|
6416
|
+
}
|
|
6417
|
+
),
|
|
6418
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6419
|
+
"span",
|
|
6420
|
+
{
|
|
6421
|
+
className: "min-w-0 truncate text-xs text-muted-foreground",
|
|
6422
|
+
title: cmd.description,
|
|
6423
|
+
children: cmd.description
|
|
6424
|
+
}
|
|
6425
|
+
)
|
|
6426
|
+
]
|
|
6427
|
+
},
|
|
6428
|
+
cmd.value
|
|
6429
|
+
)) })
|
|
6430
|
+
] })
|
|
6431
|
+
] })
|
|
6432
|
+
}
|
|
6433
|
+
)
|
|
6434
|
+
]
|
|
6435
|
+
}
|
|
6436
|
+
);
|
|
6060
6437
|
}
|
|
6061
6438
|
|
|
6062
6439
|
// src/components/ai-elements/artifact.tsx
|
|
@@ -7042,7 +7419,8 @@ function ChatThread({
|
|
|
7042
7419
|
disabledReason,
|
|
7043
7420
|
tokenUsage,
|
|
7044
7421
|
retryIndicator,
|
|
7045
|
-
onRetry
|
|
7422
|
+
onRetry,
|
|
7423
|
+
onComposerError
|
|
7046
7424
|
}) {
|
|
7047
7425
|
const groups = react.useMemo(() => groupMessages(messages), [messages]);
|
|
7048
7426
|
const artifactFallbacks = react.useMemo(() => {
|
|
@@ -7159,7 +7537,8 @@ function ChatThread({
|
|
|
7159
7537
|
isRunning,
|
|
7160
7538
|
disabled,
|
|
7161
7539
|
disabledReason,
|
|
7162
|
-
tokenUsage
|
|
7540
|
+
tokenUsage,
|
|
7541
|
+
onComposerError
|
|
7163
7542
|
}
|
|
7164
7543
|
)
|
|
7165
7544
|
] })
|
|
@@ -8477,12 +8856,24 @@ function applyRetryIndicator(state, event) {
|
|
|
8477
8856
|
}
|
|
8478
8857
|
function applyUserMessage(messages, eventId, data) {
|
|
8479
8858
|
const content = stringValue2(data.content);
|
|
8859
|
+
const images = parseUserMessageImages(data.images);
|
|
8860
|
+
const attachments = parseUserMessageAttachments(data.attachments);
|
|
8480
8861
|
const next = [...messages];
|
|
8481
8862
|
const localIdx = next.findIndex(
|
|
8482
8863
|
(m) => m.role === "user" && m.id.startsWith("local-") && m.content === content
|
|
8483
8864
|
);
|
|
8484
8865
|
if (localIdx >= 0) {
|
|
8485
|
-
|
|
8866
|
+
const local = next[localIdx];
|
|
8867
|
+
next[localIdx] = {
|
|
8868
|
+
...local,
|
|
8869
|
+
id: `evt-${eventId}`,
|
|
8870
|
+
// Server-confirmed lists replace any optimistic display-only
|
|
8871
|
+
// entries: chips that were rendered while the upload was in
|
|
8872
|
+
// flight become clickable because the persisted refs include a
|
|
8873
|
+
// workspace path.
|
|
8874
|
+
images: images ?? local.images,
|
|
8875
|
+
attachments: attachments ?? local.attachments
|
|
8876
|
+
};
|
|
8486
8877
|
return next;
|
|
8487
8878
|
}
|
|
8488
8879
|
next.push({
|
|
@@ -8490,10 +8881,41 @@ function applyUserMessage(messages, eventId, data) {
|
|
|
8490
8881
|
role: "user",
|
|
8491
8882
|
content,
|
|
8492
8883
|
createdAt: /* @__PURE__ */ new Date(),
|
|
8493
|
-
status: "complete"
|
|
8884
|
+
status: "complete",
|
|
8885
|
+
images,
|
|
8886
|
+
attachments
|
|
8494
8887
|
});
|
|
8495
8888
|
return next;
|
|
8496
8889
|
}
|
|
8890
|
+
function parseUserMessageImages(raw) {
|
|
8891
|
+
if (!Array.isArray(raw)) return void 0;
|
|
8892
|
+
const out = [];
|
|
8893
|
+
for (const item of raw) {
|
|
8894
|
+
if (!item || typeof item !== "object") continue;
|
|
8895
|
+
const r = item;
|
|
8896
|
+
const data = typeof r.data === "string" ? r.data : void 0;
|
|
8897
|
+
if (!data) continue;
|
|
8898
|
+
const mimeType = typeof r.mime_type === "string" ? r.mime_type : typeof r.mimeType === "string" ? r.mimeType : void 0;
|
|
8899
|
+
out.push({ data, mimeType });
|
|
8900
|
+
}
|
|
8901
|
+
return out.length > 0 ? out : void 0;
|
|
8902
|
+
}
|
|
8903
|
+
function parseUserMessageAttachments(raw) {
|
|
8904
|
+
if (!Array.isArray(raw)) return void 0;
|
|
8905
|
+
const out = [];
|
|
8906
|
+
for (const item of raw) {
|
|
8907
|
+
if (!item || typeof item !== "object") continue;
|
|
8908
|
+
const r = item;
|
|
8909
|
+
const filename = typeof r.filename === "string" ? r.filename : void 0;
|
|
8910
|
+
if (!filename) continue;
|
|
8911
|
+
const path = typeof r.path === "string" ? r.path : void 0;
|
|
8912
|
+
if (!path) continue;
|
|
8913
|
+
const mimeType = typeof r.mime_type === "string" ? r.mime_type : typeof r.mimeType === "string" ? r.mimeType : void 0;
|
|
8914
|
+
const size = typeof r.size === "number" ? r.size : void 0;
|
|
8915
|
+
out.push({ path, filename, mimeType, size });
|
|
8916
|
+
}
|
|
8917
|
+
return out.length > 0 ? out : void 0;
|
|
8918
|
+
}
|
|
8497
8919
|
function applyBrowserEvent(state, event, browser) {
|
|
8498
8920
|
return withMessages(
|
|
8499
8921
|
{ ...state, browser },
|
|
@@ -9065,7 +9487,7 @@ function useAgentChatRuntime({
|
|
|
9065
9487
|
};
|
|
9066
9488
|
}, [adapter, clearReconnectTimer, closeStream, openStream, sessionId]);
|
|
9067
9489
|
const markSending = react.useCallback(
|
|
9068
|
-
(content, images) => {
|
|
9490
|
+
(content, images, attachments) => {
|
|
9069
9491
|
setState((prev) => ({
|
|
9070
9492
|
...prev,
|
|
9071
9493
|
terminal: false,
|
|
@@ -9079,7 +9501,8 @@ function useAgentChatRuntime({
|
|
|
9079
9501
|
content,
|
|
9080
9502
|
createdAt: /* @__PURE__ */ new Date(),
|
|
9081
9503
|
status: "complete",
|
|
9082
|
-
images: images?.length ? images : void 0
|
|
9504
|
+
images: images?.length ? images : void 0,
|
|
9505
|
+
attachments: attachments?.length ? attachments : void 0
|
|
9083
9506
|
}
|
|
9084
9507
|
]
|
|
9085
9508
|
}));
|
|
@@ -9136,14 +9559,48 @@ function useAgentChatRuntime({
|
|
|
9136
9559
|
});
|
|
9137
9560
|
}, []);
|
|
9138
9561
|
const send = react.useCallback(
|
|
9139
|
-
async (content, images) => {
|
|
9140
|
-
|
|
9141
|
-
|
|
9562
|
+
async (content, images, pendingAttachments) => {
|
|
9563
|
+
const displayAttachments = pendingAttachments?.length ? pendingAttachments.map((a) => ({
|
|
9564
|
+
filename: a.filename,
|
|
9565
|
+
mimeType: a.mimeType,
|
|
9566
|
+
size: a.size ?? a.file.size
|
|
9567
|
+
// path absent until upload completes.
|
|
9568
|
+
})) : void 0;
|
|
9569
|
+
markSending(content, images, displayAttachments);
|
|
9570
|
+
const goal = images?.length || pendingAttachments?.length ? null : parseGoalDefinition(content);
|
|
9571
|
+
const runUploadsAndSend = async (sid) => {
|
|
9572
|
+
let refs;
|
|
9573
|
+
if (pendingAttachments?.length) {
|
|
9574
|
+
const stamp = Date.now();
|
|
9575
|
+
refs = await Promise.all(
|
|
9576
|
+
pendingAttachments.map(async (a, index) => {
|
|
9577
|
+
const safeName = sanitizeUploadFilename(a.file.name);
|
|
9578
|
+
const renamed = new File(
|
|
9579
|
+
[a.file],
|
|
9580
|
+
`${stamp}-${index}-${safeName}`,
|
|
9581
|
+
{ type: a.file.type }
|
|
9582
|
+
);
|
|
9583
|
+
const up = await adapter.uploadWorkspaceFile({
|
|
9584
|
+
sessionId: sid,
|
|
9585
|
+
file: renamed,
|
|
9586
|
+
directory: "uploads"
|
|
9587
|
+
});
|
|
9588
|
+
return {
|
|
9589
|
+
path: up.path,
|
|
9590
|
+
filename: a.filename,
|
|
9591
|
+
mimeType: a.mimeType ?? a.file.type ?? void 0,
|
|
9592
|
+
size: up.size
|
|
9593
|
+
};
|
|
9594
|
+
})
|
|
9595
|
+
);
|
|
9596
|
+
}
|
|
9597
|
+
await sendTurn(adapter, sid, content, images, refs, goal);
|
|
9598
|
+
};
|
|
9142
9599
|
if (!sessionId) {
|
|
9143
9600
|
try {
|
|
9144
9601
|
const session2 = await adapter.createSession({ agentId });
|
|
9145
9602
|
onSessionChange?.(session2.id);
|
|
9146
|
-
await
|
|
9603
|
+
await runUploadsAndSend(session2.id);
|
|
9147
9604
|
} catch (error) {
|
|
9148
9605
|
markSendError(error instanceof Error ? error.message : "send failed");
|
|
9149
9606
|
throw error;
|
|
@@ -9152,7 +9609,7 @@ function useAgentChatRuntime({
|
|
|
9152
9609
|
}
|
|
9153
9610
|
try {
|
|
9154
9611
|
ensureStreamForNewTurn(sessionId);
|
|
9155
|
-
await
|
|
9612
|
+
await runUploadsAndSend(sessionId);
|
|
9156
9613
|
} catch (error) {
|
|
9157
9614
|
markSendError(error instanceof Error ? error.message : "send failed");
|
|
9158
9615
|
throw error;
|
|
@@ -9227,7 +9684,7 @@ function findLastAssistantIndex2(messages) {
|
|
|
9227
9684
|
}
|
|
9228
9685
|
var GOAL_RUBRIC_RE = /\n\s*(?:rubric|criteria)\s*:\s*\n/i;
|
|
9229
9686
|
var GOAL_CONTROL_COMMANDS = /* @__PURE__ */ new Set(["", "status", "pause", "resume", "clear"]);
|
|
9230
|
-
async function sendTurn(adapter, sessionId, content, images, goal) {
|
|
9687
|
+
async function sendTurn(adapter, sessionId, content, images, attachments, goal) {
|
|
9231
9688
|
if (goal && adapter.defineOutcome) {
|
|
9232
9689
|
await adapter.defineOutcome({
|
|
9233
9690
|
sessionId,
|
|
@@ -9236,7 +9693,11 @@ async function sendTurn(adapter, sessionId, content, images, goal) {
|
|
|
9236
9693
|
});
|
|
9237
9694
|
return;
|
|
9238
9695
|
}
|
|
9239
|
-
await adapter.sendMessage({ sessionId, content, images });
|
|
9696
|
+
await adapter.sendMessage({ sessionId, content, images, attachments });
|
|
9697
|
+
}
|
|
9698
|
+
function sanitizeUploadFilename(name) {
|
|
9699
|
+
const stripped = name.replace(/[\/\\\0]/g, "").trim();
|
|
9700
|
+
return stripped.length > 0 ? stripped : "attachment";
|
|
9240
9701
|
}
|
|
9241
9702
|
function parseGoalDefinition(content) {
|
|
9242
9703
|
const text = content.trim();
|
|
@@ -9256,7 +9717,8 @@ function AgentChat({
|
|
|
9256
9717
|
onSessionChange,
|
|
9257
9718
|
onFileSelect,
|
|
9258
9719
|
onMessagesChange,
|
|
9259
|
-
disabled
|
|
9720
|
+
disabled,
|
|
9721
|
+
onComposerError
|
|
9260
9722
|
}) {
|
|
9261
9723
|
const [workspacePath, setWorkspacePath] = react.useState(null);
|
|
9262
9724
|
const runtime = useAgentChatRuntime({
|
|
@@ -9308,14 +9770,15 @@ function AgentChat({
|
|
|
9308
9770
|
messages: runtime.messages,
|
|
9309
9771
|
isRunning: runtime.isRunning,
|
|
9310
9772
|
isLoadingHistory: runtime.isLoadingHistory,
|
|
9311
|
-
onSend: (content, images) => runtime.send(content, images),
|
|
9773
|
+
onSend: (content, images, attachments) => runtime.send(content, images, attachments),
|
|
9312
9774
|
onStop: () => runtime.stop(),
|
|
9313
9775
|
onRetry: runtime.retry,
|
|
9314
9776
|
onFileSelect: handleFileSelect,
|
|
9315
9777
|
disabled: effectiveDisabled,
|
|
9316
9778
|
disabledReason,
|
|
9317
9779
|
tokenUsage: runtime.tokenUsage,
|
|
9318
|
-
retryIndicator: runtime.retryIndicator
|
|
9780
|
+
retryIndicator: runtime.retryIndicator,
|
|
9781
|
+
onComposerError
|
|
9319
9782
|
}
|
|
9320
9783
|
)
|
|
9321
9784
|
}
|