@nuucognition/ncm-ui 0.1.0-dev.1 → 0.1.1-beta.1

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.
Files changed (74) hide show
  1. package/README.md +90 -0
  2. package/dist/blocks/admonition/index.d.ts +2 -3
  3. package/dist/blocks/admonition/index.js +79 -23
  4. package/dist/blocks/aside/index.d.ts +2 -3
  5. package/dist/blocks/aside/index.js +79 -23
  6. package/dist/blocks/attribution/index.d.ts +2 -3
  7. package/dist/blocks/blockquote/index.d.ts +5 -4
  8. package/dist/blocks/blockquote/index.js +52 -7
  9. package/dist/blocks/chart/index.d.ts +2 -3
  10. package/dist/blocks/chart/index.js +2 -1
  11. package/dist/blocks/code/index.d.ts +2 -3
  12. package/dist/blocks/code/index.js +2 -2
  13. package/dist/blocks/container/index.d.ts +2 -3
  14. package/dist/blocks/container/index.js +50 -5
  15. package/dist/blocks/custom-code/index.d.ts +2 -3
  16. package/dist/blocks/custom-code/index.js +1 -1
  17. package/dist/blocks/dataview/index.d.ts +2 -3
  18. package/dist/blocks/dataview/index.js +2 -1
  19. package/dist/blocks/definition/index.d.ts +2 -3
  20. package/dist/blocks/definition/index.js +79 -23
  21. package/dist/blocks/embed/index.d.ts +2 -3
  22. package/dist/blocks/embedding/index.d.ts +2 -3
  23. package/dist/blocks/embedding/index.js +50 -5
  24. package/dist/blocks/figure/index.d.ts +2 -3
  25. package/dist/blocks/figure/index.js +50 -5
  26. package/dist/blocks/footnote/index.d.ts +2 -3
  27. package/dist/blocks/footnote/index.js +79 -23
  28. package/dist/blocks/frontmatter/index.d.ts +2 -3
  29. package/dist/blocks/header/index.d.ts +2 -3
  30. package/dist/blocks/header/index.js +50 -5
  31. package/dist/blocks/horizontal-rule/index.d.ts +2 -3
  32. package/dist/blocks/html/index.d.ts +3 -4
  33. package/dist/blocks/html/index.js +2 -1
  34. package/dist/blocks/index.d.ts +1 -2
  35. package/dist/blocks/index.js +79 -23
  36. package/dist/blocks/latex/index.d.ts +2 -3
  37. package/dist/blocks/latex/index.js +2 -2
  38. package/dist/blocks/link/index.d.ts +2 -3
  39. package/dist/blocks/list/index.d.ts +2 -3
  40. package/dist/blocks/list/index.js +57 -7
  41. package/dist/blocks/math/index.d.ts +2 -3
  42. package/dist/blocks/math/index.js +3 -2
  43. package/dist/blocks/mermaid/index.d.ts +2 -3
  44. package/dist/blocks/mermaid/index.js +2 -1
  45. package/dist/blocks/page-breaker/index.d.ts +2 -3
  46. package/dist/blocks/paragraph/index.d.ts +2 -3
  47. package/dist/blocks/paragraph/index.js +50 -5
  48. package/dist/blocks/spoiler/index.d.ts +2 -3
  49. package/dist/blocks/spoiler/index.js +79 -23
  50. package/dist/blocks/table/index.d.ts +2 -3
  51. package/dist/blocks/table/index.js +51 -6
  52. package/dist/blocks/task-list/index.d.ts +2 -3
  53. package/dist/blocks/task-list/index.js +52 -6
  54. package/dist/components/index.d.ts +34 -4
  55. package/dist/components/index.js +117 -25
  56. package/dist/components/ncm-block.d.ts +2 -3
  57. package/dist/components/ncm-block.js +79 -23
  58. package/dist/components/ncm-document.d.ts +4 -4
  59. package/dist/components/ncm-document.js +79 -23
  60. package/dist/context/index.d.ts +2 -3
  61. package/dist/hooks/index.d.ts +1 -2
  62. package/dist/hooks/index.js +79 -23
  63. package/dist/hooks/use-block-renderer.js +79 -23
  64. package/dist/hooks/use-ncm-config.d.ts +1 -2
  65. package/dist/index.d.ts +3 -4
  66. package/dist/index.js +118 -26
  67. package/dist/inline/index.d.ts +4 -4
  68. package/dist/inline/index.js +51 -6
  69. package/dist/lib/defaults.js +79 -23
  70. package/dist/{references-Bs5wJybr.d.ts → references-BYW5pnj_.d.ts} +4 -4
  71. package/dist/styles/nuu.css +329 -44
  72. package/dist/styles/paper.css +62 -1
  73. package/dist/{use-ncm-config-D0doMYTH.d.ts → use-ncm-config-CnntcShj.d.ts} +55 -4
  74. package/package.json +5 -5
@@ -531,9 +531,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
531
531
  case "image": {
532
532
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
533
533
  return /* @__PURE__ */ jsx(
534
- "img",
534
+ EmbedImage,
535
535
  {
536
- "data-ncm-token": "image",
536
+ attrs: { "data-ncm-token": "image" },
537
+ target: token.src,
537
538
  src,
538
539
  alt: token.alt,
539
540
  title: token.title
@@ -567,10 +568,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
567
568
  }
568
569
  if (embedType === "image") {
569
570
  return /* @__PURE__ */ jsx(
570
- "img",
571
+ EmbedImage,
571
572
  {
572
- "data-ncm-token": "wiki_embed",
573
- "data-ncm-wikiembed-type": embedType,
573
+ attrs: {
574
+ "data-ncm-token": "wiki_embed",
575
+ "data-ncm-wikiembed-type": embedType
576
+ },
577
+ target: token.target,
574
578
  src: url,
575
579
  alt: token.display ?? token.target,
576
580
  className: config.embeddings?.imageClassName
@@ -601,6 +605,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
601
605
  }
602
606
  );
603
607
  }
608
+ const onEmbedClick = config.embeddings?.onEmbedClick;
609
+ if (onEmbedClick) {
610
+ const handleClick = (e) => {
611
+ onEmbedClick(token.target, result, e);
612
+ };
613
+ return /* @__PURE__ */ jsx(
614
+ "button",
615
+ {
616
+ type: "button",
617
+ "data-ncm-token": "wiki_embed",
618
+ "data-ncm-wikiembed-type": embedType,
619
+ onClick: handleClick,
620
+ children: token.display ?? token.target
621
+ }
622
+ );
623
+ }
604
624
  return /* @__PURE__ */ jsx(
605
625
  "a",
606
626
  {
@@ -619,6 +639,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
619
639
  return null;
620
640
  }
621
641
  }
642
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
643
+ const config = useNcmConfig();
644
+ const Fallback = config.embeddings?.renderImageError;
645
+ const [failedSrc, setFailedSrc] = useState(null);
646
+ useEffect(() => {
647
+ setFailedSrc(null);
648
+ }, [src]);
649
+ if (!Fallback) {
650
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
651
+ }
652
+ if (failedSrc === src) {
653
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
654
+ }
655
+ return /* @__PURE__ */ jsx(
656
+ "img",
657
+ {
658
+ ...attrs,
659
+ src,
660
+ alt,
661
+ title,
662
+ className,
663
+ onError: () => setFailedSrc(src)
664
+ }
665
+ );
666
+ }
622
667
  function renderReadingText(content, opts) {
623
668
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
624
669
  const { bionic = false, beeline = false } = opts;
@@ -654,7 +699,7 @@ function renderReadingText(content, opts) {
654
699
  return /* @__PURE__ */ jsx(Fragment, { children: pieces });
655
700
  }
656
701
  function BlockquoteBlock({ block, className }) {
657
- const lines = block.content.split("\n").filter((line) => line.trim() !== "");
702
+ const lines = block.content.split("\n");
658
703
  return /* @__PURE__ */ jsx(
659
704
  "blockquote",
660
705
  {
@@ -664,7 +709,7 @@ function BlockquoteBlock({ block, className }) {
664
709
  "data-block-id": block.id,
665
710
  "data-ncm-blockquote": "",
666
711
  className,
667
- children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
712
+ children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: line.trim().length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: line }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" }) }, index))
668
713
  }
669
714
  );
670
715
  }
@@ -679,7 +724,8 @@ function ChartBlock({ block, className }) {
679
724
  "data-ncm-chart": "",
680
725
  "data-ncm-chart-type": block.chartType ?? "unknown",
681
726
  className: cn(
682
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
727
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
728
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
683
729
  className
684
730
  ),
685
731
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -804,7 +850,7 @@ function CodeBlock({ block, className }) {
804
850
  "data-ncm-language": block.language,
805
851
  className: cn("relative", className),
806
852
  children: [
807
- codeConfig.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
853
+ codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
808
854
  /* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
809
855
  languageLabel
810
856
  ]
@@ -823,7 +869,7 @@ function CodeBlock({ block, className }) {
823
869
  "data-ncm-language": block.language,
824
870
  className: cn("relative", className),
825
871
  children: [
826
- codeConfig?.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
872
+ codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
827
873
  /* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
828
874
  codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
829
875
  /* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
@@ -859,7 +905,8 @@ function DataviewBlock({ block, className }) {
859
905
  "data-ncm-dataview": "",
860
906
  "data-ncm-query-type": block.queryType,
861
907
  className: cn(
862
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
908
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
909
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
863
910
  className
864
911
  ),
865
912
  children: /* @__PURE__ */ jsx("code", { children: block.query })
@@ -1198,7 +1245,8 @@ function HtmlBlock({ block, className }) {
1198
1245
  "data-block-id": block.id,
1199
1246
  "data-ncm-html": "",
1200
1247
  className: cn(
1201
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1248
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1249
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1202
1250
  className
1203
1251
  ),
1204
1252
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1236,7 +1284,7 @@ function LatexDisplayBlock({ block, className }) {
1236
1284
  "data-ncm-type": block.type,
1237
1285
  "data-block-id": block.id,
1238
1286
  "data-ncm-latex-display": "",
1239
- className: cn("overflow-x-auto", className),
1287
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1240
1288
  dangerouslySetInnerHTML: { __html: html }
1241
1289
  }
1242
1290
  );
@@ -1251,7 +1299,7 @@ function LatexDisplayBlock({ block, className }) {
1251
1299
  "data-ncm-type": block.type,
1252
1300
  "data-block-id": block.id,
1253
1301
  "data-ncm-latex-display": "",
1254
- className: cn("overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1302
+ className: cn("overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1255
1303
  children: /* @__PURE__ */ jsx("code", { children: block.content })
1256
1304
  }
1257
1305
  );
@@ -1275,10 +1323,12 @@ function LinkBlock({ block, className }) {
1275
1323
  function ListBlock({ block, className }) {
1276
1324
  const isOrdered = block.type === "list_ordered";
1277
1325
  const Tag = isOrdered ? "ol" : "ul";
1326
+ const start = isOrdered && typeof block.marker === "number" && block.marker !== 1 ? block.marker : void 0;
1278
1327
  return /* @__PURE__ */ jsx(
1279
1328
  Tag,
1280
1329
  {
1281
1330
  role: "list",
1331
+ start,
1282
1332
  "data-ncm-block": block.id,
1283
1333
  "data-ncm-stable": block.stableId,
1284
1334
  "data-ncm-type": block.type,
@@ -1290,6 +1340,9 @@ function ListBlock({ block, className }) {
1290
1340
  }
1291
1341
  );
1292
1342
  }
1343
+ function ItemContent({ content }) {
1344
+ return content.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" });
1345
+ }
1293
1346
  function nestedItemIdKey(id) {
1294
1347
  if (!id) {
1295
1348
  return void 0;
@@ -1306,7 +1359,7 @@ function ListItems({ items, itemIds, ordered }) {
1306
1359
  return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, index) => {
1307
1360
  const key = nestedItemIdKey(itemIds?.[index]) ?? `item-${index}`;
1308
1361
  if (typeof item === "string") {
1309
- return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(InlineContent, { content: item }) }, key);
1362
+ return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(ItemContent, { content: item }) }, key);
1310
1363
  }
1311
1364
  if (isTypedNestedList(item)) {
1312
1365
  return /* @__PURE__ */ jsx(NestedTypedList, { list: item }, key);
@@ -1316,7 +1369,7 @@ function ListItems({ items, itemIds, ordered }) {
1316
1369
  const content = typeof first === "string" ? first : "";
1317
1370
  const nestedContent = processNestedItems(rest, ordered);
1318
1371
  return /* @__PURE__ */ jsxs("li", { "data-ncm-list-item": "", children: [
1319
- /* @__PURE__ */ jsx(InlineContent, { content }),
1372
+ /* @__PURE__ */ jsx(ItemContent, { content }),
1320
1373
  nestedContent
1321
1374
  ] }, key);
1322
1375
  }
@@ -1366,7 +1419,7 @@ function MathBlock({ block, className }) {
1366
1419
  "data-ncm-type": block.type,
1367
1420
  "data-block-id": block.id,
1368
1421
  "data-ncm-math": "",
1369
- className: cn("overflow-x-auto", className),
1422
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1370
1423
  dangerouslySetInnerHTML: { __html: html }
1371
1424
  }
1372
1425
  );
@@ -1382,7 +1435,8 @@ function MathBlock({ block, className }) {
1382
1435
  "data-block-id": block.id,
1383
1436
  "data-ncm-math": "",
1384
1437
  className: cn(
1385
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1438
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1439
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1386
1440
  className
1387
1441
  ),
1388
1442
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1400,7 +1454,8 @@ function MermaidBlock({ block, className }) {
1400
1454
  "data-ncm-mermaid": "",
1401
1455
  "data-ncm-diagram-type": block.diagramType,
1402
1456
  className: cn(
1403
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1457
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1458
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1404
1459
  className
1405
1460
  ),
1406
1461
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1461,7 +1516,7 @@ function TableBlock({ block, className }) {
1461
1516
  "data-ncm-type": block.type,
1462
1517
  "data-block-id": block.id,
1463
1518
  "data-ncm-table": "",
1464
- className: cn("w-full overflow-x-auto", className),
1519
+ className: cn("w-full overflow-x-auto overflow-y-hidden", className),
1465
1520
  children: /* @__PURE__ */ jsxs("table", { role: "table", "data-ncm-table-element": "", children: [
1466
1521
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { "data-ncm-table-row": "", children: block.headers.map((header, index) => /* @__PURE__ */ jsx("th", { "data-ncm-table-header": "", children: /* @__PURE__ */ jsx(InlineContent, { content: header }) }, index)) }) }),
1467
1522
  /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, rowIndex) => {
@@ -1475,6 +1530,7 @@ function TableBlock({ block, className }) {
1475
1530
  function TaskItems({ items }) {
1476
1531
  return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
1477
1532
  const status = item.checked ? "Completed" : "Incomplete";
1533
+ const text = serializeInlineContent(item.content);
1478
1534
  return /* @__PURE__ */ jsxs(
1479
1535
  "li",
1480
1536
  {
@@ -1501,7 +1557,7 @@ function TaskItems({ items }) {
1501
1557
  ":",
1502
1558
  " "
1503
1559
  ] }),
1504
- /* @__PURE__ */ jsx(InlineContent, { content: serializeInlineContent(item.content) })
1560
+ text.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: text }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" })
1505
1561
  ] })
1506
1562
  ] }),
1507
1563
  item.children ? /* @__PURE__ */ jsx("ul", { role: "list", "data-ncm-list-nested": "", className: "ml-6 list-none", children: /* @__PURE__ */ jsx(TaskItems, { items: item.children.items }) }) : null
@@ -1546,7 +1602,7 @@ function FallbackBlock({ block, className }) {
1546
1602
  "data-block-id": block.id,
1547
1603
  "data-ncm-fallback": "",
1548
1604
  className: cn(
1549
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1605
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1550
1606
  className
1551
1607
  )
1552
1608
  },
@@ -1602,7 +1658,7 @@ function CustomCodeBlock({ block, className }) {
1602
1658
  "data-block-id": block.id,
1603
1659
  "data-ncm-custom-code": "",
1604
1660
  "data-ncm-language": block.language,
1605
- className: cn("overflow-x-auto", className),
1661
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1606
1662
  children: [
1607
1663
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: block.language }),
1608
1664
  /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: block.content }) })
@@ -1,13 +1,12 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { AttributionBlock as AttributionBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type AttributionBlockProps = BlockRendererProps<AttributionBlock$1>;
7
6
  /**
8
7
  * Attribution renderer — a `— Source` line extracted from a blockquote, with
9
8
  * optional date and URL.
10
9
  */
11
- declare function AttributionBlock({ block, className }: AttributionBlockProps): react_jsx_runtime.JSX.Element;
10
+ declare function AttributionBlock({ block, className }: AttributionBlockProps): react.JSX.Element;
12
11
 
13
12
  export { AttributionBlock, type AttributionBlockProps };
@@ -1,15 +1,16 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { BlockquoteBlock as BlockquoteBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type BlockquoteBlockProps = BlockRendererProps<BlockquoteBlock$1>;
7
6
  /**
8
7
  * Blockquote block renderer with multiline support.
9
8
  *
10
9
  * Splits content by newlines and renders each line as a separate paragraph
11
- * to properly display multiline blockquotes.
10
+ * to properly display multiline blockquotes. An empty line (a bare `>` in
11
+ * source) renders one non-breaking space, so it keeps a full text line of
12
+ * height instead of collapsing — the same guard the list renderers use.
12
13
  */
13
- declare function BlockquoteBlock({ block, className }: BlockquoteBlockProps): react_jsx_runtime.JSX.Element;
14
+ declare function BlockquoteBlock({ block, className }: BlockquoteBlockProps): react.JSX.Element;
14
15
 
15
16
  export { BlockquoteBlock, type BlockquoteBlockProps };
@@ -468,9 +468,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
468
468
  case "image": {
469
469
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
470
470
  return /* @__PURE__ */ jsx(
471
- "img",
471
+ EmbedImage,
472
472
  {
473
- "data-ncm-token": "image",
473
+ attrs: { "data-ncm-token": "image" },
474
+ target: token.src,
474
475
  src,
475
476
  alt: token.alt,
476
477
  title: token.title
@@ -504,10 +505,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
504
505
  }
505
506
  if (embedType === "image") {
506
507
  return /* @__PURE__ */ jsx(
507
- "img",
508
+ EmbedImage,
508
509
  {
509
- "data-ncm-token": "wiki_embed",
510
- "data-ncm-wikiembed-type": embedType,
510
+ attrs: {
511
+ "data-ncm-token": "wiki_embed",
512
+ "data-ncm-wikiembed-type": embedType
513
+ },
514
+ target: token.target,
511
515
  src: url,
512
516
  alt: token.display ?? token.target,
513
517
  className: config.embeddings?.imageClassName
@@ -538,6 +542,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
538
542
  }
539
543
  );
540
544
  }
545
+ const onEmbedClick = config.embeddings?.onEmbedClick;
546
+ if (onEmbedClick) {
547
+ const handleClick = (e) => {
548
+ onEmbedClick(token.target, result, e);
549
+ };
550
+ return /* @__PURE__ */ jsx(
551
+ "button",
552
+ {
553
+ type: "button",
554
+ "data-ncm-token": "wiki_embed",
555
+ "data-ncm-wikiembed-type": embedType,
556
+ onClick: handleClick,
557
+ children: token.display ?? token.target
558
+ }
559
+ );
560
+ }
541
561
  return /* @__PURE__ */ jsx(
542
562
  "a",
543
563
  {
@@ -556,6 +576,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
556
576
  return null;
557
577
  }
558
578
  }
579
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
580
+ const config = useNcmConfig();
581
+ const Fallback = config.embeddings?.renderImageError;
582
+ const [failedSrc, setFailedSrc] = useState(null);
583
+ useEffect(() => {
584
+ setFailedSrc(null);
585
+ }, [src]);
586
+ if (!Fallback) {
587
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
588
+ }
589
+ if (failedSrc === src) {
590
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
591
+ }
592
+ return /* @__PURE__ */ jsx(
593
+ "img",
594
+ {
595
+ ...attrs,
596
+ src,
597
+ alt,
598
+ title,
599
+ className,
600
+ onError: () => setFailedSrc(src)
601
+ }
602
+ );
603
+ }
559
604
  function renderReadingText(content, opts) {
560
605
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
561
606
  const { bionic = false, beeline = false } = opts;
@@ -591,7 +636,7 @@ function renderReadingText(content, opts) {
591
636
  return /* @__PURE__ */ jsx(Fragment, { children: pieces });
592
637
  }
593
638
  function BlockquoteBlock({ block, className }) {
594
- const lines = block.content.split("\n").filter((line) => line.trim() !== "");
639
+ const lines = block.content.split("\n");
595
640
  return /* @__PURE__ */ jsx(
596
641
  "blockquote",
597
642
  {
@@ -601,7 +646,7 @@ function BlockquoteBlock({ block, className }) {
601
646
  "data-block-id": block.id,
602
647
  "data-ncm-blockquote": "",
603
648
  className,
604
- children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
649
+ children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: line.trim().length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: line }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" }) }, index))
605
650
  }
606
651
  );
607
652
  }
@@ -1,7 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ChartBlock as ChartBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type ChartBlockProps = BlockRendererProps<ChartBlock$1>;
7
6
  /**
@@ -9,6 +8,6 @@ type ChartBlockProps = BlockRendererProps<ChartBlock$1>;
9
8
  * charting runtime is bundled here. `data-ncm-chart-type` exposes the detected
10
9
  * chart kind; override the `chart` renderer key to render a live chart.
11
10
  */
12
- declare function ChartBlock({ block, className }: ChartBlockProps): react_jsx_runtime.JSX.Element;
11
+ declare function ChartBlock({ block, className }: ChartBlockProps): react.JSX.Element;
13
12
 
14
13
  export { ChartBlock, type ChartBlockProps };
@@ -18,7 +18,8 @@ function ChartBlock({ block, className }) {
18
18
  "data-ncm-chart": "",
19
19
  "data-ncm-chart-type": block.chartType ?? "unknown",
20
20
  className: cn(
21
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
21
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
22
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
22
23
  className
23
24
  ),
24
25
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1,7 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { CodeBlock as CodeBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type CodeBlockProps = BlockRendererProps<CodeBlock$1>;
7
6
  /**
@@ -10,6 +9,6 @@ type CodeBlockProps = BlockRendererProps<CodeBlock$1>;
10
9
  * Highlights code by default using Shiki's web bundle (github-light/github-dark).
11
10
  * Consumers can override with a custom renderer via NcmProvider config.code.renderer.
12
11
  */
13
- declare function CodeBlock({ block, className }: CodeBlockProps): react_jsx_runtime.JSX.Element;
12
+ declare function CodeBlock({ block, className }: CodeBlockProps): react.JSX.Element;
14
13
 
15
14
  export { CodeBlock, type CodeBlockProps };
@@ -130,7 +130,7 @@ function CodeBlock({ block, className }) {
130
130
  "data-ncm-language": block.language,
131
131
  className: cn("relative", className),
132
132
  children: [
133
- codeConfig.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
133
+ codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
134
134
  /* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
135
135
  languageLabel
136
136
  ]
@@ -149,7 +149,7 @@ function CodeBlock({ block, className }) {
149
149
  "data-ncm-language": block.language,
150
150
  className: cn("relative", className),
151
151
  children: [
152
- codeConfig?.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
152
+ codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
153
153
  /* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
154
154
  codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
155
155
  /* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
@@ -1,7 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ContainerBlock as ContainerBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type ContainerBlockProps = BlockRendererProps<ContainerBlock$1>;
7
6
  /**
@@ -12,6 +11,6 @@ type ContainerBlockProps = BlockRendererProps<ContainerBlock$1>;
12
11
  * (admonition, figure, tabs…) replace this by promoting upstream or overriding
13
12
  * the `container` renderer key.
14
13
  */
15
- declare function ContainerBlock({ block, className }: ContainerBlockProps): react_jsx_runtime.JSX.Element;
14
+ declare function ContainerBlock({ block, className }: ContainerBlockProps): react.JSX.Element;
16
15
 
17
16
  export { ContainerBlock, type ContainerBlockProps };
@@ -473,9 +473,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
473
473
  case "image": {
474
474
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
475
475
  return /* @__PURE__ */ jsx(
476
- "img",
476
+ EmbedImage,
477
477
  {
478
- "data-ncm-token": "image",
478
+ attrs: { "data-ncm-token": "image" },
479
+ target: token.src,
479
480
  src,
480
481
  alt: token.alt,
481
482
  title: token.title
@@ -509,10 +510,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
509
510
  }
510
511
  if (embedType === "image") {
511
512
  return /* @__PURE__ */ jsx(
512
- "img",
513
+ EmbedImage,
513
514
  {
514
- "data-ncm-token": "wiki_embed",
515
- "data-ncm-wikiembed-type": embedType,
515
+ attrs: {
516
+ "data-ncm-token": "wiki_embed",
517
+ "data-ncm-wikiembed-type": embedType
518
+ },
519
+ target: token.target,
516
520
  src: url,
517
521
  alt: token.display ?? token.target,
518
522
  className: config.embeddings?.imageClassName
@@ -543,6 +547,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
543
547
  }
544
548
  );
545
549
  }
550
+ const onEmbedClick = config.embeddings?.onEmbedClick;
551
+ if (onEmbedClick) {
552
+ const handleClick = (e) => {
553
+ onEmbedClick(token.target, result, e);
554
+ };
555
+ return /* @__PURE__ */ jsx(
556
+ "button",
557
+ {
558
+ type: "button",
559
+ "data-ncm-token": "wiki_embed",
560
+ "data-ncm-wikiembed-type": embedType,
561
+ onClick: handleClick,
562
+ children: token.display ?? token.target
563
+ }
564
+ );
565
+ }
546
566
  return /* @__PURE__ */ jsx(
547
567
  "a",
548
568
  {
@@ -561,6 +581,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
561
581
  return null;
562
582
  }
563
583
  }
584
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
585
+ const config = useNcmConfig();
586
+ const Fallback = config.embeddings?.renderImageError;
587
+ const [failedSrc, setFailedSrc] = useState(null);
588
+ useEffect(() => {
589
+ setFailedSrc(null);
590
+ }, [src]);
591
+ if (!Fallback) {
592
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
593
+ }
594
+ if (failedSrc === src) {
595
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
596
+ }
597
+ return /* @__PURE__ */ jsx(
598
+ "img",
599
+ {
600
+ ...attrs,
601
+ src,
602
+ alt,
603
+ title,
604
+ className,
605
+ onError: () => setFailedSrc(src)
606
+ }
607
+ );
608
+ }
564
609
  function renderReadingText(content, opts) {
565
610
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
566
611
  const { bionic = false, beeline = false } = opts;
@@ -1,7 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { CustomCodeBlock as CustomCodeBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type CustomCodeBlockProps = BlockRendererProps<CustomCodeBlock$1>;
7
6
  /**
@@ -10,6 +9,6 @@ type CustomCodeBlockProps = BlockRendererProps<CustomCodeBlock$1>;
10
9
  * Fallback when no custom renderer is registered for a language.
11
10
  * Custom renderers register via NcmProvider config.renderers.custom_code[language].
12
11
  */
13
- declare function CustomCodeBlock({ block, className }: CustomCodeBlockProps): react_jsx_runtime.JSX.Element;
12
+ declare function CustomCodeBlock({ block, className }: CustomCodeBlockProps): react.JSX.Element;
14
13
 
15
14
  export { CustomCodeBlock, type CustomCodeBlockProps };
@@ -19,7 +19,7 @@ function CustomCodeBlock({ block, className }) {
19
19
  "data-block-id": block.id,
20
20
  "data-ncm-custom-code": "",
21
21
  "data-ncm-language": block.language,
22
- className: cn("overflow-x-auto", className),
22
+ className: cn("overflow-x-auto overflow-y-hidden", className),
23
23
  children: [
24
24
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: block.language }),
25
25
  /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: block.content }) })
@@ -1,7 +1,6 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { DataviewBlock as DataviewBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type DataviewBlockProps = BlockRendererProps<DataviewBlock$1>;
7
6
  /**
@@ -9,6 +8,6 @@ type DataviewBlockProps = BlockRendererProps<DataviewBlock$1>;
9
8
  * engine is bundled here). `data-ncm-query-type` exposes the detected query
10
9
  * kind; override the `dataview` renderer key to execute and render results.
11
10
  */
12
- declare function DataviewBlock({ block, className }: DataviewBlockProps): react_jsx_runtime.JSX.Element;
11
+ declare function DataviewBlock({ block, className }: DataviewBlockProps): react.JSX.Element;
13
12
 
14
13
  export { DataviewBlock, type DataviewBlockProps };
@@ -18,7 +18,8 @@ function DataviewBlock({ block, className }) {
18
18
  "data-ncm-dataview": "",
19
19
  "data-ncm-query-type": block.queryType,
20
20
  className: cn(
21
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
21
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
22
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
22
23
  className
23
24
  ),
24
25
  children: /* @__PURE__ */ jsx("code", { children: block.query })