@nikitadmitrieff/feedback-chat 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/init.js +158 -0
- package/dist/client/index.d.ts +43 -0
- package/dist/client/index.js +2294 -0
- package/dist/server/index.d.ts +115 -0
- package/dist/server/index.js +548 -0
- package/dist/styles.css +298 -0
- package/package.json +61 -0
|
@@ -0,0 +1,2294 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/client/feedback-panel.tsx
|
|
4
|
+
import { useState as useState8, useEffect as useEffect4, useRef as useRef4, useCallback as useCallback4 } from "react";
|
|
5
|
+
import { DefaultChatTransport } from "ai";
|
|
6
|
+
import { AssistantRuntimeProvider, useThreadRuntime as useThreadRuntime3 } from "@assistant-ui/react";
|
|
7
|
+
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
|
|
8
|
+
import { ArrowUp, PanelRight, X as X3, AlertCircle, ArrowRight, Loader2 as Loader22, Lightbulb } from "lucide-react";
|
|
9
|
+
|
|
10
|
+
// src/client/attachment.tsx
|
|
11
|
+
import { useEffect, useState } from "react";
|
|
12
|
+
import { XIcon as XIcon2, PlusIcon, FileText } from "lucide-react";
|
|
13
|
+
import {
|
|
14
|
+
AttachmentPrimitive,
|
|
15
|
+
ComposerPrimitive,
|
|
16
|
+
MessagePrimitive,
|
|
17
|
+
useAuiState,
|
|
18
|
+
useAui
|
|
19
|
+
} from "@assistant-ui/react";
|
|
20
|
+
|
|
21
|
+
// ../../node_modules/zustand/esm/vanilla/shallow.mjs
|
|
22
|
+
var isIterable = (obj) => Symbol.iterator in obj;
|
|
23
|
+
var hasIterableEntries = (value) => (
|
|
24
|
+
// HACK: avoid checking entries type
|
|
25
|
+
"entries" in value
|
|
26
|
+
);
|
|
27
|
+
var compareEntries = (valueA, valueB) => {
|
|
28
|
+
const mapA = valueA instanceof Map ? valueA : new Map(valueA.entries());
|
|
29
|
+
const mapB = valueB instanceof Map ? valueB : new Map(valueB.entries());
|
|
30
|
+
if (mapA.size !== mapB.size) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
for (const [key, value] of mapA) {
|
|
34
|
+
if (!mapB.has(key) || !Object.is(value, mapB.get(key))) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
40
|
+
var compareIterables = (valueA, valueB) => {
|
|
41
|
+
const iteratorA = valueA[Symbol.iterator]();
|
|
42
|
+
const iteratorB = valueB[Symbol.iterator]();
|
|
43
|
+
let nextA = iteratorA.next();
|
|
44
|
+
let nextB = iteratorB.next();
|
|
45
|
+
while (!nextA.done && !nextB.done) {
|
|
46
|
+
if (!Object.is(nextA.value, nextB.value)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
nextA = iteratorA.next();
|
|
50
|
+
nextB = iteratorB.next();
|
|
51
|
+
}
|
|
52
|
+
return !!nextA.done && !!nextB.done;
|
|
53
|
+
};
|
|
54
|
+
function shallow(valueA, valueB) {
|
|
55
|
+
if (Object.is(valueA, valueB)) {
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
if (typeof valueA !== "object" || valueA === null || typeof valueB !== "object" || valueB === null) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
if (Object.getPrototypeOf(valueA) !== Object.getPrototypeOf(valueB)) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (isIterable(valueA) && isIterable(valueB)) {
|
|
65
|
+
if (hasIterableEntries(valueA) && hasIterableEntries(valueB)) {
|
|
66
|
+
return compareEntries(valueA, valueB);
|
|
67
|
+
}
|
|
68
|
+
return compareIterables(valueA, valueB);
|
|
69
|
+
}
|
|
70
|
+
return compareEntries(
|
|
71
|
+
{ entries: () => Object.entries(valueA) },
|
|
72
|
+
{ entries: () => Object.entries(valueB) }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ../../node_modules/zustand/esm/react/shallow.mjs
|
|
77
|
+
import React from "react";
|
|
78
|
+
function useShallow(selector) {
|
|
79
|
+
const prev = React.useRef(void 0);
|
|
80
|
+
return (state) => {
|
|
81
|
+
const next = selector(state);
|
|
82
|
+
return shallow(prev.current, next) ? prev.current : prev.current = next;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/client/ui/tooltip.tsx
|
|
87
|
+
import { Tooltip as TooltipPrimitive } from "radix-ui";
|
|
88
|
+
|
|
89
|
+
// src/client/ui/utils.ts
|
|
90
|
+
import { clsx } from "clsx";
|
|
91
|
+
import { twMerge } from "tailwind-merge";
|
|
92
|
+
function cn(...inputs) {
|
|
93
|
+
return twMerge(clsx(inputs));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/client/ui/tooltip.tsx
|
|
97
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
98
|
+
function Tooltip({
|
|
99
|
+
...props
|
|
100
|
+
}) {
|
|
101
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props });
|
|
102
|
+
}
|
|
103
|
+
function TooltipTrigger({
|
|
104
|
+
...props
|
|
105
|
+
}) {
|
|
106
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
107
|
+
}
|
|
108
|
+
function TooltipContent({
|
|
109
|
+
className,
|
|
110
|
+
sideOffset = 0,
|
|
111
|
+
children,
|
|
112
|
+
...props
|
|
113
|
+
}) {
|
|
114
|
+
return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
115
|
+
TooltipPrimitive.Content,
|
|
116
|
+
{
|
|
117
|
+
"data-slot": "tooltip-content",
|
|
118
|
+
sideOffset,
|
|
119
|
+
className: cn(
|
|
120
|
+
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
121
|
+
className
|
|
122
|
+
),
|
|
123
|
+
...props,
|
|
124
|
+
children: [
|
|
125
|
+
children,
|
|
126
|
+
/* @__PURE__ */ jsx(TooltipPrimitive.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
|
|
127
|
+
]
|
|
128
|
+
}
|
|
129
|
+
) });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// src/client/ui/dialog.tsx
|
|
133
|
+
import { XIcon } from "lucide-react";
|
|
134
|
+
import { Dialog as DialogPrimitive } from "radix-ui";
|
|
135
|
+
|
|
136
|
+
// src/client/ui/button.tsx
|
|
137
|
+
import { cva } from "class-variance-authority";
|
|
138
|
+
import { Slot } from "radix-ui";
|
|
139
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
140
|
+
var buttonVariants = cva(
|
|
141
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
|
142
|
+
{
|
|
143
|
+
variants: {
|
|
144
|
+
variant: {
|
|
145
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
146
|
+
destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
|
147
|
+
outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
|
148
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
149
|
+
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
|
150
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
151
|
+
},
|
|
152
|
+
size: {
|
|
153
|
+
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
|
154
|
+
xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
155
|
+
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
|
156
|
+
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
|
157
|
+
icon: "size-9",
|
|
158
|
+
"icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
|
|
159
|
+
"icon-sm": "size-8",
|
|
160
|
+
"icon-lg": "size-10"
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
defaultVariants: {
|
|
164
|
+
variant: "default",
|
|
165
|
+
size: "default"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
function Button({
|
|
170
|
+
className,
|
|
171
|
+
variant = "default",
|
|
172
|
+
size = "default",
|
|
173
|
+
asChild = false,
|
|
174
|
+
...props
|
|
175
|
+
}) {
|
|
176
|
+
const Comp = asChild ? Slot.Root : "button";
|
|
177
|
+
return /* @__PURE__ */ jsx2(
|
|
178
|
+
Comp,
|
|
179
|
+
{
|
|
180
|
+
"data-slot": "button",
|
|
181
|
+
"data-variant": variant,
|
|
182
|
+
"data-size": size,
|
|
183
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
184
|
+
...props
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/client/ui/dialog.tsx
|
|
190
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
191
|
+
function Dialog({
|
|
192
|
+
...props
|
|
193
|
+
}) {
|
|
194
|
+
return /* @__PURE__ */ jsx3(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
195
|
+
}
|
|
196
|
+
function DialogTrigger({
|
|
197
|
+
...props
|
|
198
|
+
}) {
|
|
199
|
+
return /* @__PURE__ */ jsx3(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
200
|
+
}
|
|
201
|
+
function DialogPortal({
|
|
202
|
+
...props
|
|
203
|
+
}) {
|
|
204
|
+
return /* @__PURE__ */ jsx3(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
205
|
+
}
|
|
206
|
+
function DialogOverlay({
|
|
207
|
+
className,
|
|
208
|
+
...props
|
|
209
|
+
}) {
|
|
210
|
+
return /* @__PURE__ */ jsx3(
|
|
211
|
+
DialogPrimitive.Overlay,
|
|
212
|
+
{
|
|
213
|
+
"data-slot": "dialog-overlay",
|
|
214
|
+
className: cn(
|
|
215
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
216
|
+
className
|
|
217
|
+
),
|
|
218
|
+
...props
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
function DialogContent({
|
|
223
|
+
className,
|
|
224
|
+
children,
|
|
225
|
+
showCloseButton = true,
|
|
226
|
+
...props
|
|
227
|
+
}) {
|
|
228
|
+
return /* @__PURE__ */ jsxs2(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
229
|
+
/* @__PURE__ */ jsx3(DialogOverlay, {}),
|
|
230
|
+
/* @__PURE__ */ jsxs2(
|
|
231
|
+
DialogPrimitive.Content,
|
|
232
|
+
{
|
|
233
|
+
"data-slot": "dialog-content",
|
|
234
|
+
className: cn(
|
|
235
|
+
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 outline-none sm:max-w-lg",
|
|
236
|
+
className
|
|
237
|
+
),
|
|
238
|
+
...props,
|
|
239
|
+
children: [
|
|
240
|
+
children,
|
|
241
|
+
showCloseButton && /* @__PURE__ */ jsxs2(
|
|
242
|
+
DialogPrimitive.Close,
|
|
243
|
+
{
|
|
244
|
+
"data-slot": "dialog-close",
|
|
245
|
+
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ jsx3(XIcon, {}),
|
|
248
|
+
/* @__PURE__ */ jsx3("span", { className: "sr-only", children: "Close" })
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
)
|
|
252
|
+
]
|
|
253
|
+
}
|
|
254
|
+
)
|
|
255
|
+
] });
|
|
256
|
+
}
|
|
257
|
+
function DialogTitle({
|
|
258
|
+
className,
|
|
259
|
+
...props
|
|
260
|
+
}) {
|
|
261
|
+
return /* @__PURE__ */ jsx3(
|
|
262
|
+
DialogPrimitive.Title,
|
|
263
|
+
{
|
|
264
|
+
"data-slot": "dialog-title",
|
|
265
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
266
|
+
...props
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/client/ui/avatar.tsx
|
|
272
|
+
import { Avatar as AvatarPrimitive } from "radix-ui";
|
|
273
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
274
|
+
function Avatar({
|
|
275
|
+
className,
|
|
276
|
+
size = "default",
|
|
277
|
+
...props
|
|
278
|
+
}) {
|
|
279
|
+
return /* @__PURE__ */ jsx4(
|
|
280
|
+
AvatarPrimitive.Root,
|
|
281
|
+
{
|
|
282
|
+
"data-slot": "avatar",
|
|
283
|
+
"data-size": size,
|
|
284
|
+
className: cn(
|
|
285
|
+
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
286
|
+
className
|
|
287
|
+
),
|
|
288
|
+
...props
|
|
289
|
+
}
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
function AvatarImage({
|
|
293
|
+
className,
|
|
294
|
+
...props
|
|
295
|
+
}) {
|
|
296
|
+
return /* @__PURE__ */ jsx4(
|
|
297
|
+
AvatarPrimitive.Image,
|
|
298
|
+
{
|
|
299
|
+
"data-slot": "avatar-image",
|
|
300
|
+
className: cn("aspect-square size-full", className),
|
|
301
|
+
...props
|
|
302
|
+
}
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
function AvatarFallback({
|
|
306
|
+
className,
|
|
307
|
+
...props
|
|
308
|
+
}) {
|
|
309
|
+
return /* @__PURE__ */ jsx4(
|
|
310
|
+
AvatarPrimitive.Fallback,
|
|
311
|
+
{
|
|
312
|
+
"data-slot": "avatar-fallback",
|
|
313
|
+
className: cn(
|
|
314
|
+
"bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
|
|
315
|
+
className
|
|
316
|
+
),
|
|
317
|
+
...props
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// src/client/tooltip-icon-button.tsx
|
|
323
|
+
import { forwardRef as forwardRef2 } from "react";
|
|
324
|
+
|
|
325
|
+
// ../../node_modules/@radix-ui/react-slot/dist/index.mjs
|
|
326
|
+
import * as React2 from "react";
|
|
327
|
+
import { Fragment as Fragment2, jsx as jsx5 } from "react/jsx-runtime";
|
|
328
|
+
var use = React2[" use ".trim().toString()];
|
|
329
|
+
var SLOTTABLE_IDENTIFIER = /* @__PURE__ */ Symbol("radix.slottable");
|
|
330
|
+
// @__NO_SIDE_EFFECTS__
|
|
331
|
+
function createSlottable(ownerName) {
|
|
332
|
+
const Slottable2 = ({ children }) => {
|
|
333
|
+
return /* @__PURE__ */ jsx5(Fragment2, { children });
|
|
334
|
+
};
|
|
335
|
+
Slottable2.displayName = `${ownerName}.Slottable`;
|
|
336
|
+
Slottable2.__radixId = SLOTTABLE_IDENTIFIER;
|
|
337
|
+
return Slottable2;
|
|
338
|
+
}
|
|
339
|
+
var Slottable = /* @__PURE__ */ createSlottable("Slottable");
|
|
340
|
+
|
|
341
|
+
// src/client/tooltip-icon-button.tsx
|
|
342
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
343
|
+
var TooltipIconButton = forwardRef2(({ children, tooltip, side = "bottom", className, ...rest }, ref) => {
|
|
344
|
+
return /* @__PURE__ */ jsxs3(Tooltip, { children: [
|
|
345
|
+
/* @__PURE__ */ jsx6(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
|
|
346
|
+
Button,
|
|
347
|
+
{
|
|
348
|
+
variant: "ghost",
|
|
349
|
+
size: "icon",
|
|
350
|
+
...rest,
|
|
351
|
+
className: cn("aui-button-icon size-6 p-1", className),
|
|
352
|
+
ref,
|
|
353
|
+
children: [
|
|
354
|
+
/* @__PURE__ */ jsx6(Slottable, { children }),
|
|
355
|
+
/* @__PURE__ */ jsx6("span", { className: "aui-sr-only sr-only", children: tooltip })
|
|
356
|
+
]
|
|
357
|
+
}
|
|
358
|
+
) }),
|
|
359
|
+
/* @__PURE__ */ jsx6(TooltipContent, { side, children: tooltip })
|
|
360
|
+
] });
|
|
361
|
+
});
|
|
362
|
+
TooltipIconButton.displayName = "TooltipIconButton";
|
|
363
|
+
|
|
364
|
+
// src/client/attachment.tsx
|
|
365
|
+
import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
366
|
+
var useFileSrc = (file) => {
|
|
367
|
+
const [src, setSrc] = useState(void 0);
|
|
368
|
+
useEffect(() => {
|
|
369
|
+
if (!file) return;
|
|
370
|
+
const objectUrl = URL.createObjectURL(file);
|
|
371
|
+
setSrc(objectUrl);
|
|
372
|
+
return () => {
|
|
373
|
+
URL.revokeObjectURL(objectUrl);
|
|
374
|
+
setSrc(void 0);
|
|
375
|
+
};
|
|
376
|
+
}, [file]);
|
|
377
|
+
return src;
|
|
378
|
+
};
|
|
379
|
+
var useAttachmentSrc = () => {
|
|
380
|
+
const { file, src } = useAuiState(
|
|
381
|
+
useShallow((s) => {
|
|
382
|
+
if (s.attachment.type !== "image") return {};
|
|
383
|
+
if (s.attachment.file) return { file: s.attachment.file };
|
|
384
|
+
const src2 = s.attachment.content?.filter((c) => c.type === "image")[0]?.image;
|
|
385
|
+
if (!src2) return {};
|
|
386
|
+
return { src: src2 };
|
|
387
|
+
})
|
|
388
|
+
);
|
|
389
|
+
return useFileSrc(file) ?? src;
|
|
390
|
+
};
|
|
391
|
+
var AttachmentPreview = ({ src }) => {
|
|
392
|
+
const [isLoaded, setIsLoaded] = useState(false);
|
|
393
|
+
return /* @__PURE__ */ jsx7(
|
|
394
|
+
"img",
|
|
395
|
+
{
|
|
396
|
+
src,
|
|
397
|
+
alt: "Image Preview",
|
|
398
|
+
className: cn(
|
|
399
|
+
"block h-auto max-h-[80vh] w-auto max-w-full object-contain",
|
|
400
|
+
isLoaded ? "aui-attachment-preview-image-loaded" : "aui-attachment-preview-image-loading invisible"
|
|
401
|
+
),
|
|
402
|
+
onLoad: () => setIsLoaded(true)
|
|
403
|
+
}
|
|
404
|
+
);
|
|
405
|
+
};
|
|
406
|
+
var AttachmentPreviewDialog = ({ children }) => {
|
|
407
|
+
const src = useAttachmentSrc();
|
|
408
|
+
if (!src) return children;
|
|
409
|
+
return /* @__PURE__ */ jsxs4(Dialog, { children: [
|
|
410
|
+
/* @__PURE__ */ jsx7(
|
|
411
|
+
DialogTrigger,
|
|
412
|
+
{
|
|
413
|
+
className: "aui-attachment-preview-trigger cursor-pointer transition-colors hover:bg-accent/50",
|
|
414
|
+
asChild: true,
|
|
415
|
+
children
|
|
416
|
+
}
|
|
417
|
+
),
|
|
418
|
+
/* @__PURE__ */ jsxs4(DialogContent, { className: "aui-attachment-preview-dialog-content p-2 sm:max-w-3xl [&>button]:rounded-full [&>button]:bg-foreground/60 [&>button]:p-1 [&>button]:opacity-100 [&>button]:ring-0! [&_svg]:text-background [&>button]:hover:[&_svg]:text-destructive", children: [
|
|
419
|
+
/* @__PURE__ */ jsx7(DialogTitle, { className: "aui-sr-only sr-only", children: "Image Attachment Preview" }),
|
|
420
|
+
/* @__PURE__ */ jsx7("div", { className: "aui-attachment-preview relative mx-auto flex max-h-[80dvh] w-full items-center justify-center overflow-hidden bg-background", children: /* @__PURE__ */ jsx7(AttachmentPreview, { src }) })
|
|
421
|
+
] })
|
|
422
|
+
] });
|
|
423
|
+
};
|
|
424
|
+
var AttachmentThumb = () => {
|
|
425
|
+
const isImage = useAuiState((s) => s.attachment.type === "image");
|
|
426
|
+
const src = useAttachmentSrc();
|
|
427
|
+
return /* @__PURE__ */ jsxs4(Avatar, { className: "aui-attachment-tile-avatar h-full w-full rounded-none", children: [
|
|
428
|
+
/* @__PURE__ */ jsx7(
|
|
429
|
+
AvatarImage,
|
|
430
|
+
{
|
|
431
|
+
src,
|
|
432
|
+
alt: "Attachment preview",
|
|
433
|
+
className: "aui-attachment-tile-image object-cover"
|
|
434
|
+
}
|
|
435
|
+
),
|
|
436
|
+
/* @__PURE__ */ jsx7(AvatarFallback, { delayMs: isImage ? 200 : 0, children: /* @__PURE__ */ jsx7(FileText, { className: "aui-attachment-tile-fallback-icon size-8 text-muted-foreground" }) })
|
|
437
|
+
] });
|
|
438
|
+
};
|
|
439
|
+
var AttachmentUI = () => {
|
|
440
|
+
const aui = useAui();
|
|
441
|
+
const isComposer = aui.attachment.source === "composer";
|
|
442
|
+
const isImage = useAuiState((s) => s.attachment.type === "image");
|
|
443
|
+
const typeLabel = useAuiState((s) => {
|
|
444
|
+
const type = s.attachment.type;
|
|
445
|
+
switch (type) {
|
|
446
|
+
case "image":
|
|
447
|
+
return "Image";
|
|
448
|
+
case "document":
|
|
449
|
+
return "Document";
|
|
450
|
+
case "file":
|
|
451
|
+
return "File";
|
|
452
|
+
default:
|
|
453
|
+
const _exhaustiveCheck = type;
|
|
454
|
+
throw new Error(`Unknown attachment type: ${_exhaustiveCheck}`);
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
return /* @__PURE__ */ jsxs4(Tooltip, { children: [
|
|
458
|
+
/* @__PURE__ */ jsxs4(
|
|
459
|
+
AttachmentPrimitive.Root,
|
|
460
|
+
{
|
|
461
|
+
className: cn(
|
|
462
|
+
"aui-attachment-root relative",
|
|
463
|
+
isImage && "aui-attachment-root-composer only:[&>#attachment-tile]:size-24"
|
|
464
|
+
),
|
|
465
|
+
children: [
|
|
466
|
+
/* @__PURE__ */ jsx7(AttachmentPreviewDialog, { children: /* @__PURE__ */ jsx7(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx7(
|
|
467
|
+
"div",
|
|
468
|
+
{
|
|
469
|
+
className: cn(
|
|
470
|
+
"aui-attachment-tile size-14 cursor-pointer overflow-hidden rounded-[14px] border bg-muted transition-opacity hover:opacity-75",
|
|
471
|
+
isComposer && "aui-attachment-tile-composer border-foreground/20"
|
|
472
|
+
),
|
|
473
|
+
role: "button",
|
|
474
|
+
id: "attachment-tile",
|
|
475
|
+
"aria-label": `${typeLabel} attachment`,
|
|
476
|
+
children: /* @__PURE__ */ jsx7(AttachmentThumb, {})
|
|
477
|
+
}
|
|
478
|
+
) }) }),
|
|
479
|
+
isComposer && /* @__PURE__ */ jsx7(AttachmentRemove, {})
|
|
480
|
+
]
|
|
481
|
+
}
|
|
482
|
+
),
|
|
483
|
+
/* @__PURE__ */ jsx7(TooltipContent, { side: "top", children: /* @__PURE__ */ jsx7(AttachmentPrimitive.Name, {}) })
|
|
484
|
+
] });
|
|
485
|
+
};
|
|
486
|
+
var AttachmentRemove = () => {
|
|
487
|
+
return /* @__PURE__ */ jsx7(AttachmentPrimitive.Remove, { asChild: true, children: /* @__PURE__ */ jsx7(
|
|
488
|
+
TooltipIconButton,
|
|
489
|
+
{
|
|
490
|
+
tooltip: "Remove file",
|
|
491
|
+
className: "aui-attachment-tile-remove absolute top-1.5 right-1.5 size-3.5 rounded-full bg-white text-muted-foreground opacity-100 shadow-sm hover:bg-white! [&_svg]:text-black hover:[&_svg]:text-destructive",
|
|
492
|
+
side: "top",
|
|
493
|
+
children: /* @__PURE__ */ jsx7(XIcon2, { className: "aui-attachment-remove-icon size-3 dark:stroke-[2.5px]" })
|
|
494
|
+
}
|
|
495
|
+
) });
|
|
496
|
+
};
|
|
497
|
+
var UserMessageAttachments = () => {
|
|
498
|
+
return /* @__PURE__ */ jsx7("div", { className: "aui-user-message-attachments-end col-span-full col-start-1 row-start-1 flex w-full flex-row justify-end gap-2", children: /* @__PURE__ */ jsx7(MessagePrimitive.Attachments, { components: { Attachment: AttachmentUI } }) });
|
|
499
|
+
};
|
|
500
|
+
var ComposerAttachments = () => {
|
|
501
|
+
return /* @__PURE__ */ jsx7("div", { className: "aui-composer-attachments mb-2 flex w-full flex-row items-center gap-2 overflow-x-auto px-1.5 pt-0.5 pb-1 empty:hidden", children: /* @__PURE__ */ jsx7(
|
|
502
|
+
ComposerPrimitive.Attachments,
|
|
503
|
+
{
|
|
504
|
+
components: { Attachment: AttachmentUI }
|
|
505
|
+
}
|
|
506
|
+
) });
|
|
507
|
+
};
|
|
508
|
+
var ComposerAddAttachment = () => {
|
|
509
|
+
return /* @__PURE__ */ jsx7(ComposerPrimitive.AddAttachment, { asChild: true, children: /* @__PURE__ */ jsx7(
|
|
510
|
+
TooltipIconButton,
|
|
511
|
+
{
|
|
512
|
+
tooltip: "Add Attachment",
|
|
513
|
+
side: "bottom",
|
|
514
|
+
variant: "ghost",
|
|
515
|
+
size: "icon",
|
|
516
|
+
className: "aui-composer-add-attachment size-8.5 rounded-full p-1 font-semibold text-xs hover:bg-muted-foreground/15 dark:border-muted-foreground/15 dark:hover:bg-muted-foreground/30",
|
|
517
|
+
"aria-label": "Add Attachment",
|
|
518
|
+
children: /* @__PURE__ */ jsx7(PlusIcon, { className: "aui-attachment-add-icon size-5 stroke-[1.5px]" })
|
|
519
|
+
}
|
|
520
|
+
) });
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// src/client/markdown-text.tsx
|
|
524
|
+
import "@assistant-ui/react-markdown/styles/dot.css";
|
|
525
|
+
import {
|
|
526
|
+
MarkdownTextPrimitive,
|
|
527
|
+
unstable_memoizeMarkdownComponents as memoizeMarkdownComponents,
|
|
528
|
+
useIsMarkdownCodeBlock
|
|
529
|
+
} from "@assistant-ui/react-markdown";
|
|
530
|
+
import remarkGfm from "remark-gfm";
|
|
531
|
+
import { memo, useState as useState2 } from "react";
|
|
532
|
+
import { CheckIcon, CopyIcon } from "lucide-react";
|
|
533
|
+
import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
534
|
+
var MarkdownTextImpl = () => {
|
|
535
|
+
return /* @__PURE__ */ jsx8(
|
|
536
|
+
MarkdownTextPrimitive,
|
|
537
|
+
{
|
|
538
|
+
remarkPlugins: [remarkGfm],
|
|
539
|
+
className: "aui-md",
|
|
540
|
+
components: defaultComponents
|
|
541
|
+
}
|
|
542
|
+
);
|
|
543
|
+
};
|
|
544
|
+
var MarkdownText = memo(MarkdownTextImpl);
|
|
545
|
+
var CodeHeader = ({ language, code }) => {
|
|
546
|
+
const { isCopied, copyToClipboard } = useCopyToClipboard();
|
|
547
|
+
const onCopy = () => {
|
|
548
|
+
if (!code || isCopied) return;
|
|
549
|
+
copyToClipboard(code);
|
|
550
|
+
};
|
|
551
|
+
return /* @__PURE__ */ jsxs5("div", { className: "aui-code-header-root mt-2.5 flex items-center justify-between rounded-t-lg border border-border/50 border-b-0 bg-muted/50 px-3 py-1.5 text-xs", children: [
|
|
552
|
+
/* @__PURE__ */ jsx8("span", { className: "aui-code-header-language font-medium text-muted-foreground lowercase", children: language }),
|
|
553
|
+
/* @__PURE__ */ jsxs5(TooltipIconButton, { tooltip: "Copy", onClick: onCopy, children: [
|
|
554
|
+
!isCopied && /* @__PURE__ */ jsx8(CopyIcon, {}),
|
|
555
|
+
isCopied && /* @__PURE__ */ jsx8(CheckIcon, {})
|
|
556
|
+
] })
|
|
557
|
+
] });
|
|
558
|
+
};
|
|
559
|
+
var useCopyToClipboard = ({
|
|
560
|
+
copiedDuration = 3e3
|
|
561
|
+
} = {}) => {
|
|
562
|
+
const [isCopied, setIsCopied] = useState2(false);
|
|
563
|
+
const copyToClipboard = (value) => {
|
|
564
|
+
if (!value) return;
|
|
565
|
+
navigator.clipboard.writeText(value).then(() => {
|
|
566
|
+
setIsCopied(true);
|
|
567
|
+
setTimeout(() => setIsCopied(false), copiedDuration);
|
|
568
|
+
});
|
|
569
|
+
};
|
|
570
|
+
return { isCopied, copyToClipboard };
|
|
571
|
+
};
|
|
572
|
+
var defaultComponents = memoizeMarkdownComponents({
|
|
573
|
+
h1: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
574
|
+
"h1",
|
|
575
|
+
{
|
|
576
|
+
className: cn(
|
|
577
|
+
"aui-md-h1 mb-2 scroll-m-20 font-semibold text-base first:mt-0 last:mb-0",
|
|
578
|
+
className
|
|
579
|
+
),
|
|
580
|
+
...props
|
|
581
|
+
}
|
|
582
|
+
),
|
|
583
|
+
h2: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
584
|
+
"h2",
|
|
585
|
+
{
|
|
586
|
+
className: cn(
|
|
587
|
+
"aui-md-h2 mt-3 mb-1.5 scroll-m-20 font-semibold text-sm first:mt-0 last:mb-0",
|
|
588
|
+
className
|
|
589
|
+
),
|
|
590
|
+
...props
|
|
591
|
+
}
|
|
592
|
+
),
|
|
593
|
+
h3: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
594
|
+
"h3",
|
|
595
|
+
{
|
|
596
|
+
className: cn(
|
|
597
|
+
"aui-md-h3 mt-2.5 mb-1 scroll-m-20 font-semibold text-sm first:mt-0 last:mb-0",
|
|
598
|
+
className
|
|
599
|
+
),
|
|
600
|
+
...props
|
|
601
|
+
}
|
|
602
|
+
),
|
|
603
|
+
h4: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
604
|
+
"h4",
|
|
605
|
+
{
|
|
606
|
+
className: cn(
|
|
607
|
+
"aui-md-h4 mt-2 mb-1 scroll-m-20 font-medium text-sm first:mt-0 last:mb-0",
|
|
608
|
+
className
|
|
609
|
+
),
|
|
610
|
+
...props
|
|
611
|
+
}
|
|
612
|
+
),
|
|
613
|
+
h5: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
614
|
+
"h5",
|
|
615
|
+
{
|
|
616
|
+
className: cn(
|
|
617
|
+
"aui-md-h5 mt-2 mb-1 font-medium text-sm first:mt-0 last:mb-0",
|
|
618
|
+
className
|
|
619
|
+
),
|
|
620
|
+
...props
|
|
621
|
+
}
|
|
622
|
+
),
|
|
623
|
+
h6: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
624
|
+
"h6",
|
|
625
|
+
{
|
|
626
|
+
className: cn(
|
|
627
|
+
"aui-md-h6 mt-2 mb-1 font-medium text-sm first:mt-0 last:mb-0",
|
|
628
|
+
className
|
|
629
|
+
),
|
|
630
|
+
...props
|
|
631
|
+
}
|
|
632
|
+
),
|
|
633
|
+
p: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
634
|
+
"p",
|
|
635
|
+
{
|
|
636
|
+
className: cn(
|
|
637
|
+
"aui-md-p my-2.5 leading-normal first:mt-0 last:mb-0",
|
|
638
|
+
className
|
|
639
|
+
),
|
|
640
|
+
...props
|
|
641
|
+
}
|
|
642
|
+
),
|
|
643
|
+
a: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
644
|
+
"a",
|
|
645
|
+
{
|
|
646
|
+
className: cn(
|
|
647
|
+
"aui-md-a text-primary underline underline-offset-2 hover:text-primary/80",
|
|
648
|
+
className
|
|
649
|
+
),
|
|
650
|
+
...props
|
|
651
|
+
}
|
|
652
|
+
),
|
|
653
|
+
blockquote: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
654
|
+
"blockquote",
|
|
655
|
+
{
|
|
656
|
+
className: cn(
|
|
657
|
+
"aui-md-blockquote my-2.5 border-muted-foreground/30 border-l-2 pl-3 text-muted-foreground italic",
|
|
658
|
+
className
|
|
659
|
+
),
|
|
660
|
+
...props
|
|
661
|
+
}
|
|
662
|
+
),
|
|
663
|
+
ul: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
664
|
+
"ul",
|
|
665
|
+
{
|
|
666
|
+
className: cn(
|
|
667
|
+
"aui-md-ul my-2 ml-4 list-disc marker:text-muted-foreground [&>li]:mt-1",
|
|
668
|
+
className
|
|
669
|
+
),
|
|
670
|
+
...props
|
|
671
|
+
}
|
|
672
|
+
),
|
|
673
|
+
ol: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
674
|
+
"ol",
|
|
675
|
+
{
|
|
676
|
+
className: cn(
|
|
677
|
+
"aui-md-ol my-2 ml-4 list-decimal marker:text-muted-foreground [&>li]:mt-1",
|
|
678
|
+
className
|
|
679
|
+
),
|
|
680
|
+
...props
|
|
681
|
+
}
|
|
682
|
+
),
|
|
683
|
+
hr: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
684
|
+
"hr",
|
|
685
|
+
{
|
|
686
|
+
className: cn("aui-md-hr my-2 border-muted-foreground/20", className),
|
|
687
|
+
...props
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
table: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
691
|
+
"table",
|
|
692
|
+
{
|
|
693
|
+
className: cn(
|
|
694
|
+
"aui-md-table my-2 w-full border-separate border-spacing-0 overflow-y-auto",
|
|
695
|
+
className
|
|
696
|
+
),
|
|
697
|
+
...props
|
|
698
|
+
}
|
|
699
|
+
),
|
|
700
|
+
th: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
701
|
+
"th",
|
|
702
|
+
{
|
|
703
|
+
className: cn(
|
|
704
|
+
"aui-md-th bg-muted px-2 py-1 text-left font-medium first:rounded-tl-lg last:rounded-tr-lg [[align=center]]:text-center [[align=right]]:text-right",
|
|
705
|
+
className
|
|
706
|
+
),
|
|
707
|
+
...props
|
|
708
|
+
}
|
|
709
|
+
),
|
|
710
|
+
td: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
711
|
+
"td",
|
|
712
|
+
{
|
|
713
|
+
className: cn(
|
|
714
|
+
"aui-md-td border-muted-foreground/20 border-b border-l px-2 py-1 text-left last:border-r [[align=center]]:text-center [[align=right]]:text-right",
|
|
715
|
+
className
|
|
716
|
+
),
|
|
717
|
+
...props
|
|
718
|
+
}
|
|
719
|
+
),
|
|
720
|
+
tr: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
721
|
+
"tr",
|
|
722
|
+
{
|
|
723
|
+
className: cn(
|
|
724
|
+
"aui-md-tr m-0 border-b p-0 first:border-t [&:last-child>td:first-child]:rounded-bl-lg [&:last-child>td:last-child]:rounded-br-lg",
|
|
725
|
+
className
|
|
726
|
+
),
|
|
727
|
+
...props
|
|
728
|
+
}
|
|
729
|
+
),
|
|
730
|
+
li: ({ className, ...props }) => /* @__PURE__ */ jsx8("li", { className: cn("aui-md-li leading-normal", className), ...props }),
|
|
731
|
+
sup: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
732
|
+
"sup",
|
|
733
|
+
{
|
|
734
|
+
className: cn("aui-md-sup [&>a]:text-xs [&>a]:no-underline", className),
|
|
735
|
+
...props
|
|
736
|
+
}
|
|
737
|
+
),
|
|
738
|
+
pre: ({ className, ...props }) => /* @__PURE__ */ jsx8(
|
|
739
|
+
"pre",
|
|
740
|
+
{
|
|
741
|
+
className: cn(
|
|
742
|
+
"aui-md-pre overflow-x-auto rounded-t-none rounded-b-lg border border-border/50 border-t-0 bg-muted/30 p-3 text-xs leading-relaxed",
|
|
743
|
+
className
|
|
744
|
+
),
|
|
745
|
+
...props
|
|
746
|
+
}
|
|
747
|
+
),
|
|
748
|
+
code: function Code({ className, ...props }) {
|
|
749
|
+
const isCodeBlock = useIsMarkdownCodeBlock();
|
|
750
|
+
return /* @__PURE__ */ jsx8(
|
|
751
|
+
"code",
|
|
752
|
+
{
|
|
753
|
+
className: cn(
|
|
754
|
+
!isCodeBlock && "aui-md-inline-code rounded-md border border-border/50 bg-muted/50 px-1.5 py-0.5 font-mono text-[0.85em]",
|
|
755
|
+
className
|
|
756
|
+
),
|
|
757
|
+
...props
|
|
758
|
+
}
|
|
759
|
+
);
|
|
760
|
+
},
|
|
761
|
+
CodeHeader
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
// src/client/tool-fallback.tsx
|
|
765
|
+
import { memo as memo2, useCallback, useRef, useState as useState3 } from "react";
|
|
766
|
+
import {
|
|
767
|
+
AlertCircleIcon,
|
|
768
|
+
CheckIcon as CheckIcon2,
|
|
769
|
+
ChevronDownIcon,
|
|
770
|
+
LoaderIcon,
|
|
771
|
+
XCircleIcon
|
|
772
|
+
} from "lucide-react";
|
|
773
|
+
import {
|
|
774
|
+
useScrollLock
|
|
775
|
+
} from "@assistant-ui/react";
|
|
776
|
+
|
|
777
|
+
// src/client/ui/collapsible.tsx
|
|
778
|
+
import { Collapsible as CollapsiblePrimitive } from "radix-ui";
|
|
779
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
780
|
+
function Collapsible({
|
|
781
|
+
...props
|
|
782
|
+
}) {
|
|
783
|
+
return /* @__PURE__ */ jsx9(CollapsiblePrimitive.Root, { "data-slot": "collapsible", ...props });
|
|
784
|
+
}
|
|
785
|
+
function CollapsibleTrigger({
|
|
786
|
+
...props
|
|
787
|
+
}) {
|
|
788
|
+
return /* @__PURE__ */ jsx9(
|
|
789
|
+
CollapsiblePrimitive.CollapsibleTrigger,
|
|
790
|
+
{
|
|
791
|
+
"data-slot": "collapsible-trigger",
|
|
792
|
+
...props
|
|
793
|
+
}
|
|
794
|
+
);
|
|
795
|
+
}
|
|
796
|
+
function CollapsibleContent({
|
|
797
|
+
...props
|
|
798
|
+
}) {
|
|
799
|
+
return /* @__PURE__ */ jsx9(
|
|
800
|
+
CollapsiblePrimitive.CollapsibleContent,
|
|
801
|
+
{
|
|
802
|
+
"data-slot": "collapsible-content",
|
|
803
|
+
...props
|
|
804
|
+
}
|
|
805
|
+
);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/client/tool-fallback.tsx
|
|
809
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
810
|
+
var ANIMATION_DURATION = 200;
|
|
811
|
+
function ToolFallbackRoot({
|
|
812
|
+
className,
|
|
813
|
+
open: controlledOpen,
|
|
814
|
+
onOpenChange: controlledOnOpenChange,
|
|
815
|
+
defaultOpen = false,
|
|
816
|
+
children,
|
|
817
|
+
...props
|
|
818
|
+
}) {
|
|
819
|
+
const collapsibleRef = useRef(null);
|
|
820
|
+
const [uncontrolledOpen, setUncontrolledOpen] = useState3(defaultOpen);
|
|
821
|
+
const lockScroll = useScrollLock(collapsibleRef, ANIMATION_DURATION);
|
|
822
|
+
const isControlled = controlledOpen !== void 0;
|
|
823
|
+
const isOpen = isControlled ? controlledOpen : uncontrolledOpen;
|
|
824
|
+
const handleOpenChange = useCallback(
|
|
825
|
+
(open) => {
|
|
826
|
+
if (!open) {
|
|
827
|
+
lockScroll();
|
|
828
|
+
}
|
|
829
|
+
if (!isControlled) {
|
|
830
|
+
setUncontrolledOpen(open);
|
|
831
|
+
}
|
|
832
|
+
controlledOnOpenChange?.(open);
|
|
833
|
+
},
|
|
834
|
+
[lockScroll, isControlled, controlledOnOpenChange]
|
|
835
|
+
);
|
|
836
|
+
return /* @__PURE__ */ jsx10(
|
|
837
|
+
Collapsible,
|
|
838
|
+
{
|
|
839
|
+
ref: collapsibleRef,
|
|
840
|
+
"data-slot": "tool-fallback-root",
|
|
841
|
+
open: isOpen,
|
|
842
|
+
onOpenChange: handleOpenChange,
|
|
843
|
+
className: cn(
|
|
844
|
+
"aui-tool-fallback-root group/tool-fallback-root w-full rounded-lg border py-3",
|
|
845
|
+
className
|
|
846
|
+
),
|
|
847
|
+
style: {
|
|
848
|
+
"--animation-duration": `${ANIMATION_DURATION}ms`
|
|
849
|
+
},
|
|
850
|
+
...props,
|
|
851
|
+
children
|
|
852
|
+
}
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
var statusIconMap = {
|
|
856
|
+
running: LoaderIcon,
|
|
857
|
+
complete: CheckIcon2,
|
|
858
|
+
incomplete: XCircleIcon,
|
|
859
|
+
"requires-action": AlertCircleIcon
|
|
860
|
+
};
|
|
861
|
+
function ToolFallbackTrigger({
|
|
862
|
+
toolName,
|
|
863
|
+
status,
|
|
864
|
+
className,
|
|
865
|
+
...props
|
|
866
|
+
}) {
|
|
867
|
+
const statusType = status?.type ?? "complete";
|
|
868
|
+
const isRunning = statusType === "running";
|
|
869
|
+
const isCancelled = status?.type === "incomplete" && status.reason === "cancelled";
|
|
870
|
+
const Icon = statusIconMap[statusType];
|
|
871
|
+
const label = isCancelled ? "Cancelled tool" : "Used tool";
|
|
872
|
+
return /* @__PURE__ */ jsxs6(
|
|
873
|
+
CollapsibleTrigger,
|
|
874
|
+
{
|
|
875
|
+
"data-slot": "tool-fallback-trigger",
|
|
876
|
+
className: cn(
|
|
877
|
+
"aui-tool-fallback-trigger group/trigger flex w-full items-center gap-2 px-4 text-sm transition-colors",
|
|
878
|
+
className
|
|
879
|
+
),
|
|
880
|
+
...props,
|
|
881
|
+
children: [
|
|
882
|
+
/* @__PURE__ */ jsx10(
|
|
883
|
+
Icon,
|
|
884
|
+
{
|
|
885
|
+
"data-slot": "tool-fallback-trigger-icon",
|
|
886
|
+
className: cn(
|
|
887
|
+
"aui-tool-fallback-trigger-icon size-4 shrink-0",
|
|
888
|
+
isCancelled && "text-muted-foreground",
|
|
889
|
+
isRunning && "animate-spin"
|
|
890
|
+
)
|
|
891
|
+
}
|
|
892
|
+
),
|
|
893
|
+
/* @__PURE__ */ jsxs6(
|
|
894
|
+
"span",
|
|
895
|
+
{
|
|
896
|
+
"data-slot": "tool-fallback-trigger-label",
|
|
897
|
+
className: cn(
|
|
898
|
+
"aui-tool-fallback-trigger-label-wrapper relative inline-block grow text-left leading-none",
|
|
899
|
+
isCancelled && "text-muted-foreground line-through"
|
|
900
|
+
),
|
|
901
|
+
children: [
|
|
902
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
903
|
+
label,
|
|
904
|
+
": ",
|
|
905
|
+
/* @__PURE__ */ jsx10("b", { children: toolName })
|
|
906
|
+
] }),
|
|
907
|
+
isRunning && /* @__PURE__ */ jsxs6(
|
|
908
|
+
"span",
|
|
909
|
+
{
|
|
910
|
+
"aria-hidden": true,
|
|
911
|
+
"data-slot": "tool-fallback-trigger-shimmer",
|
|
912
|
+
className: "aui-tool-fallback-trigger-shimmer shimmer pointer-events-none absolute inset-0 motion-reduce:animate-none",
|
|
913
|
+
children: [
|
|
914
|
+
label,
|
|
915
|
+
": ",
|
|
916
|
+
/* @__PURE__ */ jsx10("b", { children: toolName })
|
|
917
|
+
]
|
|
918
|
+
}
|
|
919
|
+
)
|
|
920
|
+
]
|
|
921
|
+
}
|
|
922
|
+
),
|
|
923
|
+
/* @__PURE__ */ jsx10(
|
|
924
|
+
ChevronDownIcon,
|
|
925
|
+
{
|
|
926
|
+
"data-slot": "tool-fallback-trigger-chevron",
|
|
927
|
+
className: cn(
|
|
928
|
+
"aui-tool-fallback-trigger-chevron size-4 shrink-0",
|
|
929
|
+
"transition-transform duration-(--animation-duration) ease-out",
|
|
930
|
+
"group-data-[state=closed]/trigger:-rotate-90",
|
|
931
|
+
"group-data-[state=open]/trigger:rotate-0"
|
|
932
|
+
)
|
|
933
|
+
}
|
|
934
|
+
)
|
|
935
|
+
]
|
|
936
|
+
}
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
function ToolFallbackContent({
|
|
940
|
+
className,
|
|
941
|
+
children,
|
|
942
|
+
...props
|
|
943
|
+
}) {
|
|
944
|
+
return /* @__PURE__ */ jsx10(
|
|
945
|
+
CollapsibleContent,
|
|
946
|
+
{
|
|
947
|
+
"data-slot": "tool-fallback-content",
|
|
948
|
+
className: cn(
|
|
949
|
+
"aui-tool-fallback-content relative overflow-hidden text-sm outline-none",
|
|
950
|
+
"group/collapsible-content ease-out",
|
|
951
|
+
"data-[state=closed]:animate-collapsible-up",
|
|
952
|
+
"data-[state=open]:animate-collapsible-down",
|
|
953
|
+
"data-[state=closed]:fill-mode-forwards",
|
|
954
|
+
"data-[state=closed]:pointer-events-none",
|
|
955
|
+
"data-[state=open]:duration-(--animation-duration)",
|
|
956
|
+
"data-[state=closed]:duration-(--animation-duration)",
|
|
957
|
+
className
|
|
958
|
+
),
|
|
959
|
+
...props,
|
|
960
|
+
children: /* @__PURE__ */ jsx10("div", { className: "mt-3 flex flex-col gap-2 border-t pt-2", children })
|
|
961
|
+
}
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
function ToolFallbackArgs({
|
|
965
|
+
argsText,
|
|
966
|
+
className,
|
|
967
|
+
...props
|
|
968
|
+
}) {
|
|
969
|
+
if (!argsText) return null;
|
|
970
|
+
return /* @__PURE__ */ jsx10(
|
|
971
|
+
"div",
|
|
972
|
+
{
|
|
973
|
+
"data-slot": "tool-fallback-args",
|
|
974
|
+
className: cn("aui-tool-fallback-args px-4", className),
|
|
975
|
+
...props,
|
|
976
|
+
children: /* @__PURE__ */ jsx10("pre", { className: "aui-tool-fallback-args-value whitespace-pre-wrap", children: argsText })
|
|
977
|
+
}
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
function ToolFallbackResult({
|
|
981
|
+
result,
|
|
982
|
+
className,
|
|
983
|
+
...props
|
|
984
|
+
}) {
|
|
985
|
+
if (result === void 0) return null;
|
|
986
|
+
return /* @__PURE__ */ jsxs6(
|
|
987
|
+
"div",
|
|
988
|
+
{
|
|
989
|
+
"data-slot": "tool-fallback-result",
|
|
990
|
+
className: cn(
|
|
991
|
+
"aui-tool-fallback-result border-t border-dashed px-4 pt-2",
|
|
992
|
+
className
|
|
993
|
+
),
|
|
994
|
+
...props,
|
|
995
|
+
children: [
|
|
996
|
+
/* @__PURE__ */ jsx10("p", { className: "aui-tool-fallback-result-header font-semibold", children: "Result:" }),
|
|
997
|
+
/* @__PURE__ */ jsx10("pre", { className: "aui-tool-fallback-result-content whitespace-pre-wrap", children: typeof result === "string" ? result : JSON.stringify(result, null, 2) })
|
|
998
|
+
]
|
|
999
|
+
}
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
function ToolFallbackError({
|
|
1003
|
+
status,
|
|
1004
|
+
className,
|
|
1005
|
+
...props
|
|
1006
|
+
}) {
|
|
1007
|
+
if (status?.type !== "incomplete") return null;
|
|
1008
|
+
const error = status.error;
|
|
1009
|
+
const errorText = error ? typeof error === "string" ? error : JSON.stringify(error) : null;
|
|
1010
|
+
if (!errorText) return null;
|
|
1011
|
+
const isCancelled = status.reason === "cancelled";
|
|
1012
|
+
const headerText = isCancelled ? "Cancelled reason:" : "Error:";
|
|
1013
|
+
return /* @__PURE__ */ jsxs6(
|
|
1014
|
+
"div",
|
|
1015
|
+
{
|
|
1016
|
+
"data-slot": "tool-fallback-error",
|
|
1017
|
+
className: cn("aui-tool-fallback-error px-4", className),
|
|
1018
|
+
...props,
|
|
1019
|
+
children: [
|
|
1020
|
+
/* @__PURE__ */ jsx10("p", { className: "aui-tool-fallback-error-header font-semibold text-muted-foreground", children: headerText }),
|
|
1021
|
+
/* @__PURE__ */ jsx10("p", { className: "aui-tool-fallback-error-reason text-muted-foreground", children: errorText })
|
|
1022
|
+
]
|
|
1023
|
+
}
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
var ToolFallbackImpl = ({
|
|
1027
|
+
toolName,
|
|
1028
|
+
argsText,
|
|
1029
|
+
result,
|
|
1030
|
+
status
|
|
1031
|
+
}) => {
|
|
1032
|
+
const isCancelled = status?.type === "incomplete" && status.reason === "cancelled";
|
|
1033
|
+
return /* @__PURE__ */ jsxs6(
|
|
1034
|
+
ToolFallbackRoot,
|
|
1035
|
+
{
|
|
1036
|
+
className: cn(isCancelled && "border-muted-foreground/30 bg-muted/30"),
|
|
1037
|
+
children: [
|
|
1038
|
+
/* @__PURE__ */ jsx10(ToolFallbackTrigger, { toolName, status }),
|
|
1039
|
+
/* @__PURE__ */ jsxs6(ToolFallbackContent, { children: [
|
|
1040
|
+
/* @__PURE__ */ jsx10(ToolFallbackError, { status }),
|
|
1041
|
+
/* @__PURE__ */ jsx10(
|
|
1042
|
+
ToolFallbackArgs,
|
|
1043
|
+
{
|
|
1044
|
+
argsText,
|
|
1045
|
+
className: cn(isCancelled && "opacity-60")
|
|
1046
|
+
}
|
|
1047
|
+
),
|
|
1048
|
+
!isCancelled && /* @__PURE__ */ jsx10(ToolFallbackResult, { result })
|
|
1049
|
+
] })
|
|
1050
|
+
]
|
|
1051
|
+
}
|
|
1052
|
+
);
|
|
1053
|
+
};
|
|
1054
|
+
var ToolFallback = memo2(
|
|
1055
|
+
ToolFallbackImpl
|
|
1056
|
+
);
|
|
1057
|
+
ToolFallback.displayName = "ToolFallback";
|
|
1058
|
+
ToolFallback.Root = ToolFallbackRoot;
|
|
1059
|
+
ToolFallback.Trigger = ToolFallbackTrigger;
|
|
1060
|
+
ToolFallback.Content = ToolFallbackContent;
|
|
1061
|
+
ToolFallback.Args = ToolFallbackArgs;
|
|
1062
|
+
ToolFallback.Result = ToolFallbackResult;
|
|
1063
|
+
ToolFallback.Error = ToolFallbackError;
|
|
1064
|
+
|
|
1065
|
+
// src/client/thread.tsx
|
|
1066
|
+
import {
|
|
1067
|
+
ActionBarMorePrimitive,
|
|
1068
|
+
ActionBarPrimitive,
|
|
1069
|
+
AuiIf,
|
|
1070
|
+
BranchPickerPrimitive,
|
|
1071
|
+
ComposerPrimitive as ComposerPrimitive2,
|
|
1072
|
+
ErrorPrimitive,
|
|
1073
|
+
MessagePrimitive as MessagePrimitive2,
|
|
1074
|
+
SuggestionPrimitive,
|
|
1075
|
+
ThreadPrimitive
|
|
1076
|
+
} from "@assistant-ui/react";
|
|
1077
|
+
import {
|
|
1078
|
+
ArrowDownIcon,
|
|
1079
|
+
ArrowUpIcon,
|
|
1080
|
+
CheckIcon as CheckIcon3,
|
|
1081
|
+
ChevronLeftIcon,
|
|
1082
|
+
ChevronRightIcon,
|
|
1083
|
+
CopyIcon as CopyIcon2,
|
|
1084
|
+
DownloadIcon,
|
|
1085
|
+
MoreHorizontalIcon,
|
|
1086
|
+
PencilIcon,
|
|
1087
|
+
RefreshCwIcon,
|
|
1088
|
+
SquareIcon
|
|
1089
|
+
} from "lucide-react";
|
|
1090
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1091
|
+
var Thread = () => {
|
|
1092
|
+
return /* @__PURE__ */ jsx11(
|
|
1093
|
+
ThreadPrimitive.Root,
|
|
1094
|
+
{
|
|
1095
|
+
className: "aui-root aui-thread-root @container flex h-full flex-col bg-transparent",
|
|
1096
|
+
style: {
|
|
1097
|
+
["--thread-max-width"]: "44rem"
|
|
1098
|
+
},
|
|
1099
|
+
children: /* @__PURE__ */ jsxs7(
|
|
1100
|
+
ThreadPrimitive.Viewport,
|
|
1101
|
+
{
|
|
1102
|
+
turnAnchor: "top",
|
|
1103
|
+
className: "aui-thread-viewport relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth px-4 pt-4",
|
|
1104
|
+
children: [
|
|
1105
|
+
/* @__PURE__ */ jsx11(AuiIf, { condition: (s) => s.thread.isEmpty, children: /* @__PURE__ */ jsx11(ThreadWelcome, {}) }),
|
|
1106
|
+
/* @__PURE__ */ jsx11(
|
|
1107
|
+
ThreadPrimitive.Messages,
|
|
1108
|
+
{
|
|
1109
|
+
components: {
|
|
1110
|
+
UserMessage,
|
|
1111
|
+
EditComposer,
|
|
1112
|
+
AssistantMessage
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
),
|
|
1116
|
+
/* @__PURE__ */ jsxs7(ThreadPrimitive.ViewportFooter, { className: "aui-thread-viewport-footer sticky bottom-0 mx-auto mt-auto flex w-full max-w-(--thread-max-width) flex-col gap-4 overflow-visible pb-3", children: [
|
|
1117
|
+
/* @__PURE__ */ jsx11(ThreadScrollToBottom, {}),
|
|
1118
|
+
/* @__PURE__ */ jsx11(Composer, {})
|
|
1119
|
+
] })
|
|
1120
|
+
]
|
|
1121
|
+
}
|
|
1122
|
+
)
|
|
1123
|
+
}
|
|
1124
|
+
);
|
|
1125
|
+
};
|
|
1126
|
+
var ThreadScrollToBottom = () => {
|
|
1127
|
+
return /* @__PURE__ */ jsx11(ThreadPrimitive.ScrollToBottom, { asChild: true, children: /* @__PURE__ */ jsx11(
|
|
1128
|
+
TooltipIconButton,
|
|
1129
|
+
{
|
|
1130
|
+
tooltip: "Scroll to bottom",
|
|
1131
|
+
variant: "outline",
|
|
1132
|
+
className: "aui-thread-scroll-to-bottom absolute -top-12 z-10 self-center rounded-full p-4 disabled:invisible dark:bg-background dark:hover:bg-accent",
|
|
1133
|
+
children: /* @__PURE__ */ jsx11(ArrowDownIcon, {})
|
|
1134
|
+
}
|
|
1135
|
+
) });
|
|
1136
|
+
};
|
|
1137
|
+
var ThreadWelcome = () => {
|
|
1138
|
+
return /* @__PURE__ */ jsxs7("div", { className: "aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col", children: [
|
|
1139
|
+
/* @__PURE__ */ jsx11("div", { className: "aui-thread-welcome-center flex w-full grow flex-col items-center justify-center", children: /* @__PURE__ */ jsxs7("div", { className: "aui-thread-welcome-message flex size-full flex-col items-center justify-center gap-1 px-4 text-center", children: [
|
|
1140
|
+
/* @__PURE__ */ jsx11("h1", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-sm font-medium text-foreground duration-200", children: "Share an idea" }),
|
|
1141
|
+
/* @__PURE__ */ jsx11("p", { className: "aui-thread-welcome-message-inner fade-in slide-in-from-bottom-1 animate-in fill-mode-both text-muted-foreground text-xs delay-75 duration-200", children: "Describe what you'd like to improve." })
|
|
1142
|
+
] }) }),
|
|
1143
|
+
/* @__PURE__ */ jsx11(ThreadSuggestions, {})
|
|
1144
|
+
] });
|
|
1145
|
+
};
|
|
1146
|
+
var ThreadSuggestions = () => {
|
|
1147
|
+
return /* @__PURE__ */ jsx11("div", { className: "aui-thread-welcome-suggestions grid w-full @md:grid-cols-2 gap-2 pb-4", children: /* @__PURE__ */ jsx11(
|
|
1148
|
+
ThreadPrimitive.Suggestions,
|
|
1149
|
+
{
|
|
1150
|
+
components: {
|
|
1151
|
+
Suggestion: ThreadSuggestionItem
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
) });
|
|
1155
|
+
};
|
|
1156
|
+
var ThreadSuggestionItem = () => {
|
|
1157
|
+
return /* @__PURE__ */ jsx11("div", { className: "aui-thread-welcome-suggestion-display fade-in slide-in-from-bottom-2 @md:nth-[n+3]:block nth-[n+3]:hidden animate-in fill-mode-both duration-200", children: /* @__PURE__ */ jsx11(SuggestionPrimitive.Trigger, { send: true, asChild: true, children: /* @__PURE__ */ jsxs7(
|
|
1158
|
+
Button,
|
|
1159
|
+
{
|
|
1160
|
+
variant: "ghost",
|
|
1161
|
+
className: "aui-thread-welcome-suggestion h-auto w-full @md:flex-col flex-wrap items-start justify-start gap-1 rounded-2xl border px-4 py-3 text-left text-sm transition-colors hover:bg-muted",
|
|
1162
|
+
children: [
|
|
1163
|
+
/* @__PURE__ */ jsx11("span", { className: "aui-thread-welcome-suggestion-text-1 font-medium", children: /* @__PURE__ */ jsx11(SuggestionPrimitive.Title, {}) }),
|
|
1164
|
+
/* @__PURE__ */ jsx11("span", { className: "aui-thread-welcome-suggestion-text-2 text-muted-foreground", children: /* @__PURE__ */ jsx11(SuggestionPrimitive.Description, {}) })
|
|
1165
|
+
]
|
|
1166
|
+
}
|
|
1167
|
+
) }) });
|
|
1168
|
+
};
|
|
1169
|
+
var Composer = () => {
|
|
1170
|
+
return /* @__PURE__ */ jsx11(ComposerPrimitive2.Root, { className: "aui-composer-root relative flex w-full flex-col", children: /* @__PURE__ */ jsxs7(ComposerPrimitive2.AttachmentDropzone, { className: "aui-composer-attachment-dropzone flex w-full flex-col rounded-3xl border border-border bg-card px-2 pt-2 shadow-[0_8px_30px_rgba(0,0,0,0.24)] outline-none transition-all duration-300 has-[textarea:focus-visible]:border-ring has-[textarea:focus-visible]:ring-2 has-[textarea:focus-visible]:ring-ring/20 data-[dragging=true]:border-ring data-[dragging=true]:border-dashed data-[dragging=true]:bg-accent/50", children: [
|
|
1171
|
+
/* @__PURE__ */ jsx11(ComposerAttachments, {}),
|
|
1172
|
+
/* @__PURE__ */ jsx11(
|
|
1173
|
+
ComposerPrimitive2.Input,
|
|
1174
|
+
{
|
|
1175
|
+
placeholder: "Describe your idea...",
|
|
1176
|
+
className: "aui-composer-input mb-1 max-h-32 min-h-14 w-full resize-none bg-transparent px-4 pt-2 pb-3 text-sm outline-none placeholder:text-muted-foreground focus-visible:ring-0",
|
|
1177
|
+
rows: 1,
|
|
1178
|
+
autoFocus: true,
|
|
1179
|
+
"aria-label": "Message input"
|
|
1180
|
+
}
|
|
1181
|
+
),
|
|
1182
|
+
/* @__PURE__ */ jsx11(ComposerAction, {})
|
|
1183
|
+
] }) });
|
|
1184
|
+
};
|
|
1185
|
+
var ComposerAction = () => {
|
|
1186
|
+
return /* @__PURE__ */ jsxs7("div", { className: "aui-composer-action-wrapper relative mx-2 mb-2 flex items-center justify-between", children: [
|
|
1187
|
+
/* @__PURE__ */ jsx11(ComposerAddAttachment, {}),
|
|
1188
|
+
/* @__PURE__ */ jsx11(AuiIf, { condition: (s) => !s.thread.isRunning, children: /* @__PURE__ */ jsx11(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx11(
|
|
1189
|
+
TooltipIconButton,
|
|
1190
|
+
{
|
|
1191
|
+
tooltip: "Send message",
|
|
1192
|
+
side: "bottom",
|
|
1193
|
+
type: "submit",
|
|
1194
|
+
variant: "default",
|
|
1195
|
+
size: "icon",
|
|
1196
|
+
className: "aui-composer-send size-8 rounded-full",
|
|
1197
|
+
"aria-label": "Send message",
|
|
1198
|
+
children: /* @__PURE__ */ jsx11(ArrowUpIcon, { className: "aui-composer-send-icon size-4" })
|
|
1199
|
+
}
|
|
1200
|
+
) }) }),
|
|
1201
|
+
/* @__PURE__ */ jsx11(AuiIf, { condition: (s) => s.thread.isRunning, children: /* @__PURE__ */ jsx11(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx11(
|
|
1202
|
+
Button,
|
|
1203
|
+
{
|
|
1204
|
+
type: "button",
|
|
1205
|
+
variant: "default",
|
|
1206
|
+
size: "icon",
|
|
1207
|
+
className: "aui-composer-cancel size-8 rounded-full",
|
|
1208
|
+
"aria-label": "Stop generating",
|
|
1209
|
+
children: /* @__PURE__ */ jsx11(SquareIcon, { className: "aui-composer-cancel-icon size-3 fill-current" })
|
|
1210
|
+
}
|
|
1211
|
+
) }) })
|
|
1212
|
+
] });
|
|
1213
|
+
};
|
|
1214
|
+
var MessageError = () => {
|
|
1215
|
+
return /* @__PURE__ */ jsx11(MessagePrimitive2.Error, { children: /* @__PURE__ */ jsx11(ErrorPrimitive.Root, { className: "aui-message-error-root mt-2 rounded-xl border border-destructive bg-destructive/10 p-3 text-destructive text-sm dark:bg-destructive/5 dark:text-red-200", children: /* @__PURE__ */ jsx11(ErrorPrimitive.Message, { className: "aui-message-error-message line-clamp-2" }) }) });
|
|
1216
|
+
};
|
|
1217
|
+
var AssistantMessage = () => {
|
|
1218
|
+
return /* @__PURE__ */ jsxs7(
|
|
1219
|
+
MessagePrimitive2.Root,
|
|
1220
|
+
{
|
|
1221
|
+
className: "aui-assistant-message-root fade-in slide-in-from-bottom-1 relative mx-auto w-full max-w-(--thread-max-width) animate-in py-2 duration-150",
|
|
1222
|
+
"data-role": "assistant",
|
|
1223
|
+
children: [
|
|
1224
|
+
/* @__PURE__ */ jsxs7("div", { className: "aui-assistant-message-content wrap-break-word px-2 text-[13px] text-foreground leading-relaxed", children: [
|
|
1225
|
+
/* @__PURE__ */ jsx11(
|
|
1226
|
+
MessagePrimitive2.Parts,
|
|
1227
|
+
{
|
|
1228
|
+
components: {
|
|
1229
|
+
Text: MarkdownText,
|
|
1230
|
+
tools: { Fallback: ToolFallback }
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
),
|
|
1234
|
+
/* @__PURE__ */ jsx11(MessageError, {})
|
|
1235
|
+
] }),
|
|
1236
|
+
/* @__PURE__ */ jsxs7("div", { className: "aui-assistant-message-footer mt-0.5 ml-2 flex", children: [
|
|
1237
|
+
/* @__PURE__ */ jsx11(BranchPicker, {}),
|
|
1238
|
+
/* @__PURE__ */ jsx11(AssistantActionBar, {})
|
|
1239
|
+
] })
|
|
1240
|
+
]
|
|
1241
|
+
}
|
|
1242
|
+
);
|
|
1243
|
+
};
|
|
1244
|
+
var AssistantActionBar = () => {
|
|
1245
|
+
return /* @__PURE__ */ jsxs7(
|
|
1246
|
+
ActionBarPrimitive.Root,
|
|
1247
|
+
{
|
|
1248
|
+
hideWhenRunning: true,
|
|
1249
|
+
autohide: "not-last",
|
|
1250
|
+
autohideFloat: "single-branch",
|
|
1251
|
+
className: "aui-assistant-action-bar-root col-start-3 row-start-2 -ml-1 flex gap-1 text-muted-foreground data-floating:absolute data-floating:rounded-xl data-floating:border data-floating:bg-background data-floating:p-1 data-floating:shadow-sm",
|
|
1252
|
+
children: [
|
|
1253
|
+
/* @__PURE__ */ jsx11(ActionBarPrimitive.Copy, { asChild: true, children: /* @__PURE__ */ jsxs7(TooltipIconButton, { tooltip: "Copy", children: [
|
|
1254
|
+
/* @__PURE__ */ jsx11(AuiIf, { condition: (s) => s.message.isCopied, children: /* @__PURE__ */ jsx11(CheckIcon3, {}) }),
|
|
1255
|
+
/* @__PURE__ */ jsx11(AuiIf, { condition: (s) => !s.message.isCopied, children: /* @__PURE__ */ jsx11(CopyIcon2, {}) })
|
|
1256
|
+
] }) }),
|
|
1257
|
+
/* @__PURE__ */ jsx11(ActionBarPrimitive.Reload, { asChild: true, children: /* @__PURE__ */ jsx11(TooltipIconButton, { tooltip: "Refresh", children: /* @__PURE__ */ jsx11(RefreshCwIcon, {}) }) }),
|
|
1258
|
+
/* @__PURE__ */ jsxs7(ActionBarMorePrimitive.Root, { children: [
|
|
1259
|
+
/* @__PURE__ */ jsx11(ActionBarMorePrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsx11(
|
|
1260
|
+
TooltipIconButton,
|
|
1261
|
+
{
|
|
1262
|
+
tooltip: "More",
|
|
1263
|
+
className: "data-[state=open]:bg-accent",
|
|
1264
|
+
children: /* @__PURE__ */ jsx11(MoreHorizontalIcon, {})
|
|
1265
|
+
}
|
|
1266
|
+
) }),
|
|
1267
|
+
/* @__PURE__ */ jsx11(
|
|
1268
|
+
ActionBarMorePrimitive.Content,
|
|
1269
|
+
{
|
|
1270
|
+
side: "bottom",
|
|
1271
|
+
align: "start",
|
|
1272
|
+
className: "aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-xl border bg-popover p-1 text-popover-foreground shadow-md",
|
|
1273
|
+
children: /* @__PURE__ */ jsx11(ActionBarPrimitive.ExportMarkdown, { asChild: true, children: /* @__PURE__ */ jsxs7(ActionBarMorePrimitive.Item, { className: "aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground", children: [
|
|
1274
|
+
/* @__PURE__ */ jsx11(DownloadIcon, { className: "size-4" }),
|
|
1275
|
+
"Export as Markdown"
|
|
1276
|
+
] }) })
|
|
1277
|
+
}
|
|
1278
|
+
)
|
|
1279
|
+
] })
|
|
1280
|
+
]
|
|
1281
|
+
}
|
|
1282
|
+
);
|
|
1283
|
+
};
|
|
1284
|
+
var UserMessage = () => {
|
|
1285
|
+
return /* @__PURE__ */ jsxs7(
|
|
1286
|
+
MessagePrimitive2.Root,
|
|
1287
|
+
{
|
|
1288
|
+
className: "aui-user-message-root fade-in slide-in-from-bottom-1 mx-auto grid w-full max-w-(--thread-max-width) animate-in auto-rows-auto grid-cols-[minmax(72px,1fr)_auto] content-start gap-y-2 px-2 py-2 duration-150 [&:where(>*)]:col-start-2",
|
|
1289
|
+
"data-role": "user",
|
|
1290
|
+
children: [
|
|
1291
|
+
/* @__PURE__ */ jsx11(UserMessageAttachments, {}),
|
|
1292
|
+
/* @__PURE__ */ jsxs7("div", { className: "aui-user-message-content-wrapper relative col-start-2 min-w-0", children: [
|
|
1293
|
+
/* @__PURE__ */ jsx11("div", { className: "aui-user-message-content wrap-break-word rounded-2xl bg-muted px-3 py-2 text-[13px] text-foreground", children: /* @__PURE__ */ jsx11(MessagePrimitive2.Parts, {}) }),
|
|
1294
|
+
/* @__PURE__ */ jsx11("div", { className: "aui-user-action-bar-wrapper absolute top-1/2 left-0 -translate-x-full -translate-y-1/2 pr-2", children: /* @__PURE__ */ jsx11(UserActionBar, {}) })
|
|
1295
|
+
] }),
|
|
1296
|
+
/* @__PURE__ */ jsx11(BranchPicker, { className: "aui-user-branch-picker col-span-full col-start-1 row-start-3 -mr-1 justify-end" })
|
|
1297
|
+
]
|
|
1298
|
+
}
|
|
1299
|
+
);
|
|
1300
|
+
};
|
|
1301
|
+
var UserActionBar = () => {
|
|
1302
|
+
return /* @__PURE__ */ jsx11(
|
|
1303
|
+
ActionBarPrimitive.Root,
|
|
1304
|
+
{
|
|
1305
|
+
hideWhenRunning: true,
|
|
1306
|
+
autohide: "not-last",
|
|
1307
|
+
className: "aui-user-action-bar-root flex flex-col items-end",
|
|
1308
|
+
children: /* @__PURE__ */ jsx11(ActionBarPrimitive.Edit, { asChild: true, children: /* @__PURE__ */ jsx11(TooltipIconButton, { tooltip: "Edit", className: "aui-user-action-edit p-4", children: /* @__PURE__ */ jsx11(PencilIcon, {}) }) })
|
|
1309
|
+
}
|
|
1310
|
+
);
|
|
1311
|
+
};
|
|
1312
|
+
var EditComposer = () => {
|
|
1313
|
+
return /* @__PURE__ */ jsx11(MessagePrimitive2.Root, { className: "aui-edit-composer-wrapper mx-auto flex w-full max-w-(--thread-max-width) flex-col px-2 py-3", children: /* @__PURE__ */ jsxs7(ComposerPrimitive2.Root, { className: "aui-edit-composer-root ml-auto flex w-full max-w-[85%] flex-col rounded-3xl bg-card", children: [
|
|
1314
|
+
/* @__PURE__ */ jsx11(
|
|
1315
|
+
ComposerPrimitive2.Input,
|
|
1316
|
+
{
|
|
1317
|
+
className: "aui-edit-composer-input min-h-14 w-full resize-none bg-transparent p-4 text-foreground text-sm outline-none",
|
|
1318
|
+
autoFocus: true
|
|
1319
|
+
}
|
|
1320
|
+
),
|
|
1321
|
+
/* @__PURE__ */ jsxs7("div", { className: "aui-edit-composer-footer mx-3 mb-3 flex items-center gap-2 self-end", children: [
|
|
1322
|
+
/* @__PURE__ */ jsx11(ComposerPrimitive2.Cancel, { asChild: true, children: /* @__PURE__ */ jsx11(Button, { variant: "ghost", size: "sm", children: "Cancel" }) }),
|
|
1323
|
+
/* @__PURE__ */ jsx11(ComposerPrimitive2.Send, { asChild: true, children: /* @__PURE__ */ jsx11(Button, { size: "sm", children: "Update" }) })
|
|
1324
|
+
] })
|
|
1325
|
+
] }) });
|
|
1326
|
+
};
|
|
1327
|
+
var BranchPicker = ({
|
|
1328
|
+
className,
|
|
1329
|
+
...rest
|
|
1330
|
+
}) => {
|
|
1331
|
+
return /* @__PURE__ */ jsxs7(
|
|
1332
|
+
BranchPickerPrimitive.Root,
|
|
1333
|
+
{
|
|
1334
|
+
hideWhenSingleBranch: true,
|
|
1335
|
+
className: cn(
|
|
1336
|
+
"aui-branch-picker-root mr-2 -ml-2 inline-flex items-center text-muted-foreground text-xs",
|
|
1337
|
+
className
|
|
1338
|
+
),
|
|
1339
|
+
...rest,
|
|
1340
|
+
children: [
|
|
1341
|
+
/* @__PURE__ */ jsx11(BranchPickerPrimitive.Previous, { asChild: true, children: /* @__PURE__ */ jsx11(TooltipIconButton, { tooltip: "Previous", children: /* @__PURE__ */ jsx11(ChevronLeftIcon, {}) }) }),
|
|
1342
|
+
/* @__PURE__ */ jsxs7("span", { className: "aui-branch-picker-state font-medium", children: [
|
|
1343
|
+
/* @__PURE__ */ jsx11(BranchPickerPrimitive.Number, {}),
|
|
1344
|
+
" / ",
|
|
1345
|
+
/* @__PURE__ */ jsx11(BranchPickerPrimitive.Count, {})
|
|
1346
|
+
] }),
|
|
1347
|
+
/* @__PURE__ */ jsx11(BranchPickerPrimitive.Next, { asChild: true, children: /* @__PURE__ */ jsx11(TooltipIconButton, { tooltip: "Next", children: /* @__PURE__ */ jsx11(ChevronRightIcon, {}) }) })
|
|
1348
|
+
]
|
|
1349
|
+
}
|
|
1350
|
+
);
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
// src/client/use-conversations.ts
|
|
1354
|
+
import { useState as useState4, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2, useMemo } from "react";
|
|
1355
|
+
import { useThreadRuntime } from "@assistant-ui/react";
|
|
1356
|
+
var CONV_INDEX_KEY = "feedback_conversations";
|
|
1357
|
+
var CONV_PREFIX = "feedback_conv_";
|
|
1358
|
+
var ACTIVE_CONV_KEY = "feedback_active_conv";
|
|
1359
|
+
var MAX_CONVERSATIONS = 10;
|
|
1360
|
+
var AUTOSAVE_DELAY = 400;
|
|
1361
|
+
var DEFAULT_TITLE = "New chat";
|
|
1362
|
+
function loadIndex() {
|
|
1363
|
+
try {
|
|
1364
|
+
const raw = localStorage.getItem(CONV_INDEX_KEY);
|
|
1365
|
+
return raw ? JSON.parse(raw) : [];
|
|
1366
|
+
} catch {
|
|
1367
|
+
return [];
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
function saveIndex(conversations) {
|
|
1371
|
+
localStorage.setItem(CONV_INDEX_KEY, JSON.stringify(conversations));
|
|
1372
|
+
}
|
|
1373
|
+
function loadState(id) {
|
|
1374
|
+
try {
|
|
1375
|
+
const raw = localStorage.getItem(CONV_PREFIX + id);
|
|
1376
|
+
return raw ? JSON.parse(raw) : null;
|
|
1377
|
+
} catch {
|
|
1378
|
+
return null;
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
function saveState(id, state) {
|
|
1382
|
+
localStorage.setItem(CONV_PREFIX + id, JSON.stringify(state));
|
|
1383
|
+
}
|
|
1384
|
+
function deleteMessages(id) {
|
|
1385
|
+
localStorage.removeItem(CONV_PREFIX + id);
|
|
1386
|
+
}
|
|
1387
|
+
function makeConversation() {
|
|
1388
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1389
|
+
return {
|
|
1390
|
+
id: crypto.randomUUID(),
|
|
1391
|
+
title: DEFAULT_TITLE,
|
|
1392
|
+
createdAt: now,
|
|
1393
|
+
updatedAt: now
|
|
1394
|
+
};
|
|
1395
|
+
}
|
|
1396
|
+
function useConversations() {
|
|
1397
|
+
const threadRuntime = useThreadRuntime();
|
|
1398
|
+
const [conversations, setConversations] = useState4([]);
|
|
1399
|
+
const [activeId, setActiveId] = useState4("");
|
|
1400
|
+
const activeIdRef = useRef2(activeId);
|
|
1401
|
+
const conversationsRef = useRef2(conversations);
|
|
1402
|
+
useEffect2(() => {
|
|
1403
|
+
activeIdRef.current = activeId;
|
|
1404
|
+
conversationsRef.current = conversations;
|
|
1405
|
+
});
|
|
1406
|
+
function saveCurrent() {
|
|
1407
|
+
try {
|
|
1408
|
+
const state = threadRuntime.exportExternalState();
|
|
1409
|
+
if (state && state.messages.length > 0) {
|
|
1410
|
+
saveState(activeIdRef.current, state);
|
|
1411
|
+
updateTitleIfNeeded(activeIdRef.current, state);
|
|
1412
|
+
}
|
|
1413
|
+
} catch {
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
function restoreOrReset(id) {
|
|
1417
|
+
const saved = loadState(id);
|
|
1418
|
+
if (saved && saved.messages.length > 0) {
|
|
1419
|
+
try {
|
|
1420
|
+
threadRuntime.importExternalState(saved);
|
|
1421
|
+
} catch {
|
|
1422
|
+
threadRuntime.reset();
|
|
1423
|
+
}
|
|
1424
|
+
} else {
|
|
1425
|
+
threadRuntime.reset();
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
function activate(id) {
|
|
1429
|
+
setActiveId(id);
|
|
1430
|
+
localStorage.setItem(ACTIVE_CONV_KEY, id);
|
|
1431
|
+
}
|
|
1432
|
+
const initialized = useRef2(false);
|
|
1433
|
+
useEffect2(() => {
|
|
1434
|
+
if (initialized.current) return;
|
|
1435
|
+
initialized.current = true;
|
|
1436
|
+
let index = loadIndex();
|
|
1437
|
+
let currentId = localStorage.getItem(ACTIVE_CONV_KEY);
|
|
1438
|
+
if (index.length === 0) {
|
|
1439
|
+
const first = makeConversation();
|
|
1440
|
+
index = [first];
|
|
1441
|
+
currentId = first.id;
|
|
1442
|
+
saveIndex(index);
|
|
1443
|
+
localStorage.setItem(ACTIVE_CONV_KEY, currentId);
|
|
1444
|
+
} else if (!currentId || !index.some((c) => c.id === currentId)) {
|
|
1445
|
+
currentId = index[0].id;
|
|
1446
|
+
localStorage.setItem(ACTIVE_CONV_KEY, currentId);
|
|
1447
|
+
}
|
|
1448
|
+
setConversations(index);
|
|
1449
|
+
setActiveId(currentId);
|
|
1450
|
+
const saved = loadState(currentId);
|
|
1451
|
+
if (saved && saved.messages.length > 0) {
|
|
1452
|
+
try {
|
|
1453
|
+
threadRuntime.importExternalState(saved);
|
|
1454
|
+
} catch {
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}, [threadRuntime]);
|
|
1458
|
+
const switchTo = useCallback2(
|
|
1459
|
+
(id) => {
|
|
1460
|
+
if (id === activeIdRef.current) return;
|
|
1461
|
+
saveCurrent();
|
|
1462
|
+
restoreOrReset(id);
|
|
1463
|
+
activate(id);
|
|
1464
|
+
},
|
|
1465
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- helpers use refs, only threadRuntime matters
|
|
1466
|
+
[threadRuntime]
|
|
1467
|
+
);
|
|
1468
|
+
const create = useCallback2(() => {
|
|
1469
|
+
saveCurrent();
|
|
1470
|
+
const newConv = makeConversation();
|
|
1471
|
+
let updated = [newConv, ...conversationsRef.current];
|
|
1472
|
+
if (updated.length > MAX_CONVERSATIONS) {
|
|
1473
|
+
const removed = updated.slice(MAX_CONVERSATIONS);
|
|
1474
|
+
for (const c of removed) deleteMessages(c.id);
|
|
1475
|
+
updated = updated.slice(0, MAX_CONVERSATIONS);
|
|
1476
|
+
}
|
|
1477
|
+
setConversations(updated);
|
|
1478
|
+
saveIndex(updated);
|
|
1479
|
+
threadRuntime.reset();
|
|
1480
|
+
activate(newConv.id);
|
|
1481
|
+
}, [threadRuntime]);
|
|
1482
|
+
const remove = useCallback2(
|
|
1483
|
+
(id) => {
|
|
1484
|
+
const current = conversationsRef.current;
|
|
1485
|
+
const filtered = current.filter((c) => c.id !== id);
|
|
1486
|
+
deleteMessages(id);
|
|
1487
|
+
if (filtered.length === 0) {
|
|
1488
|
+
const fresh = makeConversation();
|
|
1489
|
+
setConversations([fresh]);
|
|
1490
|
+
saveIndex([fresh]);
|
|
1491
|
+
threadRuntime.reset();
|
|
1492
|
+
activate(fresh.id);
|
|
1493
|
+
return;
|
|
1494
|
+
}
|
|
1495
|
+
if (id === activeIdRef.current) {
|
|
1496
|
+
const oldIndex = current.findIndex((c) => c.id === id);
|
|
1497
|
+
const nextIndex = Math.min(oldIndex, filtered.length - 1);
|
|
1498
|
+
const nextId = filtered[nextIndex].id;
|
|
1499
|
+
restoreOrReset(nextId);
|
|
1500
|
+
activate(nextId);
|
|
1501
|
+
}
|
|
1502
|
+
setConversations(filtered);
|
|
1503
|
+
saveIndex(filtered);
|
|
1504
|
+
},
|
|
1505
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps -- helpers use refs, only threadRuntime matters
|
|
1506
|
+
[threadRuntime]
|
|
1507
|
+
);
|
|
1508
|
+
const save = useCallback2(() => {
|
|
1509
|
+
const id = activeIdRef.current;
|
|
1510
|
+
if (!id) return;
|
|
1511
|
+
try {
|
|
1512
|
+
const state = threadRuntime.exportExternalState();
|
|
1513
|
+
if (state && state.messages.length > 0) {
|
|
1514
|
+
saveState(id, state);
|
|
1515
|
+
updateTitleIfNeeded(id, state);
|
|
1516
|
+
}
|
|
1517
|
+
} catch {
|
|
1518
|
+
}
|
|
1519
|
+
setConversations(loadIndex());
|
|
1520
|
+
}, [threadRuntime]);
|
|
1521
|
+
useEffect2(() => {
|
|
1522
|
+
let timeoutId = null;
|
|
1523
|
+
const unsubscribe = threadRuntime.subscribe(() => {
|
|
1524
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1525
|
+
timeoutId = setTimeout(save, AUTOSAVE_DELAY);
|
|
1526
|
+
});
|
|
1527
|
+
return () => {
|
|
1528
|
+
unsubscribe();
|
|
1529
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1530
|
+
save();
|
|
1531
|
+
};
|
|
1532
|
+
}, [threadRuntime, save]);
|
|
1533
|
+
const sorted = useMemo(
|
|
1534
|
+
() => [...conversations].sort(
|
|
1535
|
+
(a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
1536
|
+
),
|
|
1537
|
+
[conversations]
|
|
1538
|
+
);
|
|
1539
|
+
return { conversations: sorted, activeId, switchTo, create, remove, save };
|
|
1540
|
+
}
|
|
1541
|
+
function updateTitleIfNeeded(id, state) {
|
|
1542
|
+
const index = loadIndex();
|
|
1543
|
+
const conv = index.find((c) => c.id === id);
|
|
1544
|
+
if (!conv) return;
|
|
1545
|
+
if (conv.title === DEFAULT_TITLE && state.messages.length > 0) {
|
|
1546
|
+
const firstUserMsg = state.messages.find((m) => m.message?.role === "user");
|
|
1547
|
+
if (firstUserMsg?.message) {
|
|
1548
|
+
const msg = firstUserMsg.message;
|
|
1549
|
+
const textPart = msg.parts?.find(
|
|
1550
|
+
(p) => p.type === "text" && !!p.text
|
|
1551
|
+
);
|
|
1552
|
+
const text = textPart?.text ?? (typeof msg.content === "string" ? msg.content : "");
|
|
1553
|
+
if (text) {
|
|
1554
|
+
conv.title = text.slice(0, 40);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
conv.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1559
|
+
saveIndex(index);
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// src/client/conversation-tabs.tsx
|
|
1563
|
+
import { Plus, X } from "lucide-react";
|
|
1564
|
+
import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1565
|
+
function ConversationTabs({
|
|
1566
|
+
conversations,
|
|
1567
|
+
activeId,
|
|
1568
|
+
onSwitch,
|
|
1569
|
+
onCreate,
|
|
1570
|
+
onRemove,
|
|
1571
|
+
onClose
|
|
1572
|
+
}) {
|
|
1573
|
+
const canDelete = conversations.length > 1;
|
|
1574
|
+
return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
1575
|
+
/* @__PURE__ */ jsx12("div", { className: "flex items-center gap-1 overflow-x-auto [&::-webkit-scrollbar]:hidden min-w-0 flex-1", children: conversations.map((conv) => {
|
|
1576
|
+
const isActive = conv.id === activeId;
|
|
1577
|
+
return /* @__PURE__ */ jsxs8(
|
|
1578
|
+
"button",
|
|
1579
|
+
{
|
|
1580
|
+
role: "tab",
|
|
1581
|
+
"aria-selected": isActive,
|
|
1582
|
+
onClick: () => onSwitch(conv.id),
|
|
1583
|
+
className: `feedback-tab group relative flex items-center gap-1 px-2.5 h-8 text-[12px] whitespace-nowrap rounded-lg transition-all ${isActive ? "feedback-tab-active" : "feedback-tab-inactive"}`,
|
|
1584
|
+
children: [
|
|
1585
|
+
/* @__PURE__ */ jsx12("span", { className: "max-w-[100px] truncate", children: conv.title }),
|
|
1586
|
+
canDelete && /* @__PURE__ */ jsx12(
|
|
1587
|
+
"span",
|
|
1588
|
+
{
|
|
1589
|
+
role: "button",
|
|
1590
|
+
"aria-label": `Delete ${conv.title}`,
|
|
1591
|
+
onClick: (e) => {
|
|
1592
|
+
e.stopPropagation();
|
|
1593
|
+
onRemove(conv.id);
|
|
1594
|
+
},
|
|
1595
|
+
className: "ml-0.5 hidden h-4 w-4 shrink-0 items-center justify-center rounded-md group-hover:inline-flex hover:bg-white/10",
|
|
1596
|
+
children: /* @__PURE__ */ jsx12(X, { className: "h-2.5 w-2.5" })
|
|
1597
|
+
}
|
|
1598
|
+
)
|
|
1599
|
+
]
|
|
1600
|
+
},
|
|
1601
|
+
conv.id
|
|
1602
|
+
);
|
|
1603
|
+
}) }),
|
|
1604
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center shrink-0 gap-1", children: [
|
|
1605
|
+
/* @__PURE__ */ jsx12(
|
|
1606
|
+
"button",
|
|
1607
|
+
{
|
|
1608
|
+
onClick: onCreate,
|
|
1609
|
+
className: "feedback-tab-button flex h-7 w-7 items-center justify-center rounded-lg transition-all",
|
|
1610
|
+
"aria-label": "New chat",
|
|
1611
|
+
children: /* @__PURE__ */ jsx12(Plus, { className: "h-3.5 w-3.5" })
|
|
1612
|
+
}
|
|
1613
|
+
),
|
|
1614
|
+
/* @__PURE__ */ jsx12(
|
|
1615
|
+
"button",
|
|
1616
|
+
{
|
|
1617
|
+
onClick: onClose,
|
|
1618
|
+
className: "feedback-tab-button flex h-7 w-7 items-center justify-center rounded-lg transition-all",
|
|
1619
|
+
"aria-label": "Close feedback",
|
|
1620
|
+
children: /* @__PURE__ */ jsx12(X, { className: "h-3.5 w-3.5" })
|
|
1621
|
+
}
|
|
1622
|
+
)
|
|
1623
|
+
] })
|
|
1624
|
+
] });
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
// src/client/present-options-tool-ui.tsx
|
|
1628
|
+
import { useState as useState5 } from "react";
|
|
1629
|
+
import { makeAssistantToolUI } from "@assistant-ui/react";
|
|
1630
|
+
import { useThreadRuntime as useThreadRuntime2 } from "@assistant-ui/react";
|
|
1631
|
+
import { Check } from "lucide-react";
|
|
1632
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1633
|
+
var PresentOptionsToolUI = makeAssistantToolUI({
|
|
1634
|
+
toolName: "present_options",
|
|
1635
|
+
render: function PresentOptions({ args }) {
|
|
1636
|
+
const threadRuntime = useThreadRuntime2();
|
|
1637
|
+
const [selected, setSelected] = useState5(null);
|
|
1638
|
+
if (!args.options) return null;
|
|
1639
|
+
if (selected) {
|
|
1640
|
+
return /* @__PURE__ */ jsxs9("div", { className: "inline-flex items-center gap-1.5 rounded-full bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground", children: [
|
|
1641
|
+
/* @__PURE__ */ jsx13(Check, { className: "h-3.5 w-3.5" }),
|
|
1642
|
+
selected
|
|
1643
|
+
] });
|
|
1644
|
+
}
|
|
1645
|
+
return /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-2 py-2", children: args.options.map((option, i) => /* @__PURE__ */ jsx13(
|
|
1646
|
+
"button",
|
|
1647
|
+
{
|
|
1648
|
+
onClick: () => {
|
|
1649
|
+
setSelected(option);
|
|
1650
|
+
threadRuntime.append({
|
|
1651
|
+
role: "user",
|
|
1652
|
+
content: [{ type: "text", text: option }]
|
|
1653
|
+
});
|
|
1654
|
+
},
|
|
1655
|
+
className: "rounded-full border border-border bg-background px-4 py-2 text-sm font-medium transition-all hover:border-foreground/20 hover:bg-muted active:scale-95",
|
|
1656
|
+
children: option
|
|
1657
|
+
},
|
|
1658
|
+
i
|
|
1659
|
+
)) });
|
|
1660
|
+
}
|
|
1661
|
+
});
|
|
1662
|
+
|
|
1663
|
+
// src/client/submit-request-tool-ui.tsx
|
|
1664
|
+
import { useState as useState7 } from "react";
|
|
1665
|
+
import { makeAssistantToolUI as makeAssistantToolUI2 } from "@assistant-ui/react";
|
|
1666
|
+
import { Check as Check3, Copy, ChevronRight } from "lucide-react";
|
|
1667
|
+
|
|
1668
|
+
// src/client/pipeline-tracker.tsx
|
|
1669
|
+
import { useState as useState6, useEffect as useEffect3, useCallback as useCallback3, useRef as useRef3 } from "react";
|
|
1670
|
+
import { Check as Check2, X as X2, Loader2, ExternalLink, RotateCcw, Eye, MessageSquare } from "lucide-react";
|
|
1671
|
+
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1672
|
+
var STEPS = [
|
|
1673
|
+
{ label: "Issue created", stage: "created" },
|
|
1674
|
+
{ label: "Queued", stage: "queued" },
|
|
1675
|
+
{ label: "Agent running", stage: "running" },
|
|
1676
|
+
{ label: "Validating", stage: "validating" },
|
|
1677
|
+
{ label: "Preview ready", stage: "preview_ready" },
|
|
1678
|
+
{ label: "Deployed", stage: "deployed" }
|
|
1679
|
+
];
|
|
1680
|
+
var STAGE_INDEX = {
|
|
1681
|
+
created: 0,
|
|
1682
|
+
queued: 1,
|
|
1683
|
+
running: 2,
|
|
1684
|
+
validating: 3,
|
|
1685
|
+
preview_ready: 4,
|
|
1686
|
+
deployed: 5,
|
|
1687
|
+
failed: -1,
|
|
1688
|
+
rejected: -1
|
|
1689
|
+
};
|
|
1690
|
+
var TERMINAL_STAGES = ["deployed", "failed", "rejected"];
|
|
1691
|
+
var POLL_INTERVAL_MS = 5e3;
|
|
1692
|
+
var PREVIEW_POLL_INTERVAL_MS = 15e3;
|
|
1693
|
+
var ACTIVE_PIPELINE_KEY = "feedback_active_pipeline";
|
|
1694
|
+
function setPipelineActive(issueNumber, stage) {
|
|
1695
|
+
if (TERMINAL_STAGES.includes(stage)) {
|
|
1696
|
+
localStorage.removeItem(ACTIVE_PIPELINE_KEY);
|
|
1697
|
+
} else {
|
|
1698
|
+
localStorage.setItem(ACTIVE_PIPELINE_KEY, JSON.stringify({ issueNumber, stage }));
|
|
1699
|
+
}
|
|
1700
|
+
window.dispatchEvent(new Event("pipeline-status"));
|
|
1701
|
+
}
|
|
1702
|
+
function getPassword() {
|
|
1703
|
+
return sessionStorage.getItem("feedback_password") ?? "";
|
|
1704
|
+
}
|
|
1705
|
+
function getFailedStepIndex(previousStage) {
|
|
1706
|
+
if (previousStage && previousStage !== "failed") {
|
|
1707
|
+
return STAGE_INDEX[previousStage];
|
|
1708
|
+
}
|
|
1709
|
+
return 2;
|
|
1710
|
+
}
|
|
1711
|
+
function deriveStepState(stepIndex, currentIndex, failedAtIndex, stage) {
|
|
1712
|
+
const isFailed = stage === "failed";
|
|
1713
|
+
const isDeployed = stage === "deployed";
|
|
1714
|
+
if (isFailed && stepIndex === failedAtIndex) return "failed";
|
|
1715
|
+
if (isFailed && stepIndex < failedAtIndex) return "completed";
|
|
1716
|
+
if (isFailed) return "future";
|
|
1717
|
+
if (isDeployed) return "completed";
|
|
1718
|
+
if (currentIndex > stepIndex) return "completed";
|
|
1719
|
+
if (currentIndex === stepIndex) return "active";
|
|
1720
|
+
return "future";
|
|
1721
|
+
}
|
|
1722
|
+
var STEP_DOT_CLASS = {
|
|
1723
|
+
completed: "h-[7px] w-[7px] rounded-full bg-emerald-500",
|
|
1724
|
+
active: "h-[7px] w-[7px] rounded-full bg-foreground animate-pulse",
|
|
1725
|
+
failed: "h-[7px] w-[7px] rounded-full bg-destructive",
|
|
1726
|
+
future: "h-[5px] w-[5px] rounded-full bg-muted-foreground/25"
|
|
1727
|
+
};
|
|
1728
|
+
function StepDot({ state }) {
|
|
1729
|
+
return /* @__PURE__ */ jsx14("div", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx14("div", { className: STEP_DOT_CLASS[state] }) });
|
|
1730
|
+
}
|
|
1731
|
+
var STEP_LABEL_CLASS = {
|
|
1732
|
+
completed: "text-muted-foreground",
|
|
1733
|
+
active: "text-foreground",
|
|
1734
|
+
failed: "text-destructive",
|
|
1735
|
+
future: "text-muted-foreground/40"
|
|
1736
|
+
};
|
|
1737
|
+
function PipelineTracker({
|
|
1738
|
+
issueUrl,
|
|
1739
|
+
statusEndpoint = "/api/feedback/status"
|
|
1740
|
+
}) {
|
|
1741
|
+
const issueNumber = parseInt(issueUrl.split("/").pop() ?? "0", 10);
|
|
1742
|
+
const [status, setStatus] = useState6({
|
|
1743
|
+
stage: "created",
|
|
1744
|
+
issueNumber,
|
|
1745
|
+
issueUrl
|
|
1746
|
+
});
|
|
1747
|
+
const [polling, setPolling] = useState6(true);
|
|
1748
|
+
const [actionLoading, setActionLoading] = useState6(null);
|
|
1749
|
+
const [showChangeInput, setShowChangeInput] = useState6(false);
|
|
1750
|
+
const [changeComment, setChangeComment] = useState6("");
|
|
1751
|
+
const [confirmReject, setConfirmReject] = useState6(false);
|
|
1752
|
+
const previousStageRef = useRef3(null);
|
|
1753
|
+
const fetchStatus = useCallback3(async () => {
|
|
1754
|
+
try {
|
|
1755
|
+
const res = await fetch(`${statusEndpoint}?issue=${issueNumber}`);
|
|
1756
|
+
if (!res.ok) return;
|
|
1757
|
+
const data = await res.json();
|
|
1758
|
+
setStatus(data);
|
|
1759
|
+
setPipelineActive(issueNumber, data.stage);
|
|
1760
|
+
if (TERMINAL_STAGES.includes(data.stage)) {
|
|
1761
|
+
setPolling(false);
|
|
1762
|
+
}
|
|
1763
|
+
} catch {
|
|
1764
|
+
}
|
|
1765
|
+
}, [statusEndpoint, issueNumber]);
|
|
1766
|
+
useEffect3(() => {
|
|
1767
|
+
if (status.stage !== "failed") {
|
|
1768
|
+
previousStageRef.current = status.stage;
|
|
1769
|
+
}
|
|
1770
|
+
}, [status.stage]);
|
|
1771
|
+
useEffect3(() => {
|
|
1772
|
+
setPipelineActive(issueNumber, "created");
|
|
1773
|
+
fetchStatus();
|
|
1774
|
+
if (!polling) return;
|
|
1775
|
+
const interval = setInterval(
|
|
1776
|
+
fetchStatus,
|
|
1777
|
+
status.stage === "preview_ready" ? PREVIEW_POLL_INTERVAL_MS : POLL_INTERVAL_MS
|
|
1778
|
+
);
|
|
1779
|
+
return () => clearInterval(interval);
|
|
1780
|
+
}, [fetchStatus, polling, issueNumber, status.stage]);
|
|
1781
|
+
async function handleAction(action, extraBody) {
|
|
1782
|
+
setActionLoading(action);
|
|
1783
|
+
try {
|
|
1784
|
+
const res = await fetch(`${statusEndpoint}?issue=${issueNumber}&action=${action}`, {
|
|
1785
|
+
method: "POST",
|
|
1786
|
+
headers: { "Content-Type": "application/json" },
|
|
1787
|
+
body: JSON.stringify({ password: getPassword(), ...extraBody })
|
|
1788
|
+
});
|
|
1789
|
+
if (!res.ok) return;
|
|
1790
|
+
switch (action) {
|
|
1791
|
+
case "retry":
|
|
1792
|
+
case "request_changes":
|
|
1793
|
+
setStatus({ stage: "created", issueNumber, issueUrl });
|
|
1794
|
+
setPipelineActive(issueNumber, "created");
|
|
1795
|
+
setPolling(true);
|
|
1796
|
+
setChangeComment("");
|
|
1797
|
+
setShowChangeInput(false);
|
|
1798
|
+
break;
|
|
1799
|
+
case "approve":
|
|
1800
|
+
setStatus((prev) => ({ ...prev, stage: "deployed" }));
|
|
1801
|
+
setPipelineActive(issueNumber, "deployed");
|
|
1802
|
+
setPolling(false);
|
|
1803
|
+
break;
|
|
1804
|
+
case "reject":
|
|
1805
|
+
setStatus((prev) => ({ ...prev, stage: "rejected" }));
|
|
1806
|
+
setPipelineActive(issueNumber, "rejected");
|
|
1807
|
+
setPolling(false);
|
|
1808
|
+
break;
|
|
1809
|
+
}
|
|
1810
|
+
} finally {
|
|
1811
|
+
setActionLoading(null);
|
|
1812
|
+
setConfirmReject(false);
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
const currentIndex = STAGE_INDEX[status.stage];
|
|
1816
|
+
const isFailed = status.stage === "failed";
|
|
1817
|
+
const failedAtIndex = isFailed ? getFailedStepIndex(previousStageRef.current) : -1;
|
|
1818
|
+
return /* @__PURE__ */ jsxs10("div", { className: "rounded-2xl border border-border bg-card p-3 space-y-2", children: [
|
|
1819
|
+
/* @__PURE__ */ jsx14("div", { className: "space-y-0", children: STEPS.map((step, i) => {
|
|
1820
|
+
const state = deriveStepState(i, currentIndex, failedAtIndex, status.stage);
|
|
1821
|
+
return /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 h-6", children: [
|
|
1822
|
+
/* @__PURE__ */ jsx14(StepDot, { state }),
|
|
1823
|
+
/* @__PURE__ */ jsx14("span", { className: `text-xs ${STEP_LABEL_CLASS[state]}`, children: state === "failed" && status.failReason ? status.failReason : step.label }),
|
|
1824
|
+
i === 0 && /* @__PURE__ */ jsxs10("span", { className: "ml-auto text-[11px] text-muted-foreground/60", children: [
|
|
1825
|
+
"#",
|
|
1826
|
+
issueNumber
|
|
1827
|
+
] })
|
|
1828
|
+
] }, step.stage);
|
|
1829
|
+
}) }),
|
|
1830
|
+
status.stage === "preview_ready" && status.previewUrl && /* @__PURE__ */ jsxs10("div", { className: "space-y-2.5 pt-2 border-t border-border", children: [
|
|
1831
|
+
/* @__PURE__ */ jsxs10(
|
|
1832
|
+
"a",
|
|
1833
|
+
{
|
|
1834
|
+
href: status.previewUrl,
|
|
1835
|
+
target: "_blank",
|
|
1836
|
+
rel: "noopener noreferrer",
|
|
1837
|
+
className: "group flex items-center gap-2.5 rounded-lg border border-border px-3 py-2.5 transition-all hover:border-emerald-500/30 hover:bg-emerald-500/5",
|
|
1838
|
+
children: [
|
|
1839
|
+
/* @__PURE__ */ jsx14("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-emerald-500/10", children: /* @__PURE__ */ jsx14(Eye, { className: "h-3.5 w-3.5 text-emerald-400" }) }),
|
|
1840
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0", children: [
|
|
1841
|
+
/* @__PURE__ */ jsx14("span", { className: "block text-xs font-medium text-foreground", children: "View preview" }),
|
|
1842
|
+
/* @__PURE__ */ jsx14("span", { className: "block text-[10px] text-muted-foreground truncate", children: status.previewUrl.replace(/^https?:\/\//, "") })
|
|
1843
|
+
] }),
|
|
1844
|
+
/* @__PURE__ */ jsx14(ExternalLink, { className: "h-3 w-3 shrink-0 text-muted-foreground/50 transition-colors group-hover:text-foreground/70" })
|
|
1845
|
+
]
|
|
1846
|
+
}
|
|
1847
|
+
),
|
|
1848
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-stretch gap-1.5", children: [
|
|
1849
|
+
/* @__PURE__ */ jsxs10(
|
|
1850
|
+
"button",
|
|
1851
|
+
{
|
|
1852
|
+
onClick: () => handleAction("approve"),
|
|
1853
|
+
disabled: actionLoading !== null,
|
|
1854
|
+
className: "flex-1 inline-flex items-center justify-center gap-1.5 rounded-lg bg-emerald-500/15 px-2 py-2 text-[11px] font-medium text-emerald-400 transition-all hover:bg-emerald-500/25 disabled:opacity-50",
|
|
1855
|
+
children: [
|
|
1856
|
+
actionLoading === "approve" ? /* @__PURE__ */ jsx14(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx14(Check2, { className: "h-3 w-3" }),
|
|
1857
|
+
"Approve"
|
|
1858
|
+
]
|
|
1859
|
+
}
|
|
1860
|
+
),
|
|
1861
|
+
/* @__PURE__ */ jsxs10(
|
|
1862
|
+
"button",
|
|
1863
|
+
{
|
|
1864
|
+
onClick: () => setShowChangeInput((v) => !v),
|
|
1865
|
+
disabled: actionLoading !== null,
|
|
1866
|
+
className: `flex-1 inline-flex items-center justify-center gap-1.5 rounded-lg border px-2 py-2 text-[11px] font-medium transition-all disabled:opacity-50 ${showChangeInput ? "border-foreground/20 bg-foreground/5 text-foreground" : "border-border text-muted-foreground hover:border-foreground/15 hover:text-foreground"}`,
|
|
1867
|
+
children: [
|
|
1868
|
+
/* @__PURE__ */ jsx14(MessageSquare, { className: "h-3 w-3" }),
|
|
1869
|
+
"Changes"
|
|
1870
|
+
]
|
|
1871
|
+
}
|
|
1872
|
+
),
|
|
1873
|
+
/* @__PURE__ */ jsxs10(
|
|
1874
|
+
"button",
|
|
1875
|
+
{
|
|
1876
|
+
onClick: () => {
|
|
1877
|
+
if (confirmReject) {
|
|
1878
|
+
handleAction("reject");
|
|
1879
|
+
} else {
|
|
1880
|
+
setConfirmReject(true);
|
|
1881
|
+
}
|
|
1882
|
+
},
|
|
1883
|
+
disabled: actionLoading !== null,
|
|
1884
|
+
className: `flex-1 inline-flex items-center justify-center gap-1.5 rounded-lg px-2 py-2 text-[11px] font-medium transition-all disabled:opacity-50 ${confirmReject ? "bg-red-500/20 text-red-400" : "border border-border text-muted-foreground hover:border-red-500/20 hover:text-red-400"}`,
|
|
1885
|
+
children: [
|
|
1886
|
+
actionLoading === "reject" ? /* @__PURE__ */ jsx14(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx14(X2, { className: "h-3 w-3" }),
|
|
1887
|
+
confirmReject ? "Confirm" : "Reject"
|
|
1888
|
+
]
|
|
1889
|
+
}
|
|
1890
|
+
)
|
|
1891
|
+
] }),
|
|
1892
|
+
showChangeInput && /* @__PURE__ */ jsxs10("div", { className: "space-y-1.5", children: [
|
|
1893
|
+
/* @__PURE__ */ jsx14(
|
|
1894
|
+
"textarea",
|
|
1895
|
+
{
|
|
1896
|
+
value: changeComment,
|
|
1897
|
+
onChange: (e) => setChangeComment(e.target.value),
|
|
1898
|
+
placeholder: "Describe the changes you want...",
|
|
1899
|
+
className: "w-full rounded-lg border border-border bg-background px-2.5 py-2 text-xs text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:border-foreground/20 resize-none",
|
|
1900
|
+
rows: 3
|
|
1901
|
+
}
|
|
1902
|
+
),
|
|
1903
|
+
/* @__PURE__ */ jsxs10(
|
|
1904
|
+
"button",
|
|
1905
|
+
{
|
|
1906
|
+
onClick: () => handleAction("request_changes", { comment: changeComment }),
|
|
1907
|
+
disabled: actionLoading !== null || changeComment.trim() === "",
|
|
1908
|
+
className: "inline-flex items-center gap-1.5 rounded-lg bg-foreground px-3 py-1.5 text-[11px] font-medium text-background transition-colors hover:bg-foreground/90 disabled:opacity-50",
|
|
1909
|
+
children: [
|
|
1910
|
+
actionLoading === "request_changes" ? /* @__PURE__ */ jsx14(Loader2, { className: "h-3 w-3 animate-spin" }) : null,
|
|
1911
|
+
"Send"
|
|
1912
|
+
]
|
|
1913
|
+
}
|
|
1914
|
+
)
|
|
1915
|
+
] })
|
|
1916
|
+
] }),
|
|
1917
|
+
status.stage === "rejected" && /* @__PURE__ */ jsx14("p", { className: "text-xs text-muted-foreground", children: "Request rejected" }),
|
|
1918
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2 pt-1 border-t border-border", children: [
|
|
1919
|
+
isFailed && /* @__PURE__ */ jsxs10(
|
|
1920
|
+
"button",
|
|
1921
|
+
{
|
|
1922
|
+
onClick: () => handleAction("retry"),
|
|
1923
|
+
disabled: actionLoading !== null,
|
|
1924
|
+
className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50",
|
|
1925
|
+
children: [
|
|
1926
|
+
actionLoading === "retry" ? /* @__PURE__ */ jsx14(Loader2, { className: "h-3 w-3 animate-spin" }) : /* @__PURE__ */ jsx14(RotateCcw, { className: "h-3 w-3" }),
|
|
1927
|
+
"Retry"
|
|
1928
|
+
]
|
|
1929
|
+
}
|
|
1930
|
+
),
|
|
1931
|
+
/* @__PURE__ */ jsxs10(
|
|
1932
|
+
"a",
|
|
1933
|
+
{
|
|
1934
|
+
href: issueUrl,
|
|
1935
|
+
target: "_blank",
|
|
1936
|
+
rel: "noopener noreferrer",
|
|
1937
|
+
className: "inline-flex items-center gap-1 text-[11px] text-muted-foreground transition-colors hover:text-foreground",
|
|
1938
|
+
children: [
|
|
1939
|
+
/* @__PURE__ */ jsx14(ExternalLink, { className: "h-3 w-3" }),
|
|
1940
|
+
"View on GitHub"
|
|
1941
|
+
]
|
|
1942
|
+
}
|
|
1943
|
+
)
|
|
1944
|
+
] })
|
|
1945
|
+
] });
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
// src/client/submit-request-tool-ui.tsx
|
|
1949
|
+
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1950
|
+
var SubmitRequestToolUI = makeAssistantToolUI2({
|
|
1951
|
+
toolName: "submit_request",
|
|
1952
|
+
render: function SubmitRequest({ args, result, status }) {
|
|
1953
|
+
if (status.type === "running") {
|
|
1954
|
+
return /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-2 py-2 text-xs text-muted-foreground", children: [
|
|
1955
|
+
/* @__PURE__ */ jsx15("div", { className: "h-3 w-3 animate-spin rounded-full border-[1.5px] border-current border-t-transparent" }),
|
|
1956
|
+
"Submitting..."
|
|
1957
|
+
] });
|
|
1958
|
+
}
|
|
1959
|
+
if (status.type === "incomplete") {
|
|
1960
|
+
return /* @__PURE__ */ jsx15("div", { className: "rounded-xl bg-destructive/10 px-3 py-2 text-xs text-destructive", children: "An error occurred while submitting." });
|
|
1961
|
+
}
|
|
1962
|
+
if (!args.summary) return null;
|
|
1963
|
+
return /* @__PURE__ */ jsx15("div", { className: "my-3", children: /* @__PURE__ */ jsx15(SubmissionResult, { args, result }) });
|
|
1964
|
+
}
|
|
1965
|
+
});
|
|
1966
|
+
function SubmissionResult({ args, result }) {
|
|
1967
|
+
const [copied, setCopied] = useState7(false);
|
|
1968
|
+
const [promptExpanded, setPromptExpanded] = useState7(false);
|
|
1969
|
+
async function handleCopy(text) {
|
|
1970
|
+
await navigator.clipboard.writeText(text);
|
|
1971
|
+
setCopied(true);
|
|
1972
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
1973
|
+
}
|
|
1974
|
+
return /* @__PURE__ */ jsxs11("div", { className: "space-y-3", children: [
|
|
1975
|
+
/* @__PURE__ */ jsx15("div", { className: "rounded-xl bg-muted/50 px-3 py-2.5", children: /* @__PURE__ */ jsx15("p", { className: "text-[13px] leading-relaxed", children: args.summary }) }),
|
|
1976
|
+
result?.github_issue_url && /* @__PURE__ */ jsx15(PipelineTracker, { issueUrl: result.github_issue_url }),
|
|
1977
|
+
/* @__PURE__ */ jsxs11("div", { className: "rounded-xl border border-border overflow-hidden", children: [
|
|
1978
|
+
/* @__PURE__ */ jsxs11(
|
|
1979
|
+
"button",
|
|
1980
|
+
{
|
|
1981
|
+
onClick: () => setPromptExpanded(!promptExpanded),
|
|
1982
|
+
className: "flex w-full items-center gap-1.5 px-3 py-2 text-[11px] font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
1983
|
+
children: [
|
|
1984
|
+
/* @__PURE__ */ jsx15(
|
|
1985
|
+
ChevronRight,
|
|
1986
|
+
{
|
|
1987
|
+
className: "h-3 w-3 transition-transform duration-150",
|
|
1988
|
+
style: { transform: promptExpanded ? "rotate(90deg)" : void 0 }
|
|
1989
|
+
}
|
|
1990
|
+
),
|
|
1991
|
+
"Generated prompt",
|
|
1992
|
+
/* @__PURE__ */ jsxs11(
|
|
1993
|
+
"button",
|
|
1994
|
+
{
|
|
1995
|
+
onClick: (e) => {
|
|
1996
|
+
e.stopPropagation();
|
|
1997
|
+
handleCopy(args.generated_prompt);
|
|
1998
|
+
},
|
|
1999
|
+
className: "ml-auto inline-flex items-center gap-1 rounded-lg px-1.5 py-0.5 text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
2000
|
+
children: [
|
|
2001
|
+
copied ? /* @__PURE__ */ jsx15(Check3, { className: "h-2.5 w-2.5" }) : /* @__PURE__ */ jsx15(Copy, { className: "h-2.5 w-2.5" }),
|
|
2002
|
+
copied ? "Copied" : "Copy"
|
|
2003
|
+
]
|
|
2004
|
+
}
|
|
2005
|
+
)
|
|
2006
|
+
]
|
|
2007
|
+
}
|
|
2008
|
+
),
|
|
2009
|
+
promptExpanded && /* @__PURE__ */ jsx15(
|
|
2010
|
+
"pre",
|
|
2011
|
+
{
|
|
2012
|
+
className: "max-h-40 overflow-auto border-t border-border bg-[#0a0a0a] px-3 py-2.5 text-[11px] leading-relaxed text-zinc-400",
|
|
2013
|
+
style: { fontFamily: "'JetBrains Mono', monospace" },
|
|
2014
|
+
children: args.generated_prompt
|
|
2015
|
+
}
|
|
2016
|
+
)
|
|
2017
|
+
] })
|
|
2018
|
+
] });
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
// src/client/feedback-panel.tsx
|
|
2022
|
+
import { Fragment as Fragment3, jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2023
|
+
var STORAGE_KEY = "feedback_password";
|
|
2024
|
+
function FeedbackPanel({ isOpen, onToggle, apiUrl = "/api/feedback/chat" }) {
|
|
2025
|
+
const [authenticated, setAuthenticated] = useState8(
|
|
2026
|
+
() => typeof window !== "undefined" && sessionStorage.getItem(STORAGE_KEY) !== null
|
|
2027
|
+
);
|
|
2028
|
+
const [pendingMessage, setPendingMessage] = useState8("");
|
|
2029
|
+
const [triggerInput, setTriggerInput] = useState8("");
|
|
2030
|
+
const panelRef = useRef4(null);
|
|
2031
|
+
const [pipelineActive, setPipelineActive2] = useState8(false);
|
|
2032
|
+
useEffect4(() => {
|
|
2033
|
+
function check() {
|
|
2034
|
+
setPipelineActive2(localStorage.getItem("feedback_active_pipeline") !== null);
|
|
2035
|
+
}
|
|
2036
|
+
check();
|
|
2037
|
+
window.addEventListener("pipeline-status", check);
|
|
2038
|
+
window.addEventListener("storage", check);
|
|
2039
|
+
return () => {
|
|
2040
|
+
window.removeEventListener("pipeline-status", check);
|
|
2041
|
+
window.removeEventListener("storage", check);
|
|
2042
|
+
};
|
|
2043
|
+
}, []);
|
|
2044
|
+
useEffect4(() => {
|
|
2045
|
+
if (!isOpen) return;
|
|
2046
|
+
function handleMouseDown(e) {
|
|
2047
|
+
if (panelRef.current && !panelRef.current.contains(e.target)) {
|
|
2048
|
+
onToggle();
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
document.addEventListener("mousedown", handleMouseDown);
|
|
2052
|
+
return () => document.removeEventListener("mousedown", handleMouseDown);
|
|
2053
|
+
}, [isOpen, onToggle]);
|
|
2054
|
+
function handleTriggerSubmit(e) {
|
|
2055
|
+
e.preventDefault();
|
|
2056
|
+
if (!triggerInput.trim()) return;
|
|
2057
|
+
setPendingMessage(triggerInput.trim());
|
|
2058
|
+
setTriggerInput("");
|
|
2059
|
+
if (!isOpen) onToggle();
|
|
2060
|
+
}
|
|
2061
|
+
const clearPendingMessage = useCallback4(() => {
|
|
2062
|
+
setPendingMessage("");
|
|
2063
|
+
}, []);
|
|
2064
|
+
return /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
2065
|
+
/* @__PURE__ */ jsx16(
|
|
2066
|
+
"div",
|
|
2067
|
+
{
|
|
2068
|
+
className: `feedback-trigger-bar fixed bottom-6 left-1/2 z-50 w-full -translate-x-1/2 transition-all duration-300 ${isOpen ? "pointer-events-none translate-y-4 opacity-0" : "translate-y-0 opacity-100"}`,
|
|
2069
|
+
children: /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
|
|
2070
|
+
/* @__PURE__ */ jsxs12(
|
|
2071
|
+
"form",
|
|
2072
|
+
{
|
|
2073
|
+
onSubmit: handleTriggerSubmit,
|
|
2074
|
+
className: "feedback-trigger-input flex flex-1 items-center gap-3 rounded-2xl px-5 py-1",
|
|
2075
|
+
children: [
|
|
2076
|
+
/* @__PURE__ */ jsx16(
|
|
2077
|
+
"input",
|
|
2078
|
+
{
|
|
2079
|
+
type: "text",
|
|
2080
|
+
value: triggerInput,
|
|
2081
|
+
onChange: (e) => setTriggerInput(e.target.value),
|
|
2082
|
+
placeholder: "Share an idea...",
|
|
2083
|
+
className: "flex-1 bg-transparent py-3 text-sm text-[#e8eaed] outline-none placeholder:text-[#8b8d93]",
|
|
2084
|
+
"aria-label": "Share an idea"
|
|
2085
|
+
}
|
|
2086
|
+
),
|
|
2087
|
+
/* @__PURE__ */ jsx16(
|
|
2088
|
+
"button",
|
|
2089
|
+
{
|
|
2090
|
+
type: "submit",
|
|
2091
|
+
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-white/10 transition-colors hover:bg-white/15",
|
|
2092
|
+
"aria-label": "Send",
|
|
2093
|
+
children: /* @__PURE__ */ jsx16(ArrowUp, { className: "h-3.5 w-3.5 text-[#8b8d93]" })
|
|
2094
|
+
}
|
|
2095
|
+
)
|
|
2096
|
+
]
|
|
2097
|
+
}
|
|
2098
|
+
),
|
|
2099
|
+
/* @__PURE__ */ jsx16(
|
|
2100
|
+
"button",
|
|
2101
|
+
{
|
|
2102
|
+
type: "button",
|
|
2103
|
+
onClick: onToggle,
|
|
2104
|
+
className: "feedback-trigger-button flex h-[46px] w-[46px] shrink-0 items-center justify-center rounded-2xl",
|
|
2105
|
+
"aria-label": "Open panel",
|
|
2106
|
+
children: /* @__PURE__ */ jsx16(PanelRight, { className: "h-4 w-4" })
|
|
2107
|
+
}
|
|
2108
|
+
),
|
|
2109
|
+
pipelineActive && /* @__PURE__ */ jsx16("div", { className: "flex h-[46px] w-[46px] shrink-0 items-center justify-center", title: "Pipeline running", children: /* @__PURE__ */ jsxs12("span", { className: "relative flex h-2.5 w-2.5", children: [
|
|
2110
|
+
/* @__PURE__ */ jsx16("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-emerald-400 opacity-75" }),
|
|
2111
|
+
/* @__PURE__ */ jsx16("span", { className: "relative inline-flex h-2.5 w-2.5 rounded-full bg-emerald-500" })
|
|
2112
|
+
] }) })
|
|
2113
|
+
] })
|
|
2114
|
+
}
|
|
2115
|
+
),
|
|
2116
|
+
/* @__PURE__ */ jsx16(
|
|
2117
|
+
"div",
|
|
2118
|
+
{
|
|
2119
|
+
ref: panelRef,
|
|
2120
|
+
className: `feedback-panel fixed right-0 top-0 z-50 h-full p-3 transition-transform duration-300 ease-out ${isOpen ? "translate-x-0" : "translate-x-full"}`,
|
|
2121
|
+
children: /* @__PURE__ */ jsx16("div", { className: "flex h-full w-[400px] flex-col text-foreground", children: authenticated ? /* @__PURE__ */ jsx16(
|
|
2122
|
+
ChatContent,
|
|
2123
|
+
{
|
|
2124
|
+
isOpen,
|
|
2125
|
+
onClose: onToggle,
|
|
2126
|
+
pendingMessage,
|
|
2127
|
+
onPendingMessageSent: clearPendingMessage,
|
|
2128
|
+
apiUrl
|
|
2129
|
+
}
|
|
2130
|
+
) : /* @__PURE__ */ jsx16("div", { className: "feedback-panel-glass flex h-full flex-col overflow-hidden", children: /* @__PURE__ */ jsx16(PasswordGate, { onAuth: () => setAuthenticated(true), onClose: onToggle, apiUrl }) }) })
|
|
2131
|
+
}
|
|
2132
|
+
)
|
|
2133
|
+
] });
|
|
2134
|
+
}
|
|
2135
|
+
function ChatContent({
|
|
2136
|
+
isOpen,
|
|
2137
|
+
onClose,
|
|
2138
|
+
pendingMessage,
|
|
2139
|
+
onPendingMessageSent,
|
|
2140
|
+
apiUrl
|
|
2141
|
+
}) {
|
|
2142
|
+
const runtime = useChatRuntime({
|
|
2143
|
+
transport: new DefaultChatTransport({
|
|
2144
|
+
api: apiUrl,
|
|
2145
|
+
body: () => ({ password: sessionStorage.getItem(STORAGE_KEY) || "" })
|
|
2146
|
+
})
|
|
2147
|
+
});
|
|
2148
|
+
return /* @__PURE__ */ jsxs12(AssistantRuntimeProvider, { runtime, children: [
|
|
2149
|
+
/* @__PURE__ */ jsx16(
|
|
2150
|
+
ConversationManager,
|
|
2151
|
+
{
|
|
2152
|
+
isOpen,
|
|
2153
|
+
onClose,
|
|
2154
|
+
pendingMessage,
|
|
2155
|
+
onPendingMessageSent
|
|
2156
|
+
}
|
|
2157
|
+
),
|
|
2158
|
+
/* @__PURE__ */ jsx16(PresentOptionsToolUI, {}),
|
|
2159
|
+
/* @__PURE__ */ jsx16(SubmitRequestToolUI, {})
|
|
2160
|
+
] });
|
|
2161
|
+
}
|
|
2162
|
+
function ConversationManager({
|
|
2163
|
+
isOpen,
|
|
2164
|
+
onClose,
|
|
2165
|
+
pendingMessage,
|
|
2166
|
+
onPendingMessageSent
|
|
2167
|
+
}) {
|
|
2168
|
+
const { conversations, activeId, switchTo, create, remove, save } = useConversations();
|
|
2169
|
+
const threadRuntime = useThreadRuntime3();
|
|
2170
|
+
const onSentRef = useRef4(onPendingMessageSent);
|
|
2171
|
+
onSentRef.current = onPendingMessageSent;
|
|
2172
|
+
const prevOpenRef = useRef4(isOpen);
|
|
2173
|
+
useEffect4(() => {
|
|
2174
|
+
if (prevOpenRef.current && !isOpen) {
|
|
2175
|
+
save();
|
|
2176
|
+
}
|
|
2177
|
+
prevOpenRef.current = isOpen;
|
|
2178
|
+
}, [isOpen, save]);
|
|
2179
|
+
useEffect4(() => {
|
|
2180
|
+
if (!pendingMessage || !activeId) return;
|
|
2181
|
+
create();
|
|
2182
|
+
const timer = setTimeout(() => {
|
|
2183
|
+
try {
|
|
2184
|
+
threadRuntime.composer.setText(pendingMessage);
|
|
2185
|
+
} catch {
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
requestAnimationFrame(() => {
|
|
2189
|
+
try {
|
|
2190
|
+
threadRuntime.composer.send();
|
|
2191
|
+
} catch {
|
|
2192
|
+
}
|
|
2193
|
+
onSentRef.current();
|
|
2194
|
+
});
|
|
2195
|
+
}, 600);
|
|
2196
|
+
return () => clearTimeout(timer);
|
|
2197
|
+
}, [pendingMessage]);
|
|
2198
|
+
return /* @__PURE__ */ jsxs12("div", { className: "flex h-full flex-col gap-2", children: [
|
|
2199
|
+
/* @__PURE__ */ jsx16(
|
|
2200
|
+
ConversationTabs,
|
|
2201
|
+
{
|
|
2202
|
+
conversations,
|
|
2203
|
+
activeId,
|
|
2204
|
+
onSwitch: switchTo,
|
|
2205
|
+
onCreate: create,
|
|
2206
|
+
onRemove: remove,
|
|
2207
|
+
onClose
|
|
2208
|
+
}
|
|
2209
|
+
),
|
|
2210
|
+
/* @__PURE__ */ jsx16("div", { className: "feedback-panel-glass flex flex-1 flex-col overflow-hidden", children: /* @__PURE__ */ jsx16(Thread, {}) })
|
|
2211
|
+
] });
|
|
2212
|
+
}
|
|
2213
|
+
function PasswordGate({ onAuth, onClose, apiUrl }) {
|
|
2214
|
+
const [password, setPassword] = useState8("");
|
|
2215
|
+
const [error, setError] = useState8(null);
|
|
2216
|
+
const [loading, setLoading] = useState8(false);
|
|
2217
|
+
async function handleSubmit(e) {
|
|
2218
|
+
e.preventDefault();
|
|
2219
|
+
setError(null);
|
|
2220
|
+
setLoading(true);
|
|
2221
|
+
try {
|
|
2222
|
+
const res = await fetch(apiUrl, {
|
|
2223
|
+
method: "POST",
|
|
2224
|
+
headers: { "Content-Type": "application/json" },
|
|
2225
|
+
body: JSON.stringify({ messages: [], password })
|
|
2226
|
+
});
|
|
2227
|
+
if (res.status === 401) {
|
|
2228
|
+
setError("Incorrect password.");
|
|
2229
|
+
return;
|
|
2230
|
+
}
|
|
2231
|
+
sessionStorage.setItem(STORAGE_KEY, password);
|
|
2232
|
+
onAuth();
|
|
2233
|
+
} catch {
|
|
2234
|
+
setError("Connection error. Try again.");
|
|
2235
|
+
} finally {
|
|
2236
|
+
setLoading(false);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
return /* @__PURE__ */ jsxs12("div", { className: "relative flex h-full flex-col items-center justify-center px-8", children: [
|
|
2240
|
+
/* @__PURE__ */ jsx16(
|
|
2241
|
+
"button",
|
|
2242
|
+
{
|
|
2243
|
+
onClick: onClose,
|
|
2244
|
+
className: "absolute top-3 right-3 flex h-6 w-6 items-center justify-center rounded-lg text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
|
|
2245
|
+
"aria-label": "Close",
|
|
2246
|
+
children: /* @__PURE__ */ jsx16(X3, { className: "h-3.5 w-3.5" })
|
|
2247
|
+
}
|
|
2248
|
+
),
|
|
2249
|
+
/* @__PURE__ */ jsx16("div", { className: "mb-5 flex h-14 w-14 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx16(Lightbulb, { className: "h-6 w-6 text-muted-foreground" }) }),
|
|
2250
|
+
/* @__PURE__ */ jsx16("p", { className: "mb-8 text-center text-sm text-muted-foreground", children: "Share your ideas to improve the app" }),
|
|
2251
|
+
error && /* @__PURE__ */ jsxs12(
|
|
2252
|
+
"div",
|
|
2253
|
+
{
|
|
2254
|
+
role: "alert",
|
|
2255
|
+
className: "mb-4 flex w-full items-center gap-2 rounded-xl bg-destructive/10 px-4 py-3 text-sm text-destructive",
|
|
2256
|
+
children: [
|
|
2257
|
+
/* @__PURE__ */ jsx16(AlertCircle, { className: "h-4 w-4 shrink-0" }),
|
|
2258
|
+
error
|
|
2259
|
+
]
|
|
2260
|
+
}
|
|
2261
|
+
),
|
|
2262
|
+
/* @__PURE__ */ jsxs12("form", { onSubmit: handleSubmit, className: "w-full space-y-3", children: [
|
|
2263
|
+
/* @__PURE__ */ jsx16(
|
|
2264
|
+
"input",
|
|
2265
|
+
{
|
|
2266
|
+
type: "password",
|
|
2267
|
+
autoComplete: "off",
|
|
2268
|
+
placeholder: "Password",
|
|
2269
|
+
"aria-label": "Password",
|
|
2270
|
+
value: password,
|
|
2271
|
+
onChange: (e) => setPassword(e.target.value),
|
|
2272
|
+
className: "h-10 w-full rounded-xl border border-border bg-background px-4 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-none focus:ring-2 focus:ring-ring/20"
|
|
2273
|
+
}
|
|
2274
|
+
),
|
|
2275
|
+
/* @__PURE__ */ jsx16(
|
|
2276
|
+
"button",
|
|
2277
|
+
{
|
|
2278
|
+
type: "submit",
|
|
2279
|
+
disabled: loading || !password,
|
|
2280
|
+
className: "flex h-10 w-full items-center justify-center gap-2 rounded-xl bg-primary text-sm font-semibold text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
|
|
2281
|
+
children: loading ? /* @__PURE__ */ jsx16(Loader22, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
2282
|
+
"Access",
|
|
2283
|
+
/* @__PURE__ */ jsx16(ArrowRight, { className: "h-3.5 w-3.5" })
|
|
2284
|
+
] })
|
|
2285
|
+
}
|
|
2286
|
+
)
|
|
2287
|
+
] })
|
|
2288
|
+
] });
|
|
2289
|
+
}
|
|
2290
|
+
export {
|
|
2291
|
+
FeedbackPanel,
|
|
2292
|
+
PipelineTracker,
|
|
2293
|
+
useConversations
|
|
2294
|
+
};
|