@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
@@ -1,13 +1,12 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { DefinitionBlock as DefinitionBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type DefinitionBlockProps = BlockRendererProps<DefinitionBlock$1>;
7
6
  /**
8
7
  * Definition-list renderer — a term and its definition blocks. The term is
9
8
  * inline content; the definition renders through `NcmBlock`.
10
9
  */
11
- declare function DefinitionBlock({ block, className }: DefinitionBlockProps): react_jsx_runtime.JSX.Element;
10
+ declare function DefinitionBlock({ block, className }: DefinitionBlockProps): react.JSX.Element;
12
11
 
13
12
  export { DefinitionBlock, type DefinitionBlockProps };
@@ -470,9 +470,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
470
470
  case "image": {
471
471
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
472
472
  return /* @__PURE__ */ jsx(
473
- "img",
473
+ EmbedImage,
474
474
  {
475
- "data-ncm-token": "image",
475
+ attrs: { "data-ncm-token": "image" },
476
+ target: token.src,
476
477
  src,
477
478
  alt: token.alt,
478
479
  title: token.title
@@ -506,10 +507,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
506
507
  }
507
508
  if (embedType === "image") {
508
509
  return /* @__PURE__ */ jsx(
509
- "img",
510
+ EmbedImage,
510
511
  {
511
- "data-ncm-token": "wiki_embed",
512
- "data-ncm-wikiembed-type": embedType,
512
+ attrs: {
513
+ "data-ncm-token": "wiki_embed",
514
+ "data-ncm-wikiembed-type": embedType
515
+ },
516
+ target: token.target,
513
517
  src: url,
514
518
  alt: token.display ?? token.target,
515
519
  className: config.embeddings?.imageClassName
@@ -540,6 +544,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
540
544
  }
541
545
  );
542
546
  }
547
+ const onEmbedClick = config.embeddings?.onEmbedClick;
548
+ if (onEmbedClick) {
549
+ const handleClick = (e) => {
550
+ onEmbedClick(token.target, result, e);
551
+ };
552
+ return /* @__PURE__ */ jsx(
553
+ "button",
554
+ {
555
+ type: "button",
556
+ "data-ncm-token": "wiki_embed",
557
+ "data-ncm-wikiembed-type": embedType,
558
+ onClick: handleClick,
559
+ children: token.display ?? token.target
560
+ }
561
+ );
562
+ }
543
563
  return /* @__PURE__ */ jsx(
544
564
  "a",
545
565
  {
@@ -558,6 +578,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
558
578
  return null;
559
579
  }
560
580
  }
581
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
582
+ const config = useNcmConfig();
583
+ const Fallback = config.embeddings?.renderImageError;
584
+ const [failedSrc, setFailedSrc] = useState(null);
585
+ useEffect(() => {
586
+ setFailedSrc(null);
587
+ }, [src]);
588
+ if (!Fallback) {
589
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
590
+ }
591
+ if (failedSrc === src) {
592
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
593
+ }
594
+ return /* @__PURE__ */ jsx(
595
+ "img",
596
+ {
597
+ ...attrs,
598
+ src,
599
+ alt,
600
+ title,
601
+ className,
602
+ onError: () => setFailedSrc(src)
603
+ }
604
+ );
605
+ }
561
606
  function renderReadingText(content, opts) {
562
607
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
563
608
  const { bionic = false, beeline = false } = opts;
@@ -666,7 +711,7 @@ function AttributionBlock({ block, className }) {
666
711
  );
667
712
  }
668
713
  function BlockquoteBlock({ block, className }) {
669
- const lines = block.content.split("\n").filter((line) => line.trim() !== "");
714
+ const lines = block.content.split("\n");
670
715
  return /* @__PURE__ */ jsx(
671
716
  "blockquote",
672
717
  {
@@ -676,7 +721,7 @@ function BlockquoteBlock({ block, className }) {
676
721
  "data-block-id": block.id,
677
722
  "data-ncm-blockquote": "",
678
723
  className,
679
- children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
724
+ 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))
680
725
  }
681
726
  );
682
727
  }
@@ -691,7 +736,8 @@ function ChartBlock({ block, className }) {
691
736
  "data-ncm-chart": "",
692
737
  "data-ncm-chart-type": block.chartType ?? "unknown",
693
738
  className: cn(
694
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
739
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
740
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
695
741
  className
696
742
  ),
697
743
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -816,7 +862,7 @@ function CodeBlock({ block, className }) {
816
862
  "data-ncm-language": block.language,
817
863
  className: cn("relative", className),
818
864
  children: [
819
- codeConfig.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
865
+ codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
820
866
  /* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
821
867
  languageLabel
822
868
  ]
@@ -835,7 +881,7 @@ function CodeBlock({ block, className }) {
835
881
  "data-ncm-language": block.language,
836
882
  className: cn("relative", className),
837
883
  children: [
838
- codeConfig?.copyButton !== false && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
884
+ codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
839
885
  /* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
840
886
  codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
841
887
  /* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
@@ -871,7 +917,8 @@ function DataviewBlock({ block, className }) {
871
917
  "data-ncm-dataview": "",
872
918
  "data-ncm-query-type": block.queryType,
873
919
  className: cn(
874
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
920
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
921
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
875
922
  className
876
923
  ),
877
924
  children: /* @__PURE__ */ jsx("code", { children: block.query })
@@ -1195,7 +1242,8 @@ function HtmlBlock({ block, className }) {
1195
1242
  "data-block-id": block.id,
1196
1243
  "data-ncm-html": "",
1197
1244
  className: cn(
1198
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1245
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1246
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1199
1247
  className
1200
1248
  ),
1201
1249
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1233,7 +1281,7 @@ function LatexDisplayBlock({ block, className }) {
1233
1281
  "data-ncm-type": block.type,
1234
1282
  "data-block-id": block.id,
1235
1283
  "data-ncm-latex-display": "",
1236
- className: cn("overflow-x-auto", className),
1284
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1237
1285
  dangerouslySetInnerHTML: { __html: html }
1238
1286
  }
1239
1287
  );
@@ -1248,7 +1296,7 @@ function LatexDisplayBlock({ block, className }) {
1248
1296
  "data-ncm-type": block.type,
1249
1297
  "data-block-id": block.id,
1250
1298
  "data-ncm-latex-display": "",
1251
- className: cn("overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1299
+ className: cn("overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1252
1300
  children: /* @__PURE__ */ jsx("code", { children: block.content })
1253
1301
  }
1254
1302
  );
@@ -1272,10 +1320,12 @@ function LinkBlock({ block, className }) {
1272
1320
  function ListBlock({ block, className }) {
1273
1321
  const isOrdered = block.type === "list_ordered";
1274
1322
  const Tag = isOrdered ? "ol" : "ul";
1323
+ const start = isOrdered && typeof block.marker === "number" && block.marker !== 1 ? block.marker : void 0;
1275
1324
  return /* @__PURE__ */ jsx(
1276
1325
  Tag,
1277
1326
  {
1278
1327
  role: "list",
1328
+ start,
1279
1329
  "data-ncm-block": block.id,
1280
1330
  "data-ncm-stable": block.stableId,
1281
1331
  "data-ncm-type": block.type,
@@ -1287,6 +1337,9 @@ function ListBlock({ block, className }) {
1287
1337
  }
1288
1338
  );
1289
1339
  }
1340
+ function ItemContent({ content }) {
1341
+ return content.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" });
1342
+ }
1290
1343
  function nestedItemIdKey(id) {
1291
1344
  if (!id) {
1292
1345
  return void 0;
@@ -1303,7 +1356,7 @@ function ListItems({ items, itemIds, ordered }) {
1303
1356
  return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, index) => {
1304
1357
  const key = nestedItemIdKey(itemIds?.[index]) ?? `item-${index}`;
1305
1358
  if (typeof item === "string") {
1306
- return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(InlineContent, { content: item }) }, key);
1359
+ return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(ItemContent, { content: item }) }, key);
1307
1360
  }
1308
1361
  if (isTypedNestedList(item)) {
1309
1362
  return /* @__PURE__ */ jsx(NestedTypedList, { list: item }, key);
@@ -1313,7 +1366,7 @@ function ListItems({ items, itemIds, ordered }) {
1313
1366
  const content = typeof first === "string" ? first : "";
1314
1367
  const nestedContent = processNestedItems(rest, ordered);
1315
1368
  return /* @__PURE__ */ jsxs("li", { "data-ncm-list-item": "", children: [
1316
- /* @__PURE__ */ jsx(InlineContent, { content }),
1369
+ /* @__PURE__ */ jsx(ItemContent, { content }),
1317
1370
  nestedContent
1318
1371
  ] }, key);
1319
1372
  }
@@ -1363,7 +1416,7 @@ function MathBlock({ block, className }) {
1363
1416
  "data-ncm-type": block.type,
1364
1417
  "data-block-id": block.id,
1365
1418
  "data-ncm-math": "",
1366
- className: cn("overflow-x-auto", className),
1419
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1367
1420
  dangerouslySetInnerHTML: { __html: html }
1368
1421
  }
1369
1422
  );
@@ -1379,7 +1432,8 @@ function MathBlock({ block, className }) {
1379
1432
  "data-block-id": block.id,
1380
1433
  "data-ncm-math": "",
1381
1434
  className: cn(
1382
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1435
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1436
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1383
1437
  className
1384
1438
  ),
1385
1439
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1397,7 +1451,8 @@ function MermaidBlock({ block, className }) {
1397
1451
  "data-ncm-mermaid": "",
1398
1452
  "data-ncm-diagram-type": block.diagramType,
1399
1453
  className: cn(
1400
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1454
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose see the note in `lib/defaults.ts`.
1455
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1401
1456
  className
1402
1457
  ),
1403
1458
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1458,7 +1513,7 @@ function TableBlock({ block, className }) {
1458
1513
  "data-ncm-type": block.type,
1459
1514
  "data-block-id": block.id,
1460
1515
  "data-ncm-table": "",
1461
- className: cn("w-full overflow-x-auto", className),
1516
+ className: cn("w-full overflow-x-auto overflow-y-hidden", className),
1462
1517
  children: /* @__PURE__ */ jsxs("table", { role: "table", "data-ncm-table-element": "", children: [
1463
1518
  /* @__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)) }) }),
1464
1519
  /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, rowIndex) => {
@@ -1472,6 +1527,7 @@ function TableBlock({ block, className }) {
1472
1527
  function TaskItems({ items }) {
1473
1528
  return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
1474
1529
  const status = item.checked ? "Completed" : "Incomplete";
1530
+ const text = serializeInlineContent(item.content);
1475
1531
  return /* @__PURE__ */ jsxs(
1476
1532
  "li",
1477
1533
  {
@@ -1498,7 +1554,7 @@ function TaskItems({ items }) {
1498
1554
  ":",
1499
1555
  " "
1500
1556
  ] }),
1501
- /* @__PURE__ */ jsx(InlineContent, { content: serializeInlineContent(item.content) })
1557
+ text.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: text }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" })
1502
1558
  ] })
1503
1559
  ] }),
1504
1560
  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
@@ -1543,7 +1599,7 @@ function FallbackBlock({ block, className }) {
1543
1599
  "data-block-id": block.id,
1544
1600
  "data-ncm-fallback": "",
1545
1601
  className: cn(
1546
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1602
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1547
1603
  className
1548
1604
  )
1549
1605
  },
@@ -1599,7 +1655,7 @@ function CustomCodeBlock({ block, className }) {
1599
1655
  "data-block-id": block.id,
1600
1656
  "data-ncm-custom-code": "",
1601
1657
  "data-ncm-language": block.language,
1602
- className: cn("overflow-x-auto", className),
1658
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1603
1659
  children: [
1604
1660
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: block.language }),
1605
1661
  /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: block.content }) })
@@ -1,12 +1,11 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { EmbedBlock as EmbedBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type EmbedBlockProps = BlockRendererProps<EmbedBlock$1>;
7
6
  /**
8
7
  * Renderer for standalone normalized embed blocks.
9
8
  */
10
- declare function EmbedBlock({ block, className }: EmbedBlockProps): react_jsx_runtime.JSX.Element;
9
+ declare function EmbedBlock({ block, className }: EmbedBlockProps): react.JSX.Element;
11
10
 
12
11
  export { EmbedBlock, type EmbedBlockProps };
@@ -1,13 +1,12 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { EmbeddingBlock as EmbeddingBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type EmbeddingBlockProps = BlockRendererProps<EmbeddingBlock$1>;
7
6
  /**
8
7
  * Embedding block renderer that dispatches by embed_type
9
8
  * (image, video, audio, pdf, ncm_embed, iframe, generic_file).
10
9
  */
11
- declare function EmbeddingBlock({ block, className }: EmbeddingBlockProps): react_jsx_runtime.JSX.Element;
10
+ declare function EmbeddingBlock({ block, className }: EmbeddingBlockProps): react.JSX.Element;
12
11
 
13
12
  export { EmbeddingBlock, type EmbeddingBlockProps };
@@ -470,9 +470,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
470
470
  case "image": {
471
471
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
472
472
  return /* @__PURE__ */ jsx(
473
- "img",
473
+ EmbedImage,
474
474
  {
475
- "data-ncm-token": "image",
475
+ attrs: { "data-ncm-token": "image" },
476
+ target: token.src,
476
477
  src,
477
478
  alt: token.alt,
478
479
  title: token.title
@@ -506,10 +507,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
506
507
  }
507
508
  if (embedType === "image") {
508
509
  return /* @__PURE__ */ jsx(
509
- "img",
510
+ EmbedImage,
510
511
  {
511
- "data-ncm-token": "wiki_embed",
512
- "data-ncm-wikiembed-type": embedType,
512
+ attrs: {
513
+ "data-ncm-token": "wiki_embed",
514
+ "data-ncm-wikiembed-type": embedType
515
+ },
516
+ target: token.target,
513
517
  src: url,
514
518
  alt: token.display ?? token.target,
515
519
  className: config.embeddings?.imageClassName
@@ -540,6 +544,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
540
544
  }
541
545
  );
542
546
  }
547
+ const onEmbedClick = config.embeddings?.onEmbedClick;
548
+ if (onEmbedClick) {
549
+ const handleClick = (e) => {
550
+ onEmbedClick(token.target, result, e);
551
+ };
552
+ return /* @__PURE__ */ jsx(
553
+ "button",
554
+ {
555
+ type: "button",
556
+ "data-ncm-token": "wiki_embed",
557
+ "data-ncm-wikiembed-type": embedType,
558
+ onClick: handleClick,
559
+ children: token.display ?? token.target
560
+ }
561
+ );
562
+ }
543
563
  return /* @__PURE__ */ jsx(
544
564
  "a",
545
565
  {
@@ -558,6 +578,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
558
578
  return null;
559
579
  }
560
580
  }
581
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
582
+ const config = useNcmConfig();
583
+ const Fallback = config.embeddings?.renderImageError;
584
+ const [failedSrc, setFailedSrc] = useState(null);
585
+ useEffect(() => {
586
+ setFailedSrc(null);
587
+ }, [src]);
588
+ if (!Fallback) {
589
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
590
+ }
591
+ if (failedSrc === src) {
592
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
593
+ }
594
+ return /* @__PURE__ */ jsx(
595
+ "img",
596
+ {
597
+ ...attrs,
598
+ src,
599
+ alt,
600
+ title,
601
+ className,
602
+ onError: () => setFailedSrc(src)
603
+ }
604
+ );
605
+ }
561
606
  function renderReadingText(content, opts) {
562
607
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
563
608
  const { bionic = false, beeline = false } = opts;
@@ -1,13 +1,12 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { FigureBlock as FigureBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type FigureBlockProps = BlockRendererProps<FigureBlock$1>;
7
6
  /**
8
7
  * Figure renderer — an embed plus caption, promoted from `embed + paragraph`
9
8
  * (no blank line). Images render inline; other embed types render as a link.
10
9
  */
11
- declare function FigureBlock({ block, className }: FigureBlockProps): react_jsx_runtime.JSX.Element;
10
+ declare function FigureBlock({ block, className }: FigureBlockProps): react.JSX.Element;
12
11
 
13
12
  export { FigureBlock, type FigureBlockProps };
@@ -470,9 +470,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
470
470
  case "image": {
471
471
  const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
472
472
  return /* @__PURE__ */ jsx(
473
- "img",
473
+ EmbedImage,
474
474
  {
475
- "data-ncm-token": "image",
475
+ attrs: { "data-ncm-token": "image" },
476
+ target: token.src,
476
477
  src,
477
478
  alt: token.alt,
478
479
  title: token.title
@@ -506,10 +507,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
506
507
  }
507
508
  if (embedType === "image") {
508
509
  return /* @__PURE__ */ jsx(
509
- "img",
510
+ EmbedImage,
510
511
  {
511
- "data-ncm-token": "wiki_embed",
512
- "data-ncm-wikiembed-type": embedType,
512
+ attrs: {
513
+ "data-ncm-token": "wiki_embed",
514
+ "data-ncm-wikiembed-type": embedType
515
+ },
516
+ target: token.target,
513
517
  src: url,
514
518
  alt: token.display ?? token.target,
515
519
  className: config.embeddings?.imageClassName
@@ -540,6 +544,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
540
544
  }
541
545
  );
542
546
  }
547
+ const onEmbedClick = config.embeddings?.onEmbedClick;
548
+ if (onEmbedClick) {
549
+ const handleClick = (e) => {
550
+ onEmbedClick(token.target, result, e);
551
+ };
552
+ return /* @__PURE__ */ jsx(
553
+ "button",
554
+ {
555
+ type: "button",
556
+ "data-ncm-token": "wiki_embed",
557
+ "data-ncm-wikiembed-type": embedType,
558
+ onClick: handleClick,
559
+ children: token.display ?? token.target
560
+ }
561
+ );
562
+ }
543
563
  return /* @__PURE__ */ jsx(
544
564
  "a",
545
565
  {
@@ -558,6 +578,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
558
578
  return null;
559
579
  }
560
580
  }
581
+ function EmbedImage({ attrs, target, src, alt, title, className }) {
582
+ const config = useNcmConfig();
583
+ const Fallback = config.embeddings?.renderImageError;
584
+ const [failedSrc, setFailedSrc] = useState(null);
585
+ useEffect(() => {
586
+ setFailedSrc(null);
587
+ }, [src]);
588
+ if (!Fallback) {
589
+ return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
590
+ }
591
+ if (failedSrc === src) {
592
+ return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
593
+ }
594
+ return /* @__PURE__ */ jsx(
595
+ "img",
596
+ {
597
+ ...attrs,
598
+ src,
599
+ alt,
600
+ title,
601
+ className,
602
+ onError: () => setFailedSrc(src)
603
+ }
604
+ );
605
+ }
561
606
  function renderReadingText(content, opts) {
562
607
  if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
563
608
  const { bionic = false, beeline = false } = opts;
@@ -1,13 +1,12 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { FootnoteBlock as FootnoteBlock$1 } from '@nuucognition/ncm';
3
3
  import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
4
- import 'react';
5
4
 
6
5
  type FootnoteBlockProps = BlockRendererProps<FootnoteBlock$1>;
7
6
  /**
8
7
  * Footnote-definition renderer. Anchored by its `fn-{label}` stable id so inline
9
8
  * footnote references can link to it. Content renders through `NcmBlock`.
10
9
  */
11
- declare function FootnoteBlock({ block, className }: FootnoteBlockProps): react_jsx_runtime.JSX.Element;
10
+ declare function FootnoteBlock({ block, className }: FootnoteBlockProps): react.JSX.Element;
12
11
 
13
12
  export { FootnoteBlock, type FootnoteBlockProps };