@nuucognition/ncm-ui 0.1.1-beta.1 → 0.1.1-beta.2

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.
@@ -1,10 +1,12 @@
1
1
  "use client";
2
- import { createContext, useMemo, useContext, useRef, useState, useEffect, useId, useCallback } from 'react';
2
+ import { createContext, useMemo, useContext, useRef, createElement, Component, useState, useEffect, useId, useCallback } from 'react';
3
+ import { parseNcm, serializeInlineContent, isTypedNestedList } from '@nuucognition/ncm';
3
4
  import { extractExtension, embedTypeFromExtension } from '@nuucognition/mesh-core';
4
5
  import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
5
- import '@nuucognition/ncm';
6
+ import { clsx } from 'clsx';
7
+ import { twMerge } from 'tailwind-merge';
6
8
 
7
- // src/inline/renderer.tsx
9
+ // src/blocks/blockquote/blockquote.tsx
8
10
 
9
11
  // src/inline/parser.ts
10
12
  function splitOnce(input, delimiter) {
@@ -46,6 +48,52 @@ function tryParseInlineRef(input) {
46
48
  }
47
49
  return { token: { type: "inline_ref", kind: "footnote", content: inner.trim() }, length };
48
50
  }
51
+ function runLength(input, index, char) {
52
+ let end = index;
53
+ while (end < input.length && input[end] === char) end++;
54
+ return end - index;
55
+ }
56
+ function tryParseEmphasis(input) {
57
+ const char = input[0];
58
+ if (char !== "*" && char !== "_") return null;
59
+ const open = Math.min(3, runLength(input, 0, char));
60
+ const need = open;
61
+ let i = open;
62
+ while (i < input.length) {
63
+ const ch = input[i];
64
+ if (ch === "`") {
65
+ const close = input.indexOf("`", i + 1);
66
+ if (close === -1) break;
67
+ i = close + 1;
68
+ continue;
69
+ }
70
+ if (ch !== char) {
71
+ i++;
72
+ continue;
73
+ }
74
+ const run = runLength(input, i, char);
75
+ const closes = need === 1 ? run % 2 === 1 : run >= need;
76
+ if (!closes) {
77
+ i += run;
78
+ continue;
79
+ }
80
+ const extra = run - need;
81
+ const inner = input.slice(open, i + extra);
82
+ if (inner.length === 0) return null;
83
+ const length = i + run;
84
+ const children = parseInline(inner);
85
+ let token;
86
+ if (need === 3) {
87
+ token = { type: "bold", children: [{ type: "italic", children }] };
88
+ } else if (need === 2) {
89
+ token = { type: "bold", children };
90
+ } else {
91
+ token = { type: "italic", children };
92
+ }
93
+ return { token, length };
94
+ }
95
+ return null;
96
+ }
49
97
  function parseInline(content) {
50
98
  if (!content) return [];
51
99
  const tokens = [];
@@ -117,17 +165,11 @@ function parseInline(content) {
117
165
  remaining = remaining.slice(linkMatch[0].length);
118
166
  continue;
119
167
  }
120
- const boldMatch = remaining.match(/^\*\*(.+?)\*\*|^__(.+?)__/);
121
- if (boldMatch) {
122
- const innerContent = boldMatch[1] ?? boldMatch[2];
123
- if (innerContent !== void 0) {
124
- tokens.push({
125
- type: "bold",
126
- children: parseInline(innerContent)
127
- });
128
- remaining = remaining.slice(boldMatch[0].length);
129
- continue;
130
- }
168
+ const emphasis = tryParseEmphasis(remaining);
169
+ if (emphasis) {
170
+ tokens.push(emphasis.token);
171
+ remaining = remaining.slice(emphasis.length);
172
+ continue;
131
173
  }
132
174
  const strikeMatch = remaining.match(/^~~(.+?)~~/);
133
175
  if (strikeMatch && strikeMatch[1] !== void 0) {
@@ -138,18 +180,6 @@ function parseInline(content) {
138
180
  remaining = remaining.slice(strikeMatch[0].length);
139
181
  continue;
140
182
  }
141
- const italicMatch = remaining.match(/^\*([^*]+)\*|^_([^_]+)_/);
142
- if (italicMatch) {
143
- const innerContent = italicMatch[1] ?? italicMatch[2];
144
- if (innerContent !== void 0) {
145
- tokens.push({
146
- type: "italic",
147
- children: parseInline(innerContent)
148
- });
149
- remaining = remaining.slice(italicMatch[0].length);
150
- continue;
151
- }
152
- }
153
183
  const nextSpecial = findNextSpecialChar(remaining);
154
184
  if (nextSpecial > 0) {
155
185
  tokens.push({ type: "text", content: remaining.slice(0, nextSpecial) });
@@ -260,17 +290,30 @@ function ReferenceMarker({ kind, number, children }) {
260
290
  }
261
291
  );
262
292
  }
263
- var ReferenceBasesContext = createContext(null);
293
+ function normalizeReferenceContent(content) {
294
+ return content.replace(/\s+/g, " ").trim().toLowerCase();
295
+ }
296
+ var ReferenceNumberingContext = createContext(null);
264
297
  var BlockReferenceScopeContext = createContext(null);
265
- function BlockReferenceScope({ blockId, children }) {
266
- const bases = useContext(ReferenceBasesContext);
267
- const base = (blockId ? bases?.get(blockId) : void 0) ?? { ref: 0, footnote: 0 };
298
+ function BlockReferenceScope({ children }) {
299
+ const numbering = useContext(ReferenceNumberingContext);
300
+ const local = useRef({ ref: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() });
268
301
  const counter = useRef({ ref: 0, footnote: 0 });
269
- counter.current = { ref: 0, footnote: 0 };
270
302
  const value = {
303
+ numberFor(kind, content) {
304
+ const key = normalizeReferenceContent(content);
305
+ const fromDocument = numbering?.[kind].get(key);
306
+ if (fromDocument !== void 0) return fromDocument;
307
+ const map = local.current[kind];
308
+ const existing = map.get(key);
309
+ if (existing !== void 0) return existing;
310
+ counter.current[kind] += 1;
311
+ map.set(key, counter.current[kind]);
312
+ return counter.current[kind];
313
+ },
271
314
  next(kind) {
272
315
  counter.current[kind] += 1;
273
- return base[kind] + counter.current[kind];
316
+ return counter.current[kind];
274
317
  }
275
318
  };
276
319
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value, children });
@@ -278,7 +321,7 @@ function BlockReferenceScope({ blockId, children }) {
278
321
  function useBlockReferenceScope() {
279
322
  return useContext(BlockReferenceScopeContext);
280
323
  }
281
- var INERT_SCOPE = { next: () => 0 };
324
+ var INERT_SCOPE = { numberFor: () => 0, next: () => 0 };
282
325
  function InertReferenceScope({ children }) {
283
326
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value: INERT_SCOPE, children });
284
327
  }
@@ -569,7 +612,7 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
569
612
  );
570
613
  }
571
614
  case "inline_ref": {
572
- const number = refScope?.next(token.kind) ?? 0;
615
+ const number = refScope?.numberFor(token.kind, token.content) ?? 0;
573
616
  return /* @__PURE__ */ jsx(ReferenceMarker, { kind: token.kind, number, children: token.content ? /* @__PURE__ */ jsx(InertReferenceScope, { children: /* @__PURE__ */ jsx(InlineContent, { content: token.content, disableBionic: true, disableBeeline: true }) }) : null });
574
617
  }
575
618
  default:
@@ -635,8 +678,1145 @@ function renderReadingText(content, opts) {
635
678
  if (cursor < content.length) pieces.push(content.slice(cursor));
636
679
  return /* @__PURE__ */ jsx(Fragment, { children: pieces });
637
680
  }
681
+ function blockKey(block) {
682
+ return block.stableId ? `${block.id}:${block.stableId}` : block.id;
683
+ }
684
+ function blockDataAttributes(block) {
685
+ return {
686
+ "data-ncm-block": block.id,
687
+ "data-ncm-stable": block.stableId,
688
+ "data-ncm-type": block.type,
689
+ "data-block-id": block.id
690
+ };
691
+ }
692
+ function BlockShell({
693
+ as = "div",
694
+ block,
695
+ children,
696
+ ...props
697
+ }) {
698
+ return createElement(as, {
699
+ ...blockDataAttributes(block),
700
+ ...props
701
+ }, children);
702
+ }
703
+ function ChildBlocks({
704
+ as = "div",
705
+ blocks,
706
+ ...props
707
+ }) {
708
+ return createElement(
709
+ as,
710
+ props,
711
+ blocks.map((block) => /* @__PURE__ */ jsx(NcmBlock, { block }, blockKey(block)))
712
+ );
713
+ }
714
+ function cn(...inputs) {
715
+ return twMerge(clsx(inputs));
716
+ }
717
+ function defaultTitle(type) {
718
+ return type.charAt(0).toUpperCase() + type.slice(1);
719
+ }
720
+ function AdmonitionBlock({ block, className }) {
721
+ const title = block.title ?? defaultTitle(block.admonitionType);
722
+ const classes = cn("rounded-lg border border-border bg-card p-4", className);
723
+ if (block.collapsible) {
724
+ return /* @__PURE__ */ jsxs(
725
+ BlockShell,
726
+ {
727
+ as: "details",
728
+ block,
729
+ "data-ncm-admonition": "",
730
+ "data-ncm-admonition-type": block.admonitionType,
731
+ open: !block.collapsed,
732
+ className: classes,
733
+ children: [
734
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer font-medium", "data-ncm-admonition-title": "", children: title }),
735
+ /* @__PURE__ */ jsx(ChildBlocks, { blocks: block.children, className: "mt-2 space-y-2" })
736
+ ]
737
+ }
738
+ );
739
+ }
740
+ return /* @__PURE__ */ jsxs(
741
+ BlockShell,
742
+ {
743
+ block,
744
+ "data-ncm-admonition": "",
745
+ "data-ncm-admonition-type": block.admonitionType,
746
+ className: classes,
747
+ children: [
748
+ /* @__PURE__ */ jsx("p", { className: "mb-2 font-medium", "data-ncm-admonition-title": "", children: title }),
749
+ /* @__PURE__ */ jsx(ChildBlocks, { blocks: block.children, className: "space-y-2" })
750
+ ]
751
+ }
752
+ );
753
+ }
754
+ function AsideBlock({ block, className }) {
755
+ return /* @__PURE__ */ jsx(
756
+ BlockShell,
757
+ {
758
+ as: "aside",
759
+ block,
760
+ "data-ncm-aside": "",
761
+ className: cn("border-l-2 border-border pl-4 text-muted-foreground", className),
762
+ children: /* @__PURE__ */ jsx(ChildBlocks, { blocks: block.content, className: "space-y-2" })
763
+ }
764
+ );
765
+ }
766
+ function AttributionBlock({ block, className }) {
767
+ return /* @__PURE__ */ jsxs(
768
+ "p",
769
+ {
770
+ "data-ncm-block": block.id,
771
+ "data-ncm-stable": block.stableId,
772
+ "data-ncm-type": block.type,
773
+ "data-block-id": block.id,
774
+ "data-ncm-attribution": "",
775
+ className: cn("text-sm text-muted-foreground", className),
776
+ children: [
777
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2014 " }),
778
+ block.url ? /* @__PURE__ */ jsx("a", { href: block.url, className: "hover:underline", children: /* @__PURE__ */ jsx("cite", { children: block.source }) }) : /* @__PURE__ */ jsx("cite", { children: block.source }),
779
+ block.date ? /* @__PURE__ */ jsxs("span", { children: [
780
+ ", ",
781
+ block.date
782
+ ] }) : null
783
+ ]
784
+ }
785
+ );
786
+ }
787
+ function ChartBlock({ block, className }) {
788
+ return /* @__PURE__ */ jsx(
789
+ "pre",
790
+ {
791
+ "data-ncm-block": block.id,
792
+ "data-ncm-stable": block.stableId,
793
+ "data-ncm-type": block.type,
794
+ "data-block-id": block.id,
795
+ "data-ncm-chart": "",
796
+ "data-ncm-chart-type": block.chartType ?? "unknown",
797
+ className: cn(
798
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
799
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
800
+ className
801
+ ),
802
+ children: /* @__PURE__ */ jsx("code", { children: block.content })
803
+ }
804
+ );
805
+ }
806
+ var highlighterPromise = import('shiki/bundle/web').then(async (shiki) => {
807
+ const highlighter = await shiki.createHighlighter({
808
+ themes: ["github-light", "github-dark"],
809
+ langs: Object.keys(shiki.bundledLanguages)
810
+ });
811
+ return highlighter;
812
+ }).catch(() => null);
813
+ function useIsDark() {
814
+ const [isDark, setIsDark] = useState(false);
815
+ useEffect(() => {
816
+ if (typeof document === "undefined") return;
817
+ const root = document.documentElement;
818
+ const update = () => setIsDark(root.classList.contains("dark"));
819
+ update();
820
+ const observer = new MutationObserver(update);
821
+ observer.observe(root, { attributes: true, attributeFilter: ["class"] });
822
+ return () => observer.disconnect();
823
+ }, []);
824
+ return isDark;
825
+ }
826
+ function DefaultShikiRenderer({
827
+ code,
828
+ language
829
+ }) {
830
+ const isDark = useIsDark();
831
+ const [highlighter, setHighlighter] = useState(null);
832
+ const shikiTheme = isDark ? "github-dark" : "github-light";
833
+ useEffect(() => {
834
+ let active = true;
835
+ highlighterPromise.then((h) => {
836
+ if (active) setHighlighter(h);
837
+ });
838
+ return () => {
839
+ active = false;
840
+ };
841
+ }, []);
842
+ const tokens = useMemo(() => {
843
+ if (!highlighter) return null;
844
+ const loaded = highlighter.getLoadedLanguages();
845
+ const lang = language && loaded.includes(language) ? language : "text";
846
+ try {
847
+ return highlighter.codeToTokens(code, { lang, theme: shikiTheme }).tokens;
848
+ } catch {
849
+ return null;
850
+ }
851
+ }, [code, language, highlighter, shikiTheme]);
852
+ if (!tokens) {
853
+ return /* @__PURE__ */ jsx(Fragment, { children: code });
854
+ }
855
+ return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((line, lineIndex) => /* @__PURE__ */ jsx("span", { className: "block whitespace-pre", children: line.length === 0 ? " " : line.map((token, tokenIndex) => /* @__PURE__ */ jsx("span", { style: { color: token.color || "inherit" }, children: token.content }, tokenIndex)) }, lineIndex)) });
856
+ }
857
+ function CopyButton({ content }) {
858
+ const [copied, setCopied] = useState(false);
859
+ const handleCopy = useCallback(async () => {
860
+ try {
861
+ await navigator.clipboard.writeText(content);
862
+ setCopied(true);
863
+ setTimeout(() => setCopied(false), 2e3);
864
+ } catch {
865
+ const textarea = document.createElement("textarea");
866
+ textarea.value = content;
867
+ document.body.appendChild(textarea);
868
+ textarea.select();
869
+ document.execCommand("copy");
870
+ document.body.removeChild(textarea);
871
+ setCopied(true);
872
+ setTimeout(() => setCopied(false), 2e3);
873
+ }
874
+ }, [content]);
875
+ return /* @__PURE__ */ jsx(
876
+ "button",
877
+ {
878
+ type: "button",
879
+ onClick: handleCopy,
880
+ "aria-label": copied ? "Copied!" : "Copy code",
881
+ "data-ncm-copy-button": "",
882
+ className: "absolute right-2 top-2 rounded-md bg-background/80 px-2 py-1 text-xs font-medium text-muted-foreground hover:bg-background hover:text-foreground focus:outline-none focus:ring-2 focus:ring-ring",
883
+ children: copied ? "Copied!" : "Copy"
884
+ }
885
+ );
886
+ }
887
+ function LineNumbers({ count }) {
888
+ return /* @__PURE__ */ jsx(
889
+ "div",
890
+ {
891
+ "aria-hidden": "true",
892
+ "data-ncm-line-numbers": "",
893
+ className: "select-none pr-4 text-right text-muted-foreground/60",
894
+ children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx("div", { children: i + 1 }, i + 1))
895
+ }
896
+ );
897
+ }
898
+ function CodeBlock({ block, className }) {
899
+ const config = useNcmConfig();
900
+ const { code: codeConfig } = config;
901
+ const lineCount = block.content.split("\n").length;
902
+ const languageLabel = block.language ? /* @__PURE__ */ jsx(
903
+ "div",
904
+ {
905
+ "data-ncm-code-language": "",
906
+ className: "absolute right-2 bottom-2 text-xs text-muted-foreground/60",
907
+ children: block.language
908
+ }
909
+ ) : null;
910
+ if (codeConfig?.renderer) {
911
+ const CustomRenderer = codeConfig.renderer;
912
+ return /* @__PURE__ */ jsxs(
913
+ "div",
914
+ {
915
+ role: "region",
916
+ "aria-label": block.language ? `Code block: ${block.language}` : "Code block",
917
+ "data-ncm-block": block.id,
918
+ "data-ncm-stable": block.stableId,
919
+ "data-ncm-type": block.type,
920
+ "data-block-id": block.id,
921
+ "data-ncm-language": block.language,
922
+ className: cn("relative", className),
923
+ children: [
924
+ codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
925
+ /* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
926
+ languageLabel
927
+ ]
928
+ }
929
+ );
930
+ }
931
+ return /* @__PURE__ */ jsxs(
932
+ "div",
933
+ {
934
+ role: "region",
935
+ "aria-label": block.language ? `Code block: ${block.language}` : "Code block",
936
+ "data-ncm-block": block.id,
937
+ "data-ncm-stable": block.stableId,
938
+ "data-ncm-type": block.type,
939
+ "data-block-id": block.id,
940
+ "data-ncm-language": block.language,
941
+ className: cn("relative", className),
942
+ children: [
943
+ codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
944
+ /* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
945
+ codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
946
+ /* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
947
+ ] }) }),
948
+ languageLabel
949
+ ]
950
+ }
951
+ );
952
+ }
953
+ function ContainerBlock({ block, className }) {
954
+ return /* @__PURE__ */ jsxs(
955
+ BlockShell,
956
+ {
957
+ block,
958
+ "data-ncm-container": "",
959
+ "data-ncm-directive": block.directive,
960
+ className: cn("rounded-lg border border-border bg-card p-4", className),
961
+ children: [
962
+ block.directive && /* @__PURE__ */ jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsx("span", { className: "inline-flex items-center rounded-md bg-muted px-1.5 py-0.5 text-xs font-medium uppercase tracking-wide text-muted-foreground", children: block.directive }) }),
963
+ /* @__PURE__ */ jsx(InlineContent, { content: block.innerContent })
964
+ ]
965
+ }
966
+ );
967
+ }
968
+ function DataviewBlock({ block, className }) {
969
+ return /* @__PURE__ */ jsx(
970
+ "pre",
971
+ {
972
+ "data-ncm-block": block.id,
973
+ "data-ncm-stable": block.stableId,
974
+ "data-ncm-type": block.type,
975
+ "data-block-id": block.id,
976
+ "data-ncm-dataview": "",
977
+ "data-ncm-query-type": block.queryType,
978
+ className: cn(
979
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
980
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
981
+ className
982
+ ),
983
+ children: /* @__PURE__ */ jsx("code", { children: block.query })
984
+ }
985
+ );
986
+ }
987
+ function DefinitionBlock({ block, className }) {
988
+ return /* @__PURE__ */ jsxs(
989
+ BlockShell,
990
+ {
991
+ as: "dl",
992
+ block,
993
+ "data-ncm-definition": "",
994
+ className: cn("space-y-1", className),
995
+ children: [
996
+ /* @__PURE__ */ jsx("dt", { className: "font-medium", children: /* @__PURE__ */ jsx(InlineContent, { content: serializeInlineContent(block.term) }) }),
997
+ /* @__PURE__ */ jsx(ChildBlocks, { as: "dd", blocks: block.definition, className: "ml-4 space-y-2" })
998
+ ]
999
+ }
1000
+ );
1001
+ }
1002
+ function toEmbeddingConfigType(embedType) {
1003
+ switch (embedType) {
1004
+ case "image":
1005
+ return "image";
1006
+ case "document":
1007
+ return "pdf";
1008
+ case "file":
1009
+ default:
1010
+ return "generic_file";
1011
+ }
1012
+ }
1013
+ function dataAttrs(block) {
1014
+ return {
1015
+ "data-ncm-block": block.id,
1016
+ "data-ncm-stable": block.stableId,
1017
+ "data-ncm-type": block.type,
1018
+ "data-block-id": block.id,
1019
+ "data-ncm-embed": "",
1020
+ "data-ncm-embed-type": block.embedType
1021
+ };
1022
+ }
1023
+ function EmbedBlock({ block, className }) {
1024
+ const config = useNcmConfig();
1025
+ const configEmbedType = toEmbeddingConfigType(block.embedType);
1026
+ const resolvedUrl = config.embeddings?.resolveUrl?.(block.src, configEmbedType) ?? block.src;
1027
+ if (block.embedType === "image") {
1028
+ return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className: cn(config.embeddings?.imageClassName, className), children: /* @__PURE__ */ jsx("img", { src: resolvedUrl, alt: block.alt, title: block.title ?? void 0 }) });
1029
+ }
1030
+ return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className, children: /* @__PURE__ */ jsx("a", { href: resolvedUrl, title: block.title ?? void 0, children: block.alt || block.src }) });
1031
+ }
1032
+ var embedFrameStyle = {
1033
+ display: "block",
1034
+ width: "100%",
1035
+ maxWidth: "100%",
1036
+ height: "min(80vh, 900px)",
1037
+ minHeight: "min(360px, 80vh)"
1038
+ };
1039
+ var embedMediaStyle = {
1040
+ display: "block",
1041
+ maxWidth: "100%",
1042
+ height: "auto"
1043
+ };
1044
+ function EmbeddingBlock({ block, className }) {
1045
+ const config = useNcmConfig();
1046
+ const resolvedUrl = config.embeddings?.resolveUrl?.(block.target, block.embed_type) ?? block.target;
1047
+ const CustomRenderer = config.embeddings?.renderers?.[block.embed_type];
1048
+ if (CustomRenderer) {
1049
+ return /* @__PURE__ */ jsx(CustomRenderer, { block, className, resolvedUrl });
1050
+ }
1051
+ switch (block.embed_type) {
1052
+ case "image":
1053
+ return /* @__PURE__ */ jsx(
1054
+ ImageEmbed,
1055
+ {
1056
+ block,
1057
+ resolvedUrl,
1058
+ className: cn(config.embeddings?.imageClassName, className)
1059
+ }
1060
+ );
1061
+ case "video":
1062
+ return /* @__PURE__ */ jsx(
1063
+ VideoEmbed,
1064
+ {
1065
+ block,
1066
+ resolvedUrl,
1067
+ className: cn(config.embeddings?.videoClassName, className)
1068
+ }
1069
+ );
1070
+ case "audio":
1071
+ return /* @__PURE__ */ jsx(
1072
+ AudioEmbed,
1073
+ {
1074
+ block,
1075
+ resolvedUrl,
1076
+ className: cn(config.embeddings?.audioClassName, className)
1077
+ }
1078
+ );
1079
+ case "pdf":
1080
+ return /* @__PURE__ */ jsx(PdfEmbed, { block, resolvedUrl, className });
1081
+ case "ncm_embed":
1082
+ return /* @__PURE__ */ jsx(NcmEmbed, { block, resolvedUrl, className });
1083
+ case "iframe":
1084
+ return /* @__PURE__ */ jsx(IframeEmbed, { block, resolvedUrl, className });
1085
+ case "generic_file":
1086
+ default:
1087
+ return /* @__PURE__ */ jsx(GenericFileEmbed, { block, resolvedUrl, className });
1088
+ }
1089
+ }
1090
+ function dataAttrs2(block, embedType) {
1091
+ return {
1092
+ "data-ncm-block": block.id,
1093
+ "data-ncm-stable": block.stableId,
1094
+ "data-ncm-type": block.type,
1095
+ "data-block-id": block.id,
1096
+ "data-ncm-embedding": "",
1097
+ "data-ncm-embed-type": embedType
1098
+ };
1099
+ }
1100
+ function ImageEmbed({ block, resolvedUrl, className }) {
1101
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "image"), className, children: [
1102
+ /* @__PURE__ */ jsx("img", { src: resolvedUrl, alt: block.caption ?? "" }),
1103
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1104
+ ] });
1105
+ }
1106
+ function VideoEmbed({ block, resolvedUrl, className }) {
1107
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "video"), className, children: [
1108
+ /* @__PURE__ */ jsx("video", { src: resolvedUrl, controls: true }),
1109
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1110
+ ] });
1111
+ }
1112
+ function AudioEmbed({ block, resolvedUrl, className }) {
1113
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "audio"), className, children: [
1114
+ /* @__PURE__ */ jsx("audio", { src: resolvedUrl, controls: true, className: "w-full" }),
1115
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1116
+ ] });
1117
+ }
1118
+ function PdfEmbed({ block, resolvedUrl, className }) {
1119
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "pdf"), className: cn("w-full max-w-full", className), children: [
1120
+ /* @__PURE__ */ jsx(
1121
+ "iframe",
1122
+ {
1123
+ src: resolvedUrl,
1124
+ title: block.caption ?? "PDF document",
1125
+ className: "rounded-lg border border-border",
1126
+ style: embedFrameStyle
1127
+ }
1128
+ ),
1129
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1130
+ ] });
1131
+ }
1132
+ function IframeEmbed({ block, resolvedUrl, className }) {
1133
+ const [useFrame, setUseFrame] = useState(false);
1134
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "iframe"), className: cn("w-full max-w-full", className), children: [
1135
+ useFrame ? /* @__PURE__ */ jsx(
1136
+ "iframe",
1137
+ {
1138
+ src: resolvedUrl,
1139
+ title: block.caption ?? block.alt ?? block.target,
1140
+ className: "rounded-lg border border-border",
1141
+ style: embedFrameStyle
1142
+ }
1143
+ ) : /* @__PURE__ */ jsx(
1144
+ "img",
1145
+ {
1146
+ src: resolvedUrl,
1147
+ alt: block.alt || block.caption || block.target,
1148
+ style: embedMediaStyle,
1149
+ onError: () => setUseFrame(true)
1150
+ }
1151
+ ),
1152
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1153
+ ] });
1154
+ }
1155
+ function NcmEmbed({ block, resolvedUrl, className }) {
1156
+ return /* @__PURE__ */ jsxs(
1157
+ "div",
1158
+ {
1159
+ ...dataAttrs2(block, "ncm_embed"),
1160
+ className: cn("rounded-lg border border-dashed border-border bg-muted p-4", className),
1161
+ children: [
1162
+ /* @__PURE__ */ jsxs("div", { className: "text-sm text-muted-foreground", children: [
1163
+ "Embedded: ",
1164
+ /* @__PURE__ */ jsx("code", { className: "rounded bg-background px-1", children: block.target }),
1165
+ resolvedUrl !== block.target && /* @__PURE__ */ jsxs(Fragment, { children: [
1166
+ " ",
1167
+ /* @__PURE__ */ jsx("a", { href: resolvedUrl, className: "underline underline-offset-4", children: "open" })
1168
+ ] })
1169
+ ] }),
1170
+ block.caption && /* @__PURE__ */ jsx("div", { className: "mt-2 text-sm text-muted-foreground", children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1171
+ ]
1172
+ }
1173
+ );
1174
+ }
1175
+ function GenericFileEmbed({ block, resolvedUrl, className }) {
1176
+ return /* @__PURE__ */ jsxs("figure", { ...dataAttrs2(block, "generic_file"), className, children: [
1177
+ /* @__PURE__ */ jsxs(
1178
+ "a",
1179
+ {
1180
+ href: resolvedUrl,
1181
+ download: true,
1182
+ className: "flex items-center gap-2 rounded-lg border border-border bg-muted p-4 hover:bg-background",
1183
+ children: [
1184
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: block.target }),
1185
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
1186
+ "(",
1187
+ block.file_format.toUpperCase(),
1188
+ ")"
1189
+ ] })
1190
+ ]
1191
+ }
1192
+ ),
1193
+ block.caption && /* @__PURE__ */ jsx("figcaption", { children: /* @__PURE__ */ jsx(InlineContent, { content: block.caption }) })
1194
+ ] });
1195
+ }
1196
+ function FigureBlock({ block, className }) {
1197
+ const { embed, caption, number } = block;
1198
+ return /* @__PURE__ */ jsxs(
1199
+ BlockShell,
1200
+ {
1201
+ as: "figure",
1202
+ block,
1203
+ "data-ncm-figure": "",
1204
+ className: cn("space-y-2", className),
1205
+ children: [
1206
+ embed.embedType === "image" ? /* @__PURE__ */ jsx("img", { src: embed.src, alt: embed.alt, title: embed.title ?? void 0 }) : /* @__PURE__ */ jsx(
1207
+ "a",
1208
+ {
1209
+ href: embed.src,
1210
+ title: embed.title ?? void 0,
1211
+ "data-ncm-embed-type": embed.embedType,
1212
+ children: embed.alt || embed.src
1213
+ }
1214
+ ),
1215
+ /* @__PURE__ */ jsxs(
1216
+ "figcaption",
1217
+ {
1218
+ className: "text-sm text-muted-foreground",
1219
+ "data-ncm-figure-number": number,
1220
+ children: [
1221
+ typeof number === "number" ? /* @__PURE__ */ jsxs("span", { "data-ncm-figure-label": "", children: [
1222
+ "Figure ",
1223
+ number,
1224
+ ". "
1225
+ ] }) : null,
1226
+ /* @__PURE__ */ jsx(InlineContent, { content: serializeInlineContent(caption.content) })
1227
+ ]
1228
+ }
1229
+ )
1230
+ ]
1231
+ }
1232
+ );
1233
+ }
1234
+ function FootnoteBlock({ block, className }) {
1235
+ return /* @__PURE__ */ jsxs(
1236
+ BlockShell,
1237
+ {
1238
+ id: block.stableId,
1239
+ block,
1240
+ "data-ncm-footnote": "",
1241
+ "data-ncm-footnote-label": block.label,
1242
+ className: cn("flex gap-2 text-sm text-muted-foreground", className),
1243
+ children: [
1244
+ /* @__PURE__ */ jsxs("span", { "data-ncm-footnote-marker": "", className: "font-medium", children: [
1245
+ block.label,
1246
+ "."
1247
+ ] }),
1248
+ /* @__PURE__ */ jsx(ChildBlocks, { blocks: block.content, className: "space-y-2" })
1249
+ ]
1250
+ }
1251
+ );
1252
+ }
1253
+ function formatValue(value) {
1254
+ if (Array.isArray(value)) return value.map((v) => String(v)).join(", ");
1255
+ if (value === null || value === void 0) return "";
1256
+ if (typeof value === "object") return JSON.stringify(value);
1257
+ return String(value);
1258
+ }
1259
+ function FrontmatterBlock({ block, className }) {
1260
+ const entries = Object.entries(block.data ?? {});
1261
+ return /* @__PURE__ */ jsx(
1262
+ "dl",
1263
+ {
1264
+ "data-ncm-block": block.id,
1265
+ "data-ncm-stable": block.stableId,
1266
+ "data-ncm-type": block.type,
1267
+ "data-block-id": block.id,
1268
+ "data-ncm-frontmatter": "",
1269
+ className: cn(
1270
+ "grid grid-cols-[auto_1fr] gap-x-4 gap-y-1 rounded-lg border border-border bg-muted/30 p-4 text-sm",
1271
+ className
1272
+ ),
1273
+ children: entries.map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "contents", children: [
1274
+ /* @__PURE__ */ jsx("dt", { className: "font-medium text-muted-foreground", children: key }),
1275
+ /* @__PURE__ */ jsx("dd", { className: "text-foreground", children: formatValue(value) })
1276
+ ] }, key))
1277
+ }
1278
+ );
1279
+ }
1280
+ function HeaderBlock({ block, className }) {
1281
+ const Tag = `h${block.level}`;
1282
+ return /* @__PURE__ */ jsx(
1283
+ Tag,
1284
+ {
1285
+ id: block.stableId ?? block.id,
1286
+ "data-ncm-block": block.id,
1287
+ "data-ncm-stable": block.stableId,
1288
+ "data-ncm-type": block.type,
1289
+ "data-block-id": block.id,
1290
+ "data-ncm-level": block.level,
1291
+ className,
1292
+ children: /* @__PURE__ */ jsx(InlineContent, { content: block.content, disableBionic: true, disableBeeline: true })
1293
+ }
1294
+ );
1295
+ }
1296
+ function HorizontalRuleBlock({ block, className }) {
1297
+ return /* @__PURE__ */ jsx(
1298
+ "hr",
1299
+ {
1300
+ "data-ncm-block": block.id,
1301
+ "data-ncm-stable": block.stableId,
1302
+ "data-ncm-type": block.type,
1303
+ "data-block-id": block.id,
1304
+ "data-ncm-horizontal-rule": "",
1305
+ className
1306
+ }
1307
+ );
1308
+ }
1309
+ function HtmlBlock({ block, className }) {
1310
+ return /* @__PURE__ */ jsx(
1311
+ "pre",
1312
+ {
1313
+ "data-ncm-block": block.id,
1314
+ "data-ncm-stable": block.stableId,
1315
+ "data-ncm-type": block.type,
1316
+ "data-block-id": block.id,
1317
+ "data-ncm-html": "",
1318
+ className: cn(
1319
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1320
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1321
+ className
1322
+ ),
1323
+ children: /* @__PURE__ */ jsx("code", { children: block.content })
1324
+ }
1325
+ );
1326
+ }
1327
+ function LatexDisplayBlock({ block, className }) {
1328
+ const config = useNcmConfig();
1329
+ const CustomRenderer = config.inline?.customLatexRenderer;
1330
+ const html = useKatexHtml(block.content, true, !CustomRenderer);
1331
+ if (CustomRenderer) {
1332
+ return /* @__PURE__ */ jsx(
1333
+ "div",
1334
+ {
1335
+ role: "region",
1336
+ "aria-label": "Mathematical expression",
1337
+ "data-ncm-block": block.id,
1338
+ "data-ncm-stable": block.stableId,
1339
+ "data-ncm-type": block.type,
1340
+ "data-block-id": block.id,
1341
+ "data-ncm-latex-display": "",
1342
+ className,
1343
+ children: /* @__PURE__ */ jsx(CustomRenderer, { content: block.content, display: true })
1344
+ }
1345
+ );
1346
+ }
1347
+ if (html) {
1348
+ return /* @__PURE__ */ jsx(
1349
+ "div",
1350
+ {
1351
+ role: "region",
1352
+ "aria-label": "Mathematical expression",
1353
+ "data-ncm-block": block.id,
1354
+ "data-ncm-stable": block.stableId,
1355
+ "data-ncm-type": block.type,
1356
+ "data-block-id": block.id,
1357
+ "data-ncm-latex-display": "",
1358
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1359
+ dangerouslySetInnerHTML: { __html: html }
1360
+ }
1361
+ );
1362
+ }
1363
+ return /* @__PURE__ */ jsx(
1364
+ "pre",
1365
+ {
1366
+ role: "region",
1367
+ "aria-label": "Mathematical expression (source)",
1368
+ "data-ncm-block": block.id,
1369
+ "data-ncm-stable": block.stableId,
1370
+ "data-ncm-type": block.type,
1371
+ "data-block-id": block.id,
1372
+ "data-ncm-latex-display": "",
1373
+ className: cn("overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1374
+ children: /* @__PURE__ */ jsx("code", { children: block.content })
1375
+ }
1376
+ );
1377
+ }
1378
+ function LinkBlock({ block, className }) {
1379
+ return /* @__PURE__ */ jsx(
1380
+ "a",
1381
+ {
1382
+ "data-ncm-block": block.id,
1383
+ "data-ncm-stable": block.stableId,
1384
+ "data-ncm-type": block.type,
1385
+ "data-block-id": block.id,
1386
+ "data-ncm-link": "",
1387
+ href: block.url,
1388
+ title: block.title ?? void 0,
1389
+ className: cn("block text-primary hover:underline", className),
1390
+ children: block.text || block.url
1391
+ }
1392
+ );
1393
+ }
1394
+ function ListBlock({ block, className }) {
1395
+ const isOrdered = block.type === "list_ordered";
1396
+ const Tag = isOrdered ? "ol" : "ul";
1397
+ const start = isOrdered && typeof block.marker === "number" && block.marker !== 1 ? block.marker : void 0;
1398
+ return /* @__PURE__ */ jsx(
1399
+ Tag,
1400
+ {
1401
+ role: "list",
1402
+ start,
1403
+ "data-ncm-block": block.id,
1404
+ "data-ncm-stable": block.stableId,
1405
+ "data-ncm-type": block.type,
1406
+ "data-block-id": block.id,
1407
+ "data-ncm-list": "",
1408
+ "data-ncm-list-type": isOrdered ? "ordered" : "unordered",
1409
+ className,
1410
+ children: /* @__PURE__ */ jsx(ListItems, { items: block.items, itemIds: block.itemIds, ordered: isOrdered })
1411
+ }
1412
+ );
1413
+ }
1414
+ function ItemContent({ content }) {
1415
+ return content.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" });
1416
+ }
1417
+ function nestedItemIdKey(id) {
1418
+ if (!id) {
1419
+ return void 0;
1420
+ }
1421
+ if (typeof id === "string") {
1422
+ return id;
1423
+ }
1424
+ if (Array.isArray(id)) {
1425
+ return id.map(nestedItemIdKey).filter(Boolean).join(".");
1426
+ }
1427
+ return `${id.kind}:${id.items.map(nestedItemIdKey).filter(Boolean).join(".")}`;
1428
+ }
1429
+ function ListItems({ items, itemIds, ordered }) {
1430
+ return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, index) => {
1431
+ const key = nestedItemIdKey(itemIds?.[index]) ?? `item-${index}`;
1432
+ if (typeof item === "string") {
1433
+ return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(ItemContent, { content: item }) }, key);
1434
+ }
1435
+ if (isTypedNestedList(item)) {
1436
+ return /* @__PURE__ */ jsx(NestedTypedList, { list: item }, key);
1437
+ }
1438
+ if (Array.isArray(item) && item.length > 0) {
1439
+ const [first, ...rest] = item;
1440
+ const content = typeof first === "string" ? first : "";
1441
+ const nestedContent = processNestedItems(rest, ordered);
1442
+ return /* @__PURE__ */ jsxs("li", { "data-ncm-list-item": "", children: [
1443
+ /* @__PURE__ */ jsx(ItemContent, { content }),
1444
+ nestedContent
1445
+ ] }, key);
1446
+ }
1447
+ return null;
1448
+ }) });
1449
+ }
1450
+ function processNestedItems(items, parentOrdered) {
1451
+ if (items.length === 0) return null;
1452
+ const groups = [];
1453
+ for (const item of items) {
1454
+ if (isTypedNestedList(item)) {
1455
+ const isOrdered = item.kind === "ordered";
1456
+ const lastGroup = groups[groups.length - 1];
1457
+ if (lastGroup && lastGroup.ordered === isOrdered) {
1458
+ lastGroup.items.push(...item.items);
1459
+ } else {
1460
+ groups.push({ ordered: isOrdered, items: [...item.items] });
1461
+ }
1462
+ } else {
1463
+ const lastGroup = groups[groups.length - 1];
1464
+ if (lastGroup && lastGroup.ordered === parentOrdered) {
1465
+ lastGroup.items.push(item);
1466
+ } else {
1467
+ groups.push({ ordered: parentOrdered, items: [item] });
1468
+ }
1469
+ }
1470
+ }
1471
+ return groups.map((group, index) => {
1472
+ const Tag = group.ordered ? "ol" : "ul";
1473
+ return /* @__PURE__ */ jsx(Tag, { "data-ncm-list-nested": "", children: /* @__PURE__ */ jsx(ListItems, { items: group.items, ordered: group.ordered }) }, index);
1474
+ });
1475
+ }
1476
+ function NestedTypedList({ list }) {
1477
+ const Tag = list.kind === "ordered" ? "ol" : "ul";
1478
+ return /* @__PURE__ */ jsx(Tag, { "data-ncm-list-nested": "", children: /* @__PURE__ */ jsx(ListItems, { items: list.items, ordered: list.kind === "ordered" }) });
1479
+ }
1480
+ function MathBlock({ block, className }) {
1481
+ const html = useKatexHtml(block.content, true);
1482
+ if (html) {
1483
+ return /* @__PURE__ */ jsx(
1484
+ "div",
1485
+ {
1486
+ role: "region",
1487
+ "aria-label": "Mathematical expression",
1488
+ "data-ncm-block": block.id,
1489
+ "data-ncm-stable": block.stableId,
1490
+ "data-ncm-type": block.type,
1491
+ "data-block-id": block.id,
1492
+ "data-ncm-math": "",
1493
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1494
+ dangerouslySetInnerHTML: { __html: html }
1495
+ }
1496
+ );
1497
+ }
1498
+ return /* @__PURE__ */ jsx(
1499
+ "pre",
1500
+ {
1501
+ role: "region",
1502
+ "aria-label": "Mathematical expression (source)",
1503
+ "data-ncm-block": block.id,
1504
+ "data-ncm-stable": block.stableId,
1505
+ "data-ncm-type": block.type,
1506
+ "data-block-id": block.id,
1507
+ "data-ncm-math": "",
1508
+ className: cn(
1509
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1510
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1511
+ className
1512
+ ),
1513
+ children: /* @__PURE__ */ jsx("code", { children: block.content })
1514
+ }
1515
+ );
1516
+ }
1517
+ function MermaidBlock({ block, className }) {
1518
+ return /* @__PURE__ */ jsx(
1519
+ "pre",
1520
+ {
1521
+ "data-ncm-block": block.id,
1522
+ "data-ncm-stable": block.stableId,
1523
+ "data-ncm-type": block.type,
1524
+ "data-block-id": block.id,
1525
+ "data-ncm-mermaid": "",
1526
+ "data-ncm-diagram-type": block.diagramType,
1527
+ className: cn(
1528
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1529
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1530
+ className
1531
+ ),
1532
+ children: /* @__PURE__ */ jsx("code", { children: block.content })
1533
+ }
1534
+ );
1535
+ }
1536
+ function PageBreakerBlock({ block, className }) {
1537
+ return /* @__PURE__ */ jsx(
1538
+ "div",
1539
+ {
1540
+ "data-ncm-block": block.id,
1541
+ "data-ncm-stable": block.stableId,
1542
+ "data-ncm-type": block.type,
1543
+ "data-block-id": block.id,
1544
+ "data-ncm-page": block.page,
1545
+ "aria-hidden": "true",
1546
+ className,
1547
+ style: { height: 0, margin: 0, padding: 0, border: 0 }
1548
+ }
1549
+ );
1550
+ }
1551
+ function ParagraphBlock({ block, className }) {
1552
+ return /* @__PURE__ */ jsx(
1553
+ "p",
1554
+ {
1555
+ "data-ncm-block": block.id,
1556
+ "data-ncm-stable": block.stableId,
1557
+ "data-ncm-type": block.type,
1558
+ "data-block-id": block.id,
1559
+ "data-ncm-paragraph": "",
1560
+ className,
1561
+ children: /* @__PURE__ */ jsx(InlineContent, { content: block.content })
1562
+ }
1563
+ );
1564
+ }
1565
+ function SpoilerBlock({ block, className }) {
1566
+ return /* @__PURE__ */ jsxs(
1567
+ BlockShell,
1568
+ {
1569
+ as: "details",
1570
+ block,
1571
+ "data-ncm-spoiler": "",
1572
+ open: block.open,
1573
+ className: cn("rounded-lg border border-border bg-card p-4", className),
1574
+ children: [
1575
+ /* @__PURE__ */ jsx("summary", { className: "cursor-pointer font-medium", children: block.summary }),
1576
+ /* @__PURE__ */ jsx(ChildBlocks, { blocks: block.children, className: "mt-2 space-y-2" })
1577
+ ]
1578
+ }
1579
+ );
1580
+ }
1581
+ function TableBlock({ block, className }) {
1582
+ return /* @__PURE__ */ jsx(
1583
+ "div",
1584
+ {
1585
+ "data-ncm-block": block.id,
1586
+ "data-ncm-stable": block.stableId,
1587
+ "data-ncm-type": block.type,
1588
+ "data-block-id": block.id,
1589
+ "data-ncm-table": "",
1590
+ className: cn("w-full overflow-x-auto overflow-y-hidden", className),
1591
+ children: /* @__PURE__ */ jsxs("table", { role: "table", "data-ncm-table-element": "", children: [
1592
+ /* @__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)) }) }),
1593
+ /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, rowIndex) => {
1594
+ const rowKey = block.rowIds?.[rowIndex] ?? `${block.id}.${rowIndex + 1}`;
1595
+ return /* @__PURE__ */ jsx("tr", { "data-ncm-table-row": "", children: row.map((cell, cellIndex) => /* @__PURE__ */ jsx("td", { "data-ncm-table-cell": "", children: /* @__PURE__ */ jsx(InlineContent, { content: cell }) }, `${rowKey}.${cellIndex + 1}`)) }, rowKey);
1596
+ }) })
1597
+ ] })
1598
+ }
1599
+ );
1600
+ }
1601
+ function TaskItems({ items }) {
1602
+ return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
1603
+ const status = item.checked ? "Completed" : "Incomplete";
1604
+ const text = serializeInlineContent(item.content);
1605
+ return /* @__PURE__ */ jsxs(
1606
+ "li",
1607
+ {
1608
+ "data-ncm-task-item": "",
1609
+ "data-block-id": item.id,
1610
+ "data-ncm-checked": item.checked ? "true" : "false",
1611
+ className: "flex flex-col",
1612
+ children: [
1613
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
1614
+ /* @__PURE__ */ jsx(
1615
+ "input",
1616
+ {
1617
+ type: "checkbox",
1618
+ checked: item.checked,
1619
+ disabled: true,
1620
+ readOnly: true,
1621
+ "aria-label": `${status} task`,
1622
+ className: "size-4 shrink-0"
1623
+ }
1624
+ ),
1625
+ /* @__PURE__ */ jsxs("span", { children: [
1626
+ /* @__PURE__ */ jsxs("span", { "data-ncm-task-status": "", className: "sr-only", children: [
1627
+ status,
1628
+ ":",
1629
+ " "
1630
+ ] }),
1631
+ text.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: text }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" })
1632
+ ] })
1633
+ ] }),
1634
+ 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
1635
+ ]
1636
+ },
1637
+ item.stableId ?? item.id
1638
+ );
1639
+ }) });
1640
+ }
1641
+ function TaskListBlock({ block, className }) {
1642
+ return /* @__PURE__ */ jsx(
1643
+ BlockShell,
1644
+ {
1645
+ as: "ul",
1646
+ block,
1647
+ role: "list",
1648
+ "data-ncm-task-list": "",
1649
+ className: cn("list-none space-y-1", className),
1650
+ children: /* @__PURE__ */ jsx(TaskItems, { items: block.items })
1651
+ }
1652
+ );
1653
+ }
1654
+
1655
+ // src/lib/defaults.ts
1656
+ function fallbackSource(block) {
1657
+ const maybeSource = block;
1658
+ if (typeof maybeSource.raw === "string" && maybeSource.raw.length > 0) {
1659
+ return maybeSource.raw;
1660
+ }
1661
+ if (typeof maybeSource.content === "string" && maybeSource.content.length > 0) {
1662
+ return maybeSource.content;
1663
+ }
1664
+ return JSON.stringify(block, null, 2);
1665
+ }
1666
+ function FallbackBlock({ block, className }) {
1667
+ return createElement(
1668
+ "pre",
1669
+ {
1670
+ "data-ncm-block": block.id,
1671
+ "data-ncm-stable": block.stableId,
1672
+ "data-ncm-type": block.type,
1673
+ "data-block-id": block.id,
1674
+ "data-ncm-fallback": "",
1675
+ className: cn(
1676
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1677
+ className
1678
+ )
1679
+ },
1680
+ createElement("code", null, fallbackSource(block))
1681
+ );
1682
+ }
1683
+ var defaultRenderers = {
1684
+ // Standard blocks
1685
+ paragraph: ParagraphBlock,
1686
+ header: HeaderBlock,
1687
+ blockquote: BlockquoteBlock,
1688
+ code: CodeBlock,
1689
+ container: ContainerBlock,
1690
+ frontmatter: FrontmatterBlock,
1691
+ html: HtmlBlock,
1692
+ list_ordered: ListBlock,
1693
+ list_unordered: ListBlock,
1694
+ table: TableBlock,
1695
+ embed: EmbedBlock,
1696
+ embedding: EmbeddingBlock,
1697
+ latex_display: LatexDisplayBlock,
1698
+ horizontal_rule: HorizontalRuleBlock,
1699
+ page_breaker: PageBreakerBlock,
1700
+ // Promoted blocks
1701
+ link: LinkBlock,
1702
+ figure: FigureBlock,
1703
+ // Extension blocks
1704
+ admonition: AdmonitionBlock,
1705
+ footnote: FootnoteBlock,
1706
+ task_list: TaskListBlock,
1707
+ definition: DefinitionBlock,
1708
+ spoiler: SpoilerBlock,
1709
+ aside: AsideBlock,
1710
+ attribution: AttributionBlock,
1711
+ // Promoted code blocks
1712
+ mermaid: MermaidBlock,
1713
+ dataview: DataviewBlock,
1714
+ math: MathBlock,
1715
+ chart: ChartBlock,
1716
+ // custom_code is handled separately by language (see NcmBlock dispatch)
1717
+ custom_code: {},
1718
+ fallback: FallbackBlock
1719
+ };
1720
+ function CustomCodeBlock({ block, className }) {
1721
+ return /* @__PURE__ */ jsxs(
1722
+ "div",
1723
+ {
1724
+ role: "region",
1725
+ "aria-label": `Custom code block: ${block.language}`,
1726
+ "data-ncm-block": block.id,
1727
+ "data-ncm-stable": block.stableId,
1728
+ "data-ncm-type": block.type,
1729
+ "data-block-id": block.id,
1730
+ "data-ncm-custom-code": "",
1731
+ "data-ncm-language": block.language,
1732
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1733
+ children: [
1734
+ /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: block.language }),
1735
+ /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: block.content }) })
1736
+ ]
1737
+ }
1738
+ );
1739
+ }
1740
+ var NcmBlockErrorBoundary = class extends Component {
1741
+ state = {
1742
+ error: null
1743
+ };
1744
+ static getDerivedStateFromError(error) {
1745
+ return { error };
1746
+ }
1747
+ componentDidCatch(error, info) {
1748
+ console.warn(
1749
+ `[ncm-ui] Renderer for block '${this.props.block.type}' failed; rendering fallback.`,
1750
+ { block: this.props.block, error, info }
1751
+ );
1752
+ }
1753
+ render() {
1754
+ if (this.state.error) {
1755
+ return /* @__PURE__ */ jsx(FallbackBlock, { block: this.props.block, className: this.props.className });
1756
+ }
1757
+ return this.props.children;
1758
+ }
1759
+ };
1760
+ function warnMissingRenderer(block) {
1761
+ console.warn(
1762
+ `[ncm-ui] No renderer registered for block type '${block.type}'; rendering fallback.`,
1763
+ { block }
1764
+ );
1765
+ }
1766
+ function NcmBlock({ block, className, renderers }) {
1767
+ const contextRenderers = useNcmConfig().renderers;
1768
+ const mergedRenderers = { ...defaultRenderers, ...contextRenderers, ...renderers };
1769
+ let content = null;
1770
+ if (block.type === "custom_code") {
1771
+ const customCodeBlock = block;
1772
+ const customCodeRenderers = {
1773
+ ...defaultRenderers.custom_code,
1774
+ ...contextRenderers?.custom_code,
1775
+ ...renderers?.custom_code
1776
+ };
1777
+ const LanguageRenderer = customCodeRenderers[customCodeBlock.language];
1778
+ content = LanguageRenderer ? /* @__PURE__ */ jsx(LanguageRenderer, { block: customCodeBlock, className }) : (
1779
+ // Fallback: use default CustomCodeBlock renderer
1780
+ /* @__PURE__ */ jsx(CustomCodeBlock, { block: customCodeBlock, className })
1781
+ );
1782
+ } else {
1783
+ const blockType = block.type;
1784
+ const Renderer = mergedRenderers[blockType];
1785
+ if (Renderer) {
1786
+ content = /* @__PURE__ */ jsx(Renderer, { block, className });
1787
+ } else if (mergedRenderers.fallback) {
1788
+ warnMissingRenderer(block);
1789
+ content = /* @__PURE__ */ jsx(mergedRenderers.fallback, { block, className });
1790
+ }
1791
+ }
1792
+ return /* @__PURE__ */ jsx(BlockReferenceScope, { blockId: block.id, children: /* @__PURE__ */ jsx(NcmBlockErrorBoundary, { block, className, children: content }) });
1793
+ }
1794
+ function quoteBlocks(block) {
1795
+ try {
1796
+ const doc = parseNcm(block.content, {
1797
+ preset: "ncm-full",
1798
+ allowMissingFrontmatter: true,
1799
+ // Attribution off, as the NUU Editor's bridge keeps it: the promoter's
1800
+ // trigger is any last quote line starting with `-`, so a nested quote
1801
+ // ending in a list item would lose that item to a `— Author` line.
1802
+ blockOptions: { extensions: { attribution: false } }
1803
+ });
1804
+ const blocks = doc.blocks ?? [];
1805
+ if (blocks.length === 0 || blocks.some((child) => child.type === "frontmatter")) {
1806
+ return null;
1807
+ }
1808
+ return blocks.map((child, index) => ({ ...child, id: `${block.id}.${index + 1}`, stableId: void 0 }));
1809
+ } catch {
1810
+ return null;
1811
+ }
1812
+ }
1813
+ function QuotedLines({ content }) {
1814
+ const lines = content.split("\n");
1815
+ return /* @__PURE__ */ jsx(Fragment, { children: 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)) });
1816
+ }
638
1817
  function BlockquoteBlock({ block, className }) {
639
- const lines = block.content.split("\n");
1818
+ const multiline = block.content.includes("\n");
1819
+ const blocks = useMemo(() => multiline ? quoteBlocks(block) : null, [block, multiline]);
640
1820
  return /* @__PURE__ */ jsx(
641
1821
  "blockquote",
642
1822
  {
@@ -646,7 +1826,9 @@ function BlockquoteBlock({ block, className }) {
646
1826
  "data-block-id": block.id,
647
1827
  "data-ncm-blockquote": "",
648
1828
  className,
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))
1829
+ children: !multiline ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : blocks === null ? /* @__PURE__ */ jsx(QuotedLines, { content: block.content }) : blocks.map(
1830
+ (child) => child.type === "paragraph" ? /* @__PURE__ */ jsx(QuotedLines, { content: child.content ?? "" }, blockKey(child)) : /* @__PURE__ */ jsx(NcmBlock, { block: child }, blockKey(child))
1831
+ )
650
1832
  }
651
1833
  );
652
1834
  }