@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.cjs
CHANGED
|
@@ -7,6 +7,8 @@ var Link = require('next/link');
|
|
|
7
7
|
var lucideReact = require('lucide-react');
|
|
8
8
|
var radixUi = require('radix-ui');
|
|
9
9
|
var jsxRuntime = require('react/jsx-runtime');
|
|
10
|
+
var ReactMarkdown = require('react-markdown');
|
|
11
|
+
var remarkGfm = require('remark-gfm');
|
|
10
12
|
var RechartsPrimitive = require('recharts');
|
|
11
13
|
var react = require('@ai-sdk/react');
|
|
12
14
|
|
|
@@ -32,6 +34,8 @@ function _interopNamespace(e) {
|
|
|
32
34
|
|
|
33
35
|
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
34
36
|
var Link__default = /*#__PURE__*/_interopDefault(Link);
|
|
37
|
+
var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
|
|
38
|
+
var remarkGfm__default = /*#__PURE__*/_interopDefault(remarkGfm);
|
|
35
39
|
var RechartsPrimitive__namespace = /*#__PURE__*/_interopNamespace(RechartsPrimitive);
|
|
36
40
|
|
|
37
41
|
// src/ui/_shared/cn.ts
|
|
@@ -512,22 +516,54 @@ function isBlockEmpty(b) {
|
|
|
512
516
|
if (b.kind === "table") return !Array.isArray(b.rows) || b.rows.length === 0;
|
|
513
517
|
return false;
|
|
514
518
|
}
|
|
519
|
+
var PROSE_MD_COMPONENTS = {
|
|
520
|
+
p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 last:mb-0", children }),
|
|
521
|
+
ul: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "my-1 list-disc pl-6", children }),
|
|
522
|
+
ol: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("ol", { className: "my-1 list-decimal pl-6", children }),
|
|
523
|
+
li: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "leading-6", children }),
|
|
524
|
+
strong: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("strong", { className: "font-semibold", children }),
|
|
525
|
+
em: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("em", { className: "italic", children }),
|
|
526
|
+
code: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx("code", { className: "rounded bg-muted px-1 py-0.5 font-mono text-[0.85em]", children }),
|
|
527
|
+
a: ({ children, href }) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
528
|
+
"a",
|
|
529
|
+
{
|
|
530
|
+
href,
|
|
531
|
+
target: "_blank",
|
|
532
|
+
rel: "noreferrer noopener",
|
|
533
|
+
className: "underline underline-offset-2",
|
|
534
|
+
children
|
|
535
|
+
}
|
|
536
|
+
)
|
|
537
|
+
};
|
|
538
|
+
var INLINE_MD_COMPONENTS = {
|
|
539
|
+
...PROSE_MD_COMPONENTS,
|
|
540
|
+
p: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children })
|
|
541
|
+
};
|
|
542
|
+
function ProseMarkdown({ children }) {
|
|
543
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown__default.default, { remarkPlugins: [remarkGfm__default.default], components: PROSE_MD_COMPONENTS, children });
|
|
544
|
+
}
|
|
545
|
+
function InlineMarkdown({ children }) {
|
|
546
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ReactMarkdown__default.default, { remarkPlugins: [remarkGfm__default.default], components: INLINE_MD_COMPONENTS, children });
|
|
547
|
+
}
|
|
515
548
|
function AnswerBlocks({ blocks }) {
|
|
516
549
|
const visible = blocks.filter((b) => !isBlockEmpty(b));
|
|
517
550
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: visible.map((b, i) => /* @__PURE__ */ jsxRuntime.jsx(BlockView, { block: b }, i)) });
|
|
518
551
|
}
|
|
519
552
|
function BlockView({ block }) {
|
|
520
553
|
if (block.kind === "paragraph_brief") {
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
554
|
+
if (!block.prose) {
|
|
555
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm leading-6 text-muted-foreground", children: [
|
|
556
|
+
block.key_facts.join(". "),
|
|
557
|
+
"\u2026"
|
|
558
|
+
] });
|
|
559
|
+
}
|
|
560
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm leading-6 text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(ProseMarkdown, { children: block.prose }) });
|
|
525
561
|
}
|
|
526
562
|
if (block.kind === "list") {
|
|
527
563
|
const Tag = block.style === "numbered" ? "ol" : "ul";
|
|
528
564
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
529
565
|
block.title && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-1 text-sm font-medium", children: block.title }),
|
|
530
|
-
/* @__PURE__ */ jsxRuntime.jsx(Tag, { className: block.style === "numbered" ? "list-decimal pl-6" : "list-disc pl-6", children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "text-sm leading-6", children: it }, i)) })
|
|
566
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tag, { className: block.style === "numbered" ? "list-decimal pl-6" : "list-disc pl-6", children: block.items.map((it, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { className: "text-sm leading-6", children: /* @__PURE__ */ jsxRuntime.jsx(InlineMarkdown, { children: it }) }, i)) })
|
|
531
567
|
] });
|
|
532
568
|
}
|
|
533
569
|
if (block.kind === "chart") {
|
|
@@ -544,7 +580,7 @@ function BlockView({ block }) {
|
|
|
544
580
|
}
|
|
545
581
|
if (block.kind === "callout") {
|
|
546
582
|
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";
|
|
547
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `rounded border p-3 text-sm ${cls}`, children: block.text });
|
|
583
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: `rounded border p-3 text-sm leading-6 ${cls}`, children: /* @__PURE__ */ jsxRuntime.jsx(ProseMarkdown, { children: block.text }) });
|
|
548
584
|
}
|
|
549
585
|
return null;
|
|
550
586
|
}
|
|
@@ -723,6 +759,11 @@ function AiChat({
|
|
|
723
759
|
cancelled = true;
|
|
724
760
|
};
|
|
725
761
|
}, []);
|
|
762
|
+
React.useEffect(() => {
|
|
763
|
+
if (initialSessionId != null && activeSessionId !== initialSessionId) {
|
|
764
|
+
setActiveSessionId(initialSessionId);
|
|
765
|
+
}
|
|
766
|
+
}, [initialSessionId]);
|
|
726
767
|
React.useEffect(() => {
|
|
727
768
|
if (initialSessionId == null) {
|
|
728
769
|
setAnswers([]);
|
|
@@ -735,13 +776,18 @@ function AiChat({
|
|
|
735
776
|
const res = await fetch(`/api/chat/sessions/${id}`, { cache: "no-store" });
|
|
736
777
|
if (cancelled) return;
|
|
737
778
|
if (!res.ok) {
|
|
779
|
+
console.warn(
|
|
780
|
+
"[AiChat] hydration: GET /api/chat/sessions failed",
|
|
781
|
+
{ id, status: res.status }
|
|
782
|
+
);
|
|
738
783
|
setAnswers([]);
|
|
739
784
|
return;
|
|
740
785
|
}
|
|
741
786
|
const data = await res.json();
|
|
742
787
|
if (cancelled) return;
|
|
743
788
|
setAnswers(messagesToAnswers(data.messages ?? []));
|
|
744
|
-
} catch {
|
|
789
|
+
} catch (err) {
|
|
790
|
+
console.error("[AiChat] hydration threw \u2014 setting empty answers", err);
|
|
745
791
|
if (!cancelled) setAnswers([]);
|
|
746
792
|
} finally {
|
|
747
793
|
if (!cancelled) setLoadingSession(false);
|
|
@@ -1483,6 +1529,15 @@ function updateLast(prev, updater) {
|
|
|
1483
1529
|
next[next.length - 1] = updater(next[next.length - 1]);
|
|
1484
1530
|
return next;
|
|
1485
1531
|
}
|
|
1532
|
+
function coerceJsonField(v) {
|
|
1533
|
+
if (v == null) return null;
|
|
1534
|
+
if (typeof v !== "string") return v;
|
|
1535
|
+
try {
|
|
1536
|
+
return JSON.parse(v);
|
|
1537
|
+
} catch {
|
|
1538
|
+
return null;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1486
1541
|
function messagesToAnswers(messages) {
|
|
1487
1542
|
const out = [];
|
|
1488
1543
|
let pendingUser = null;
|
|
@@ -1493,19 +1548,24 @@ function messagesToAnswers(messages) {
|
|
|
1493
1548
|
const question = pendingUser ?? "";
|
|
1494
1549
|
pendingUser = null;
|
|
1495
1550
|
const blocks = [];
|
|
1496
|
-
const
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1551
|
+
const storedBlocks = coerceJsonField(m.blocks) ?? [];
|
|
1552
|
+
const storedProse = coerceJsonField(m.prose) ?? {};
|
|
1553
|
+
const storedError = coerceJsonField(m.errorJson);
|
|
1554
|
+
if (Array.isArray(storedBlocks)) {
|
|
1555
|
+
storedBlocks.forEach((b, i) => {
|
|
1556
|
+
const sanitised = sanitiseBlock({
|
|
1557
|
+
...b});
|
|
1558
|
+
if (sanitised.kind === "paragraph_brief") {
|
|
1559
|
+
sanitised.prose = storedProse[String(i)] ?? "";
|
|
1560
|
+
}
|
|
1561
|
+
blocks[i] = sanitised;
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1504
1564
|
out.push({
|
|
1505
1565
|
question,
|
|
1506
1566
|
blocks,
|
|
1507
1567
|
done: true,
|
|
1508
|
-
error:
|
|
1568
|
+
error: storedError ?? void 0
|
|
1509
1569
|
});
|
|
1510
1570
|
}
|
|
1511
1571
|
}
|
|
@@ -2401,6 +2461,15 @@ function UserChip2({ text }) {
|
|
|
2401
2461
|
)
|
|
2402
2462
|
] });
|
|
2403
2463
|
}
|
|
2464
|
+
function coerceJsonField2(v) {
|
|
2465
|
+
if (v == null) return null;
|
|
2466
|
+
if (typeof v !== "string") return v;
|
|
2467
|
+
try {
|
|
2468
|
+
return JSON.parse(v);
|
|
2469
|
+
} catch {
|
|
2470
|
+
return null;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2404
2473
|
function storedToUseChat(stored) {
|
|
2405
2474
|
const uiMessages = [];
|
|
2406
2475
|
const blocksMap = {};
|
|
@@ -2416,15 +2485,20 @@ function storedToUseChat(stored) {
|
|
|
2416
2485
|
if (m.role === "assistant") {
|
|
2417
2486
|
const id = `assistant-${m.id}`;
|
|
2418
2487
|
const blocks = [];
|
|
2419
|
-
const
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2488
|
+
const storedBlocks = coerceJsonField2(m.blocks) ?? [];
|
|
2489
|
+
const storedProse = coerceJsonField2(m.prose) ?? {};
|
|
2490
|
+
const storedError = coerceJsonField2(m.errorJson);
|
|
2491
|
+
if (Array.isArray(storedBlocks)) {
|
|
2492
|
+
storedBlocks.forEach((b, i) => {
|
|
2493
|
+
const sanitised = sanitiseBlock({
|
|
2494
|
+
...b});
|
|
2495
|
+
if (sanitised.kind === "paragraph_brief") {
|
|
2496
|
+
sanitised.prose = storedProse[String(i)] ?? "";
|
|
2497
|
+
}
|
|
2498
|
+
blocks[i] = sanitised;
|
|
2499
|
+
});
|
|
2500
|
+
}
|
|
2501
|
+
const joinedProse = Object.values(storedProse).filter(Boolean).join("\n\n");
|
|
2428
2502
|
uiMessages.push({
|
|
2429
2503
|
id,
|
|
2430
2504
|
role: "assistant",
|
|
@@ -2432,7 +2506,7 @@ function storedToUseChat(stored) {
|
|
|
2432
2506
|
});
|
|
2433
2507
|
blocksMap[id] = blocks;
|
|
2434
2508
|
if (joinedProse) proseMap[id] = joinedProse;
|
|
2435
|
-
if (
|
|
2509
|
+
if (storedError) errorsMap[id] = storedError;
|
|
2436
2510
|
}
|
|
2437
2511
|
}
|
|
2438
2512
|
return { uiMessages, blocksMap, proseMap, errorsMap };
|