@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/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
- return /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm leading-6 text-foreground", children: block.prose || /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
498
- block.key_facts.join(". "),
499
- "\u2026"
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 stored = m.blocks ?? [];
1473
- stored.forEach((b, i) => {
1474
- const sanitised = sanitiseBlock({ ...b});
1475
- if (sanitised.kind === "paragraph_brief") {
1476
- sanitised.prose = m.prose?.[String(i)] ?? "";
1477
- }
1478
- blocks[i] = sanitised;
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: m.errorJson ?? void 0
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 stored2 = m.blocks ?? [];
2396
- stored2.forEach((b, i) => {
2397
- const sanitised = sanitiseBlock({ ...b});
2398
- if (sanitised.kind === "paragraph_brief") {
2399
- sanitised.prose = m.prose?.[String(i)] ?? "";
2400
- }
2401
- blocks[i] = sanitised;
2402
- });
2403
- const joinedProse = Object.values(m.prose ?? {}).filter(Boolean).join("\n\n");
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 (m.errorJson) errorsMap[id] = m.errorJson;
2483
+ if (storedError) errorsMap[id] = storedError;
2412
2484
  }
2413
2485
  }
2414
2486
  return { uiMessages, blocksMap, proseMap, errorsMap };