@firstlovecenter/ai-chat 0.9.3 → 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 CHANGED
@@ -5,6 +5,18 @@ All notable changes to `@firstlovecenter/ai-chat` are documented here.
5
5
  The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and the project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.9.4] — 2026-05-09
9
+
10
+ Markdown in assistant answers now actually renders as markdown.
11
+
12
+ ### Fixed
13
+
14
+ - **Literal `**bold**` / `*italic*` showing in chat output.** `paragraph_brief` prose, `list` items, and `callout` text were rendered as plain text inside `<p>{block.prose}</p>`, so markdown emitted by the model leaked through verbatim. The renderer now pipes those three block types through `react-markdown` (with `remark-gfm`) and a small components map that keeps the existing `text-sm leading-6` look. Errors and the user's own messages are still rendered as plain text — only model-authored content is parsed.
15
+
16
+ ### Added
17
+
18
+ - **`react-markdown` + `remark-gfm` dependencies.** Required by the change above. The `ui` bundle grows by ~30 KB gzipped — small enough that no lazy-load gymnastics are warranted.
19
+
8
20
  ## [0.9.3] — 2026-05-09
9
21
 
10
22
  Fixes the actual root cause of the "messages persisted but reload shows empty thread" bug — turns out it was at the data layer, not the navigation layer.
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
  }