@contractspec/module.ai-chat 4.1.4 → 4.2.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/README.md +29 -3
- package/dist/browser/core/index.js +53 -17
- package/dist/browser/index.js +982 -426
- package/dist/browser/presentation/components/index.js +975 -419
- package/dist/browser/presentation/hooks/index.js +8 -1
- package/dist/browser/presentation/index.js +983 -427
- package/dist/core/create-chat-route.d.ts +14 -3
- package/dist/core/index.js +53 -17
- package/dist/core/message-types.d.ts +4 -0
- package/dist/index.js +982 -426
- package/dist/node/core/index.js +53 -17
- package/dist/node/index.js +982 -426
- package/dist/node/presentation/components/index.js +975 -419
- package/dist/node/presentation/hooks/index.js +8 -1
- package/dist/node/presentation/index.js +983 -427
- package/dist/presentation/components/ChainOfThought.d.ts +30 -0
- package/dist/presentation/components/ChatInput.d.ts +3 -4
- package/dist/presentation/components/ChatMessage.d.ts +6 -4
- package/dist/presentation/components/ChatWithExport.d.ts +14 -1
- package/dist/presentation/components/ChatWithSidebar.d.ts +12 -1
- package/dist/presentation/components/Reasoning.d.ts +24 -0
- package/dist/presentation/components/Sources.d.ts +22 -0
- package/dist/presentation/components/Suggestion.d.ts +12 -0
- package/dist/presentation/components/ToolResultRenderer.d.ts +20 -3
- package/dist/presentation/components/component-types.d.ts +52 -0
- package/dist/presentation/components/index.d.ts +6 -1
- package/dist/presentation/components/index.js +975 -419
- package/dist/presentation/hooks/index.js +8 -1
- package/dist/presentation/index.js +983 -427
- package/package.json +13 -13
|
@@ -85,8 +85,8 @@ function ChatContainer({
|
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
// src/presentation/components/ChatMessage.tsx
|
|
88
|
-
import * as
|
|
89
|
-
import { cn as
|
|
88
|
+
import * as React4 from "react";
|
|
89
|
+
import { cn as cn5 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
90
90
|
import { Avatar, AvatarFallback } from "@contractspec/lib.ui-kit-web/ui/avatar";
|
|
91
91
|
import { Skeleton } from "@contractspec/lib.ui-kit-web/ui/skeleton";
|
|
92
92
|
import {
|
|
@@ -95,7 +95,6 @@ import {
|
|
|
95
95
|
AlertCircle,
|
|
96
96
|
Copy as Copy2,
|
|
97
97
|
Check as Check2,
|
|
98
|
-
ExternalLink,
|
|
99
98
|
Wrench,
|
|
100
99
|
Pencil,
|
|
101
100
|
X
|
|
@@ -249,22 +248,98 @@ function CodePreview({
|
|
|
249
248
|
// src/presentation/components/ToolResultRenderer.tsx
|
|
250
249
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
251
250
|
"use client";
|
|
251
|
+
function isUIMessageLike(result) {
|
|
252
|
+
return typeof result === "object" && result !== null && "parts" in result && Array.isArray(result.parts);
|
|
253
|
+
}
|
|
252
254
|
function isPresentationToolResult(result) {
|
|
253
255
|
return typeof result === "object" && result !== null && "presentationKey" in result && typeof result.presentationKey === "string";
|
|
254
256
|
}
|
|
255
257
|
function isFormToolResult(result) {
|
|
256
258
|
return typeof result === "object" && result !== null && "formKey" in result && typeof result.formKey === "string";
|
|
257
259
|
}
|
|
260
|
+
function isDataViewToolResult(result) {
|
|
261
|
+
return typeof result === "object" && result !== null && "dataViewKey" in result && typeof result.dataViewKey === "string";
|
|
262
|
+
}
|
|
263
|
+
function UIMessagePartRenderer({
|
|
264
|
+
part,
|
|
265
|
+
presentationRenderer,
|
|
266
|
+
formRenderer,
|
|
267
|
+
dataViewRenderer,
|
|
268
|
+
depth = 0
|
|
269
|
+
}) {
|
|
270
|
+
if (part === null || part === undefined)
|
|
271
|
+
return null;
|
|
272
|
+
const p = part;
|
|
273
|
+
if (p.type === "text" && typeof p.text === "string") {
|
|
274
|
+
return /* @__PURE__ */ jsx3("p", {
|
|
275
|
+
className: "text-sm whitespace-pre-wrap",
|
|
276
|
+
children: p.text
|
|
277
|
+
}, depth);
|
|
278
|
+
}
|
|
279
|
+
if (p.type && String(p.type).startsWith("tool-") && p.output) {
|
|
280
|
+
const output = p.output;
|
|
281
|
+
if (isUIMessageLike(output)) {
|
|
282
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
283
|
+
className: "border-border ml-2 border-l-2 pl-2",
|
|
284
|
+
children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
|
|
285
|
+
parts: output.parts ?? [],
|
|
286
|
+
presentationRenderer,
|
|
287
|
+
formRenderer,
|
|
288
|
+
dataViewRenderer,
|
|
289
|
+
depth: depth + 1
|
|
290
|
+
})
|
|
291
|
+
}, depth);
|
|
292
|
+
}
|
|
293
|
+
return /* @__PURE__ */ jsx3("pre", {
|
|
294
|
+
className: "bg-background overflow-x-auto rounded p-2 text-xs",
|
|
295
|
+
children: typeof output === "object" ? JSON.stringify(output, null, 2) : String(output)
|
|
296
|
+
}, depth);
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function UIMessagePartsRenderer({
|
|
301
|
+
parts,
|
|
302
|
+
presentationRenderer,
|
|
303
|
+
formRenderer,
|
|
304
|
+
dataViewRenderer,
|
|
305
|
+
depth = 0
|
|
306
|
+
}) {
|
|
307
|
+
if (parts.length === 0)
|
|
308
|
+
return null;
|
|
309
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
310
|
+
className: "space-y-2",
|
|
311
|
+
children: parts.map((part, i) => /* @__PURE__ */ jsx3(UIMessagePartRenderer, {
|
|
312
|
+
part,
|
|
313
|
+
presentationRenderer,
|
|
314
|
+
formRenderer,
|
|
315
|
+
dataViewRenderer,
|
|
316
|
+
depth
|
|
317
|
+
}, `${depth}-${i}`))
|
|
318
|
+
});
|
|
319
|
+
}
|
|
258
320
|
function ToolResultRenderer({
|
|
259
|
-
toolName,
|
|
321
|
+
toolName: _toolName,
|
|
260
322
|
result,
|
|
261
323
|
presentationRenderer,
|
|
262
324
|
formRenderer,
|
|
263
|
-
|
|
325
|
+
dataViewRenderer,
|
|
326
|
+
showRawFallback = true,
|
|
327
|
+
renderNestedUIMessage = true
|
|
264
328
|
}) {
|
|
265
329
|
if (result === undefined || result === null) {
|
|
266
330
|
return null;
|
|
267
331
|
}
|
|
332
|
+
if (renderNestedUIMessage && isUIMessageLike(result) && (result.parts?.length ?? 0) > 0) {
|
|
333
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
334
|
+
className: "border-border bg-background/50 mt-2 rounded-md border p-3",
|
|
335
|
+
children: /* @__PURE__ */ jsx3(UIMessagePartsRenderer, {
|
|
336
|
+
parts: result.parts ?? [],
|
|
337
|
+
presentationRenderer,
|
|
338
|
+
formRenderer,
|
|
339
|
+
dataViewRenderer
|
|
340
|
+
})
|
|
341
|
+
});
|
|
342
|
+
}
|
|
268
343
|
if (isPresentationToolResult(result) && presentationRenderer) {
|
|
269
344
|
const rendered = presentationRenderer(result.presentationKey, result.data);
|
|
270
345
|
if (rendered != null) {
|
|
@@ -283,6 +358,15 @@ function ToolResultRenderer({
|
|
|
283
358
|
});
|
|
284
359
|
}
|
|
285
360
|
}
|
|
361
|
+
if (isDataViewToolResult(result) && dataViewRenderer) {
|
|
362
|
+
const rendered = dataViewRenderer(result.dataViewKey, result.items);
|
|
363
|
+
if (rendered != null) {
|
|
364
|
+
return /* @__PURE__ */ jsx3("div", {
|
|
365
|
+
className: "border-border bg-background/50 mt-2 rounded-md border p-3",
|
|
366
|
+
children: rendered
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
}
|
|
286
370
|
if (!showRawFallback) {
|
|
287
371
|
return null;
|
|
288
372
|
}
|
|
@@ -300,8 +384,158 @@ function ToolResultRenderer({
|
|
|
300
384
|
});
|
|
301
385
|
}
|
|
302
386
|
|
|
387
|
+
// src/presentation/components/Reasoning.tsx
|
|
388
|
+
import * as React3 from "react";
|
|
389
|
+
import {
|
|
390
|
+
Collapsible,
|
|
391
|
+
CollapsibleContent,
|
|
392
|
+
CollapsibleTrigger
|
|
393
|
+
} from "@contractspec/lib.ui-kit-web/ui/collapsible";
|
|
394
|
+
import { cn as cn3 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
395
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
396
|
+
"use client";
|
|
397
|
+
function Reasoning({
|
|
398
|
+
isStreaming = false,
|
|
399
|
+
open,
|
|
400
|
+
defaultOpen = false,
|
|
401
|
+
onOpenChange,
|
|
402
|
+
children,
|
|
403
|
+
className
|
|
404
|
+
}) {
|
|
405
|
+
const [internalOpen, setInternalOpen] = React3.useState(defaultOpen);
|
|
406
|
+
const prevStreamingRef = React3.useRef(isStreaming);
|
|
407
|
+
const isControlled = open !== undefined;
|
|
408
|
+
React3.useEffect(() => {
|
|
409
|
+
if (isStreaming) {
|
|
410
|
+
if (isControlled) {
|
|
411
|
+
onOpenChange?.(true);
|
|
412
|
+
} else {
|
|
413
|
+
setInternalOpen(true);
|
|
414
|
+
}
|
|
415
|
+
} else if (prevStreamingRef.current) {
|
|
416
|
+
if (isControlled) {
|
|
417
|
+
onOpenChange?.(false);
|
|
418
|
+
} else {
|
|
419
|
+
setInternalOpen(false);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
prevStreamingRef.current = isStreaming;
|
|
423
|
+
}, [isStreaming, isControlled, onOpenChange]);
|
|
424
|
+
const handleOpenChange = React3.useCallback((next) => {
|
|
425
|
+
if (isControlled) {
|
|
426
|
+
onOpenChange?.(next);
|
|
427
|
+
} else {
|
|
428
|
+
setInternalOpen(next);
|
|
429
|
+
}
|
|
430
|
+
}, [isControlled, onOpenChange]);
|
|
431
|
+
return /* @__PURE__ */ jsx4(Collapsible, {
|
|
432
|
+
open: isControlled ? open : internalOpen,
|
|
433
|
+
onOpenChange: handleOpenChange,
|
|
434
|
+
className: cn3("w-full", className),
|
|
435
|
+
children
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
function ReasoningTrigger({
|
|
439
|
+
children,
|
|
440
|
+
isStreaming = false,
|
|
441
|
+
className
|
|
442
|
+
}) {
|
|
443
|
+
return /* @__PURE__ */ jsxs4(CollapsibleTrigger, {
|
|
444
|
+
className: cn3("text-muted-foreground hover:bg-muted hover:text-foreground flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors", className),
|
|
445
|
+
children: [
|
|
446
|
+
isStreaming && /* @__PURE__ */ jsx4("span", {
|
|
447
|
+
className: "bg-primary h-1.5 w-1.5 animate-pulse rounded-full",
|
|
448
|
+
"aria-hidden": true
|
|
449
|
+
}),
|
|
450
|
+
children ?? (isStreaming ? "Thinking..." : "View reasoning")
|
|
451
|
+
]
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
function ReasoningContent({
|
|
455
|
+
children,
|
|
456
|
+
className
|
|
457
|
+
}) {
|
|
458
|
+
return /* @__PURE__ */ jsx4(CollapsibleContent, {
|
|
459
|
+
children: /* @__PURE__ */ jsx4("div", {
|
|
460
|
+
className: cn3("text-muted-foreground bg-muted mt-1 rounded-md p-2 text-sm", className),
|
|
461
|
+
children: /* @__PURE__ */ jsx4("p", {
|
|
462
|
+
className: "whitespace-pre-wrap",
|
|
463
|
+
children
|
|
464
|
+
})
|
|
465
|
+
})
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// src/presentation/components/Sources.tsx
|
|
470
|
+
import {
|
|
471
|
+
Collapsible as Collapsible2,
|
|
472
|
+
CollapsibleContent as CollapsibleContent2,
|
|
473
|
+
CollapsibleTrigger as CollapsibleTrigger2
|
|
474
|
+
} from "@contractspec/lib.ui-kit-web/ui/collapsible";
|
|
475
|
+
import { cn as cn4 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
476
|
+
import { ExternalLink } from "lucide-react";
|
|
477
|
+
import { jsx as jsx5, jsxs as jsxs5, Fragment } from "react/jsx-runtime";
|
|
478
|
+
"use client";
|
|
479
|
+
function Sources({ children, className }) {
|
|
480
|
+
return /* @__PURE__ */ jsx5(Collapsible2, {
|
|
481
|
+
className: cn4("mt-2", className),
|
|
482
|
+
defaultOpen: false,
|
|
483
|
+
children
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
function SourcesTrigger({
|
|
487
|
+
count,
|
|
488
|
+
children,
|
|
489
|
+
className
|
|
490
|
+
}) {
|
|
491
|
+
return /* @__PURE__ */ jsx5(CollapsibleTrigger2, {
|
|
492
|
+
className: cn4("text-muted-foreground hover:text-foreground hover:bg-muted inline-flex cursor-pointer items-center gap-1.5 rounded-md px-2 py-1 text-xs transition-colors", className),
|
|
493
|
+
children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
|
|
494
|
+
children: [
|
|
495
|
+
/* @__PURE__ */ jsx5(ExternalLink, {
|
|
496
|
+
className: "h-3 w-3"
|
|
497
|
+
}),
|
|
498
|
+
count,
|
|
499
|
+
" source",
|
|
500
|
+
count !== 1 ? "s" : ""
|
|
501
|
+
]
|
|
502
|
+
})
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
function SourcesContent({ children, className }) {
|
|
506
|
+
return /* @__PURE__ */ jsx5(CollapsibleContent2, {
|
|
507
|
+
children: /* @__PURE__ */ jsx5("div", {
|
|
508
|
+
className: cn4("mt-2 flex flex-wrap gap-2", className),
|
|
509
|
+
children
|
|
510
|
+
})
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
function Source({
|
|
514
|
+
href,
|
|
515
|
+
title,
|
|
516
|
+
className,
|
|
517
|
+
children,
|
|
518
|
+
...props
|
|
519
|
+
}) {
|
|
520
|
+
return /* @__PURE__ */ jsx5("a", {
|
|
521
|
+
href,
|
|
522
|
+
target: "_blank",
|
|
523
|
+
rel: "noopener noreferrer",
|
|
524
|
+
className: cn4("text-muted-foreground hover:text-foreground bg-muted inline-flex items-center gap-1 rounded-md px-2 py-1 text-xs transition-colors", className),
|
|
525
|
+
...props,
|
|
526
|
+
children: children ?? /* @__PURE__ */ jsxs5(Fragment, {
|
|
527
|
+
children: [
|
|
528
|
+
/* @__PURE__ */ jsx5(ExternalLink, {
|
|
529
|
+
className: "h-3 w-3"
|
|
530
|
+
}),
|
|
531
|
+
title ?? href
|
|
532
|
+
]
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
303
537
|
// src/presentation/components/ChatMessage.tsx
|
|
304
|
-
import { jsx as
|
|
538
|
+
import { jsx as jsx6, jsxs as jsxs6, Fragment as Fragment2 } from "react/jsx-runtime";
|
|
305
539
|
"use client";
|
|
306
540
|
function extractCodeBlocks(content) {
|
|
307
541
|
const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
|
|
@@ -324,11 +558,11 @@ function renderInlineMarkdown(text) {
|
|
|
324
558
|
let key = 0;
|
|
325
559
|
while ((match = linkRegex.exec(text)) !== null) {
|
|
326
560
|
if (match.index > lastIndex) {
|
|
327
|
-
parts.push(/* @__PURE__ */
|
|
561
|
+
parts.push(/* @__PURE__ */ jsx6("span", {
|
|
328
562
|
children: text.slice(lastIndex, match.index)
|
|
329
563
|
}, key++));
|
|
330
564
|
}
|
|
331
|
-
parts.push(/* @__PURE__ */
|
|
565
|
+
parts.push(/* @__PURE__ */ jsx6("a", {
|
|
332
566
|
href: match[2],
|
|
333
567
|
target: "_blank",
|
|
334
568
|
rel: "noopener noreferrer",
|
|
@@ -338,7 +572,7 @@ function renderInlineMarkdown(text) {
|
|
|
338
572
|
lastIndex = match.index + match[0].length;
|
|
339
573
|
}
|
|
340
574
|
if (lastIndex < text.length) {
|
|
341
|
-
parts.push(/* @__PURE__ */
|
|
575
|
+
parts.push(/* @__PURE__ */ jsx6("span", {
|
|
342
576
|
children: text.slice(lastIndex)
|
|
343
577
|
}, key++));
|
|
344
578
|
}
|
|
@@ -347,7 +581,7 @@ function renderInlineMarkdown(text) {
|
|
|
347
581
|
function MessageContent({ content }) {
|
|
348
582
|
const codeBlocks = extractCodeBlocks(content);
|
|
349
583
|
if (codeBlocks.length === 0) {
|
|
350
|
-
return /* @__PURE__ */
|
|
584
|
+
return /* @__PURE__ */ jsx6("p", {
|
|
351
585
|
className: "whitespace-pre-wrap",
|
|
352
586
|
children: renderInlineMarkdown(content)
|
|
353
587
|
});
|
|
@@ -358,12 +592,12 @@ function MessageContent({ content }) {
|
|
|
358
592
|
for (const block of codeBlocks) {
|
|
359
593
|
const [before, after] = remaining.split(block.raw);
|
|
360
594
|
if (before) {
|
|
361
|
-
parts.push(/* @__PURE__ */
|
|
595
|
+
parts.push(/* @__PURE__ */ jsx6("p", {
|
|
362
596
|
className: "whitespace-pre-wrap",
|
|
363
597
|
children: renderInlineMarkdown(before.trim())
|
|
364
598
|
}, key++));
|
|
365
599
|
}
|
|
366
|
-
parts.push(/* @__PURE__ */
|
|
600
|
+
parts.push(/* @__PURE__ */ jsx6(CodePreview, {
|
|
367
601
|
code: block.code,
|
|
368
602
|
language: block.language,
|
|
369
603
|
className: "my-2"
|
|
@@ -371,15 +605,22 @@ function MessageContent({ content }) {
|
|
|
371
605
|
remaining = after ?? "";
|
|
372
606
|
}
|
|
373
607
|
if (remaining.trim()) {
|
|
374
|
-
parts.push(/* @__PURE__ */
|
|
608
|
+
parts.push(/* @__PURE__ */ jsx6("p", {
|
|
375
609
|
className: "whitespace-pre-wrap",
|
|
376
610
|
children: renderInlineMarkdown(remaining.trim())
|
|
377
611
|
}, key++));
|
|
378
612
|
}
|
|
379
|
-
return /* @__PURE__ */
|
|
613
|
+
return /* @__PURE__ */ jsx6(Fragment2, {
|
|
380
614
|
children: parts
|
|
381
615
|
});
|
|
382
616
|
}
|
|
617
|
+
function toolStatusToCotStatus(status) {
|
|
618
|
+
if (status === "completed")
|
|
619
|
+
return "complete";
|
|
620
|
+
if (status === "running")
|
|
621
|
+
return "active";
|
|
622
|
+
return "pending";
|
|
623
|
+
}
|
|
383
624
|
function ChatMessage({
|
|
384
625
|
message,
|
|
385
626
|
className,
|
|
@@ -391,119 +632,128 @@ function ChatMessage({
|
|
|
391
632
|
editable = false,
|
|
392
633
|
onEdit,
|
|
393
634
|
presentationRenderer,
|
|
394
|
-
formRenderer
|
|
635
|
+
formRenderer,
|
|
636
|
+
dataViewRenderer,
|
|
637
|
+
components: comps
|
|
395
638
|
}) {
|
|
396
|
-
const [copied, setCopied] =
|
|
639
|
+
const [copied, setCopied] = React4.useState(false);
|
|
397
640
|
const isUser = message.role === "user";
|
|
398
641
|
const isError = message.status === "error";
|
|
399
642
|
const isStreaming = message.status === "streaming";
|
|
400
|
-
const handleCopy =
|
|
643
|
+
const handleCopy = React4.useCallback(async () => {
|
|
401
644
|
await navigator.clipboard.writeText(message.content);
|
|
402
645
|
setCopied(true);
|
|
403
646
|
setTimeout(() => setCopied(false), 2000);
|
|
404
647
|
}, [message.content]);
|
|
405
|
-
const handleSelectChange =
|
|
648
|
+
const handleSelectChange = React4.useCallback((checked) => {
|
|
406
649
|
if (checked !== "indeterminate")
|
|
407
650
|
onSelect?.(message.id);
|
|
408
651
|
}, [message.id, onSelect]);
|
|
409
|
-
const [isEditing, setIsEditing] =
|
|
410
|
-
const [editContent, setEditContent] =
|
|
411
|
-
|
|
652
|
+
const [isEditing, setIsEditing] = React4.useState(false);
|
|
653
|
+
const [editContent, setEditContent] = React4.useState(message.content);
|
|
654
|
+
const editTextareaRef = React4.useRef(null);
|
|
655
|
+
React4.useEffect(() => {
|
|
412
656
|
setEditContent(message.content);
|
|
413
657
|
}, [message.content]);
|
|
414
|
-
|
|
658
|
+
React4.useEffect(() => {
|
|
659
|
+
if (isEditing) {
|
|
660
|
+
editTextareaRef.current?.focus();
|
|
661
|
+
}
|
|
662
|
+
}, [isEditing]);
|
|
663
|
+
const handleStartEdit = React4.useCallback(() => {
|
|
415
664
|
setEditContent(message.content);
|
|
416
665
|
setIsEditing(true);
|
|
417
666
|
}, [message.content]);
|
|
418
|
-
const handleSaveEdit =
|
|
667
|
+
const handleSaveEdit = React4.useCallback(async () => {
|
|
419
668
|
const trimmed = editContent.trim();
|
|
420
669
|
if (trimmed !== message.content) {
|
|
421
670
|
await onEdit?.(message.id, trimmed);
|
|
422
671
|
}
|
|
423
672
|
setIsEditing(false);
|
|
424
673
|
}, [editContent, message.id, message.content, onEdit]);
|
|
425
|
-
const handleCancelEdit =
|
|
674
|
+
const handleCancelEdit = React4.useCallback(() => {
|
|
426
675
|
setEditContent(message.content);
|
|
427
676
|
setIsEditing(false);
|
|
428
677
|
}, [message.content]);
|
|
429
|
-
return /* @__PURE__ */
|
|
430
|
-
className:
|
|
678
|
+
return /* @__PURE__ */ jsxs6("div", {
|
|
679
|
+
className: cn5("group flex gap-3", isUser && "flex-row-reverse", className),
|
|
431
680
|
children: [
|
|
432
|
-
selectable && /* @__PURE__ */
|
|
433
|
-
className:
|
|
434
|
-
children: /* @__PURE__ */
|
|
681
|
+
selectable && /* @__PURE__ */ jsx6("div", {
|
|
682
|
+
className: cn5("flex shrink-0 items-start pt-1", "opacity-0 transition-opacity group-hover:opacity-100"),
|
|
683
|
+
children: /* @__PURE__ */ jsx6(Checkbox, {
|
|
435
684
|
checked: selected,
|
|
436
685
|
onCheckedChange: handleSelectChange,
|
|
437
686
|
"aria-label": selected ? "Deselect message" : "Select message"
|
|
438
687
|
})
|
|
439
688
|
}),
|
|
440
|
-
showAvatar && /* @__PURE__ */
|
|
689
|
+
showAvatar && /* @__PURE__ */ jsx6(Avatar, {
|
|
441
690
|
className: "h-8 w-8 shrink-0",
|
|
442
|
-
children: /* @__PURE__ */
|
|
443
|
-
className:
|
|
444
|
-
children: isUser ? /* @__PURE__ */
|
|
691
|
+
children: /* @__PURE__ */ jsx6(AvatarFallback, {
|
|
692
|
+
className: cn5(isUser ? "bg-primary text-primary-foreground" : "bg-muted"),
|
|
693
|
+
children: isUser ? /* @__PURE__ */ jsx6(User, {
|
|
445
694
|
className: "h-4 w-4"
|
|
446
|
-
}) : /* @__PURE__ */
|
|
695
|
+
}) : /* @__PURE__ */ jsx6(Bot, {
|
|
447
696
|
className: "h-4 w-4"
|
|
448
697
|
})
|
|
449
698
|
})
|
|
450
699
|
}),
|
|
451
|
-
/* @__PURE__ */
|
|
452
|
-
className:
|
|
700
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
701
|
+
className: cn5("flex max-w-[80%] flex-col gap-1", isUser && "items-end"),
|
|
453
702
|
children: [
|
|
454
|
-
/* @__PURE__ */
|
|
455
|
-
className:
|
|
456
|
-
children: isError && message.error ? /* @__PURE__ */
|
|
703
|
+
/* @__PURE__ */ jsx6("div", {
|
|
704
|
+
className: cn5("rounded-2xl px-4 py-2", isUser ? "bg-primary text-primary-foreground" : "bg-muted text-foreground", isError && "border-destructive bg-destructive/10 border"),
|
|
705
|
+
children: isError && message.error ? /* @__PURE__ */ jsxs6("div", {
|
|
457
706
|
className: "flex items-start gap-2",
|
|
458
707
|
children: [
|
|
459
|
-
/* @__PURE__ */
|
|
708
|
+
/* @__PURE__ */ jsx6(AlertCircle, {
|
|
460
709
|
className: "text-destructive mt-0.5 h-4 w-4 shrink-0"
|
|
461
710
|
}),
|
|
462
|
-
/* @__PURE__ */
|
|
711
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
463
712
|
children: [
|
|
464
|
-
/* @__PURE__ */
|
|
713
|
+
/* @__PURE__ */ jsx6("p", {
|
|
465
714
|
className: "text-destructive font-medium",
|
|
466
715
|
children: message.error.code
|
|
467
716
|
}),
|
|
468
|
-
/* @__PURE__ */
|
|
717
|
+
/* @__PURE__ */ jsx6("p", {
|
|
469
718
|
className: "text-muted-foreground text-sm",
|
|
470
719
|
children: message.error.message
|
|
471
720
|
})
|
|
472
721
|
]
|
|
473
722
|
})
|
|
474
723
|
]
|
|
475
|
-
}) : isEditing ? /* @__PURE__ */
|
|
724
|
+
}) : isEditing ? /* @__PURE__ */ jsxs6("div", {
|
|
476
725
|
className: "flex flex-col gap-2",
|
|
477
726
|
children: [
|
|
478
|
-
/* @__PURE__ */
|
|
727
|
+
/* @__PURE__ */ jsx6("textarea", {
|
|
728
|
+
ref: editTextareaRef,
|
|
479
729
|
value: editContent,
|
|
480
730
|
onChange: (e) => setEditContent(e.target.value),
|
|
481
731
|
className: "bg-background/50 min-h-[80px] w-full resize-y rounded-md border px-3 py-2 text-sm",
|
|
482
732
|
rows: 4,
|
|
483
|
-
|
|
733
|
+
"aria-label": "Edit message"
|
|
484
734
|
}),
|
|
485
|
-
/* @__PURE__ */
|
|
735
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
486
736
|
className: "flex gap-2",
|
|
487
737
|
children: [
|
|
488
|
-
/* @__PURE__ */
|
|
738
|
+
/* @__PURE__ */ jsxs6(Button2, {
|
|
489
739
|
variant: "default",
|
|
490
740
|
size: "sm",
|
|
491
741
|
onPress: handleSaveEdit,
|
|
492
742
|
"aria-label": "Save edit",
|
|
493
743
|
children: [
|
|
494
|
-
/* @__PURE__ */
|
|
744
|
+
/* @__PURE__ */ jsx6(Check2, {
|
|
495
745
|
className: "h-3 w-3"
|
|
496
746
|
}),
|
|
497
747
|
"Save"
|
|
498
748
|
]
|
|
499
749
|
}),
|
|
500
|
-
/* @__PURE__ */
|
|
750
|
+
/* @__PURE__ */ jsxs6(Button2, {
|
|
501
751
|
variant: "ghost",
|
|
502
752
|
size: "sm",
|
|
503
753
|
onPress: handleCancelEdit,
|
|
504
754
|
"aria-label": "Cancel edit",
|
|
505
755
|
children: [
|
|
506
|
-
/* @__PURE__ */
|
|
756
|
+
/* @__PURE__ */ jsx6(X, {
|
|
507
757
|
className: "h-3 w-3"
|
|
508
758
|
}),
|
|
509
759
|
"Cancel"
|
|
@@ -512,153 +762,256 @@ function ChatMessage({
|
|
|
512
762
|
]
|
|
513
763
|
})
|
|
514
764
|
]
|
|
515
|
-
}) : isStreaming && !message.content ? /* @__PURE__ */
|
|
765
|
+
}) : isStreaming && !message.content ? /* @__PURE__ */ jsxs6("div", {
|
|
516
766
|
className: "flex flex-col gap-2",
|
|
517
767
|
children: [
|
|
518
|
-
/* @__PURE__ */
|
|
768
|
+
/* @__PURE__ */ jsx6(Skeleton, {
|
|
519
769
|
className: "h-4 w-48"
|
|
520
770
|
}),
|
|
521
|
-
/* @__PURE__ */
|
|
771
|
+
/* @__PURE__ */ jsx6(Skeleton, {
|
|
522
772
|
className: "h-4 w-32"
|
|
523
773
|
})
|
|
524
774
|
]
|
|
525
|
-
}) : /* @__PURE__ */
|
|
775
|
+
}) : /* @__PURE__ */ jsx6(MessageContent, {
|
|
526
776
|
content: message.content
|
|
527
777
|
})
|
|
528
778
|
}),
|
|
529
|
-
/* @__PURE__ */
|
|
530
|
-
className:
|
|
779
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
780
|
+
className: cn5("flex items-center gap-2 text-xs", "text-muted-foreground opacity-0 transition-opacity", "group-hover:opacity-100"),
|
|
531
781
|
children: [
|
|
532
|
-
/* @__PURE__ */
|
|
782
|
+
/* @__PURE__ */ jsx6("span", {
|
|
533
783
|
children: new Date(message.createdAt).toLocaleTimeString([], {
|
|
534
784
|
hour: "2-digit",
|
|
535
785
|
minute: "2-digit"
|
|
536
786
|
})
|
|
537
787
|
}),
|
|
538
|
-
message.usage && /* @__PURE__ */
|
|
788
|
+
message.usage && /* @__PURE__ */ jsxs6("span", {
|
|
539
789
|
children: [
|
|
540
790
|
message.usage.inputTokens + message.usage.outputTokens,
|
|
541
791
|
" tokens"
|
|
542
792
|
]
|
|
543
793
|
}),
|
|
544
|
-
showCopy && !isUser && message.content && /* @__PURE__ */
|
|
794
|
+
showCopy && !isUser && message.content && /* @__PURE__ */ jsx6(Button2, {
|
|
545
795
|
variant: "ghost",
|
|
546
796
|
size: "sm",
|
|
547
797
|
className: "h-6 w-6 p-0",
|
|
548
798
|
onPress: handleCopy,
|
|
549
799
|
"aria-label": copied ? "Copied" : "Copy message",
|
|
550
|
-
children: copied ? /* @__PURE__ */
|
|
800
|
+
children: copied ? /* @__PURE__ */ jsx6(Check2, {
|
|
551
801
|
className: "h-3 w-3"
|
|
552
|
-
}) : /* @__PURE__ */
|
|
802
|
+
}) : /* @__PURE__ */ jsx6(Copy2, {
|
|
553
803
|
className: "h-3 w-3"
|
|
554
804
|
})
|
|
555
805
|
}),
|
|
556
|
-
editable && isUser && !isEditing && /* @__PURE__ */
|
|
806
|
+
editable && isUser && !isEditing && /* @__PURE__ */ jsx6(Button2, {
|
|
557
807
|
variant: "ghost",
|
|
558
808
|
size: "sm",
|
|
559
809
|
className: "h-6 w-6 p-0",
|
|
560
810
|
onPress: handleStartEdit,
|
|
561
811
|
"aria-label": "Edit message",
|
|
562
|
-
children: /* @__PURE__ */
|
|
812
|
+
children: /* @__PURE__ */ jsx6(Pencil, {
|
|
563
813
|
className: "h-3 w-3"
|
|
564
814
|
})
|
|
565
815
|
})
|
|
566
816
|
]
|
|
567
817
|
}),
|
|
568
|
-
message.reasoning && /* @__PURE__ */
|
|
569
|
-
|
|
818
|
+
message.reasoning && (comps?.Reasoning ? /* @__PURE__ */ jsx6(comps.Reasoning, {
|
|
819
|
+
isStreaming: isStreaming && !!message.reasoning,
|
|
820
|
+
children: message.reasoning
|
|
821
|
+
}) : /* @__PURE__ */ jsxs6(Reasoning, {
|
|
822
|
+
isStreaming: isStreaming && !!message.reasoning,
|
|
823
|
+
className: "mt-2",
|
|
570
824
|
children: [
|
|
571
|
-
/* @__PURE__ */
|
|
572
|
-
|
|
573
|
-
children: "View reasoning"
|
|
825
|
+
/* @__PURE__ */ jsx6(ReasoningTrigger, {
|
|
826
|
+
isStreaming
|
|
574
827
|
}),
|
|
575
|
-
/* @__PURE__ */
|
|
576
|
-
|
|
577
|
-
children: /* @__PURE__ */ jsx4("p", {
|
|
578
|
-
className: "whitespace-pre-wrap",
|
|
579
|
-
children: message.reasoning
|
|
580
|
-
})
|
|
828
|
+
/* @__PURE__ */ jsx6(ReasoningContent, {
|
|
829
|
+
children: message.reasoning
|
|
581
830
|
})
|
|
582
831
|
]
|
|
583
|
-
}),
|
|
584
|
-
message.sources && message.sources.length > 0 &&
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
832
|
+
})),
|
|
833
|
+
message.sources && message.sources.length > 0 && (() => {
|
|
834
|
+
const SourcesComp = comps?.Sources;
|
|
835
|
+
const SourcesTriggerComp = comps?.SourcesTrigger;
|
|
836
|
+
const SourceComp = comps?.Source;
|
|
837
|
+
if (SourcesComp && SourcesTriggerComp && SourceComp) {
|
|
838
|
+
return /* @__PURE__ */ jsxs6(SourcesComp, {
|
|
839
|
+
children: [
|
|
840
|
+
/* @__PURE__ */ jsx6(SourcesTriggerComp, {
|
|
841
|
+
count: message.sources.length
|
|
842
|
+
}),
|
|
843
|
+
message.sources.map((source) => /* @__PURE__ */ jsx6(SourceComp, {
|
|
844
|
+
href: source.url ?? "#",
|
|
845
|
+
title: source.title || source.url || source.id
|
|
846
|
+
}, source.id))
|
|
847
|
+
]
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
return /* @__PURE__ */ jsxs6(Sources, {
|
|
851
|
+
className: "mt-2",
|
|
591
852
|
children: [
|
|
592
|
-
/* @__PURE__ */
|
|
593
|
-
|
|
853
|
+
/* @__PURE__ */ jsx6(SourcesTrigger, {
|
|
854
|
+
count: message.sources.length
|
|
594
855
|
}),
|
|
595
|
-
|
|
856
|
+
/* @__PURE__ */ jsx6(SourcesContent, {
|
|
857
|
+
children: message.sources.map((source) => /* @__PURE__ */ jsx6(Source, {
|
|
858
|
+
href: source.url ?? "#",
|
|
859
|
+
title: source.title || source.url || source.id
|
|
860
|
+
}, source.id))
|
|
861
|
+
})
|
|
596
862
|
]
|
|
597
|
-
}
|
|
598
|
-
}),
|
|
599
|
-
message.toolCalls && message.toolCalls.length > 0 &&
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
tc.name,
|
|
611
|
-
/* @__PURE__ */ jsx4("span", {
|
|
612
|
-
className: cn3("ml-auto rounded px-1.5 py-0.5 text-xs", tc.status === "completed" && "bg-green-500/20 text-green-700 dark:text-green-400", tc.status === "error" && "bg-destructive/20 text-destructive", tc.status === "running" && "bg-blue-500/20 text-blue-700 dark:text-blue-400"),
|
|
613
|
-
children: tc.status
|
|
614
|
-
})
|
|
615
|
-
]
|
|
616
|
-
}),
|
|
617
|
-
/* @__PURE__ */ jsxs4("div", {
|
|
618
|
-
className: "border-border border-t px-3 py-2 text-xs",
|
|
863
|
+
});
|
|
864
|
+
})(),
|
|
865
|
+
message.toolCalls && message.toolCalls.length > 0 && (() => {
|
|
866
|
+
const CotComp = comps?.ChainOfThought;
|
|
867
|
+
const CotStepComp = comps?.ChainOfThoughtStep;
|
|
868
|
+
if (CotComp && CotStepComp) {
|
|
869
|
+
return /* @__PURE__ */ jsx6(CotComp, {
|
|
870
|
+
defaultOpen: false,
|
|
871
|
+
className: "mt-2",
|
|
872
|
+
children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6(CotStepComp, {
|
|
873
|
+
label: tc.name,
|
|
874
|
+
description: Object.keys(tc.args).length > 0 ? `Input: ${JSON.stringify(tc.args)}` : undefined,
|
|
875
|
+
status: toolStatusToCotStatus(tc.status),
|
|
619
876
|
children: [
|
|
620
|
-
|
|
621
|
-
className: "
|
|
622
|
-
children:
|
|
623
|
-
/* @__PURE__ */ jsx4("span", {
|
|
624
|
-
className: "text-muted-foreground font-medium",
|
|
625
|
-
children: "Input:"
|
|
626
|
-
}),
|
|
627
|
-
/* @__PURE__ */ jsx4("pre", {
|
|
628
|
-
className: "bg-background mt-1 overflow-x-auto rounded p-2",
|
|
629
|
-
children: JSON.stringify(tc.args, null, 2)
|
|
630
|
-
})
|
|
631
|
-
]
|
|
877
|
+
tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
|
|
878
|
+
className: "text-muted-foreground mt-1 text-xs",
|
|
879
|
+
children: "Running…"
|
|
632
880
|
}),
|
|
633
|
-
tc.result !== undefined && /* @__PURE__ */
|
|
881
|
+
(tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
|
|
634
882
|
toolName: tc.name,
|
|
635
|
-
result: tc.result,
|
|
883
|
+
result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
|
|
636
884
|
presentationRenderer,
|
|
637
885
|
formRenderer,
|
|
886
|
+
dataViewRenderer,
|
|
638
887
|
showRawFallback: true
|
|
639
888
|
}),
|
|
640
|
-
tc.error && /* @__PURE__ */
|
|
641
|
-
className: "text-destructive mt-1",
|
|
889
|
+
tc.error && /* @__PURE__ */ jsx6("p", {
|
|
890
|
+
className: "text-destructive mt-1 text-xs",
|
|
642
891
|
children: tc.error
|
|
643
892
|
})
|
|
644
893
|
]
|
|
645
|
-
})
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
|
|
894
|
+
}, tc.id))
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
return /* @__PURE__ */ jsx6("div", {
|
|
898
|
+
className: "mt-2 space-y-2",
|
|
899
|
+
children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxs6("details", {
|
|
900
|
+
className: "bg-muted border-border rounded-md border",
|
|
901
|
+
children: [
|
|
902
|
+
/* @__PURE__ */ jsxs6("summary", {
|
|
903
|
+
className: "flex cursor-pointer items-center gap-2 px-3 py-2 text-sm font-medium",
|
|
904
|
+
children: [
|
|
905
|
+
/* @__PURE__ */ jsx6(Wrench, {
|
|
906
|
+
className: "text-muted-foreground h-4 w-4"
|
|
907
|
+
}),
|
|
908
|
+
tc.name,
|
|
909
|
+
/* @__PURE__ */ jsx6("span", {
|
|
910
|
+
className: cn5("ml-auto rounded px-1.5 py-0.5 text-xs", tc.status === "completed" && "bg-green-500/20 text-green-700 dark:text-green-400", tc.status === "error" && "bg-destructive/20 text-destructive", tc.status === "running" && "bg-blue-500/20 text-blue-700 dark:text-blue-400"),
|
|
911
|
+
children: tc.status
|
|
912
|
+
})
|
|
913
|
+
]
|
|
914
|
+
}),
|
|
915
|
+
/* @__PURE__ */ jsxs6("div", {
|
|
916
|
+
className: "border-border border-t px-3 py-2 text-xs",
|
|
917
|
+
children: [
|
|
918
|
+
Object.keys(tc.args).length > 0 && /* @__PURE__ */ jsxs6("div", {
|
|
919
|
+
className: "mb-2",
|
|
920
|
+
children: [
|
|
921
|
+
/* @__PURE__ */ jsx6("span", {
|
|
922
|
+
className: "text-muted-foreground font-medium",
|
|
923
|
+
children: "Input:"
|
|
924
|
+
}),
|
|
925
|
+
/* @__PURE__ */ jsx6("pre", {
|
|
926
|
+
className: "bg-background mt-1 overflow-x-auto rounded p-2",
|
|
927
|
+
children: JSON.stringify(tc.args, null, 2)
|
|
928
|
+
})
|
|
929
|
+
]
|
|
930
|
+
}),
|
|
931
|
+
tc.preliminary && tc.status === "running" && /* @__PURE__ */ jsx6("p", {
|
|
932
|
+
className: "text-muted-foreground mt-1 text-xs",
|
|
933
|
+
children: "Running…"
|
|
934
|
+
}),
|
|
935
|
+
(tc.result !== undefined || tc.nestedParts?.length) && /* @__PURE__ */ jsx6(ToolResultRenderer, {
|
|
936
|
+
toolName: tc.name,
|
|
937
|
+
result: tc.nestedParts?.length ? { parts: tc.nestedParts } : tc.result,
|
|
938
|
+
presentationRenderer,
|
|
939
|
+
formRenderer,
|
|
940
|
+
dataViewRenderer,
|
|
941
|
+
showRawFallback: true
|
|
942
|
+
}),
|
|
943
|
+
tc.error && /* @__PURE__ */ jsx6("p", {
|
|
944
|
+
className: "text-destructive mt-1",
|
|
945
|
+
children: tc.error
|
|
946
|
+
})
|
|
947
|
+
]
|
|
948
|
+
})
|
|
949
|
+
]
|
|
950
|
+
}, tc.id))
|
|
951
|
+
});
|
|
952
|
+
})()
|
|
649
953
|
]
|
|
650
954
|
})
|
|
651
955
|
]
|
|
652
956
|
});
|
|
653
957
|
}
|
|
654
958
|
// src/presentation/components/ChatInput.tsx
|
|
655
|
-
import * as
|
|
656
|
-
import { cn as
|
|
959
|
+
import * as React5 from "react";
|
|
960
|
+
import { cn as cn6 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
657
961
|
import { Textarea } from "@contractspec/lib.design-system";
|
|
658
962
|
import { Button as Button3 } from "@contractspec/lib.design-system";
|
|
659
|
-
import {
|
|
660
|
-
|
|
963
|
+
import {
|
|
964
|
+
Send,
|
|
965
|
+
Paperclip,
|
|
966
|
+
X as X2,
|
|
967
|
+
Loader2,
|
|
968
|
+
FileText,
|
|
969
|
+
Code,
|
|
970
|
+
ImageIcon
|
|
971
|
+
} from "lucide-react";
|
|
972
|
+
import { jsx as jsx7, jsxs as jsxs7, Fragment as Fragment3 } from "react/jsx-runtime";
|
|
661
973
|
"use client";
|
|
974
|
+
var DEFAULT_MAX_FILE_SIZE_BYTES = 5 * 1024 * 1024;
|
|
975
|
+
var CODE_EXTENSIONS = [
|
|
976
|
+
"ts",
|
|
977
|
+
"tsx",
|
|
978
|
+
"js",
|
|
979
|
+
"jsx",
|
|
980
|
+
"py",
|
|
981
|
+
"go",
|
|
982
|
+
"rs",
|
|
983
|
+
"java",
|
|
984
|
+
"json",
|
|
985
|
+
"md",
|
|
986
|
+
"txt",
|
|
987
|
+
"yaml",
|
|
988
|
+
"yml"
|
|
989
|
+
];
|
|
990
|
+
function readFileAsContent(file) {
|
|
991
|
+
const extension = file.name.split(".").pop()?.toLowerCase() ?? "";
|
|
992
|
+
const isCode = CODE_EXTENSIONS.includes(extension);
|
|
993
|
+
const isImage = file.type.startsWith("image/");
|
|
994
|
+
if (isImage) {
|
|
995
|
+
return new Promise((resolve, reject) => {
|
|
996
|
+
const reader = new FileReader;
|
|
997
|
+
reader.onload = () => {
|
|
998
|
+
const result = reader.result;
|
|
999
|
+
resolve({
|
|
1000
|
+
content: typeof result === "string" ? result : new TextDecoder().decode(result ?? new ArrayBuffer(0)),
|
|
1001
|
+
type: "image"
|
|
1002
|
+
});
|
|
1003
|
+
};
|
|
1004
|
+
reader.onerror = () => reject(new Error("Could not read file"));
|
|
1005
|
+
reader.readAsDataURL(file);
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
return file.text().then((content) => ({
|
|
1009
|
+
content,
|
|
1010
|
+
type: isCode ? "code" : "file"
|
|
1011
|
+
})).catch(() => {
|
|
1012
|
+
throw new Error("Could not read file");
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
662
1015
|
function ChatInput({
|
|
663
1016
|
onSend,
|
|
664
1017
|
disabled = false,
|
|
@@ -666,147 +1019,163 @@ function ChatInput({
|
|
|
666
1019
|
placeholder = "Type a message...",
|
|
667
1020
|
className,
|
|
668
1021
|
showAttachments = true,
|
|
669
|
-
maxAttachments = 5
|
|
1022
|
+
maxAttachments = 5,
|
|
1023
|
+
maxFileSizeBytes = DEFAULT_MAX_FILE_SIZE_BYTES
|
|
670
1024
|
}) {
|
|
671
|
-
const [content, setContent] =
|
|
672
|
-
const [attachments, setAttachments] =
|
|
673
|
-
const
|
|
674
|
-
const
|
|
1025
|
+
const [content, setContent] = React5.useState("");
|
|
1026
|
+
const [attachments, setAttachments] = React5.useState([]);
|
|
1027
|
+
const [fileError, setFileError] = React5.useState(null);
|
|
1028
|
+
const textareaRef = React5.useRef(null);
|
|
1029
|
+
const fileInputRef = React5.useRef(null);
|
|
675
1030
|
const canSend = content.trim().length > 0 || attachments.length > 0;
|
|
676
|
-
const handleSubmit =
|
|
1031
|
+
const handleSubmit = React5.useCallback((e) => {
|
|
677
1032
|
e?.preventDefault();
|
|
678
1033
|
if (!canSend || disabled || isLoading)
|
|
679
1034
|
return;
|
|
680
1035
|
onSend(content.trim(), attachments.length > 0 ? attachments : undefined);
|
|
681
1036
|
setContent("");
|
|
682
1037
|
setAttachments([]);
|
|
1038
|
+
setFileError(null);
|
|
683
1039
|
textareaRef.current?.focus();
|
|
684
1040
|
}, [canSend, content, attachments, disabled, isLoading, onSend]);
|
|
685
|
-
const handleKeyDown =
|
|
1041
|
+
const handleKeyDown = React5.useCallback((e) => {
|
|
686
1042
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
687
1043
|
e.preventDefault();
|
|
688
1044
|
handleSubmit();
|
|
689
1045
|
}
|
|
690
1046
|
}, [handleSubmit]);
|
|
691
|
-
const handleFileSelect =
|
|
1047
|
+
const handleFileSelect = React5.useCallback(async (e) => {
|
|
692
1048
|
const files = e.target.files;
|
|
693
1049
|
if (!files)
|
|
694
1050
|
return;
|
|
1051
|
+
setFileError(null);
|
|
695
1052
|
const newAttachments = [];
|
|
1053
|
+
const errors = [];
|
|
696
1054
|
for (const file of Array.from(files)) {
|
|
697
|
-
if (attachments.length + newAttachments.length >= maxAttachments)
|
|
1055
|
+
if (attachments.length + newAttachments.length >= maxAttachments) {
|
|
1056
|
+
errors.push(`Maximum ${maxAttachments} attachments allowed`);
|
|
698
1057
|
break;
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1058
|
+
}
|
|
1059
|
+
if (file.size > maxFileSizeBytes) {
|
|
1060
|
+
errors.push(`${file.name} exceeds ${Math.round(maxFileSizeBytes / 1024 / 1024)}MB limit`);
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
try {
|
|
1064
|
+
const { content: fileContent, type } = await readFileAsContent(file);
|
|
1065
|
+
newAttachments.push({
|
|
1066
|
+
id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`,
|
|
1067
|
+
type,
|
|
1068
|
+
name: file.name,
|
|
1069
|
+
content: fileContent,
|
|
1070
|
+
mimeType: file.type,
|
|
1071
|
+
size: file.size
|
|
1072
|
+
});
|
|
1073
|
+
} catch {
|
|
1074
|
+
errors.push(`Could not read ${file.name}`);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
if (errors.length > 0) {
|
|
1078
|
+
setFileError(errors[0] ?? "Could not add file");
|
|
1079
|
+
}
|
|
1080
|
+
if (newAttachments.length > 0) {
|
|
1081
|
+
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
719
1082
|
}
|
|
720
|
-
setAttachments((prev) => [...prev, ...newAttachments]);
|
|
721
1083
|
e.target.value = "";
|
|
722
|
-
}, [attachments.length, maxAttachments]);
|
|
723
|
-
const removeAttachment =
|
|
1084
|
+
}, [attachments.length, maxAttachments, maxFileSizeBytes]);
|
|
1085
|
+
const removeAttachment = React5.useCallback((id) => {
|
|
724
1086
|
setAttachments((prev) => prev.filter((a) => a.id !== id));
|
|
725
1087
|
}, []);
|
|
726
|
-
return /* @__PURE__ */
|
|
727
|
-
className:
|
|
1088
|
+
return /* @__PURE__ */ jsxs7("div", {
|
|
1089
|
+
className: cn6("flex flex-col gap-2", className),
|
|
728
1090
|
children: [
|
|
729
|
-
attachments.length > 0 && /* @__PURE__ */
|
|
1091
|
+
attachments.length > 0 && /* @__PURE__ */ jsx7("div", {
|
|
730
1092
|
className: "flex flex-wrap gap-2",
|
|
731
|
-
children: attachments.map((attachment) => /* @__PURE__ */
|
|
732
|
-
className:
|
|
1093
|
+
children: attachments.map((attachment) => /* @__PURE__ */ jsxs7("div", {
|
|
1094
|
+
className: cn6("flex items-center gap-1.5 rounded-md px-2 py-1", "bg-muted text-muted-foreground text-sm"),
|
|
733
1095
|
children: [
|
|
734
|
-
attachment.type === "code" ? /* @__PURE__ */
|
|
1096
|
+
attachment.type === "code" ? /* @__PURE__ */ jsx7(Code, {
|
|
735
1097
|
className: "h-3.5 w-3.5"
|
|
736
|
-
}) : /* @__PURE__ */
|
|
1098
|
+
}) : attachment.type === "image" ? /* @__PURE__ */ jsx7(ImageIcon, {
|
|
1099
|
+
className: "h-3.5 w-3.5"
|
|
1100
|
+
}) : /* @__PURE__ */ jsx7(FileText, {
|
|
737
1101
|
className: "h-3.5 w-3.5"
|
|
738
1102
|
}),
|
|
739
|
-
/* @__PURE__ */
|
|
1103
|
+
/* @__PURE__ */ jsx7("span", {
|
|
740
1104
|
className: "max-w-[150px] truncate",
|
|
741
1105
|
children: attachment.name
|
|
742
1106
|
}),
|
|
743
|
-
/* @__PURE__ */
|
|
1107
|
+
/* @__PURE__ */ jsx7("button", {
|
|
744
1108
|
type: "button",
|
|
745
1109
|
onClick: () => removeAttachment(attachment.id),
|
|
746
1110
|
className: "hover:text-foreground",
|
|
747
1111
|
"aria-label": `Remove ${attachment.name}`,
|
|
748
|
-
children: /* @__PURE__ */
|
|
1112
|
+
children: /* @__PURE__ */ jsx7(X2, {
|
|
749
1113
|
className: "h-3.5 w-3.5"
|
|
750
1114
|
})
|
|
751
1115
|
})
|
|
752
1116
|
]
|
|
753
1117
|
}, attachment.id))
|
|
754
1118
|
}),
|
|
755
|
-
/* @__PURE__ */
|
|
1119
|
+
fileError && /* @__PURE__ */ jsx7("p", {
|
|
1120
|
+
className: "text-destructive text-xs",
|
|
1121
|
+
role: "alert",
|
|
1122
|
+
children: fileError
|
|
1123
|
+
}),
|
|
1124
|
+
/* @__PURE__ */ jsxs7("form", {
|
|
756
1125
|
onSubmit: handleSubmit,
|
|
757
1126
|
className: "flex items-end gap-2",
|
|
758
1127
|
children: [
|
|
759
|
-
showAttachments && /* @__PURE__ */
|
|
1128
|
+
showAttachments && /* @__PURE__ */ jsxs7(Fragment3, {
|
|
760
1129
|
children: [
|
|
761
|
-
/* @__PURE__ */
|
|
1130
|
+
/* @__PURE__ */ jsx7("input", {
|
|
762
1131
|
ref: fileInputRef,
|
|
763
1132
|
type: "file",
|
|
764
1133
|
multiple: true,
|
|
765
|
-
accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml",
|
|
1134
|
+
accept: ".ts,.tsx,.js,.jsx,.json,.md,.txt,.py,.go,.rs,.java,.yaml,.yml,image/*",
|
|
766
1135
|
onChange: handleFileSelect,
|
|
767
1136
|
className: "hidden",
|
|
768
1137
|
"aria-label": "Attach files"
|
|
769
1138
|
}),
|
|
770
|
-
/* @__PURE__ */
|
|
1139
|
+
/* @__PURE__ */ jsx7(Button3, {
|
|
771
1140
|
type: "button",
|
|
772
1141
|
variant: "ghost",
|
|
773
1142
|
size: "sm",
|
|
774
1143
|
onPress: () => fileInputRef.current?.click(),
|
|
775
1144
|
disabled: disabled || attachments.length >= maxAttachments,
|
|
776
1145
|
"aria-label": "Attach files",
|
|
777
|
-
children: /* @__PURE__ */
|
|
1146
|
+
children: /* @__PURE__ */ jsx7(Paperclip, {
|
|
778
1147
|
className: "h-4 w-4"
|
|
779
1148
|
})
|
|
780
1149
|
})
|
|
781
1150
|
]
|
|
782
1151
|
}),
|
|
783
|
-
/* @__PURE__ */
|
|
1152
|
+
/* @__PURE__ */ jsx7("div", {
|
|
784
1153
|
className: "relative flex-1",
|
|
785
|
-
children: /* @__PURE__ */
|
|
1154
|
+
children: /* @__PURE__ */ jsx7(Textarea, {
|
|
786
1155
|
value: content,
|
|
787
1156
|
onChange: (e) => setContent(e.target.value),
|
|
788
1157
|
onKeyDown: handleKeyDown,
|
|
789
1158
|
placeholder,
|
|
790
1159
|
disabled,
|
|
791
|
-
className:
|
|
1160
|
+
className: cn6("max-h-[200px] min-h-[44px] resize-none pr-12", "focus-visible:ring-1"),
|
|
792
1161
|
rows: 1,
|
|
793
1162
|
"aria-label": "Chat message"
|
|
794
1163
|
})
|
|
795
1164
|
}),
|
|
796
|
-
/* @__PURE__ */
|
|
1165
|
+
/* @__PURE__ */ jsx7(Button3, {
|
|
797
1166
|
type: "submit",
|
|
798
1167
|
disabled: !canSend || disabled || isLoading,
|
|
799
1168
|
size: "sm",
|
|
800
1169
|
"aria-label": isLoading ? "Sending..." : "Send message",
|
|
801
|
-
children: isLoading ? /* @__PURE__ */
|
|
1170
|
+
children: isLoading ? /* @__PURE__ */ jsx7(Loader2, {
|
|
802
1171
|
className: "h-4 w-4 animate-spin"
|
|
803
|
-
}) : /* @__PURE__ */
|
|
1172
|
+
}) : /* @__PURE__ */ jsx7(Send, {
|
|
804
1173
|
className: "h-4 w-4"
|
|
805
1174
|
})
|
|
806
1175
|
})
|
|
807
1176
|
]
|
|
808
1177
|
}),
|
|
809
|
-
/* @__PURE__ */
|
|
1178
|
+
/* @__PURE__ */ jsx7("p", {
|
|
810
1179
|
className: "text-muted-foreground text-xs",
|
|
811
1180
|
children: "Press Enter to send, Shift+Enter for new line"
|
|
812
1181
|
})
|
|
@@ -814,7 +1183,7 @@ function ChatInput({
|
|
|
814
1183
|
});
|
|
815
1184
|
}
|
|
816
1185
|
// src/presentation/components/ChatExportToolbar.tsx
|
|
817
|
-
import * as
|
|
1186
|
+
import * as React6 from "react";
|
|
818
1187
|
import { Download as Download2, FileText as FileText2, Copy as Copy3, Check as Check3, Plus, GitFork } from "lucide-react";
|
|
819
1188
|
import { Button as Button4 } from "@contractspec/lib.design-system";
|
|
820
1189
|
import {
|
|
@@ -1026,7 +1395,7 @@ function exportToFile(messages, format, conversation) {
|
|
|
1026
1395
|
}
|
|
1027
1396
|
|
|
1028
1397
|
// src/presentation/components/ChatExportToolbar.tsx
|
|
1029
|
-
import { jsx as
|
|
1398
|
+
import { jsx as jsx8, jsxs as jsxs8, Fragment as Fragment4 } from "react/jsx-runtime";
|
|
1030
1399
|
"use client";
|
|
1031
1400
|
function ChatExportToolbar({
|
|
1032
1401
|
messages,
|
|
@@ -1041,19 +1410,19 @@ function ChatExportToolbar({
|
|
|
1041
1410
|
onCreateNew,
|
|
1042
1411
|
onFork
|
|
1043
1412
|
}) {
|
|
1044
|
-
const [copied, setCopied] =
|
|
1045
|
-
const toExport =
|
|
1413
|
+
const [copied, setCopied] = React6.useState(false);
|
|
1414
|
+
const toExport = React6.useMemo(() => {
|
|
1046
1415
|
if (selectedIds.size > 0) {
|
|
1047
1416
|
const idSet = selectedIds;
|
|
1048
1417
|
return messages.filter((m) => idSet.has(m.id));
|
|
1049
1418
|
}
|
|
1050
1419
|
return messages;
|
|
1051
1420
|
}, [messages, selectedIds]);
|
|
1052
|
-
const handleExport =
|
|
1421
|
+
const handleExport = React6.useCallback((format) => {
|
|
1053
1422
|
exportToFile(toExport, format, conversation);
|
|
1054
1423
|
onExported?.(format, toExport.length);
|
|
1055
1424
|
}, [toExport, conversation, onExported]);
|
|
1056
|
-
const handleCopy =
|
|
1425
|
+
const handleCopy = React6.useCallback(async () => {
|
|
1057
1426
|
const content = formatMessagesAsMarkdown(toExport);
|
|
1058
1427
|
await navigator.clipboard.writeText(content);
|
|
1059
1428
|
setCopied(true);
|
|
@@ -1061,8 +1430,8 @@ function ChatExportToolbar({
|
|
|
1061
1430
|
onExported?.("markdown", toExport.length);
|
|
1062
1431
|
}, [toExport, onExported]);
|
|
1063
1432
|
const disabled = messages.length === 0;
|
|
1064
|
-
const [forking, setForking] =
|
|
1065
|
-
const handleFork =
|
|
1433
|
+
const [forking, setForking] = React6.useState(false);
|
|
1434
|
+
const handleFork = React6.useCallback(async (upToMessageId) => {
|
|
1066
1435
|
if (!onFork)
|
|
1067
1436
|
return;
|
|
1068
1437
|
setForking(true);
|
|
@@ -1072,35 +1441,35 @@ function ChatExportToolbar({
|
|
|
1072
1441
|
setForking(false);
|
|
1073
1442
|
}
|
|
1074
1443
|
}, [onFork]);
|
|
1075
|
-
return /* @__PURE__ */
|
|
1444
|
+
return /* @__PURE__ */ jsxs8("div", {
|
|
1076
1445
|
className: "flex items-center gap-2",
|
|
1077
1446
|
children: [
|
|
1078
|
-
onCreateNew && /* @__PURE__ */
|
|
1447
|
+
onCreateNew && /* @__PURE__ */ jsxs8(Button4, {
|
|
1079
1448
|
variant: "outline",
|
|
1080
1449
|
size: "sm",
|
|
1081
1450
|
onPress: onCreateNew,
|
|
1082
1451
|
"aria-label": "New conversation",
|
|
1083
1452
|
children: [
|
|
1084
|
-
/* @__PURE__ */
|
|
1453
|
+
/* @__PURE__ */ jsx8(Plus, {
|
|
1085
1454
|
className: "h-4 w-4"
|
|
1086
1455
|
}),
|
|
1087
1456
|
"New"
|
|
1088
1457
|
]
|
|
1089
1458
|
}),
|
|
1090
|
-
onFork && messages.length > 0 && /* @__PURE__ */
|
|
1459
|
+
onFork && messages.length > 0 && /* @__PURE__ */ jsxs8(Button4, {
|
|
1091
1460
|
variant: "outline",
|
|
1092
1461
|
size: "sm",
|
|
1093
1462
|
disabled: forking,
|
|
1094
1463
|
onPress: () => handleFork(),
|
|
1095
1464
|
"aria-label": "Fork conversation",
|
|
1096
1465
|
children: [
|
|
1097
|
-
/* @__PURE__ */
|
|
1466
|
+
/* @__PURE__ */ jsx8(GitFork, {
|
|
1098
1467
|
className: "h-4 w-4"
|
|
1099
1468
|
}),
|
|
1100
1469
|
"Fork"
|
|
1101
1470
|
]
|
|
1102
1471
|
}),
|
|
1103
|
-
showSelectionSummary && selectedCount > 0 && /* @__PURE__ */
|
|
1472
|
+
showSelectionSummary && selectedCount > 0 && /* @__PURE__ */ jsxs8("span", {
|
|
1104
1473
|
className: "text-muted-foreground text-sm",
|
|
1105
1474
|
children: [
|
|
1106
1475
|
selectedCount,
|
|
@@ -1109,16 +1478,16 @@ function ChatExportToolbar({
|
|
|
1109
1478
|
" selected"
|
|
1110
1479
|
]
|
|
1111
1480
|
}),
|
|
1112
|
-
onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */
|
|
1481
|
+
onSelectAll && onClearSelection && totalCount > 0 && /* @__PURE__ */ jsxs8(Fragment4, {
|
|
1113
1482
|
children: [
|
|
1114
|
-
/* @__PURE__ */
|
|
1483
|
+
/* @__PURE__ */ jsx8(Button4, {
|
|
1115
1484
|
variant: "ghost",
|
|
1116
1485
|
size: "sm",
|
|
1117
1486
|
onPress: onSelectAll,
|
|
1118
1487
|
className: "text-xs",
|
|
1119
1488
|
children: "Select all"
|
|
1120
1489
|
}),
|
|
1121
|
-
selectedCount > 0 && /* @__PURE__ */
|
|
1490
|
+
selectedCount > 0 && /* @__PURE__ */ jsx8(Button4, {
|
|
1122
1491
|
variant: "ghost",
|
|
1123
1492
|
size: "sm",
|
|
1124
1493
|
onPress: onClearSelection,
|
|
@@ -1127,64 +1496,64 @@ function ChatExportToolbar({
|
|
|
1127
1496
|
})
|
|
1128
1497
|
]
|
|
1129
1498
|
}),
|
|
1130
|
-
/* @__PURE__ */
|
|
1499
|
+
/* @__PURE__ */ jsxs8(DropdownMenu, {
|
|
1131
1500
|
children: [
|
|
1132
|
-
/* @__PURE__ */
|
|
1501
|
+
/* @__PURE__ */ jsx8(DropdownMenuTrigger, {
|
|
1133
1502
|
asChild: true,
|
|
1134
|
-
children: /* @__PURE__ */
|
|
1503
|
+
children: /* @__PURE__ */ jsxs8(Button4, {
|
|
1135
1504
|
variant: "outline",
|
|
1136
1505
|
size: "sm",
|
|
1137
1506
|
disabled,
|
|
1138
1507
|
"aria-label": selectedCount > 0 ? "Export selected messages" : "Export conversation",
|
|
1139
1508
|
children: [
|
|
1140
|
-
/* @__PURE__ */
|
|
1509
|
+
/* @__PURE__ */ jsx8(Download2, {
|
|
1141
1510
|
className: "h-4 w-4"
|
|
1142
1511
|
}),
|
|
1143
1512
|
"Export"
|
|
1144
1513
|
]
|
|
1145
1514
|
})
|
|
1146
1515
|
}),
|
|
1147
|
-
/* @__PURE__ */
|
|
1516
|
+
/* @__PURE__ */ jsxs8(DropdownMenuContent, {
|
|
1148
1517
|
align: "end",
|
|
1149
1518
|
children: [
|
|
1150
|
-
/* @__PURE__ */
|
|
1519
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1151
1520
|
onSelect: () => handleExport("markdown"),
|
|
1152
1521
|
disabled,
|
|
1153
1522
|
children: [
|
|
1154
|
-
/* @__PURE__ */
|
|
1523
|
+
/* @__PURE__ */ jsx8(FileText2, {
|
|
1155
1524
|
className: "h-4 w-4"
|
|
1156
1525
|
}),
|
|
1157
1526
|
"Export as Markdown (.md)"
|
|
1158
1527
|
]
|
|
1159
1528
|
}),
|
|
1160
|
-
/* @__PURE__ */
|
|
1529
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1161
1530
|
onSelect: () => handleExport("txt"),
|
|
1162
1531
|
disabled,
|
|
1163
1532
|
children: [
|
|
1164
|
-
/* @__PURE__ */
|
|
1533
|
+
/* @__PURE__ */ jsx8(FileText2, {
|
|
1165
1534
|
className: "h-4 w-4"
|
|
1166
1535
|
}),
|
|
1167
1536
|
"Export as Plain Text (.txt)"
|
|
1168
1537
|
]
|
|
1169
1538
|
}),
|
|
1170
|
-
/* @__PURE__ */
|
|
1539
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1171
1540
|
onSelect: () => handleExport("json"),
|
|
1172
1541
|
disabled,
|
|
1173
1542
|
children: [
|
|
1174
|
-
/* @__PURE__ */
|
|
1543
|
+
/* @__PURE__ */ jsx8(FileText2, {
|
|
1175
1544
|
className: "h-4 w-4"
|
|
1176
1545
|
}),
|
|
1177
1546
|
"Export as JSON (.json)"
|
|
1178
1547
|
]
|
|
1179
1548
|
}),
|
|
1180
|
-
/* @__PURE__ */
|
|
1181
|
-
/* @__PURE__ */
|
|
1549
|
+
/* @__PURE__ */ jsx8(DropdownMenuSeparator, {}),
|
|
1550
|
+
/* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
1182
1551
|
onSelect: () => handleCopy(),
|
|
1183
1552
|
disabled,
|
|
1184
1553
|
children: [
|
|
1185
|
-
copied ? /* @__PURE__ */
|
|
1554
|
+
copied ? /* @__PURE__ */ jsx8(Check3, {
|
|
1186
1555
|
className: "h-4 w-4 text-green-500"
|
|
1187
|
-
}) : /* @__PURE__ */
|
|
1556
|
+
}) : /* @__PURE__ */ jsx8(Copy3, {
|
|
1188
1557
|
className: "h-4 w-4"
|
|
1189
1558
|
}),
|
|
1190
1559
|
copied ? "Copied to clipboard" : "Copy to clipboard"
|
|
@@ -1198,11 +1567,11 @@ function ChatExportToolbar({
|
|
|
1198
1567
|
});
|
|
1199
1568
|
}
|
|
1200
1569
|
// src/presentation/components/ChatWithExport.tsx
|
|
1201
|
-
import * as
|
|
1570
|
+
import * as React10 from "react";
|
|
1202
1571
|
|
|
1203
1572
|
// src/presentation/components/ThinkingLevelPicker.tsx
|
|
1204
|
-
import * as
|
|
1205
|
-
import { cn as
|
|
1573
|
+
import * as React7 from "react";
|
|
1574
|
+
import { cn as cn7 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
1206
1575
|
import {
|
|
1207
1576
|
Select,
|
|
1208
1577
|
SelectContent,
|
|
@@ -1264,7 +1633,7 @@ function getProviderOptions(level, providerName) {
|
|
|
1264
1633
|
}
|
|
1265
1634
|
|
|
1266
1635
|
// src/presentation/components/ThinkingLevelPicker.tsx
|
|
1267
|
-
import { jsx as
|
|
1636
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1268
1637
|
"use client";
|
|
1269
1638
|
var THINKING_LEVELS = [
|
|
1270
1639
|
"instant",
|
|
@@ -1278,20 +1647,20 @@ function ThinkingLevelPicker({
|
|
|
1278
1647
|
className,
|
|
1279
1648
|
compact = false
|
|
1280
1649
|
}) {
|
|
1281
|
-
const handleChange =
|
|
1650
|
+
const handleChange = React7.useCallback((v) => {
|
|
1282
1651
|
onChange(v);
|
|
1283
1652
|
}, [onChange]);
|
|
1284
1653
|
if (compact) {
|
|
1285
|
-
return /* @__PURE__ */
|
|
1654
|
+
return /* @__PURE__ */ jsxs9(Select, {
|
|
1286
1655
|
value,
|
|
1287
1656
|
onValueChange: handleChange,
|
|
1288
1657
|
children: [
|
|
1289
|
-
/* @__PURE__ */
|
|
1290
|
-
className:
|
|
1291
|
-
children: /* @__PURE__ */
|
|
1658
|
+
/* @__PURE__ */ jsx9(SelectTrigger, {
|
|
1659
|
+
className: cn7("w-[140px]", className),
|
|
1660
|
+
children: /* @__PURE__ */ jsx9(SelectValue, {})
|
|
1292
1661
|
}),
|
|
1293
|
-
/* @__PURE__ */
|
|
1294
|
-
children: THINKING_LEVELS.map((level) => /* @__PURE__ */
|
|
1662
|
+
/* @__PURE__ */ jsx9(SelectContent, {
|
|
1663
|
+
children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
|
|
1295
1664
|
value: level,
|
|
1296
1665
|
children: THINKING_LEVEL_LABELS[level]
|
|
1297
1666
|
}, level))
|
|
@@ -1299,26 +1668,26 @@ function ThinkingLevelPicker({
|
|
|
1299
1668
|
]
|
|
1300
1669
|
});
|
|
1301
1670
|
}
|
|
1302
|
-
return /* @__PURE__ */
|
|
1303
|
-
className:
|
|
1671
|
+
return /* @__PURE__ */ jsxs9("div", {
|
|
1672
|
+
className: cn7("flex flex-col gap-1.5", className),
|
|
1304
1673
|
children: [
|
|
1305
|
-
/* @__PURE__ */
|
|
1674
|
+
/* @__PURE__ */ jsx9(Label, {
|
|
1306
1675
|
htmlFor: "thinking-level-picker",
|
|
1307
1676
|
className: "text-sm font-medium",
|
|
1308
1677
|
children: "Thinking Level"
|
|
1309
1678
|
}),
|
|
1310
|
-
/* @__PURE__ */
|
|
1679
|
+
/* @__PURE__ */ jsxs9(Select, {
|
|
1311
1680
|
name: "thinking-level-picker",
|
|
1312
1681
|
value,
|
|
1313
1682
|
onValueChange: handleChange,
|
|
1314
1683
|
children: [
|
|
1315
|
-
/* @__PURE__ */
|
|
1316
|
-
children: /* @__PURE__ */
|
|
1684
|
+
/* @__PURE__ */ jsx9(SelectTrigger, {
|
|
1685
|
+
children: /* @__PURE__ */ jsx9(SelectValue, {
|
|
1317
1686
|
placeholder: "Select thinking level"
|
|
1318
1687
|
})
|
|
1319
1688
|
}),
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
children: THINKING_LEVELS.map((level) => /* @__PURE__ */
|
|
1689
|
+
/* @__PURE__ */ jsx9(SelectContent, {
|
|
1690
|
+
children: THINKING_LEVELS.map((level) => /* @__PURE__ */ jsx9(SelectItem, {
|
|
1322
1691
|
value: level,
|
|
1323
1692
|
title: THINKING_LEVEL_DESCRIPTIONS[level],
|
|
1324
1693
|
children: THINKING_LEVEL_LABELS[level]
|
|
@@ -1331,12 +1700,12 @@ function ThinkingLevelPicker({
|
|
|
1331
1700
|
}
|
|
1332
1701
|
|
|
1333
1702
|
// src/presentation/hooks/useMessageSelection.ts
|
|
1334
|
-
import * as
|
|
1703
|
+
import * as React8 from "react";
|
|
1335
1704
|
"use client";
|
|
1336
1705
|
function useMessageSelection(messageIds) {
|
|
1337
|
-
const [selectedIds, setSelectedIds] =
|
|
1338
|
-
const idSet =
|
|
1339
|
-
|
|
1706
|
+
const [selectedIds, setSelectedIds] = React8.useState(() => new Set);
|
|
1707
|
+
const idSet = React8.useMemo(() => new Set(messageIds), [messageIds.join(",")]);
|
|
1708
|
+
React8.useEffect(() => {
|
|
1340
1709
|
setSelectedIds((prev) => {
|
|
1341
1710
|
const next = new Set;
|
|
1342
1711
|
for (const id of prev) {
|
|
@@ -1346,7 +1715,7 @@ function useMessageSelection(messageIds) {
|
|
|
1346
1715
|
return next.size === prev.size ? prev : next;
|
|
1347
1716
|
});
|
|
1348
1717
|
}, [idSet]);
|
|
1349
|
-
const toggle =
|
|
1718
|
+
const toggle = React8.useCallback((id) => {
|
|
1350
1719
|
setSelectedIds((prev) => {
|
|
1351
1720
|
const next = new Set(prev);
|
|
1352
1721
|
if (next.has(id))
|
|
@@ -1356,13 +1725,13 @@ function useMessageSelection(messageIds) {
|
|
|
1356
1725
|
return next;
|
|
1357
1726
|
});
|
|
1358
1727
|
}, []);
|
|
1359
|
-
const selectAll =
|
|
1728
|
+
const selectAll = React8.useCallback(() => {
|
|
1360
1729
|
setSelectedIds(new Set(messageIds));
|
|
1361
1730
|
}, [messageIds.join(",")]);
|
|
1362
|
-
const clearSelection =
|
|
1731
|
+
const clearSelection = React8.useCallback(() => {
|
|
1363
1732
|
setSelectedIds(new Set);
|
|
1364
1733
|
}, []);
|
|
1365
|
-
const isSelected =
|
|
1734
|
+
const isSelected = React8.useCallback((id) => selectedIds.has(id), [selectedIds]);
|
|
1366
1735
|
const selectedCount = selectedIds.size;
|
|
1367
1736
|
return {
|
|
1368
1737
|
selectedIds,
|
|
@@ -1374,8 +1743,38 @@ function useMessageSelection(messageIds) {
|
|
|
1374
1743
|
};
|
|
1375
1744
|
}
|
|
1376
1745
|
|
|
1746
|
+
// src/presentation/components/Suggestion.tsx
|
|
1747
|
+
import * as React9 from "react";
|
|
1748
|
+
import { Button as Button5 } from "@contractspec/lib.design-system";
|
|
1749
|
+
import { cn as cn8 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
1750
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1751
|
+
"use client";
|
|
1752
|
+
function Suggestions({ children, className }) {
|
|
1753
|
+
return /* @__PURE__ */ jsx10("div", {
|
|
1754
|
+
className: cn8("flex flex-wrap gap-2", className),
|
|
1755
|
+
children
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
function Suggestion({
|
|
1759
|
+
suggestion,
|
|
1760
|
+
onClick,
|
|
1761
|
+
className
|
|
1762
|
+
}) {
|
|
1763
|
+
const handleClick = React9.useCallback(() => {
|
|
1764
|
+
onClick?.(suggestion);
|
|
1765
|
+
}, [suggestion, onClick]);
|
|
1766
|
+
return /* @__PURE__ */ jsx10(Button5, {
|
|
1767
|
+
type: "button",
|
|
1768
|
+
variant: "outline",
|
|
1769
|
+
size: "sm",
|
|
1770
|
+
onPress: handleClick,
|
|
1771
|
+
className: cn8("text-muted-foreground hover:text-foreground", className),
|
|
1772
|
+
children: suggestion
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1377
1776
|
// src/presentation/components/ChatWithExport.tsx
|
|
1378
|
-
import { jsx as
|
|
1777
|
+
import { jsx as jsx11, jsxs as jsxs10, Fragment as Fragment5 } from "react/jsx-runtime";
|
|
1379
1778
|
"use client";
|
|
1380
1779
|
function ChatWithExport({
|
|
1381
1780
|
messages,
|
|
@@ -1391,20 +1790,27 @@ function ChatWithExport({
|
|
|
1391
1790
|
thinkingLevel = "thinking",
|
|
1392
1791
|
onThinkingLevelChange,
|
|
1393
1792
|
presentationRenderer,
|
|
1394
|
-
formRenderer
|
|
1793
|
+
formRenderer,
|
|
1794
|
+
dataViewRenderer,
|
|
1795
|
+
components,
|
|
1796
|
+
suggestions,
|
|
1797
|
+
onSuggestionClick,
|
|
1798
|
+
suggestionComponents: suggestionComps,
|
|
1799
|
+
showSuggestionsWhenEmpty = true
|
|
1395
1800
|
}) {
|
|
1396
|
-
const messageIds =
|
|
1801
|
+
const messageIds = React10.useMemo(() => messages.map((m) => m.id), [messages]);
|
|
1397
1802
|
const selection = useMessageSelection(messageIds);
|
|
1803
|
+
const showSuggestions = suggestions && suggestions.length > 0 && (messages.length === 0 || showSuggestionsWhenEmpty);
|
|
1398
1804
|
const hasToolbar = showExport || showMessageSelection;
|
|
1399
1805
|
const hasPicker = Boolean(onThinkingLevelChange);
|
|
1400
|
-
const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */
|
|
1806
|
+
const headerContent = hasPicker || hasToolbar ? /* @__PURE__ */ jsxs10(Fragment5, {
|
|
1401
1807
|
children: [
|
|
1402
|
-
hasPicker && /* @__PURE__ */
|
|
1808
|
+
hasPicker && onThinkingLevelChange && /* @__PURE__ */ jsx11(ThinkingLevelPicker, {
|
|
1403
1809
|
value: thinkingLevel,
|
|
1404
1810
|
onChange: onThinkingLevelChange,
|
|
1405
1811
|
compact: true
|
|
1406
1812
|
}),
|
|
1407
|
-
hasToolbar && /* @__PURE__ */
|
|
1813
|
+
hasToolbar && /* @__PURE__ */ jsx11(ChatExportToolbar, {
|
|
1408
1814
|
messages,
|
|
1409
1815
|
conversation,
|
|
1410
1816
|
selectedIds: selection.selectedIds,
|
|
@@ -1418,12 +1824,12 @@ function ChatWithExport({
|
|
|
1418
1824
|
})
|
|
1419
1825
|
]
|
|
1420
1826
|
}) : null;
|
|
1421
|
-
return /* @__PURE__ */
|
|
1827
|
+
return /* @__PURE__ */ jsxs10(ChatContainer, {
|
|
1422
1828
|
className,
|
|
1423
1829
|
headerContent,
|
|
1424
1830
|
showScrollButton,
|
|
1425
1831
|
children: [
|
|
1426
|
-
messages.map((msg) => /* @__PURE__ */
|
|
1832
|
+
messages.map((msg) => /* @__PURE__ */ jsx11(ChatMessage, {
|
|
1427
1833
|
message: msg,
|
|
1428
1834
|
selectable: showMessageSelection,
|
|
1429
1835
|
selected: selection.isSelected(msg.id),
|
|
@@ -1431,26 +1837,47 @@ function ChatWithExport({
|
|
|
1431
1837
|
editable: msg.role === "user" && !!onEditMessage,
|
|
1432
1838
|
onEdit: onEditMessage,
|
|
1433
1839
|
presentationRenderer,
|
|
1434
|
-
formRenderer
|
|
1840
|
+
formRenderer,
|
|
1841
|
+
dataViewRenderer,
|
|
1842
|
+
components
|
|
1435
1843
|
}, msg.id)),
|
|
1844
|
+
showSuggestions && (() => {
|
|
1845
|
+
const SuggestionsComp = suggestionComps?.Suggestions;
|
|
1846
|
+
const SuggestionComp = suggestionComps?.Suggestion;
|
|
1847
|
+
if (SuggestionsComp && SuggestionComp) {
|
|
1848
|
+
return /* @__PURE__ */ jsx11(SuggestionsComp, {
|
|
1849
|
+
children: suggestions.map((s) => /* @__PURE__ */ jsx11(SuggestionComp, {
|
|
1850
|
+
suggestion: s,
|
|
1851
|
+
onClick: onSuggestionClick
|
|
1852
|
+
}, s))
|
|
1853
|
+
});
|
|
1854
|
+
}
|
|
1855
|
+
return /* @__PURE__ */ jsx11(Suggestions, {
|
|
1856
|
+
className: "mb-4",
|
|
1857
|
+
children: suggestions.map((s) => /* @__PURE__ */ jsx11(Suggestion, {
|
|
1858
|
+
suggestion: s,
|
|
1859
|
+
onClick: onSuggestionClick
|
|
1860
|
+
}, s))
|
|
1861
|
+
});
|
|
1862
|
+
})(),
|
|
1436
1863
|
children
|
|
1437
1864
|
]
|
|
1438
1865
|
});
|
|
1439
1866
|
}
|
|
1440
1867
|
// src/presentation/components/ChatSidebar.tsx
|
|
1441
|
-
import * as
|
|
1868
|
+
import * as React12 from "react";
|
|
1442
1869
|
import { Plus as Plus2, Trash2, MessageSquare } from "lucide-react";
|
|
1443
|
-
import { Button as
|
|
1444
|
-
import { cn as
|
|
1870
|
+
import { Button as Button6 } from "@contractspec/lib.design-system";
|
|
1871
|
+
import { cn as cn9 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
1445
1872
|
|
|
1446
1873
|
// src/presentation/hooks/useConversations.ts
|
|
1447
|
-
import * as
|
|
1874
|
+
import * as React11 from "react";
|
|
1448
1875
|
"use client";
|
|
1449
1876
|
function useConversations(options) {
|
|
1450
1877
|
const { store, projectId, tags, limit = 50 } = options;
|
|
1451
|
-
const [conversations, setConversations] =
|
|
1452
|
-
const [isLoading, setIsLoading] =
|
|
1453
|
-
const refresh =
|
|
1878
|
+
const [conversations, setConversations] = React11.useState([]);
|
|
1879
|
+
const [isLoading, setIsLoading] = React11.useState(true);
|
|
1880
|
+
const refresh = React11.useCallback(async () => {
|
|
1454
1881
|
setIsLoading(true);
|
|
1455
1882
|
try {
|
|
1456
1883
|
const list = await store.list({
|
|
@@ -1464,10 +1891,10 @@ function useConversations(options) {
|
|
|
1464
1891
|
setIsLoading(false);
|
|
1465
1892
|
}
|
|
1466
1893
|
}, [store, projectId, tags, limit]);
|
|
1467
|
-
|
|
1894
|
+
React11.useEffect(() => {
|
|
1468
1895
|
refresh();
|
|
1469
1896
|
}, [refresh]);
|
|
1470
|
-
const deleteConversation =
|
|
1897
|
+
const deleteConversation = React11.useCallback(async (id) => {
|
|
1471
1898
|
const ok = await store.delete(id);
|
|
1472
1899
|
if (ok) {
|
|
1473
1900
|
setConversations((prev) => prev.filter((c) => c.id !== id));
|
|
@@ -1483,7 +1910,7 @@ function useConversations(options) {
|
|
|
1483
1910
|
}
|
|
1484
1911
|
|
|
1485
1912
|
// src/presentation/components/ChatSidebar.tsx
|
|
1486
|
-
import { jsx as
|
|
1913
|
+
import { jsx as jsx12, jsxs as jsxs11, Fragment as Fragment6 } from "react/jsx-runtime";
|
|
1487
1914
|
"use client";
|
|
1488
1915
|
function formatDate(date) {
|
|
1489
1916
|
const d = new Date(date);
|
|
@@ -1505,7 +1932,7 @@ function ConversationItem({
|
|
|
1505
1932
|
}) {
|
|
1506
1933
|
const title = conversation.title ?? conversation.messages[0]?.content?.slice(0, 50) ?? "New chat";
|
|
1507
1934
|
const displayTitle = title.length > 40 ? `${title.slice(0, 40)}…` : title;
|
|
1508
|
-
return /* @__PURE__ */
|
|
1935
|
+
return /* @__PURE__ */ jsxs11("div", {
|
|
1509
1936
|
role: "button",
|
|
1510
1937
|
tabIndex: 0,
|
|
1511
1938
|
onClick: onSelect,
|
|
@@ -1515,24 +1942,24 @@ function ConversationItem({
|
|
|
1515
1942
|
onSelect();
|
|
1516
1943
|
}
|
|
1517
1944
|
},
|
|
1518
|
-
className:
|
|
1945
|
+
className: cn9("group flex cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm transition-colors", selected ? "bg-accent text-accent-foreground" : "hover:bg-accent/50"),
|
|
1519
1946
|
children: [
|
|
1520
|
-
/* @__PURE__ */
|
|
1947
|
+
/* @__PURE__ */ jsx12(MessageSquare, {
|
|
1521
1948
|
className: "text-muted-foreground h-4 w-4 shrink-0"
|
|
1522
1949
|
}),
|
|
1523
|
-
/* @__PURE__ */
|
|
1950
|
+
/* @__PURE__ */ jsxs11("div", {
|
|
1524
1951
|
className: "min-w-0 flex-1",
|
|
1525
1952
|
children: [
|
|
1526
|
-
/* @__PURE__ */
|
|
1953
|
+
/* @__PURE__ */ jsx12("p", {
|
|
1527
1954
|
className: "truncate",
|
|
1528
1955
|
children: displayTitle
|
|
1529
1956
|
}),
|
|
1530
|
-
/* @__PURE__ */
|
|
1957
|
+
/* @__PURE__ */ jsxs11("p", {
|
|
1531
1958
|
className: "text-muted-foreground text-xs",
|
|
1532
1959
|
children: [
|
|
1533
1960
|
formatDate(conversation.updatedAt),
|
|
1534
1961
|
conversation.projectName && ` · ${conversation.projectName}`,
|
|
1535
|
-
conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */
|
|
1962
|
+
conversation.tags && conversation.tags.length > 0 && /* @__PURE__ */ jsxs11(Fragment6, {
|
|
1536
1963
|
children: [
|
|
1537
1964
|
" · ",
|
|
1538
1965
|
conversation.tags.slice(0, 2).join(", ")
|
|
@@ -1542,15 +1969,17 @@ function ConversationItem({
|
|
|
1542
1969
|
})
|
|
1543
1970
|
]
|
|
1544
1971
|
}),
|
|
1545
|
-
/* @__PURE__ */
|
|
1972
|
+
/* @__PURE__ */ jsx12("span", {
|
|
1973
|
+
role: "group",
|
|
1546
1974
|
onClick: (e) => e.stopPropagation(),
|
|
1547
|
-
|
|
1975
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
1976
|
+
children: /* @__PURE__ */ jsx12(Button6, {
|
|
1548
1977
|
variant: "ghost",
|
|
1549
1978
|
size: "sm",
|
|
1550
1979
|
className: "h-6 w-6 shrink-0 p-0 opacity-0 group-hover:opacity-100",
|
|
1551
1980
|
onPress: onDelete,
|
|
1552
1981
|
"aria-label": "Delete conversation",
|
|
1553
|
-
children: /* @__PURE__ */
|
|
1982
|
+
children: /* @__PURE__ */ jsx12(Trash2, {
|
|
1554
1983
|
className: "h-3 w-3"
|
|
1555
1984
|
})
|
|
1556
1985
|
})
|
|
@@ -1571,8 +2000,13 @@ function ChatSidebar({
|
|
|
1571
2000
|
onUpdateConversation,
|
|
1572
2001
|
selectedConversation
|
|
1573
2002
|
}) {
|
|
1574
|
-
const { conversations, isLoading,
|
|
1575
|
-
|
|
2003
|
+
const { conversations, isLoading, deleteConversation } = useConversations({
|
|
2004
|
+
store,
|
|
2005
|
+
projectId,
|
|
2006
|
+
tags,
|
|
2007
|
+
limit
|
|
2008
|
+
});
|
|
2009
|
+
const handleDelete = React12.useCallback(async (id) => {
|
|
1576
2010
|
const ok = await deleteConversation(id);
|
|
1577
2011
|
if (ok && selectedConversationId === id) {
|
|
1578
2012
|
onSelectConversation(null);
|
|
@@ -1580,39 +2014,39 @@ function ChatSidebar({
|
|
|
1580
2014
|
}, [deleteConversation, selectedConversationId, onSelectConversation]);
|
|
1581
2015
|
if (collapsed)
|
|
1582
2016
|
return null;
|
|
1583
|
-
return /* @__PURE__ */
|
|
1584
|
-
className:
|
|
2017
|
+
return /* @__PURE__ */ jsxs11("div", {
|
|
2018
|
+
className: cn9("border-border flex w-64 shrink-0 flex-col border-r", className),
|
|
1585
2019
|
children: [
|
|
1586
|
-
/* @__PURE__ */
|
|
2020
|
+
/* @__PURE__ */ jsxs11("div", {
|
|
1587
2021
|
className: "border-border flex shrink-0 items-center justify-between border-b p-2",
|
|
1588
2022
|
children: [
|
|
1589
|
-
/* @__PURE__ */
|
|
2023
|
+
/* @__PURE__ */ jsx12("span", {
|
|
1590
2024
|
className: "text-muted-foreground text-sm font-medium",
|
|
1591
2025
|
children: "Conversations"
|
|
1592
2026
|
}),
|
|
1593
|
-
/* @__PURE__ */
|
|
2027
|
+
/* @__PURE__ */ jsx12(Button6, {
|
|
1594
2028
|
variant: "ghost",
|
|
1595
2029
|
size: "sm",
|
|
1596
2030
|
className: "h-8 w-8 p-0",
|
|
1597
2031
|
onPress: onCreateNew,
|
|
1598
2032
|
"aria-label": "New conversation",
|
|
1599
|
-
children: /* @__PURE__ */
|
|
2033
|
+
children: /* @__PURE__ */ jsx12(Plus2, {
|
|
1600
2034
|
className: "h-4 w-4"
|
|
1601
2035
|
})
|
|
1602
2036
|
})
|
|
1603
2037
|
]
|
|
1604
2038
|
}),
|
|
1605
|
-
/* @__PURE__ */
|
|
2039
|
+
/* @__PURE__ */ jsx12("div", {
|
|
1606
2040
|
className: "flex-1 overflow-y-auto p-2",
|
|
1607
|
-
children: isLoading ? /* @__PURE__ */
|
|
2041
|
+
children: isLoading ? /* @__PURE__ */ jsx12("div", {
|
|
1608
2042
|
className: "text-muted-foreground py-4 text-center text-sm",
|
|
1609
2043
|
children: "Loading…"
|
|
1610
|
-
}) : conversations.length === 0 ? /* @__PURE__ */
|
|
2044
|
+
}) : conversations.length === 0 ? /* @__PURE__ */ jsx12("div", {
|
|
1611
2045
|
className: "text-muted-foreground py-4 text-center text-sm",
|
|
1612
2046
|
children: "No conversations yet"
|
|
1613
|
-
}) : /* @__PURE__ */
|
|
2047
|
+
}) : /* @__PURE__ */ jsx12("div", {
|
|
1614
2048
|
className: "flex flex-col gap-1",
|
|
1615
|
-
children: conversations.map((conv) => /* @__PURE__ */
|
|
2049
|
+
children: conversations.map((conv) => /* @__PURE__ */ jsx12(ConversationItem, {
|
|
1616
2050
|
conversation: conv,
|
|
1617
2051
|
selected: conv.id === selectedConversationId,
|
|
1618
2052
|
onSelect: () => onSelectConversation(conv.id),
|
|
@@ -1620,7 +2054,7 @@ function ChatSidebar({
|
|
|
1620
2054
|
}, conv.id))
|
|
1621
2055
|
})
|
|
1622
2056
|
}),
|
|
1623
|
-
selectedConversation && onUpdateConversation && /* @__PURE__ */
|
|
2057
|
+
selectedConversation && onUpdateConversation && /* @__PURE__ */ jsx12(ConversationMeta, {
|
|
1624
2058
|
conversation: selectedConversation,
|
|
1625
2059
|
onUpdate: onUpdateConversation
|
|
1626
2060
|
})
|
|
@@ -1631,13 +2065,13 @@ function ConversationMeta({
|
|
|
1631
2065
|
conversation,
|
|
1632
2066
|
onUpdate
|
|
1633
2067
|
}) {
|
|
1634
|
-
const [projectName, setProjectName] =
|
|
1635
|
-
const [tagsStr, setTagsStr] =
|
|
1636
|
-
|
|
2068
|
+
const [projectName, setProjectName] = React12.useState(conversation.projectName ?? "");
|
|
2069
|
+
const [tagsStr, setTagsStr] = React12.useState(conversation.tags?.join(", ") ?? "");
|
|
2070
|
+
React12.useEffect(() => {
|
|
1637
2071
|
setProjectName(conversation.projectName ?? "");
|
|
1638
2072
|
setTagsStr(conversation.tags?.join(", ") ?? "");
|
|
1639
2073
|
}, [conversation.id, conversation.projectName, conversation.tags]);
|
|
1640
|
-
const handleBlur =
|
|
2074
|
+
const handleBlur = React12.useCallback(() => {
|
|
1641
2075
|
const tags = tagsStr.split(",").map((t) => t.trim()).filter(Boolean);
|
|
1642
2076
|
if (projectName !== (conversation.projectName ?? "") || JSON.stringify(tags) !== JSON.stringify(conversation.tags ?? [])) {
|
|
1643
2077
|
onUpdate(conversation.id, {
|
|
@@ -1654,14 +2088,14 @@ function ConversationMeta({
|
|
|
1654
2088
|
tagsStr,
|
|
1655
2089
|
onUpdate
|
|
1656
2090
|
]);
|
|
1657
|
-
return /* @__PURE__ */
|
|
2091
|
+
return /* @__PURE__ */ jsxs11("div", {
|
|
1658
2092
|
className: "border-border shrink-0 border-t p-2",
|
|
1659
2093
|
children: [
|
|
1660
|
-
/* @__PURE__ */
|
|
2094
|
+
/* @__PURE__ */ jsx12("p", {
|
|
1661
2095
|
className: "text-muted-foreground mb-1 text-xs font-medium",
|
|
1662
2096
|
children: "Project"
|
|
1663
2097
|
}),
|
|
1664
|
-
/* @__PURE__ */
|
|
2098
|
+
/* @__PURE__ */ jsx12("input", {
|
|
1665
2099
|
type: "text",
|
|
1666
2100
|
value: projectName,
|
|
1667
2101
|
onChange: (e) => setProjectName(e.target.value),
|
|
@@ -1669,11 +2103,11 @@ function ConversationMeta({
|
|
|
1669
2103
|
placeholder: "Project name",
|
|
1670
2104
|
className: "border-input bg-background mb-2 w-full rounded px-2 py-1 text-xs"
|
|
1671
2105
|
}),
|
|
1672
|
-
/* @__PURE__ */
|
|
2106
|
+
/* @__PURE__ */ jsx12("p", {
|
|
1673
2107
|
className: "text-muted-foreground mb-1 text-xs font-medium",
|
|
1674
2108
|
children: "Tags"
|
|
1675
2109
|
}),
|
|
1676
|
-
/* @__PURE__ */
|
|
2110
|
+
/* @__PURE__ */ jsx12("input", {
|
|
1677
2111
|
type: "text",
|
|
1678
2112
|
value: tagsStr,
|
|
1679
2113
|
onChange: (e) => setTagsStr(e.target.value),
|
|
@@ -1685,10 +2119,10 @@ function ConversationMeta({
|
|
|
1685
2119
|
});
|
|
1686
2120
|
}
|
|
1687
2121
|
// src/presentation/components/ChatWithSidebar.tsx
|
|
1688
|
-
import * as
|
|
2122
|
+
import * as React14 from "react";
|
|
1689
2123
|
|
|
1690
2124
|
// src/presentation/hooks/useChat.tsx
|
|
1691
|
-
import * as
|
|
2125
|
+
import * as React13 from "react";
|
|
1692
2126
|
import { tool as tool4 } from "ai";
|
|
1693
2127
|
import { z as z4 } from "zod";
|
|
1694
2128
|
|
|
@@ -2762,16 +3196,16 @@ function useChat(options = {}) {
|
|
|
2762
3196
|
mcpServers,
|
|
2763
3197
|
agentMode
|
|
2764
3198
|
} = options;
|
|
2765
|
-
const [messages, setMessages] =
|
|
2766
|
-
const [mcpTools, setMcpTools] =
|
|
2767
|
-
const mcpCleanupRef =
|
|
2768
|
-
const [conversation, setConversation] =
|
|
2769
|
-
const [isLoading, setIsLoading] =
|
|
2770
|
-
const [error, setError] =
|
|
2771
|
-
const [conversationId, setConversationId] =
|
|
2772
|
-
const abortControllerRef =
|
|
2773
|
-
const chatServiceRef =
|
|
2774
|
-
|
|
3199
|
+
const [messages, setMessages] = React13.useState([]);
|
|
3200
|
+
const [mcpTools, setMcpTools] = React13.useState(null);
|
|
3201
|
+
const mcpCleanupRef = React13.useRef(null);
|
|
3202
|
+
const [conversation, setConversation] = React13.useState(null);
|
|
3203
|
+
const [isLoading, setIsLoading] = React13.useState(false);
|
|
3204
|
+
const [error, setError] = React13.useState(null);
|
|
3205
|
+
const [conversationId, setConversationId] = React13.useState(initialConversationId ?? null);
|
|
3206
|
+
const abortControllerRef = React13.useRef(null);
|
|
3207
|
+
const chatServiceRef = React13.useRef(null);
|
|
3208
|
+
React13.useEffect(() => {
|
|
2775
3209
|
if (!mcpServers?.length) {
|
|
2776
3210
|
setMcpTools(null);
|
|
2777
3211
|
return;
|
|
@@ -2803,7 +3237,7 @@ function useChat(options = {}) {
|
|
|
2803
3237
|
setMcpTools(null);
|
|
2804
3238
|
};
|
|
2805
3239
|
}, [mcpServers]);
|
|
2806
|
-
|
|
3240
|
+
React13.useEffect(() => {
|
|
2807
3241
|
const chatProvider = createProvider({
|
|
2808
3242
|
provider,
|
|
2809
3243
|
model,
|
|
@@ -2840,7 +3274,7 @@ function useChat(options = {}) {
|
|
|
2840
3274
|
surfacePlanConfig,
|
|
2841
3275
|
mcpTools
|
|
2842
3276
|
]);
|
|
2843
|
-
|
|
3277
|
+
React13.useEffect(() => {
|
|
2844
3278
|
if (!conversationId || !chatServiceRef.current)
|
|
2845
3279
|
return;
|
|
2846
3280
|
const loadConversation = async () => {
|
|
@@ -2854,7 +3288,7 @@ function useChat(options = {}) {
|
|
|
2854
3288
|
};
|
|
2855
3289
|
loadConversation().catch(console.error);
|
|
2856
3290
|
}, [conversationId]);
|
|
2857
|
-
const sendMessage =
|
|
3291
|
+
const sendMessage = React13.useCallback(async (content, attachments, opts) => {
|
|
2858
3292
|
if (agentMode?.agent) {
|
|
2859
3293
|
setIsLoading(true);
|
|
2860
3294
|
setError(null);
|
|
@@ -3076,14 +3510,21 @@ function useChat(options = {}) {
|
|
|
3076
3510
|
agentMode,
|
|
3077
3511
|
store
|
|
3078
3512
|
]);
|
|
3079
|
-
const clearConversation =
|
|
3513
|
+
const clearConversation = React13.useCallback(() => {
|
|
3080
3514
|
setMessages([]);
|
|
3081
3515
|
setConversation(null);
|
|
3082
3516
|
setConversationId(null);
|
|
3083
3517
|
setError(null);
|
|
3084
3518
|
}, []);
|
|
3085
|
-
const regenerate =
|
|
3086
|
-
|
|
3519
|
+
const regenerate = React13.useCallback(async () => {
|
|
3520
|
+
let lastUserMessageIndex = -1;
|
|
3521
|
+
for (let i = messages.length - 1;i >= 0; i--) {
|
|
3522
|
+
const m = messages[i];
|
|
3523
|
+
if (m?.role === "user") {
|
|
3524
|
+
lastUserMessageIndex = i;
|
|
3525
|
+
break;
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3087
3528
|
if (lastUserMessageIndex === -1)
|
|
3088
3529
|
return;
|
|
3089
3530
|
const lastUserMessage = messages[lastUserMessageIndex];
|
|
@@ -3092,12 +3533,12 @@ function useChat(options = {}) {
|
|
|
3092
3533
|
setMessages((prev) => prev.slice(0, lastUserMessageIndex + 1));
|
|
3093
3534
|
await sendMessage(lastUserMessage.content, lastUserMessage.attachments);
|
|
3094
3535
|
}, [messages, sendMessage]);
|
|
3095
|
-
const stop =
|
|
3536
|
+
const stop = React13.useCallback(() => {
|
|
3096
3537
|
abortControllerRef.current?.abort();
|
|
3097
3538
|
setIsLoading(false);
|
|
3098
3539
|
}, []);
|
|
3099
3540
|
const createNewConversation = clearConversation;
|
|
3100
|
-
const editMessage =
|
|
3541
|
+
const editMessage = React13.useCallback(async (messageId, newContent) => {
|
|
3101
3542
|
if (!chatServiceRef.current || !conversationId)
|
|
3102
3543
|
return;
|
|
3103
3544
|
const msg = messages.find((m) => m.id === messageId);
|
|
@@ -3112,7 +3553,7 @@ function useChat(options = {}) {
|
|
|
3112
3553
|
}
|
|
3113
3554
|
await sendMessage(newContent, undefined, { skipUserAppend: true });
|
|
3114
3555
|
}, [conversationId, messages, sendMessage]);
|
|
3115
|
-
const forkConversation =
|
|
3556
|
+
const forkConversation = React13.useCallback(async (upToMessageId) => {
|
|
3116
3557
|
if (!chatServiceRef.current)
|
|
3117
3558
|
return null;
|
|
3118
3559
|
const idToFork = conversationId ?? conversation?.id;
|
|
@@ -3128,7 +3569,7 @@ function useChat(options = {}) {
|
|
|
3128
3569
|
return null;
|
|
3129
3570
|
}
|
|
3130
3571
|
}, [conversationId, conversation]);
|
|
3131
|
-
const updateConversationFn =
|
|
3572
|
+
const updateConversationFn = React13.useCallback(async (updates) => {
|
|
3132
3573
|
if (!chatServiceRef.current || !conversationId)
|
|
3133
3574
|
return null;
|
|
3134
3575
|
const updated = await chatServiceRef.current.updateConversation(conversationId, updates);
|
|
@@ -3136,7 +3577,7 @@ function useChat(options = {}) {
|
|
|
3136
3577
|
setConversation(updated);
|
|
3137
3578
|
return updated;
|
|
3138
3579
|
}, [conversationId]);
|
|
3139
|
-
const addToolApprovalResponse =
|
|
3580
|
+
const addToolApprovalResponse = React13.useCallback((_toolCallId, _result) => {
|
|
3140
3581
|
throw new Error(`addToolApprovalResponse: Tool approval requires server route with toUIMessageStreamResponse. ` + `Use createChatRoute and @ai-sdk/react useChat for tools with requireApproval.`);
|
|
3141
3582
|
}, []);
|
|
3142
3583
|
const hasApprovalTools = toolsDefs?.some((t) => t.requireApproval) ?? false;
|
|
@@ -3387,7 +3828,7 @@ function createLocalStorageConversationStore(storageKey) {
|
|
|
3387
3828
|
}
|
|
3388
3829
|
|
|
3389
3830
|
// src/presentation/components/ChatWithSidebar.tsx
|
|
3390
|
-
import { jsx as
|
|
3831
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3391
3832
|
"use client";
|
|
3392
3833
|
var defaultStore = createLocalStorageConversationStore();
|
|
3393
3834
|
function ChatWithSidebar({
|
|
@@ -3398,10 +3839,15 @@ function ChatWithSidebar({
|
|
|
3398
3839
|
thinkingLevel: initialThinkingLevel = "thinking",
|
|
3399
3840
|
presentationRenderer,
|
|
3400
3841
|
formRenderer,
|
|
3842
|
+
dataViewRenderer,
|
|
3843
|
+
components,
|
|
3844
|
+
suggestions,
|
|
3845
|
+
suggestionComponents,
|
|
3846
|
+
showSuggestionsWhenEmpty = false,
|
|
3401
3847
|
...useChatOptions
|
|
3402
3848
|
}) {
|
|
3403
3849
|
const effectiveStore = store;
|
|
3404
|
-
const [thinkingLevel, setThinkingLevel] =
|
|
3850
|
+
const [thinkingLevel, setThinkingLevel] = React14.useState(initialThinkingLevel);
|
|
3405
3851
|
const chat = useChat({
|
|
3406
3852
|
...useChatOptions,
|
|
3407
3853
|
store: effectiveStore,
|
|
@@ -3419,13 +3865,16 @@ function ChatWithSidebar({
|
|
|
3419
3865
|
updateConversation
|
|
3420
3866
|
} = chat;
|
|
3421
3867
|
const selectedConversationId = conversation?.id ?? null;
|
|
3422
|
-
const handleSelectConversation =
|
|
3868
|
+
const handleSelectConversation = React14.useCallback((id) => {
|
|
3423
3869
|
setConversationId(id);
|
|
3424
3870
|
}, [setConversationId]);
|
|
3425
|
-
|
|
3871
|
+
const handleSuggestionClick = React14.useCallback((suggestion) => {
|
|
3872
|
+
sendMessage(suggestion);
|
|
3873
|
+
}, [sendMessage]);
|
|
3874
|
+
return /* @__PURE__ */ jsxs12("div", {
|
|
3426
3875
|
className: className ?? "flex h-full w-full",
|
|
3427
3876
|
children: [
|
|
3428
|
-
/* @__PURE__ */
|
|
3877
|
+
/* @__PURE__ */ jsx13(ChatSidebar, {
|
|
3429
3878
|
store: effectiveStore,
|
|
3430
3879
|
selectedConversationId,
|
|
3431
3880
|
onSelectConversation: handleSelectConversation,
|
|
@@ -3439,9 +3888,9 @@ function ChatWithSidebar({
|
|
|
3439
3888
|
}
|
|
3440
3889
|
} : undefined
|
|
3441
3890
|
}),
|
|
3442
|
-
/* @__PURE__ */
|
|
3891
|
+
/* @__PURE__ */ jsx13("div", {
|
|
3443
3892
|
className: "flex min-w-0 flex-1 flex-col",
|
|
3444
|
-
children: /* @__PURE__ */
|
|
3893
|
+
children: /* @__PURE__ */ jsx13(ChatWithExport, {
|
|
3445
3894
|
messages,
|
|
3446
3895
|
conversation,
|
|
3447
3896
|
onCreateNew: createNewConversation,
|
|
@@ -3451,7 +3900,13 @@ function ChatWithSidebar({
|
|
|
3451
3900
|
onThinkingLevelChange: setThinkingLevel,
|
|
3452
3901
|
presentationRenderer,
|
|
3453
3902
|
formRenderer,
|
|
3454
|
-
|
|
3903
|
+
dataViewRenderer,
|
|
3904
|
+
components,
|
|
3905
|
+
suggestions,
|
|
3906
|
+
onSuggestionClick: handleSuggestionClick,
|
|
3907
|
+
suggestionComponents,
|
|
3908
|
+
showSuggestionsWhenEmpty,
|
|
3909
|
+
children: /* @__PURE__ */ jsx13(ChatInput, {
|
|
3455
3910
|
onSend: (content, att) => sendMessage(content, att),
|
|
3456
3911
|
disabled: isLoading,
|
|
3457
3912
|
isLoading
|
|
@@ -3462,9 +3917,9 @@ function ChatWithSidebar({
|
|
|
3462
3917
|
});
|
|
3463
3918
|
}
|
|
3464
3919
|
// src/presentation/components/ModelPicker.tsx
|
|
3465
|
-
import * as
|
|
3466
|
-
import { cn as
|
|
3467
|
-
import { Button as
|
|
3920
|
+
import * as React15 from "react";
|
|
3921
|
+
import { cn as cn10 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
3922
|
+
import { Button as Button7 } from "@contractspec/lib.design-system";
|
|
3468
3923
|
import {
|
|
3469
3924
|
Select as Select2,
|
|
3470
3925
|
SelectContent as SelectContent2,
|
|
@@ -3478,22 +3933,22 @@ import { Bot as Bot2, Cloud, Cpu, Sparkles } from "lucide-react";
|
|
|
3478
3933
|
import {
|
|
3479
3934
|
getModelsForProvider
|
|
3480
3935
|
} from "@contractspec/lib.ai-providers";
|
|
3481
|
-
import { jsx as
|
|
3936
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
3482
3937
|
"use client";
|
|
3483
3938
|
var PROVIDER_ICONS = {
|
|
3484
|
-
ollama: /* @__PURE__ */
|
|
3939
|
+
ollama: /* @__PURE__ */ jsx14(Cpu, {
|
|
3485
3940
|
className: "h-4 w-4"
|
|
3486
3941
|
}),
|
|
3487
|
-
openai: /* @__PURE__ */
|
|
3942
|
+
openai: /* @__PURE__ */ jsx14(Bot2, {
|
|
3488
3943
|
className: "h-4 w-4"
|
|
3489
3944
|
}),
|
|
3490
|
-
anthropic: /* @__PURE__ */
|
|
3945
|
+
anthropic: /* @__PURE__ */ jsx14(Sparkles, {
|
|
3491
3946
|
className: "h-4 w-4"
|
|
3492
3947
|
}),
|
|
3493
|
-
mistral: /* @__PURE__ */
|
|
3948
|
+
mistral: /* @__PURE__ */ jsx14(Cloud, {
|
|
3494
3949
|
className: "h-4 w-4"
|
|
3495
3950
|
}),
|
|
3496
|
-
gemini: /* @__PURE__ */
|
|
3951
|
+
gemini: /* @__PURE__ */ jsx14(Sparkles, {
|
|
3497
3952
|
className: "h-4 w-4"
|
|
3498
3953
|
})
|
|
3499
3954
|
};
|
|
@@ -3525,7 +3980,7 @@ function ModelPicker({
|
|
|
3525
3980
|
];
|
|
3526
3981
|
const models = getModelsForProvider(value.provider);
|
|
3527
3982
|
const selectedModel = models.find((m) => m.id === value.model);
|
|
3528
|
-
const handleProviderChange =
|
|
3983
|
+
const handleProviderChange = React15.useCallback((providerName) => {
|
|
3529
3984
|
const provider = providerName;
|
|
3530
3985
|
const providerInfo = providers.find((p) => p.provider === provider);
|
|
3531
3986
|
const providerModels = getModelsForProvider(provider);
|
|
@@ -3536,33 +3991,33 @@ function ModelPicker({
|
|
|
3536
3991
|
mode: providerInfo?.mode ?? "byok"
|
|
3537
3992
|
});
|
|
3538
3993
|
}, [onChange, providers]);
|
|
3539
|
-
const handleModelChange =
|
|
3994
|
+
const handleModelChange = React15.useCallback((modelId) => {
|
|
3540
3995
|
onChange({
|
|
3541
3996
|
...value,
|
|
3542
3997
|
model: modelId
|
|
3543
3998
|
});
|
|
3544
3999
|
}, [onChange, value]);
|
|
3545
4000
|
if (compact) {
|
|
3546
|
-
return /* @__PURE__ */
|
|
3547
|
-
className:
|
|
4001
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
4002
|
+
className: cn10("flex items-center gap-2", className),
|
|
3548
4003
|
children: [
|
|
3549
|
-
/* @__PURE__ */
|
|
4004
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
3550
4005
|
value: value.provider,
|
|
3551
4006
|
onValueChange: handleProviderChange,
|
|
3552
4007
|
children: [
|
|
3553
|
-
/* @__PURE__ */
|
|
4008
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
3554
4009
|
className: "w-[140px]",
|
|
3555
|
-
children: /* @__PURE__ */
|
|
4010
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {})
|
|
3556
4011
|
}),
|
|
3557
|
-
/* @__PURE__ */
|
|
3558
|
-
children: providers.map((p) => /* @__PURE__ */
|
|
4012
|
+
/* @__PURE__ */ jsx14(SelectContent2, {
|
|
4013
|
+
children: providers.map((p) => /* @__PURE__ */ jsx14(SelectItem2, {
|
|
3559
4014
|
value: p.provider,
|
|
3560
4015
|
disabled: !p.available,
|
|
3561
|
-
children: /* @__PURE__ */
|
|
4016
|
+
children: /* @__PURE__ */ jsxs13("div", {
|
|
3562
4017
|
className: "flex items-center gap-2",
|
|
3563
4018
|
children: [
|
|
3564
4019
|
PROVIDER_ICONS[p.provider],
|
|
3565
|
-
/* @__PURE__ */
|
|
4020
|
+
/* @__PURE__ */ jsx14("span", {
|
|
3566
4021
|
children: PROVIDER_NAMES[p.provider]
|
|
3567
4022
|
})
|
|
3568
4023
|
]
|
|
@@ -3571,16 +4026,16 @@ function ModelPicker({
|
|
|
3571
4026
|
})
|
|
3572
4027
|
]
|
|
3573
4028
|
}),
|
|
3574
|
-
/* @__PURE__ */
|
|
4029
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
3575
4030
|
value: value.model,
|
|
3576
4031
|
onValueChange: handleModelChange,
|
|
3577
4032
|
children: [
|
|
3578
|
-
/* @__PURE__ */
|
|
4033
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
3579
4034
|
className: "w-[160px]",
|
|
3580
|
-
children: /* @__PURE__ */
|
|
4035
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {})
|
|
3581
4036
|
}),
|
|
3582
|
-
/* @__PURE__ */
|
|
3583
|
-
children: models.map((m) => /* @__PURE__ */
|
|
4037
|
+
/* @__PURE__ */ jsx14(SelectContent2, {
|
|
4038
|
+
children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
|
|
3584
4039
|
value: m.id,
|
|
3585
4040
|
children: m.name
|
|
3586
4041
|
}, m.id))
|
|
@@ -3590,32 +4045,32 @@ function ModelPicker({
|
|
|
3590
4045
|
]
|
|
3591
4046
|
});
|
|
3592
4047
|
}
|
|
3593
|
-
return /* @__PURE__ */
|
|
3594
|
-
className:
|
|
4048
|
+
return /* @__PURE__ */ jsxs13("div", {
|
|
4049
|
+
className: cn10("flex flex-col gap-3", className),
|
|
3595
4050
|
children: [
|
|
3596
|
-
/* @__PURE__ */
|
|
4051
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3597
4052
|
className: "flex flex-col gap-1.5",
|
|
3598
4053
|
children: [
|
|
3599
|
-
/* @__PURE__ */
|
|
4054
|
+
/* @__PURE__ */ jsx14(Label2, {
|
|
3600
4055
|
htmlFor: "provider-selection",
|
|
3601
4056
|
className: "text-sm font-medium",
|
|
3602
4057
|
children: "Provider"
|
|
3603
4058
|
}),
|
|
3604
|
-
/* @__PURE__ */
|
|
4059
|
+
/* @__PURE__ */ jsx14("div", {
|
|
3605
4060
|
className: "flex flex-wrap gap-2",
|
|
3606
4061
|
id: "provider-selection",
|
|
3607
|
-
children: providers.map((p) => /* @__PURE__ */
|
|
4062
|
+
children: providers.map((p) => /* @__PURE__ */ jsxs13(Button7, {
|
|
3608
4063
|
variant: value.provider === p.provider ? "default" : "outline",
|
|
3609
4064
|
size: "sm",
|
|
3610
4065
|
onPress: () => p.available && handleProviderChange(p.provider),
|
|
3611
4066
|
disabled: !p.available,
|
|
3612
|
-
className:
|
|
4067
|
+
className: cn10(!p.available && "opacity-50"),
|
|
3613
4068
|
children: [
|
|
3614
4069
|
PROVIDER_ICONS[p.provider],
|
|
3615
|
-
/* @__PURE__ */
|
|
4070
|
+
/* @__PURE__ */ jsx14("span", {
|
|
3616
4071
|
children: PROVIDER_NAMES[p.provider]
|
|
3617
4072
|
}),
|
|
3618
|
-
/* @__PURE__ */
|
|
4073
|
+
/* @__PURE__ */ jsx14(Badge, {
|
|
3619
4074
|
variant: MODE_BADGES[p.mode].variant,
|
|
3620
4075
|
className: "ml-1",
|
|
3621
4076
|
children: MODE_BADGES[p.mode].label
|
|
@@ -3625,46 +4080,46 @@ function ModelPicker({
|
|
|
3625
4080
|
})
|
|
3626
4081
|
]
|
|
3627
4082
|
}),
|
|
3628
|
-
/* @__PURE__ */
|
|
4083
|
+
/* @__PURE__ */ jsxs13("div", {
|
|
3629
4084
|
className: "flex flex-col gap-1.5",
|
|
3630
4085
|
children: [
|
|
3631
|
-
/* @__PURE__ */
|
|
4086
|
+
/* @__PURE__ */ jsx14(Label2, {
|
|
3632
4087
|
htmlFor: "model-picker",
|
|
3633
4088
|
className: "text-sm font-medium",
|
|
3634
4089
|
children: "Model"
|
|
3635
4090
|
}),
|
|
3636
|
-
/* @__PURE__ */
|
|
4091
|
+
/* @__PURE__ */ jsxs13(Select2, {
|
|
3637
4092
|
name: "model-picker",
|
|
3638
4093
|
value: value.model,
|
|
3639
4094
|
onValueChange: handleModelChange,
|
|
3640
4095
|
children: [
|
|
3641
|
-
/* @__PURE__ */
|
|
3642
|
-
children: /* @__PURE__ */
|
|
4096
|
+
/* @__PURE__ */ jsx14(SelectTrigger2, {
|
|
4097
|
+
children: /* @__PURE__ */ jsx14(SelectValue2, {
|
|
3643
4098
|
placeholder: "Select a model"
|
|
3644
4099
|
})
|
|
3645
4100
|
}),
|
|
3646
|
-
/* @__PURE__ */
|
|
3647
|
-
children: models.map((m) => /* @__PURE__ */
|
|
4101
|
+
/* @__PURE__ */ jsx14(SelectContent2, {
|
|
4102
|
+
children: models.map((m) => /* @__PURE__ */ jsx14(SelectItem2, {
|
|
3648
4103
|
value: m.id,
|
|
3649
|
-
children: /* @__PURE__ */
|
|
4104
|
+
children: /* @__PURE__ */ jsxs13("div", {
|
|
3650
4105
|
className: "flex items-center gap-2",
|
|
3651
4106
|
children: [
|
|
3652
|
-
/* @__PURE__ */
|
|
4107
|
+
/* @__PURE__ */ jsx14("span", {
|
|
3653
4108
|
children: m.name
|
|
3654
4109
|
}),
|
|
3655
|
-
/* @__PURE__ */
|
|
4110
|
+
/* @__PURE__ */ jsxs13("span", {
|
|
3656
4111
|
className: "text-muted-foreground text-xs",
|
|
3657
4112
|
children: [
|
|
3658
4113
|
Math.round(m.contextWindow / 1000),
|
|
3659
4114
|
"K"
|
|
3660
4115
|
]
|
|
3661
4116
|
}),
|
|
3662
|
-
m.capabilities.vision && /* @__PURE__ */
|
|
4117
|
+
m.capabilities.vision && /* @__PURE__ */ jsx14(Badge, {
|
|
3663
4118
|
variant: "outline",
|
|
3664
4119
|
className: "text-xs",
|
|
3665
4120
|
children: "Vision"
|
|
3666
4121
|
}),
|
|
3667
|
-
m.capabilities.reasoning && /* @__PURE__ */
|
|
4122
|
+
m.capabilities.reasoning && /* @__PURE__ */ jsx14(Badge, {
|
|
3668
4123
|
variant: "outline",
|
|
3669
4124
|
className: "text-xs",
|
|
3670
4125
|
children: "Reasoning"
|
|
@@ -3677,23 +4132,23 @@ function ModelPicker({
|
|
|
3677
4132
|
})
|
|
3678
4133
|
]
|
|
3679
4134
|
}),
|
|
3680
|
-
selectedModel && /* @__PURE__ */
|
|
4135
|
+
selectedModel && /* @__PURE__ */ jsxs13("div", {
|
|
3681
4136
|
className: "text-muted-foreground flex flex-wrap gap-2 text-xs",
|
|
3682
4137
|
children: [
|
|
3683
|
-
/* @__PURE__ */
|
|
4138
|
+
/* @__PURE__ */ jsxs13("span", {
|
|
3684
4139
|
children: [
|
|
3685
4140
|
"Context: ",
|
|
3686
4141
|
Math.round(selectedModel.contextWindow / 1000),
|
|
3687
4142
|
"K tokens"
|
|
3688
4143
|
]
|
|
3689
4144
|
}),
|
|
3690
|
-
selectedModel.capabilities.vision && /* @__PURE__ */
|
|
4145
|
+
selectedModel.capabilities.vision && /* @__PURE__ */ jsx14("span", {
|
|
3691
4146
|
children: "• Vision"
|
|
3692
4147
|
}),
|
|
3693
|
-
selectedModel.capabilities.tools && /* @__PURE__ */
|
|
4148
|
+
selectedModel.capabilities.tools && /* @__PURE__ */ jsx14("span", {
|
|
3694
4149
|
children: "• Tools"
|
|
3695
4150
|
}),
|
|
3696
|
-
selectedModel.capabilities.reasoning && /* @__PURE__ */
|
|
4151
|
+
selectedModel.capabilities.reasoning && /* @__PURE__ */ jsx14("span", {
|
|
3697
4152
|
children: "• Reasoning"
|
|
3698
4153
|
})
|
|
3699
4154
|
]
|
|
@@ -3702,7 +4157,7 @@ function ModelPicker({
|
|
|
3702
4157
|
});
|
|
3703
4158
|
}
|
|
3704
4159
|
// src/presentation/components/ContextIndicator.tsx
|
|
3705
|
-
import { cn as
|
|
4160
|
+
import { cn as cn11 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
3706
4161
|
import { Badge as Badge2 } from "@contractspec/lib.ui-kit-web/ui/badge";
|
|
3707
4162
|
import {
|
|
3708
4163
|
Tooltip,
|
|
@@ -3711,7 +4166,7 @@ import {
|
|
|
3711
4166
|
TooltipTrigger
|
|
3712
4167
|
} from "@contractspec/lib.ui-kit-web/ui/tooltip";
|
|
3713
4168
|
import { FolderOpen, FileCode, Zap, Info } from "lucide-react";
|
|
3714
|
-
import { jsx as
|
|
4169
|
+
import { jsx as jsx15, jsxs as jsxs14, Fragment as Fragment7 } from "react/jsx-runtime";
|
|
3715
4170
|
"use client";
|
|
3716
4171
|
function ContextIndicator({
|
|
3717
4172
|
summary,
|
|
@@ -3720,51 +4175,51 @@ function ContextIndicator({
|
|
|
3720
4175
|
showDetails = true
|
|
3721
4176
|
}) {
|
|
3722
4177
|
if (!summary && !active) {
|
|
3723
|
-
return /* @__PURE__ */
|
|
3724
|
-
className:
|
|
4178
|
+
return /* @__PURE__ */ jsxs14("div", {
|
|
4179
|
+
className: cn11("flex items-center gap-1.5 text-sm", "text-muted-foreground", className),
|
|
3725
4180
|
children: [
|
|
3726
|
-
/* @__PURE__ */
|
|
4181
|
+
/* @__PURE__ */ jsx15(Info, {
|
|
3727
4182
|
className: "h-4 w-4"
|
|
3728
4183
|
}),
|
|
3729
|
-
/* @__PURE__ */
|
|
4184
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3730
4185
|
children: "No workspace context"
|
|
3731
4186
|
})
|
|
3732
4187
|
]
|
|
3733
4188
|
});
|
|
3734
4189
|
}
|
|
3735
|
-
const content = /* @__PURE__ */
|
|
3736
|
-
className:
|
|
4190
|
+
const content = /* @__PURE__ */ jsxs14("div", {
|
|
4191
|
+
className: cn11("flex items-center gap-2", active ? "text-foreground" : "text-muted-foreground", className),
|
|
3737
4192
|
children: [
|
|
3738
|
-
/* @__PURE__ */
|
|
4193
|
+
/* @__PURE__ */ jsxs14(Badge2, {
|
|
3739
4194
|
variant: active ? "default" : "secondary",
|
|
3740
4195
|
className: "flex items-center gap-1",
|
|
3741
4196
|
children: [
|
|
3742
|
-
/* @__PURE__ */
|
|
4197
|
+
/* @__PURE__ */ jsx15(Zap, {
|
|
3743
4198
|
className: "h-3 w-3"
|
|
3744
4199
|
}),
|
|
3745
4200
|
"Context"
|
|
3746
4201
|
]
|
|
3747
4202
|
}),
|
|
3748
|
-
summary && showDetails && /* @__PURE__ */
|
|
4203
|
+
summary && showDetails && /* @__PURE__ */ jsxs14(Fragment7, {
|
|
3749
4204
|
children: [
|
|
3750
|
-
/* @__PURE__ */
|
|
4205
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
3751
4206
|
className: "flex items-center gap-1 text-xs",
|
|
3752
4207
|
children: [
|
|
3753
|
-
/* @__PURE__ */
|
|
4208
|
+
/* @__PURE__ */ jsx15(FolderOpen, {
|
|
3754
4209
|
className: "h-3.5 w-3.5"
|
|
3755
4210
|
}),
|
|
3756
|
-
/* @__PURE__ */
|
|
4211
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3757
4212
|
children: summary.name
|
|
3758
4213
|
})
|
|
3759
4214
|
]
|
|
3760
4215
|
}),
|
|
3761
|
-
/* @__PURE__ */
|
|
4216
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
3762
4217
|
className: "flex items-center gap-1 text-xs",
|
|
3763
4218
|
children: [
|
|
3764
|
-
/* @__PURE__ */
|
|
4219
|
+
/* @__PURE__ */ jsx15(FileCode, {
|
|
3765
4220
|
className: "h-3.5 w-3.5"
|
|
3766
4221
|
}),
|
|
3767
|
-
/* @__PURE__ */
|
|
4222
|
+
/* @__PURE__ */ jsxs14("span", {
|
|
3768
4223
|
children: [
|
|
3769
4224
|
summary.specs.total,
|
|
3770
4225
|
" specs"
|
|
@@ -3779,77 +4234,77 @@ function ContextIndicator({
|
|
|
3779
4234
|
if (!summary) {
|
|
3780
4235
|
return content;
|
|
3781
4236
|
}
|
|
3782
|
-
return /* @__PURE__ */
|
|
3783
|
-
children: /* @__PURE__ */
|
|
4237
|
+
return /* @__PURE__ */ jsx15(TooltipProvider, {
|
|
4238
|
+
children: /* @__PURE__ */ jsxs14(Tooltip, {
|
|
3784
4239
|
children: [
|
|
3785
|
-
/* @__PURE__ */
|
|
4240
|
+
/* @__PURE__ */ jsx15(TooltipTrigger, {
|
|
3786
4241
|
asChild: true,
|
|
3787
4242
|
children: content
|
|
3788
4243
|
}),
|
|
3789
|
-
/* @__PURE__ */
|
|
4244
|
+
/* @__PURE__ */ jsx15(TooltipContent, {
|
|
3790
4245
|
side: "bottom",
|
|
3791
4246
|
className: "max-w-[300px]",
|
|
3792
|
-
children: /* @__PURE__ */
|
|
4247
|
+
children: /* @__PURE__ */ jsxs14("div", {
|
|
3793
4248
|
className: "flex flex-col gap-2 text-sm",
|
|
3794
4249
|
children: [
|
|
3795
|
-
/* @__PURE__ */
|
|
4250
|
+
/* @__PURE__ */ jsx15("div", {
|
|
3796
4251
|
className: "font-medium",
|
|
3797
4252
|
children: summary.name
|
|
3798
4253
|
}),
|
|
3799
|
-
/* @__PURE__ */
|
|
4254
|
+
/* @__PURE__ */ jsx15("div", {
|
|
3800
4255
|
className: "text-muted-foreground text-xs",
|
|
3801
4256
|
children: summary.path
|
|
3802
4257
|
}),
|
|
3803
|
-
/* @__PURE__ */
|
|
4258
|
+
/* @__PURE__ */ jsx15("div", {
|
|
3804
4259
|
className: "border-t pt-2",
|
|
3805
|
-
children: /* @__PURE__ */
|
|
4260
|
+
children: /* @__PURE__ */ jsxs14("div", {
|
|
3806
4261
|
className: "grid grid-cols-2 gap-1 text-xs",
|
|
3807
4262
|
children: [
|
|
3808
|
-
/* @__PURE__ */
|
|
4263
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3809
4264
|
children: "Commands:"
|
|
3810
4265
|
}),
|
|
3811
|
-
/* @__PURE__ */
|
|
4266
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3812
4267
|
className: "text-right",
|
|
3813
4268
|
children: summary.specs.commands
|
|
3814
4269
|
}),
|
|
3815
|
-
/* @__PURE__ */
|
|
4270
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3816
4271
|
children: "Queries:"
|
|
3817
4272
|
}),
|
|
3818
|
-
/* @__PURE__ */
|
|
4273
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3819
4274
|
className: "text-right",
|
|
3820
4275
|
children: summary.specs.queries
|
|
3821
4276
|
}),
|
|
3822
|
-
/* @__PURE__ */
|
|
4277
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3823
4278
|
children: "Events:"
|
|
3824
4279
|
}),
|
|
3825
|
-
/* @__PURE__ */
|
|
4280
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3826
4281
|
className: "text-right",
|
|
3827
4282
|
children: summary.specs.events
|
|
3828
4283
|
}),
|
|
3829
|
-
/* @__PURE__ */
|
|
4284
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3830
4285
|
children: "Presentations:"
|
|
3831
4286
|
}),
|
|
3832
|
-
/* @__PURE__ */
|
|
4287
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3833
4288
|
className: "text-right",
|
|
3834
4289
|
children: summary.specs.presentations
|
|
3835
4290
|
})
|
|
3836
4291
|
]
|
|
3837
4292
|
})
|
|
3838
4293
|
}),
|
|
3839
|
-
/* @__PURE__ */
|
|
4294
|
+
/* @__PURE__ */ jsxs14("div", {
|
|
3840
4295
|
className: "border-t pt-2 text-xs",
|
|
3841
4296
|
children: [
|
|
3842
|
-
/* @__PURE__ */
|
|
4297
|
+
/* @__PURE__ */ jsxs14("span", {
|
|
3843
4298
|
children: [
|
|
3844
4299
|
summary.files.total,
|
|
3845
4300
|
" files"
|
|
3846
4301
|
]
|
|
3847
4302
|
}),
|
|
3848
|
-
/* @__PURE__ */
|
|
4303
|
+
/* @__PURE__ */ jsx15("span", {
|
|
3849
4304
|
className: "mx-1",
|
|
3850
4305
|
children: "•"
|
|
3851
4306
|
}),
|
|
3852
|
-
/* @__PURE__ */
|
|
4307
|
+
/* @__PURE__ */ jsxs14("span", {
|
|
3853
4308
|
children: [
|
|
3854
4309
|
summary.files.specFiles,
|
|
3855
4310
|
" spec files"
|
|
@@ -3864,11 +4319,108 @@ function ContextIndicator({
|
|
|
3864
4319
|
})
|
|
3865
4320
|
});
|
|
3866
4321
|
}
|
|
4322
|
+
// src/presentation/components/ChainOfThought.tsx
|
|
4323
|
+
import {
|
|
4324
|
+
Collapsible as Collapsible3,
|
|
4325
|
+
CollapsibleContent as CollapsibleContent3,
|
|
4326
|
+
CollapsibleTrigger as CollapsibleTrigger3
|
|
4327
|
+
} from "@contractspec/lib.ui-kit-web/ui/collapsible";
|
|
4328
|
+
import { cn as cn12 } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
4329
|
+
import { ChevronDown, Dot } from "lucide-react";
|
|
4330
|
+
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4331
|
+
"use client";
|
|
4332
|
+
function ChainOfThought({
|
|
4333
|
+
children,
|
|
4334
|
+
open,
|
|
4335
|
+
defaultOpen = false,
|
|
4336
|
+
onOpenChange,
|
|
4337
|
+
className
|
|
4338
|
+
}) {
|
|
4339
|
+
return /* @__PURE__ */ jsx16(Collapsible3, {
|
|
4340
|
+
open,
|
|
4341
|
+
defaultOpen,
|
|
4342
|
+
onOpenChange,
|
|
4343
|
+
className: cn12("group/cot mt-2", className),
|
|
4344
|
+
children
|
|
4345
|
+
});
|
|
4346
|
+
}
|
|
4347
|
+
function ChainOfThoughtHeader({
|
|
4348
|
+
children = "Chain of Thought",
|
|
4349
|
+
className
|
|
4350
|
+
}) {
|
|
4351
|
+
return /* @__PURE__ */ jsxs15(CollapsibleTrigger3, {
|
|
4352
|
+
className: cn12("hover:bg-muted flex w-full cursor-pointer items-center gap-2 rounded-md px-3 py-2 text-sm font-medium transition-colors", className),
|
|
4353
|
+
children: [
|
|
4354
|
+
/* @__PURE__ */ jsx16(ChevronDown, {
|
|
4355
|
+
className: "text-muted-foreground h-4 w-4 shrink-0 transition-transform group-data-[state=open]/cot:rotate-180"
|
|
4356
|
+
}),
|
|
4357
|
+
children
|
|
4358
|
+
]
|
|
4359
|
+
});
|
|
4360
|
+
}
|
|
4361
|
+
function ChainOfThoughtStep({
|
|
4362
|
+
label,
|
|
4363
|
+
description,
|
|
4364
|
+
status = "complete",
|
|
4365
|
+
icon: Icon = Dot,
|
|
4366
|
+
children,
|
|
4367
|
+
className
|
|
4368
|
+
}) {
|
|
4369
|
+
return /* @__PURE__ */ jsxs15("div", {
|
|
4370
|
+
className: cn12("border-border flex gap-3 border-b py-2 last:border-b-0", className),
|
|
4371
|
+
children: [
|
|
4372
|
+
/* @__PURE__ */ jsx16("div", {
|
|
4373
|
+
className: cn12("mt-1.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full", status === "complete" && "bg-green-500/20 text-green-700 dark:text-green-400", status === "active" && "bg-blue-500/20 text-blue-700 dark:text-blue-400", status === "pending" && "bg-muted text-muted-foreground"),
|
|
4374
|
+
children: /* @__PURE__ */ jsx16(Icon, {
|
|
4375
|
+
className: "h-3 w-3"
|
|
4376
|
+
})
|
|
4377
|
+
}),
|
|
4378
|
+
/* @__PURE__ */ jsxs15("div", {
|
|
4379
|
+
className: "min-w-0 flex-1",
|
|
4380
|
+
children: [
|
|
4381
|
+
/* @__PURE__ */ jsx16("p", {
|
|
4382
|
+
className: "font-medium",
|
|
4383
|
+
children: label
|
|
4384
|
+
}),
|
|
4385
|
+
description && /* @__PURE__ */ jsx16("p", {
|
|
4386
|
+
className: "text-muted-foreground mt-0.5 text-xs",
|
|
4387
|
+
children: description
|
|
4388
|
+
}),
|
|
4389
|
+
children && /* @__PURE__ */ jsx16("div", {
|
|
4390
|
+
className: "mt-2",
|
|
4391
|
+
children
|
|
4392
|
+
})
|
|
4393
|
+
]
|
|
4394
|
+
})
|
|
4395
|
+
]
|
|
4396
|
+
});
|
|
4397
|
+
}
|
|
4398
|
+
function ChainOfThoughtContent({
|
|
4399
|
+
children,
|
|
4400
|
+
className
|
|
4401
|
+
}) {
|
|
4402
|
+
return /* @__PURE__ */ jsx16(CollapsibleContent3, {
|
|
4403
|
+
children: /* @__PURE__ */ jsx16("div", {
|
|
4404
|
+
className: cn12("bg-muted border-border mt-1 rounded-md border px-3 py-2", className),
|
|
4405
|
+
children
|
|
4406
|
+
})
|
|
4407
|
+
});
|
|
4408
|
+
}
|
|
3867
4409
|
export {
|
|
3868
4410
|
isPresentationToolResult,
|
|
3869
4411
|
isFormToolResult,
|
|
4412
|
+
isDataViewToolResult,
|
|
3870
4413
|
ToolResultRenderer,
|
|
3871
4414
|
ThinkingLevelPicker,
|
|
4415
|
+
Suggestions,
|
|
4416
|
+
Suggestion,
|
|
4417
|
+
SourcesTrigger,
|
|
4418
|
+
SourcesContent,
|
|
4419
|
+
Sources,
|
|
4420
|
+
Source,
|
|
4421
|
+
ReasoningTrigger,
|
|
4422
|
+
ReasoningContent,
|
|
4423
|
+
Reasoning,
|
|
3872
4424
|
ModelPicker,
|
|
3873
4425
|
ContextIndicator,
|
|
3874
4426
|
CodePreview,
|
|
@@ -3878,5 +4430,9 @@ export {
|
|
|
3878
4430
|
ChatMessage,
|
|
3879
4431
|
ChatInput,
|
|
3880
4432
|
ChatExportToolbar,
|
|
3881
|
-
ChatContainer
|
|
4433
|
+
ChatContainer,
|
|
4434
|
+
ChainOfThoughtStep,
|
|
4435
|
+
ChainOfThoughtHeader,
|
|
4436
|
+
ChainOfThoughtContent,
|
|
4437
|
+
ChainOfThought
|
|
3882
4438
|
};
|