@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.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
- return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm leading-6 text-foreground", children: block.prose || /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-muted-foreground", children: [
522
- block.key_facts.join(". "),
523
- "\u2026"
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 stored = m.blocks ?? [];
1497
- stored.forEach((b, i) => {
1498
- const sanitised = sanitiseBlock({ ...b});
1499
- if (sanitised.kind === "paragraph_brief") {
1500
- sanitised.prose = m.prose?.[String(i)] ?? "";
1501
- }
1502
- blocks[i] = sanitised;
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: m.errorJson ?? void 0
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 stored2 = m.blocks ?? [];
2420
- stored2.forEach((b, i) => {
2421
- const sanitised = sanitiseBlock({ ...b});
2422
- if (sanitised.kind === "paragraph_brief") {
2423
- sanitised.prose = m.prose?.[String(i)] ?? "";
2424
- }
2425
- blocks[i] = sanitised;
2426
- });
2427
- const joinedProse = Object.values(m.prose ?? {}).filter(Boolean).join("\n\n");
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 (m.errorJson) errorsMap[id] = m.errorJson;
2509
+ if (storedError) errorsMap[id] = storedError;
2436
2510
  }
2437
2511
  }
2438
2512
  return { uiMessages, blocksMap, proseMap, errorsMap };