@invergent/agent-chat-react 1.5.5 → 1.5.6

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.js CHANGED
@@ -14,9 +14,9 @@ import { useControllableState } from '@radix-ui/react-use-controllable-state';
14
14
  import { createHighlighter } from 'shiki';
15
15
  import { diffLines, diffWordsWithSpace } from 'diff';
16
16
  import Ansi from 'ansi-to-react';
17
- import { getUsage } from 'tokenlens';
18
17
  import { Command as Command$1 } from 'cmdk';
19
18
  import { nanoid } from 'nanoid';
19
+ import { getUsage } from 'tokenlens';
20
20
  import 'pdfjs-dist/web/pdf_viewer.css';
21
21
  import { formatDistanceToNow } from 'date-fns';
22
22
 
@@ -3692,7 +3692,18 @@ var ChatMessage = memo(function ChatMessage2({
3692
3692
  onFileSelect
3693
3693
  }) {
3694
3694
  if (message.role === "user") {
3695
- return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsx(MessageContent, { children: message.content }) });
3695
+ return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
3696
+ message.content,
3697
+ message.images && message.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsx(
3698
+ "img",
3699
+ {
3700
+ src: img.data,
3701
+ alt: "Attached",
3702
+ className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
3703
+ },
3704
+ i
3705
+ )) })
3706
+ ] }) });
3696
3707
  }
3697
3708
  return /* @__PURE__ */ jsx(AssistantMessage, { message, isLast, onFileSelect });
3698
3709
  });
@@ -3718,491 +3729,172 @@ function AssistantMessage({
3718
3729
  ] })
3719
3730
  ] }) });
3720
3731
  }
3721
- function HoverCard({
3732
+ function InputGroup({ className, ...props }) {
3733
+ return /* @__PURE__ */ jsx(
3734
+ "div",
3735
+ {
3736
+ "data-slot": "input-group",
3737
+ role: "group",
3738
+ className: cn(
3739
+ "group/input-group relative flex h-10 w-full min-w-0 items-center rounded-none border border-transparent border-b-input bg-transparent transition-[color,border-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-none has-data-[align=block-start]:rounded-none has-[[data-slot=input-group-control]:focus-visible]:border-b-ring has-[[data-slot][aria-invalid=true]]:border-b-destructive has-[textarea]:rounded-none has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:has-[[data-slot][aria-invalid=true]]:border-b-destructive/50 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3",
3740
+ className
3741
+ ),
3742
+ ...props
3743
+ }
3744
+ );
3745
+ }
3746
+ var inputGroupAddonVariants = cva(
3747
+ "flex h-auto cursor-text items-center justify-center gap-2 py-2 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-none **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
3748
+ {
3749
+ variants: {
3750
+ align: {
3751
+ "inline-start": "order-first",
3752
+ "inline-end": "order-last",
3753
+ "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
3754
+ "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
3755
+ }
3756
+ },
3757
+ defaultVariants: {
3758
+ align: "inline-start"
3759
+ }
3760
+ }
3761
+ );
3762
+ function InputGroupAddon({
3763
+ className,
3764
+ align = "inline-start",
3722
3765
  ...props
3723
3766
  }) {
3724
- return /* @__PURE__ */ jsx(HoverCard$1.Root, { "data-slot": "hover-card", ...props });
3767
+ return /* @__PURE__ */ jsx(
3768
+ "div",
3769
+ {
3770
+ role: "group",
3771
+ "data-slot": "input-group-addon",
3772
+ "data-align": align,
3773
+ className: cn(inputGroupAddonVariants({ align }), className),
3774
+ onClick: (e) => {
3775
+ if (e.target.closest("button")) {
3776
+ return;
3777
+ }
3778
+ e.currentTarget.parentElement?.querySelector("input")?.focus();
3779
+ },
3780
+ ...props
3781
+ }
3782
+ );
3725
3783
  }
3726
- function HoverCardTrigger({
3784
+ var inputGroupButtonVariants = cva(
3785
+ "flex items-center gap-2 rounded-none text-sm shadow-none",
3786
+ {
3787
+ variants: {
3788
+ size: {
3789
+ xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
3790
+ sm: "",
3791
+ "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
3792
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0"
3793
+ }
3794
+ },
3795
+ defaultVariants: {
3796
+ size: "xs"
3797
+ }
3798
+ }
3799
+ );
3800
+ function InputGroupButton({
3801
+ className,
3802
+ type = "button",
3803
+ variant = "ghost",
3804
+ size = "xs",
3727
3805
  ...props
3728
3806
  }) {
3729
- return /* @__PURE__ */ jsx(HoverCard$1.Trigger, { "data-slot": "hover-card-trigger", ...props });
3807
+ return /* @__PURE__ */ jsx(
3808
+ Button,
3809
+ {
3810
+ type,
3811
+ "data-size": size,
3812
+ variant,
3813
+ className: cn(inputGroupButtonVariants({ size }), className),
3814
+ ...props
3815
+ }
3816
+ );
3730
3817
  }
3731
- function HoverCardContent({
3818
+ function InputGroupTextarea({
3732
3819
  className,
3733
- align = "center",
3734
- sideOffset = 4,
3735
3820
  ...props
3736
3821
  }) {
3737
- return /* @__PURE__ */ jsx(HoverCard$1.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsx(
3738
- HoverCard$1.Content,
3822
+ return /* @__PURE__ */ jsx(
3823
+ Textarea,
3739
3824
  {
3740
- "data-slot": "hover-card-content",
3741
- align,
3742
- sideOffset,
3825
+ "data-slot": "input-group-control",
3743
3826
  className: cn(
3744
- "z-50 w-72 origin-(--radix-hover-card-content-transform-origin) bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
3827
+ "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
3745
3828
  className
3746
3829
  ),
3747
3830
  ...props
3748
3831
  }
3749
- ) });
3832
+ );
3750
3833
  }
3751
- function Progress({
3834
+ function Command({
3752
3835
  className,
3753
- value,
3754
3836
  ...props
3755
3837
  }) {
3756
3838
  return /* @__PURE__ */ jsx(
3757
- Progress$1.Root,
3839
+ Command$1,
3758
3840
  {
3759
- "data-slot": "progress",
3841
+ "data-slot": "command",
3760
3842
  className: cn(
3761
- "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
3843
+ "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
3762
3844
  className
3763
3845
  ),
3764
- ...props,
3765
- children: /* @__PURE__ */ jsx(
3766
- Progress$1.Indicator,
3767
- {
3768
- "data-slot": "progress-indicator",
3769
- className: "size-full flex-1 bg-primary transition-all",
3770
- style: { transform: `translateX(-${100 - (value || 0)}%)` }
3771
- }
3772
- )
3846
+ ...props
3773
3847
  }
3774
3848
  );
3775
3849
  }
3776
- var PERCENT_MAX = 100;
3777
- var ICON_RADIUS = 10;
3778
- var ICON_VIEWBOX = 24;
3779
- var ICON_CENTER = 12;
3780
- var ICON_STROKE_WIDTH = 2;
3781
- var ContextContext = createContext(null);
3782
- var useContextValue = () => {
3783
- const context = useContext(ContextContext);
3784
- if (!context) {
3785
- throw new Error("Context components must be used within Context");
3786
- }
3787
- return context;
3788
- };
3789
- var Context = ({
3790
- usedTokens,
3791
- maxTokens,
3792
- usage,
3793
- modelId,
3850
+ function CommandList({
3851
+ className,
3794
3852
  ...props
3795
- }) => {
3796
- const contextValue = useMemo(
3797
- () => ({ maxTokens, modelId, usage, usedTokens }),
3798
- [maxTokens, modelId, usage, usedTokens]
3853
+ }) {
3854
+ return /* @__PURE__ */ jsx(
3855
+ Command$1.List,
3856
+ {
3857
+ "data-slot": "command-list",
3858
+ className: cn(
3859
+ "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
3860
+ className
3861
+ ),
3862
+ ...props
3863
+ }
3799
3864
  );
3800
- return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
3801
- };
3802
- var ContextIcon = () => {
3803
- const { usedTokens, maxTokens } = useContextValue();
3804
- const circumference = 2 * Math.PI * ICON_RADIUS;
3805
- const usedPercent = usedTokens / maxTokens;
3806
- const dashOffset = circumference * (1 - usedPercent);
3807
- return /* @__PURE__ */ jsxs(
3808
- "svg",
3865
+ }
3866
+ function CommandEmpty({
3867
+ className,
3868
+ ...props
3869
+ }) {
3870
+ return /* @__PURE__ */ jsx(
3871
+ Command$1.Empty,
3809
3872
  {
3810
- "aria-label": "Model context usage",
3811
- height: "20",
3812
- role: "img",
3813
- style: { color: "currentcolor" },
3814
- viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
3815
- width: "20",
3816
- children: [
3817
- /* @__PURE__ */ jsx(
3818
- "circle",
3819
- {
3820
- cx: ICON_CENTER,
3821
- cy: ICON_CENTER,
3822
- fill: "none",
3823
- opacity: "0.25",
3824
- r: ICON_RADIUS,
3825
- stroke: "currentColor",
3826
- strokeWidth: ICON_STROKE_WIDTH
3827
- }
3828
- ),
3829
- /* @__PURE__ */ jsx(
3830
- "circle",
3831
- {
3832
- cx: ICON_CENTER,
3833
- cy: ICON_CENTER,
3834
- fill: "none",
3835
- opacity: "0.7",
3836
- r: ICON_RADIUS,
3837
- stroke: "currentColor",
3838
- strokeDasharray: `${circumference} ${circumference}`,
3839
- strokeDashoffset: dashOffset,
3840
- strokeLinecap: "round",
3841
- strokeWidth: ICON_STROKE_WIDTH,
3842
- style: { transform: "rotate(-90deg)", transformOrigin: "center" }
3843
- }
3844
- )
3845
- ]
3873
+ "data-slot": "command-empty",
3874
+ className: cn("py-6 text-center text-sm", className),
3875
+ ...props
3846
3876
  }
3847
3877
  );
3848
- };
3849
- var ContextTrigger = ({ children, ...props }) => {
3850
- return /* @__PURE__ */ jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsx(ContextIcon, {}) }) });
3851
- };
3852
- var ContextContent = ({
3878
+ }
3879
+ function CommandGroup({
3853
3880
  className,
3854
3881
  ...props
3855
- }) => /* @__PURE__ */ jsx(
3856
- HoverCardContent,
3857
- {
3858
- className: cn("min-w-60 divide-y overflow-hidden p-0", className),
3859
- ...props
3860
- }
3861
- );
3862
- var ContextContentHeader = ({
3863
- children,
3882
+ }) {
3883
+ return /* @__PURE__ */ jsx(
3884
+ Command$1.Group,
3885
+ {
3886
+ "data-slot": "command-group",
3887
+ className: cn(
3888
+ "overflow-hidden p-1.5 text-foreground **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:tracking-wider **:[[cmdk-group-heading]]:text-muted-foreground **:[[cmdk-group-heading]]:uppercase",
3889
+ className
3890
+ ),
3891
+ ...props
3892
+ }
3893
+ );
3894
+ }
3895
+ function CommandItem({
3864
3896
  className,
3865
- ...props
3866
- }) => {
3867
- const { usedTokens, maxTokens } = useContextValue();
3868
- const usedPercent = usedTokens / maxTokens;
3869
- const displayPct = new Intl.NumberFormat("en-US", {
3870
- maximumFractionDigits: 1,
3871
- style: "percent"
3872
- }).format(usedPercent);
3873
- const used = new Intl.NumberFormat("en-US", {
3874
- notation: "compact"
3875
- }).format(usedTokens);
3876
- const total = new Intl.NumberFormat("en-US", {
3877
- notation: "compact"
3878
- }).format(maxTokens);
3879
- return /* @__PURE__ */ jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
3880
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
3881
- /* @__PURE__ */ jsx("p", { children: displayPct }),
3882
- /* @__PURE__ */ jsxs("p", { className: " text-muted-foreground", children: [
3883
- used,
3884
- " / ",
3885
- total
3886
- ] })
3887
- ] }),
3888
- /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
3889
- ] }) });
3890
- };
3891
- var ContextContentBody = ({
3892
- children,
3893
- className,
3894
- ...props
3895
- }) => /* @__PURE__ */ jsx("div", { className: cn("w-full p-3", className), ...props, children });
3896
- var TokensWithCost = ({
3897
- tokens,
3898
- costText
3899
- }) => /* @__PURE__ */ jsxs("span", { children: [
3900
- tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
3901
- notation: "compact"
3902
- }).format(tokens),
3903
- costText ? /* @__PURE__ */ jsxs("span", { className: "ml-2 text-muted-foreground", children: [
3904
- "\u2022 ",
3905
- costText
3906
- ] }) : null
3907
- ] });
3908
- var ContextInputUsage = ({
3909
- className,
3910
- children,
3911
- ...props
3912
- }) => {
3913
- const { usage, modelId } = useContextValue();
3914
- const inputTokens = usage?.inputTokens ?? 0;
3915
- if (children) {
3916
- return children;
3917
- }
3918
- if (!inputTokens) {
3919
- return null;
3920
- }
3921
- const inputCost = modelId ? getUsage({
3922
- modelId,
3923
- usage: { input: inputTokens, output: 0 }
3924
- }).costUSD?.totalUSD : void 0;
3925
- const inputCostText = new Intl.NumberFormat("en-US", {
3926
- currency: "USD",
3927
- style: "currency"
3928
- }).format(inputCost ?? 0);
3929
- return /* @__PURE__ */ jsxs(
3930
- "div",
3931
- {
3932
- className: cn("flex items-center justify-between text-xs", className),
3933
- ...props,
3934
- children: [
3935
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
3936
- /* @__PURE__ */ jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
3937
- ]
3938
- }
3939
- );
3940
- };
3941
- var ContextOutputUsage = ({
3942
- className,
3943
- children,
3944
- ...props
3945
- }) => {
3946
- const { usage, modelId } = useContextValue();
3947
- const outputTokens = usage?.outputTokens ?? 0;
3948
- if (children) {
3949
- return children;
3950
- }
3951
- if (!outputTokens) {
3952
- return null;
3953
- }
3954
- const outputCost = modelId ? getUsage({
3955
- modelId,
3956
- usage: { input: 0, output: outputTokens }
3957
- }).costUSD?.totalUSD : void 0;
3958
- const outputCostText = new Intl.NumberFormat("en-US", {
3959
- currency: "USD",
3960
- style: "currency"
3961
- }).format(outputCost ?? 0);
3962
- return /* @__PURE__ */ jsxs(
3963
- "div",
3964
- {
3965
- className: cn("flex items-center justify-between text-xs", className),
3966
- ...props,
3967
- children: [
3968
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
3969
- /* @__PURE__ */ jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
3970
- ]
3971
- }
3972
- );
3973
- };
3974
- var ContextReasoningUsage = ({
3975
- className,
3976
- children,
3977
- ...props
3978
- }) => {
3979
- const { usage, modelId } = useContextValue();
3980
- const reasoningTokens = usage?.reasoningTokens ?? 0;
3981
- if (children) {
3982
- return children;
3983
- }
3984
- if (!reasoningTokens) {
3985
- return null;
3986
- }
3987
- const reasoningCost = modelId ? getUsage({
3988
- modelId,
3989
- usage: { reasoningTokens }
3990
- }).costUSD?.totalUSD : void 0;
3991
- const reasoningCostText = new Intl.NumberFormat("en-US", {
3992
- currency: "USD",
3993
- style: "currency"
3994
- }).format(reasoningCost ?? 0);
3995
- return /* @__PURE__ */ jsxs(
3996
- "div",
3997
- {
3998
- className: cn("flex items-center justify-between text-xs", className),
3999
- ...props,
4000
- children: [
4001
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
4002
- /* @__PURE__ */ jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
4003
- ]
4004
- }
4005
- );
4006
- };
4007
- var ContextCacheUsage = ({
4008
- className,
4009
- children,
4010
- ...props
4011
- }) => {
4012
- const { usage, modelId } = useContextValue();
4013
- const cacheTokens = usage?.cachedInputTokens ?? 0;
4014
- if (children) {
4015
- return children;
4016
- }
4017
- if (!cacheTokens) {
4018
- return null;
4019
- }
4020
- const cacheCost = modelId ? getUsage({
4021
- modelId,
4022
- usage: { cacheReads: cacheTokens, input: 0, output: 0 }
4023
- }).costUSD?.totalUSD : void 0;
4024
- const cacheCostText = new Intl.NumberFormat("en-US", {
4025
- currency: "USD",
4026
- style: "currency"
4027
- }).format(cacheCost ?? 0);
4028
- return /* @__PURE__ */ jsxs(
4029
- "div",
4030
- {
4031
- className: cn("flex items-center justify-between text-xs", className),
4032
- ...props,
4033
- children: [
4034
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
4035
- /* @__PURE__ */ jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
4036
- ]
4037
- }
4038
- );
4039
- };
4040
- function InputGroup({ className, ...props }) {
4041
- return /* @__PURE__ */ jsx(
4042
- "div",
4043
- {
4044
- "data-slot": "input-group",
4045
- role: "group",
4046
- className: cn(
4047
- "group/input-group relative flex h-10 w-full min-w-0 items-center rounded-none border border-transparent border-b-input bg-transparent transition-[color,border-color] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-data-[align=block-end]:rounded-none has-data-[align=block-start]:rounded-none has-[[data-slot=input-group-control]:focus-visible]:border-b-ring has-[[data-slot][aria-invalid=true]]:border-b-destructive has-[textarea]:rounded-none has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:has-[[data-slot][aria-invalid=true]]:border-b-destructive/50 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3",
4048
- className
4049
- ),
4050
- ...props
4051
- }
4052
- );
4053
- }
4054
- var inputGroupAddonVariants = cva(
4055
- "flex h-auto cursor-text items-center justify-center gap-2 py-2 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 **:data-[slot=kbd]:rounded-none **:data-[slot=kbd]:bg-muted-foreground/10 **:data-[slot=kbd]:px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
4056
- {
4057
- variants: {
4058
- align: {
4059
- "inline-start": "order-first",
4060
- "inline-end": "order-last",
4061
- "block-start": "order-first w-full justify-start pt-3 group-has-[>input]/input-group:pt-3.5 [.border-b]:pb-3.5",
4062
- "block-end": "order-last w-full justify-start pb-3 group-has-[>input]/input-group:pb-3.5 [.border-t]:pt-3.5"
4063
- }
4064
- },
4065
- defaultVariants: {
4066
- align: "inline-start"
4067
- }
4068
- }
4069
- );
4070
- function InputGroupAddon({
4071
- className,
4072
- align = "inline-start",
4073
- ...props
4074
- }) {
4075
- return /* @__PURE__ */ jsx(
4076
- "div",
4077
- {
4078
- role: "group",
4079
- "data-slot": "input-group-addon",
4080
- "data-align": align,
4081
- className: cn(inputGroupAddonVariants({ align }), className),
4082
- onClick: (e) => {
4083
- if (e.target.closest("button")) {
4084
- return;
4085
- }
4086
- e.currentTarget.parentElement?.querySelector("input")?.focus();
4087
- },
4088
- ...props
4089
- }
4090
- );
4091
- }
4092
- var inputGroupButtonVariants = cva(
4093
- "flex items-center gap-2 rounded-none text-sm shadow-none",
4094
- {
4095
- variants: {
4096
- size: {
4097
- xs: "h-6 gap-1 rounded-none px-1.5 text-xs [&>svg:not([class*='size-'])]:size-3.5",
4098
- sm: "",
4099
- "icon-xs": "size-6 p-0 text-xs has-[>svg]:p-0",
4100
- "icon-sm": "size-8 p-0 has-[>svg]:p-0"
4101
- }
4102
- },
4103
- defaultVariants: {
4104
- size: "xs"
4105
- }
4106
- }
4107
- );
4108
- function InputGroupButton({
4109
- className,
4110
- type = "button",
4111
- variant = "ghost",
4112
- size = "xs",
4113
- ...props
4114
- }) {
4115
- return /* @__PURE__ */ jsx(
4116
- Button,
4117
- {
4118
- type,
4119
- "data-size": size,
4120
- variant,
4121
- className: cn(inputGroupButtonVariants({ size }), className),
4122
- ...props
4123
- }
4124
- );
4125
- }
4126
- function InputGroupTextarea({
4127
- className,
4128
- ...props
4129
- }) {
4130
- return /* @__PURE__ */ jsx(
4131
- Textarea,
4132
- {
4133
- "data-slot": "input-group-control",
4134
- className: cn(
4135
- "flex-1 resize-none border-0 bg-transparent py-2.5 ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
4136
- className
4137
- ),
4138
- ...props
4139
- }
4140
- );
4141
- }
4142
- function Command({
4143
- className,
4144
- ...props
4145
- }) {
4146
- return /* @__PURE__ */ jsx(
4147
- Command$1,
4148
- {
4149
- "data-slot": "command",
4150
- className: cn(
4151
- "flex size-full flex-col overflow-hidden bg-popover text-popover-foreground",
4152
- className
4153
- ),
4154
- ...props
4155
- }
4156
- );
4157
- }
4158
- function CommandList({
4159
- className,
4160
- ...props
4161
- }) {
4162
- return /* @__PURE__ */ jsx(
4163
- Command$1.List,
4164
- {
4165
- "data-slot": "command-list",
4166
- className: cn(
4167
- "no-scrollbar max-h-72 scroll-py-1 overflow-x-hidden overflow-y-auto outline-none",
4168
- className
4169
- ),
4170
- ...props
4171
- }
4172
- );
4173
- }
4174
- function CommandEmpty({
4175
- className,
4176
- ...props
4177
- }) {
4178
- return /* @__PURE__ */ jsx(
4179
- Command$1.Empty,
4180
- {
4181
- "data-slot": "command-empty",
4182
- className: cn("py-6 text-center text-sm", className),
4183
- ...props
4184
- }
4185
- );
4186
- }
4187
- function CommandGroup({
4188
- className,
4189
- ...props
4190
- }) {
4191
- return /* @__PURE__ */ jsx(
4192
- Command$1.Group,
4193
- {
4194
- "data-slot": "command-group",
4195
- className: cn(
4196
- "overflow-hidden p-1.5 text-foreground **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-2 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:tracking-wider **:[[cmdk-group-heading]]:text-muted-foreground **:[[cmdk-group-heading]]:uppercase",
4197
- className
4198
- ),
4199
- ...props
4200
- }
4201
- );
4202
- }
4203
- function CommandItem({
4204
- className,
4205
- children,
3897
+ children,
4206
3898
  ...props
4207
3899
  }) {
4208
3900
  return /* @__PURE__ */ jsxs(
@@ -4274,6 +3966,36 @@ function DropdownMenuItem({
4274
3966
  }
4275
3967
  );
4276
3968
  }
3969
+ function HoverCard({
3970
+ ...props
3971
+ }) {
3972
+ return /* @__PURE__ */ jsx(HoverCard$1.Root, { "data-slot": "hover-card", ...props });
3973
+ }
3974
+ function HoverCardTrigger({
3975
+ ...props
3976
+ }) {
3977
+ return /* @__PURE__ */ jsx(HoverCard$1.Trigger, { "data-slot": "hover-card-trigger", ...props });
3978
+ }
3979
+ function HoverCardContent({
3980
+ className,
3981
+ align = "center",
3982
+ sideOffset = 4,
3983
+ ...props
3984
+ }) {
3985
+ return /* @__PURE__ */ jsx(HoverCard$1.Portal, { "data-slot": "hover-card-portal", children: /* @__PURE__ */ jsx(
3986
+ HoverCard$1.Content,
3987
+ {
3988
+ "data-slot": "hover-card-content",
3989
+ align,
3990
+ sideOffset,
3991
+ className: cn(
3992
+ "z-50 w-72 origin-(--radix-hover-card-content-transform-origin) bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
3993
+ className
3994
+ ),
3995
+ ...props
3996
+ }
3997
+ ) });
3998
+ }
4277
3999
  function Spinner({ className, ...props }) {
4278
4000
  return /* @__PURE__ */ jsx(Loader2Icon, { role: "status", "aria-label": "Loading", className: cn("size-4 animate-spin", className), ...props });
4279
4001
  }
@@ -4307,6 +4029,15 @@ var usePromptInputController = () => {
4307
4029
  return ctx;
4308
4030
  };
4309
4031
  var useOptionalPromptInputController = () => useContext(PromptInputController);
4032
+ var useProviderAttachments = () => {
4033
+ const ctx = useContext(ProviderAttachmentsContext);
4034
+ if (!ctx) {
4035
+ throw new Error(
4036
+ "Wrap your component inside <PromptInputProvider> to use useProviderAttachments()."
4037
+ );
4038
+ }
4039
+ return ctx;
4040
+ };
4310
4041
  var useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext);
4311
4042
  var PromptInputProvider = ({
4312
4043
  initialInput: initialTextInput = "",
@@ -4882,95 +4613,384 @@ var PromptInputTools = ({
4882
4613
  className,
4883
4614
  ...props
4884
4615
  }) => /* @__PURE__ */ jsx(
4885
- "div",
4616
+ "div",
4617
+ {
4618
+ className: cn("flex min-w-0 items-center gap-1", className),
4619
+ ...props
4620
+ }
4621
+ );
4622
+ var PromptInputButton = ({
4623
+ variant = "ghost",
4624
+ className,
4625
+ size,
4626
+ tooltip,
4627
+ ...props
4628
+ }) => {
4629
+ const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
4630
+ const button = /* @__PURE__ */ jsx(
4631
+ InputGroupButton,
4632
+ {
4633
+ className: cn(className),
4634
+ size: newSize,
4635
+ type: "button",
4636
+ variant,
4637
+ ...props
4638
+ }
4639
+ );
4640
+ if (!tooltip) {
4641
+ return button;
4642
+ }
4643
+ const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
4644
+ const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
4645
+ const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
4646
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
4647
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: button }),
4648
+ /* @__PURE__ */ jsxs(TooltipContent, { side, children: [
4649
+ tooltipContent,
4650
+ shortcut && /* @__PURE__ */ jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
4651
+ ] })
4652
+ ] });
4653
+ };
4654
+ var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu, { ...props });
4655
+ var PromptInputActionMenuTrigger = ({
4656
+ className,
4657
+ children,
4658
+ ...props
4659
+ }) => /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }) });
4660
+ var PromptInputActionMenuContent = ({
4661
+ className,
4662
+ ...props
4663
+ }) => /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
4664
+ var PromptInputSubmit = ({
4665
+ className,
4666
+ variant = "default",
4667
+ size = "icon-sm",
4668
+ status,
4669
+ onStop,
4670
+ onClick,
4671
+ children,
4672
+ ...props
4673
+ }) => {
4674
+ const isGenerating = status === "submitted" || status === "streaming";
4675
+ let Icon = /* @__PURE__ */ jsx(CornerDownLeftIcon, { className: "size-4" });
4676
+ if (status === "submitted") {
4677
+ Icon = /* @__PURE__ */ jsx(Spinner, {});
4678
+ } else if (status === "streaming") {
4679
+ Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
4680
+ } else if (status === "error") {
4681
+ Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
4682
+ }
4683
+ const handleClick = useCallback(
4684
+ (e) => {
4685
+ if (isGenerating && onStop) {
4686
+ e.preventDefault();
4687
+ onStop();
4688
+ return;
4689
+ }
4690
+ onClick?.(e);
4691
+ },
4692
+ [isGenerating, onStop, onClick]
4693
+ );
4694
+ return /* @__PURE__ */ jsx(
4695
+ InputGroupButton,
4696
+ {
4697
+ "aria-label": isGenerating ? "Stop" : "Submit",
4698
+ className: cn(className),
4699
+ onClick: handleClick,
4700
+ size,
4701
+ type: isGenerating && onStop ? "button" : "submit",
4702
+ variant,
4703
+ ...props,
4704
+ children: children ?? Icon
4705
+ }
4706
+ );
4707
+ };
4708
+ function Progress({
4709
+ className,
4710
+ value,
4711
+ ...props
4712
+ }) {
4713
+ return /* @__PURE__ */ jsx(
4714
+ Progress$1.Root,
4715
+ {
4716
+ "data-slot": "progress",
4717
+ className: cn(
4718
+ "relative flex h-0.5 w-full items-center overflow-x-hidden rounded-none bg-muted",
4719
+ className
4720
+ ),
4721
+ ...props,
4722
+ children: /* @__PURE__ */ jsx(
4723
+ Progress$1.Indicator,
4724
+ {
4725
+ "data-slot": "progress-indicator",
4726
+ className: "size-full flex-1 bg-primary transition-all",
4727
+ style: { transform: `translateX(-${100 - (value || 0)}%)` }
4728
+ }
4729
+ )
4730
+ }
4731
+ );
4732
+ }
4733
+ var PERCENT_MAX = 100;
4734
+ var ICON_RADIUS = 10;
4735
+ var ICON_VIEWBOX = 24;
4736
+ var ICON_CENTER = 12;
4737
+ var ICON_STROKE_WIDTH = 2;
4738
+ var ContextContext = createContext(null);
4739
+ var useContextValue = () => {
4740
+ const context = useContext(ContextContext);
4741
+ if (!context) {
4742
+ throw new Error("Context components must be used within Context");
4743
+ }
4744
+ return context;
4745
+ };
4746
+ var Context = ({
4747
+ usedTokens,
4748
+ maxTokens,
4749
+ usage,
4750
+ modelId,
4751
+ ...props
4752
+ }) => {
4753
+ const contextValue = useMemo(
4754
+ () => ({ maxTokens, modelId, usage, usedTokens }),
4755
+ [maxTokens, modelId, usage, usedTokens]
4756
+ );
4757
+ return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(HoverCard, { closeDelay: 0, openDelay: 0, ...props }) });
4758
+ };
4759
+ var ContextIcon = () => {
4760
+ const { usedTokens, maxTokens } = useContextValue();
4761
+ const circumference = 2 * Math.PI * ICON_RADIUS;
4762
+ const usedPercent = usedTokens / maxTokens;
4763
+ const dashOffset = circumference * (1 - usedPercent);
4764
+ return /* @__PURE__ */ jsxs(
4765
+ "svg",
4766
+ {
4767
+ "aria-label": "Model context usage",
4768
+ height: "20",
4769
+ role: "img",
4770
+ style: { color: "currentcolor" },
4771
+ viewBox: `0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`,
4772
+ width: "20",
4773
+ children: [
4774
+ /* @__PURE__ */ jsx(
4775
+ "circle",
4776
+ {
4777
+ cx: ICON_CENTER,
4778
+ cy: ICON_CENTER,
4779
+ fill: "none",
4780
+ opacity: "0.25",
4781
+ r: ICON_RADIUS,
4782
+ stroke: "currentColor",
4783
+ strokeWidth: ICON_STROKE_WIDTH
4784
+ }
4785
+ ),
4786
+ /* @__PURE__ */ jsx(
4787
+ "circle",
4788
+ {
4789
+ cx: ICON_CENTER,
4790
+ cy: ICON_CENTER,
4791
+ fill: "none",
4792
+ opacity: "0.7",
4793
+ r: ICON_RADIUS,
4794
+ stroke: "currentColor",
4795
+ strokeDasharray: `${circumference} ${circumference}`,
4796
+ strokeDashoffset: dashOffset,
4797
+ strokeLinecap: "round",
4798
+ strokeWidth: ICON_STROKE_WIDTH,
4799
+ style: { transform: "rotate(-90deg)", transformOrigin: "center" }
4800
+ }
4801
+ )
4802
+ ]
4803
+ }
4804
+ );
4805
+ };
4806
+ var ContextTrigger = ({ children, ...props }) => {
4807
+ return /* @__PURE__ */ jsx(HoverCardTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsx(ContextIcon, {}) }) });
4808
+ };
4809
+ var ContextContent = ({
4810
+ className,
4811
+ ...props
4812
+ }) => /* @__PURE__ */ jsx(
4813
+ HoverCardContent,
4886
4814
  {
4887
- className: cn("flex min-w-0 items-center gap-1", className),
4815
+ className: cn("min-w-60 divide-y overflow-hidden p-0", className),
4888
4816
  ...props
4889
4817
  }
4890
4818
  );
4891
- var PromptInputButton = ({
4892
- variant = "ghost",
4819
+ var ContextContentHeader = ({
4820
+ children,
4893
4821
  className,
4894
- size,
4895
- tooltip,
4896
4822
  ...props
4897
4823
  }) => {
4898
- const newSize = size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
4899
- const button = /* @__PURE__ */ jsx(
4900
- InputGroupButton,
4824
+ const { usedTokens, maxTokens } = useContextValue();
4825
+ const usedPercent = usedTokens / maxTokens;
4826
+ const displayPct = new Intl.NumberFormat("en-US", {
4827
+ maximumFractionDigits: 1,
4828
+ style: "percent"
4829
+ }).format(usedPercent);
4830
+ const used = new Intl.NumberFormat("en-US", {
4831
+ notation: "compact"
4832
+ }).format(usedTokens);
4833
+ const total = new Intl.NumberFormat("en-US", {
4834
+ notation: "compact"
4835
+ }).format(maxTokens);
4836
+ return /* @__PURE__ */ jsx("div", { className: cn("w-full space-y-2 p-3", className), ...props, children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
4837
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 text-xs", children: [
4838
+ /* @__PURE__ */ jsx("p", { children: displayPct }),
4839
+ /* @__PURE__ */ jsxs("p", { className: " text-muted-foreground", children: [
4840
+ used,
4841
+ " / ",
4842
+ total
4843
+ ] })
4844
+ ] }),
4845
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: /* @__PURE__ */ jsx(Progress, { className: "bg-muted", value: usedPercent * PERCENT_MAX }) })
4846
+ ] }) });
4847
+ };
4848
+ var ContextContentBody = ({
4849
+ children,
4850
+ className,
4851
+ ...props
4852
+ }) => /* @__PURE__ */ jsx("div", { className: cn("w-full p-3", className), ...props, children });
4853
+ var TokensWithCost = ({
4854
+ tokens,
4855
+ costText
4856
+ }) => /* @__PURE__ */ jsxs("span", { children: [
4857
+ tokens === void 0 ? "\u2014" : new Intl.NumberFormat("en-US", {
4858
+ notation: "compact"
4859
+ }).format(tokens),
4860
+ costText ? /* @__PURE__ */ jsxs("span", { className: "ml-2 text-muted-foreground", children: [
4861
+ "\u2022 ",
4862
+ costText
4863
+ ] }) : null
4864
+ ] });
4865
+ var ContextInputUsage = ({
4866
+ className,
4867
+ children,
4868
+ ...props
4869
+ }) => {
4870
+ const { usage, modelId } = useContextValue();
4871
+ const inputTokens = usage?.inputTokens ?? 0;
4872
+ if (children) {
4873
+ return children;
4874
+ }
4875
+ if (!inputTokens) {
4876
+ return null;
4877
+ }
4878
+ const inputCost = modelId ? getUsage({
4879
+ modelId,
4880
+ usage: { input: inputTokens, output: 0 }
4881
+ }).costUSD?.totalUSD : void 0;
4882
+ const inputCostText = new Intl.NumberFormat("en-US", {
4883
+ currency: "USD",
4884
+ style: "currency"
4885
+ }).format(inputCost ?? 0);
4886
+ return /* @__PURE__ */ jsxs(
4887
+ "div",
4901
4888
  {
4902
- className: cn(className),
4903
- size: newSize,
4904
- type: "button",
4905
- variant,
4906
- ...props
4889
+ className: cn("flex items-center justify-between text-xs", className),
4890
+ ...props,
4891
+ children: [
4892
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
4893
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: inputCostText, tokens: inputTokens })
4894
+ ]
4907
4895
  }
4908
4896
  );
4909
- if (!tooltip) {
4910
- return button;
4911
- }
4912
- const tooltipContent = typeof tooltip === "string" ? tooltip : tooltip.content;
4913
- const shortcut = typeof tooltip === "string" ? void 0 : tooltip.shortcut;
4914
- const side = typeof tooltip === "string" ? "top" : tooltip.side ?? "top";
4915
- return /* @__PURE__ */ jsxs(Tooltip, { children: [
4916
- /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: button }),
4917
- /* @__PURE__ */ jsxs(TooltipContent, { side, children: [
4918
- tooltipContent,
4919
- shortcut && /* @__PURE__ */ jsx("span", { className: "ml-2 text-muted-foreground", children: shortcut })
4920
- ] })
4921
- ] });
4922
4897
  };
4923
- var PromptInputActionMenu = (props) => /* @__PURE__ */ jsx(DropdownMenu, { ...props });
4924
- var PromptInputActionMenuTrigger = ({
4898
+ var ContextOutputUsage = ({
4925
4899
  className,
4926
4900
  children,
4927
4901
  ...props
4928
- }) => /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PromptInputButton, { className, ...props, children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" }) }) });
4929
- var PromptInputActionMenuContent = ({
4902
+ }) => {
4903
+ const { usage, modelId } = useContextValue();
4904
+ const outputTokens = usage?.outputTokens ?? 0;
4905
+ if (children) {
4906
+ return children;
4907
+ }
4908
+ if (!outputTokens) {
4909
+ return null;
4910
+ }
4911
+ const outputCost = modelId ? getUsage({
4912
+ modelId,
4913
+ usage: { input: 0, output: outputTokens }
4914
+ }).costUSD?.totalUSD : void 0;
4915
+ const outputCostText = new Intl.NumberFormat("en-US", {
4916
+ currency: "USD",
4917
+ style: "currency"
4918
+ }).format(outputCost ?? 0);
4919
+ return /* @__PURE__ */ jsxs(
4920
+ "div",
4921
+ {
4922
+ className: cn("flex items-center justify-between text-xs", className),
4923
+ ...props,
4924
+ children: [
4925
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
4926
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: outputCostText, tokens: outputTokens })
4927
+ ]
4928
+ }
4929
+ );
4930
+ };
4931
+ var ContextReasoningUsage = ({
4930
4932
  className,
4933
+ children,
4931
4934
  ...props
4932
- }) => /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", className: cn(className), ...props });
4933
- var PromptInputSubmit = ({
4935
+ }) => {
4936
+ const { usage, modelId } = useContextValue();
4937
+ const reasoningTokens = usage?.reasoningTokens ?? 0;
4938
+ if (children) {
4939
+ return children;
4940
+ }
4941
+ if (!reasoningTokens) {
4942
+ return null;
4943
+ }
4944
+ const reasoningCost = modelId ? getUsage({
4945
+ modelId,
4946
+ usage: { reasoningTokens }
4947
+ }).costUSD?.totalUSD : void 0;
4948
+ const reasoningCostText = new Intl.NumberFormat("en-US", {
4949
+ currency: "USD",
4950
+ style: "currency"
4951
+ }).format(reasoningCost ?? 0);
4952
+ return /* @__PURE__ */ jsxs(
4953
+ "div",
4954
+ {
4955
+ className: cn("flex items-center justify-between text-xs", className),
4956
+ ...props,
4957
+ children: [
4958
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
4959
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: reasoningCostText, tokens: reasoningTokens })
4960
+ ]
4961
+ }
4962
+ );
4963
+ };
4964
+ var ContextCacheUsage = ({
4934
4965
  className,
4935
- variant = "default",
4936
- size = "icon-sm",
4937
- status,
4938
- onStop,
4939
- onClick,
4940
4966
  children,
4941
4967
  ...props
4942
4968
  }) => {
4943
- const isGenerating = status === "submitted" || status === "streaming";
4944
- let Icon = /* @__PURE__ */ jsx(CornerDownLeftIcon, { className: "size-4" });
4945
- if (status === "submitted") {
4946
- Icon = /* @__PURE__ */ jsx(Spinner, {});
4947
- } else if (status === "streaming") {
4948
- Icon = /* @__PURE__ */ jsx(SquareIcon, { className: "size-4" });
4949
- } else if (status === "error") {
4950
- Icon = /* @__PURE__ */ jsx(XIcon, { className: "size-4" });
4969
+ const { usage, modelId } = useContextValue();
4970
+ const cacheTokens = usage?.cachedInputTokens ?? 0;
4971
+ if (children) {
4972
+ return children;
4951
4973
  }
4952
- const handleClick = useCallback(
4953
- (e) => {
4954
- if (isGenerating && onStop) {
4955
- e.preventDefault();
4956
- onStop();
4957
- return;
4958
- }
4959
- onClick?.(e);
4960
- },
4961
- [isGenerating, onStop, onClick]
4962
- );
4963
- return /* @__PURE__ */ jsx(
4964
- InputGroupButton,
4974
+ if (!cacheTokens) {
4975
+ return null;
4976
+ }
4977
+ const cacheCost = modelId ? getUsage({
4978
+ modelId,
4979
+ usage: { cacheReads: cacheTokens, input: 0, output: 0 }
4980
+ }).costUSD?.totalUSD : void 0;
4981
+ const cacheCostText = new Intl.NumberFormat("en-US", {
4982
+ currency: "USD",
4983
+ style: "currency"
4984
+ }).format(cacheCost ?? 0);
4985
+ return /* @__PURE__ */ jsxs(
4986
+ "div",
4965
4987
  {
4966
- "aria-label": isGenerating ? "Stop" : "Submit",
4967
- className: cn(className),
4968
- onClick: handleClick,
4969
- size,
4970
- type: isGenerating && onStop ? "button" : "submit",
4971
- variant,
4988
+ className: cn("flex items-center justify-between text-xs", className),
4972
4989
  ...props,
4973
- children: children ?? Icon
4990
+ children: [
4991
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
4992
+ /* @__PURE__ */ jsx(TokensWithCost, { costText: cacheCostText, tokens: cacheTokens })
4993
+ ]
4974
4994
  }
4975
4995
  );
4976
4996
  };
@@ -5007,6 +5027,29 @@ function PopoverAnchor({
5007
5027
  function ChatComposer(props) {
5008
5028
  return /* @__PURE__ */ jsx(PromptInputProvider, { children: /* @__PURE__ */ jsx(ChatComposerInner, { ...props }) });
5009
5029
  }
5030
+ function AttachmentPreviewStrip() {
5031
+ const attachments = useProviderAttachments();
5032
+ if (attachments.files.length === 0) return null;
5033
+ return /* @__PURE__ */ jsx("div", { className: "flex gap-2 px-3 pt-2 pb-1 flex-wrap", children: attachments.files.map((file) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
5034
+ file.mediaType?.startsWith("image/") && file.url ? /* @__PURE__ */ jsx(
5035
+ "img",
5036
+ {
5037
+ src: file.url,
5038
+ alt: file.filename,
5039
+ className: "h-16 w-16 rounded-lg border border-border object-cover"
5040
+ }
5041
+ ) : /* @__PURE__ */ 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 }),
5042
+ /* @__PURE__ */ jsx(
5043
+ "button",
5044
+ {
5045
+ type: "button",
5046
+ onClick: () => attachments.remove(file.id),
5047
+ className: "absolute -top-1.5 -right-1.5 hidden group-hover:flex items-center justify-center w-4 h-4 rounded-full bg-destructive text-destructive-foreground text-[10px]",
5048
+ children: "\xD7"
5049
+ }
5050
+ )
5051
+ ] }, file.id)) });
5052
+ }
5010
5053
  function ChatComposerInner({
5011
5054
  onSend,
5012
5055
  onStop,
@@ -5099,7 +5142,13 @@ function ChatComposerInner({
5099
5142
  (message) => {
5100
5143
  const text = message.text.trim();
5101
5144
  if (!text || isRunning || disabled) return;
5102
- onSend(text);
5145
+ const images = [];
5146
+ for (const file of message.files) {
5147
+ if (file.mediaType?.startsWith("image/") && file.url) {
5148
+ images.push({ data: file.url, mimeType: file.mediaType });
5149
+ }
5150
+ }
5151
+ onSend(text, images.length > 0 ? images : void 0);
5103
5152
  },
5104
5153
  [onSend, isRunning, disabled]
5105
5154
  );
@@ -5108,7 +5157,8 @@ function ChatComposerInner({
5108
5157
  setButtonMenuOpen(false);
5109
5158
  }
5110
5159
  }, children: [
5111
- /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(PromptInput, { onSubmit: handleSubmit, children: [
5160
+ /* @__PURE__ */ jsx(AttachmentPreviewStrip, {}),
5161
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs(PromptInput, { onSubmit: handleSubmit, accept: "image/*", multiple: true, children: [
5112
5162
  /* @__PURE__ */ jsx(PromptInputBody, { children: /* @__PURE__ */ jsx(
5113
5163
  PromptInputTextarea,
5114
5164
  {
@@ -7989,23 +8039,27 @@ function useAgentChatRuntime({
7989
8039
  closeStream();
7990
8040
  };
7991
8041
  }, [adapter, clearReconnectTimer, closeStream, sessionId]);
7992
- const markSending = useCallback((content) => {
7993
- setState((prev) => ({
7994
- ...prev,
7995
- terminal: false,
7996
- isRunning: true,
7997
- messages: [
7998
- ...prev.messages,
7999
- {
8000
- id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8001
- role: "user",
8002
- content,
8003
- createdAt: /* @__PURE__ */ new Date(),
8004
- status: "complete"
8005
- }
8006
- ]
8007
- }));
8008
- }, []);
8042
+ const markSending = useCallback(
8043
+ (content, images) => {
8044
+ setState((prev) => ({
8045
+ ...prev,
8046
+ terminal: false,
8047
+ isRunning: true,
8048
+ messages: [
8049
+ ...prev.messages,
8050
+ {
8051
+ id: `local-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
8052
+ role: "user",
8053
+ content,
8054
+ createdAt: /* @__PURE__ */ new Date(),
8055
+ status: "complete",
8056
+ images: images?.length ? images : void 0
8057
+ }
8058
+ ]
8059
+ }));
8060
+ },
8061
+ []
8062
+ );
8009
8063
  const markSendError = useCallback((errorText) => {
8010
8064
  setState((prev) => {
8011
8065
  for (let i = prev.messages.length - 1; i >= 0; i--) {
@@ -8056,13 +8110,13 @@ function useAgentChatRuntime({
8056
8110
  });
8057
8111
  }, []);
8058
8112
  const send = useCallback(
8059
- async (content) => {
8060
- markSending(content);
8113
+ async (content, images) => {
8114
+ markSending(content, images);
8061
8115
  if (!sessionId) {
8062
8116
  try {
8063
8117
  const session = await adapter.createSession({ agentId });
8064
8118
  onSessionChange?.(session.id);
8065
- await adapter.sendMessage({ sessionId: session.id, content });
8119
+ await adapter.sendMessage({ sessionId: session.id, content, images });
8066
8120
  } catch (error) {
8067
8121
  markSendError(error instanceof Error ? error.message : "send failed");
8068
8122
  throw error;
@@ -8070,7 +8124,7 @@ function useAgentChatRuntime({
8070
8124
  return;
8071
8125
  }
8072
8126
  try {
8073
- await adapter.sendMessage({ sessionId, content });
8127
+ await adapter.sendMessage({ sessionId, content, images });
8074
8128
  } catch (error) {
8075
8129
  markSendError(error instanceof Error ? error.message : "send failed");
8076
8130
  throw error;
@@ -8176,7 +8230,7 @@ function AgentChat({
8176
8230
  messages: runtime.messages,
8177
8231
  isRunning: runtime.isRunning,
8178
8232
  isLoadingHistory: runtime.isLoadingHistory,
8179
- onSend: (content) => void runtime.send(content),
8233
+ onSend: (content, images) => void runtime.send(content, images),
8180
8234
  onStop: () => void runtime.stop(),
8181
8235
  onRetry: runtime.retry,
8182
8236
  onFileSelect: handleFileSelect,