@firstlovecenter/ai-chat 0.9.2 → 0.9.4
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/CHANGELOG.md +25 -0
- package/dist/drizzle/index.cjs +11 -3
- package/dist/drizzle/index.cjs.map +1 -1
- package/dist/drizzle/index.js +11 -3
- package/dist/drizzle/index.js.map +1 -1
- package/dist/ui/index.cjs +100 -26
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.js +98 -26
- package/dist/ui/index.js.map +1 -1
- package/package.json +12 -12
package/dist/ui/index.js
CHANGED
|
@@ -6,6 +6,8 @@ import Link from 'next/link';
|
|
|
6
6
|
import { PanelLeftClose, Plus, Pencil, Trash2, Menu, Sparkles, Loader2, ChevronDown, Check, ArrowUp, Copy, RotateCcw, ChevronUp } from 'lucide-react';
|
|
7
7
|
import { DropdownMenu as DropdownMenu$1 } from 'radix-ui';
|
|
8
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
import ReactMarkdown from 'react-markdown';
|
|
10
|
+
import remarkGfm from 'remark-gfm';
|
|
9
11
|
import * as RechartsPrimitive from 'recharts';
|
|
10
12
|
import { PieChart, Pie, Cell, Label, BarChart, CartesianGrid, XAxis, YAxis, Bar, LineChart, Line } from 'recharts';
|
|
11
13
|
import { useChat } from '@ai-sdk/react';
|
|
@@ -488,22 +490,54 @@ function isBlockEmpty(b) {
|
|
|
488
490
|
if (b.kind === "table") return !Array.isArray(b.rows) || b.rows.length === 0;
|
|
489
491
|
return false;
|
|
490
492
|
}
|
|
493
|
+
var PROSE_MD_COMPONENTS = {
|
|
494
|
+
p: ({ children }) => /* @__PURE__ */ jsx("p", { className: "mb-2 last:mb-0", children }),
|
|
495
|
+
ul: ({ children }) => /* @__PURE__ */ jsx("ul", { className: "my-1 list-disc pl-6", children }),
|
|
496
|
+
ol: ({ children }) => /* @__PURE__ */ jsx("ol", { className: "my-1 list-decimal pl-6", children }),
|
|
497
|
+
li: ({ children }) => /* @__PURE__ */ jsx("li", { className: "leading-6", children }),
|
|
498
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { className: "font-semibold", children }),
|
|
499
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { className: "italic", children }),
|
|
500
|
+
code: ({ children }) => /* @__PURE__ */ jsx("code", { className: "rounded bg-muted px-1 py-0.5 font-mono text-[0.85em]", children }),
|
|
501
|
+
a: ({ children, href }) => /* @__PURE__ */ jsx(
|
|
502
|
+
"a",
|
|
503
|
+
{
|
|
504
|
+
href,
|
|
505
|
+
target: "_blank",
|
|
506
|
+
rel: "noreferrer noopener",
|
|
507
|
+
className: "underline underline-offset-2",
|
|
508
|
+
children
|
|
509
|
+
}
|
|
510
|
+
)
|
|
511
|
+
};
|
|
512
|
+
var INLINE_MD_COMPONENTS = {
|
|
513
|
+
...PROSE_MD_COMPONENTS,
|
|
514
|
+
p: ({ children }) => /* @__PURE__ */ jsx(Fragment, { children })
|
|
515
|
+
};
|
|
516
|
+
function ProseMarkdown({ children }) {
|
|
517
|
+
return /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: PROSE_MD_COMPONENTS, children });
|
|
518
|
+
}
|
|
519
|
+
function InlineMarkdown({ children }) {
|
|
520
|
+
return /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: INLINE_MD_COMPONENTS, children });
|
|
521
|
+
}
|
|
491
522
|
function AnswerBlocks({ blocks }) {
|
|
492
523
|
const visible = blocks.filter((b) => !isBlockEmpty(b));
|
|
493
524
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3", children: visible.map((b, i) => /* @__PURE__ */ jsx(BlockView, { block: b }, i)) });
|
|
494
525
|
}
|
|
495
526
|
function BlockView({ block }) {
|
|
496
527
|
if (block.kind === "paragraph_brief") {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
528
|
+
if (!block.prose) {
|
|
529
|
+
return /* @__PURE__ */ jsxs("p", { className: "text-sm leading-6 text-muted-foreground", children: [
|
|
530
|
+
block.key_facts.join(". "),
|
|
531
|
+
"\u2026"
|
|
532
|
+
] });
|
|
533
|
+
}
|
|
534
|
+
return /* @__PURE__ */ jsx("div", { className: "text-sm leading-6 text-foreground", children: /* @__PURE__ */ jsx(ProseMarkdown, { children: block.prose }) });
|
|
501
535
|
}
|
|
502
536
|
if (block.kind === "list") {
|
|
503
537
|
const Tag = block.style === "numbered" ? "ol" : "ul";
|
|
504
538
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
505
539
|
block.title && /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium", children: block.title }),
|
|
506
|
-
/* @__PURE__ */ jsx(Tag, { className: block.style === "numbered" ? "list-decimal pl-6" : "list-disc pl-6", children: block.items.map((it, i) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-6", children: it }, i)) })
|
|
540
|
+
/* @__PURE__ */ jsx(Tag, { className: block.style === "numbered" ? "list-decimal pl-6" : "list-disc pl-6", children: block.items.map((it, i) => /* @__PURE__ */ jsx("li", { className: "text-sm leading-6", children: /* @__PURE__ */ jsx(InlineMarkdown, { children: it }) }, i)) })
|
|
507
541
|
] });
|
|
508
542
|
}
|
|
509
543
|
if (block.kind === "chart") {
|
|
@@ -520,7 +554,7 @@ function BlockView({ block }) {
|
|
|
520
554
|
}
|
|
521
555
|
if (block.kind === "callout") {
|
|
522
556
|
const cls = block.tone === "warn" ? "border-amber-300 dark:border-amber-800 bg-amber-50 dark:bg-amber-950/30 text-amber-900 dark:text-amber-100" : block.tone === "success" ? "border-emerald-300 dark:border-emerald-800 bg-emerald-50 dark:bg-emerald-950/30 text-emerald-900 dark:text-emerald-100" : "border-sky-300 bg-sky-50 text-sky-900";
|
|
523
|
-
return /* @__PURE__ */ jsx("div", { className: `rounded border p-3 text-sm ${cls}`, children: block.text });
|
|
557
|
+
return /* @__PURE__ */ jsx("div", { className: `rounded border p-3 text-sm leading-6 ${cls}`, children: /* @__PURE__ */ jsx(ProseMarkdown, { children: block.text }) });
|
|
524
558
|
}
|
|
525
559
|
return null;
|
|
526
560
|
}
|
|
@@ -699,6 +733,11 @@ function AiChat({
|
|
|
699
733
|
cancelled = true;
|
|
700
734
|
};
|
|
701
735
|
}, []);
|
|
736
|
+
useEffect(() => {
|
|
737
|
+
if (initialSessionId != null && activeSessionId !== initialSessionId) {
|
|
738
|
+
setActiveSessionId(initialSessionId);
|
|
739
|
+
}
|
|
740
|
+
}, [initialSessionId]);
|
|
702
741
|
useEffect(() => {
|
|
703
742
|
if (initialSessionId == null) {
|
|
704
743
|
setAnswers([]);
|
|
@@ -711,13 +750,18 @@ function AiChat({
|
|
|
711
750
|
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
712
751
|
if (cancelled) return;
|
|
713
752
|
if (!res.ok) {
|
|
753
|
+
console.warn(
|
|
754
|
+
"[AiChat] hydration: GET /api/chat/sessions failed",
|
|
755
|
+
{ id, status: res.status }
|
|
756
|
+
);
|
|
714
757
|
setAnswers([]);
|
|
715
758
|
return;
|
|
716
759
|
}
|
|
717
760
|
const data = await res.json();
|
|
718
761
|
if (cancelled) return;
|
|
719
762
|
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
720
|
-
} catch {
|
|
763
|
+
} catch (err) {
|
|
764
|
+
console.error("[AiChat] hydration threw \u2014 setting empty answers", err);
|
|
721
765
|
if (!cancelled) setAnswers([]);
|
|
722
766
|
} finally {
|
|
723
767
|
if (!cancelled) setLoadingSession(false);
|
|
@@ -1459,6 +1503,15 @@ function updateLast(prev, updater) {
|
|
|
1459
1503
|
next[next.length - 1] = updater(next[next.length - 1]);
|
|
1460
1504
|
return next;
|
|
1461
1505
|
}
|
|
1506
|
+
function coerceJsonField(v) {
|
|
1507
|
+
if (v == null) return null;
|
|
1508
|
+
if (typeof v !== "string") return v;
|
|
1509
|
+
try {
|
|
1510
|
+
return JSON.parse(v);
|
|
1511
|
+
} catch {
|
|
1512
|
+
return null;
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1462
1515
|
function messagesToAnswers(messages) {
|
|
1463
1516
|
const out = [];
|
|
1464
1517
|
let pendingUser = null;
|
|
@@ -1469,19 +1522,24 @@ function messagesToAnswers(messages) {
|
|
|
1469
1522
|
const question = pendingUser ?? "";
|
|
1470
1523
|
pendingUser = null;
|
|
1471
1524
|
const blocks = [];
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1525
|
+
const storedBlocks = coerceJsonField(m.blocks) ?? [];
|
|
1526
|
+
const storedProse = coerceJsonField(m.prose) ?? {};
|
|
1527
|
+
const storedError = coerceJsonField(m.errorJson);
|
|
1528
|
+
if (Array.isArray(storedBlocks)) {
|
|
1529
|
+
storedBlocks.forEach((b, i) => {
|
|
1530
|
+
const sanitised = sanitiseBlock({
|
|
1531
|
+
...b});
|
|
1532
|
+
if (sanitised.kind === "paragraph_brief") {
|
|
1533
|
+
sanitised.prose = storedProse[String(i)] ?? "";
|
|
1534
|
+
}
|
|
1535
|
+
blocks[i] = sanitised;
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1480
1538
|
out.push({
|
|
1481
1539
|
question,
|
|
1482
1540
|
blocks,
|
|
1483
1541
|
done: true,
|
|
1484
|
-
error:
|
|
1542
|
+
error: storedError ?? void 0
|
|
1485
1543
|
});
|
|
1486
1544
|
}
|
|
1487
1545
|
}
|
|
@@ -2377,6 +2435,15 @@ function UserChip2({ text }) {
|
|
|
2377
2435
|
)
|
|
2378
2436
|
] });
|
|
2379
2437
|
}
|
|
2438
|
+
function coerceJsonField2(v) {
|
|
2439
|
+
if (v == null) return null;
|
|
2440
|
+
if (typeof v !== "string") return v;
|
|
2441
|
+
try {
|
|
2442
|
+
return JSON.parse(v);
|
|
2443
|
+
} catch {
|
|
2444
|
+
return null;
|
|
2445
|
+
}
|
|
2446
|
+
}
|
|
2380
2447
|
function storedToUseChat(stored) {
|
|
2381
2448
|
const uiMessages = [];
|
|
2382
2449
|
const blocksMap = {};
|
|
@@ -2392,15 +2459,20 @@ function storedToUseChat(stored) {
|
|
|
2392
2459
|
if (m.role === "assistant") {
|
|
2393
2460
|
const id = `assistant-${m.id}`;
|
|
2394
2461
|
const blocks = [];
|
|
2395
|
-
const
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2462
|
+
const storedBlocks = coerceJsonField2(m.blocks) ?? [];
|
|
2463
|
+
const storedProse = coerceJsonField2(m.prose) ?? {};
|
|
2464
|
+
const storedError = coerceJsonField2(m.errorJson);
|
|
2465
|
+
if (Array.isArray(storedBlocks)) {
|
|
2466
|
+
storedBlocks.forEach((b, i) => {
|
|
2467
|
+
const sanitised = sanitiseBlock({
|
|
2468
|
+
...b});
|
|
2469
|
+
if (sanitised.kind === "paragraph_brief") {
|
|
2470
|
+
sanitised.prose = storedProse[String(i)] ?? "";
|
|
2471
|
+
}
|
|
2472
|
+
blocks[i] = sanitised;
|
|
2473
|
+
});
|
|
2474
|
+
}
|
|
2475
|
+
const joinedProse = Object.values(storedProse).filter(Boolean).join("\n\n");
|
|
2404
2476
|
uiMessages.push({
|
|
2405
2477
|
id,
|
|
2406
2478
|
role: "assistant",
|
|
@@ -2408,7 +2480,7 @@ function storedToUseChat(stored) {
|
|
|
2408
2480
|
});
|
|
2409
2481
|
blocksMap[id] = blocks;
|
|
2410
2482
|
if (joinedProse) proseMap[id] = joinedProse;
|
|
2411
|
-
if (
|
|
2483
|
+
if (storedError) errorsMap[id] = storedError;
|
|
2412
2484
|
}
|
|
2413
2485
|
}
|
|
2414
2486
|
return { uiMessages, blocksMap, proseMap, errorsMap };
|