@nuucognition/ncm-ui 0.1.1-beta.0 → 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.
Files changed (39) hide show
  1. package/dist/blocks/admonition/index.js +118 -46
  2. package/dist/blocks/aside/index.js +118 -46
  3. package/dist/blocks/blockquote/index.d.ts +7 -5
  4. package/dist/blocks/blockquote/index.js +1218 -36
  5. package/dist/blocks/chart/index.js +2 -1
  6. package/dist/blocks/container/index.js +72 -31
  7. package/dist/blocks/custom-code/index.js +1 -1
  8. package/dist/blocks/dataview/index.js +2 -1
  9. package/dist/blocks/definition/index.js +117 -45
  10. package/dist/blocks/embedding/index.js +72 -31
  11. package/dist/blocks/figure/index.js +72 -31
  12. package/dist/blocks/footnote/index.js +118 -46
  13. package/dist/blocks/header/index.js +72 -31
  14. package/dist/blocks/html/index.js +2 -1
  15. package/dist/blocks/index.js +118 -46
  16. package/dist/blocks/latex/index.js +2 -2
  17. package/dist/blocks/list/index.js +72 -31
  18. package/dist/blocks/math/index.js +3 -2
  19. package/dist/blocks/mermaid/index.js +2 -1
  20. package/dist/blocks/paragraph/index.js +72 -31
  21. package/dist/blocks/spoiler/index.js +118 -46
  22. package/dist/blocks/table/index.js +73 -32
  23. package/dist/blocks/task-list/index.js +72 -31
  24. package/dist/components/index.js +157 -65
  25. package/dist/components/ncm-block.js +118 -46
  26. package/dist/components/ncm-document.js +157 -65
  27. package/dist/context/index.d.ts +1 -1
  28. package/dist/context/index.js +119 -44
  29. package/dist/hooks/index.js +118 -46
  30. package/dist/hooks/use-block-renderer.js +118 -46
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +164 -58
  33. package/dist/inline/index.d.ts +2 -3
  34. package/dist/inline/index.js +72 -31
  35. package/dist/lib/defaults.js +118 -46
  36. package/dist/references-BM-lvGrq.d.ts +99 -0
  37. package/dist/styles/nuu.css +39 -16
  38. package/package.json +3 -4
  39. package/dist/references-BYW5pnj_.d.ts +0 -63
@@ -1,9 +1,9 @@
1
1
  "use client";
2
- import { createContext, useState, createElement, useMemo, useContext, useCallback, useEffect, useRef, Component, useId } from 'react';
2
+ import { createContext, useMemo, useState, createElement, useContext, useCallback, useEffect, useRef, Component, useId } from 'react';
3
3
  import { clsx } from 'clsx';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
- import { serializeInlineContent, isTypedNestedList } from '@nuucognition/ncm';
6
+ import { serializeInlineContent, isTypedNestedList, parseNcm } from '@nuucognition/ncm';
7
7
  import { extractExtension, embedTypeFromExtension } from '@nuucognition/mesh-core';
8
8
 
9
9
  // src/lib/defaults.ts
@@ -22,7 +22,7 @@ function CustomCodeBlock({ block, className }) {
22
22
  "data-block-id": block.id,
23
23
  "data-ncm-custom-code": "",
24
24
  "data-ncm-language": block.language,
25
- className: cn("overflow-x-auto", className),
25
+ className: cn("overflow-x-auto overflow-y-hidden", className),
26
26
  children: [
27
27
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs font-medium text-muted-foreground", children: block.language }),
28
28
  /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", { children: block.content }) })
@@ -71,6 +71,52 @@ function tryParseInlineRef(input) {
71
71
  }
72
72
  return { token: { type: "inline_ref", kind: "footnote", content: inner.trim() }, length };
73
73
  }
74
+ function runLength(input, index, char) {
75
+ let end = index;
76
+ while (end < input.length && input[end] === char) end++;
77
+ return end - index;
78
+ }
79
+ function tryParseEmphasis(input) {
80
+ const char = input[0];
81
+ if (char !== "*" && char !== "_") return null;
82
+ const open = Math.min(3, runLength(input, 0, char));
83
+ const need = open;
84
+ let i = open;
85
+ while (i < input.length) {
86
+ const ch = input[i];
87
+ if (ch === "`") {
88
+ const close = input.indexOf("`", i + 1);
89
+ if (close === -1) break;
90
+ i = close + 1;
91
+ continue;
92
+ }
93
+ if (ch !== char) {
94
+ i++;
95
+ continue;
96
+ }
97
+ const run = runLength(input, i, char);
98
+ const closes = need === 1 ? run % 2 === 1 : run >= need;
99
+ if (!closes) {
100
+ i += run;
101
+ continue;
102
+ }
103
+ const extra = run - need;
104
+ const inner = input.slice(open, i + extra);
105
+ if (inner.length === 0) return null;
106
+ const length = i + run;
107
+ const children = parseInline(inner);
108
+ let token;
109
+ if (need === 3) {
110
+ token = { type: "bold", children: [{ type: "italic", children }] };
111
+ } else if (need === 2) {
112
+ token = { type: "bold", children };
113
+ } else {
114
+ token = { type: "italic", children };
115
+ }
116
+ return { token, length };
117
+ }
118
+ return null;
119
+ }
74
120
  function parseInline(content) {
75
121
  if (!content) return [];
76
122
  const tokens = [];
@@ -142,17 +188,11 @@ function parseInline(content) {
142
188
  remaining = remaining.slice(linkMatch[0].length);
143
189
  continue;
144
190
  }
145
- const boldMatch = remaining.match(/^\*\*(.+?)\*\*|^__(.+?)__/);
146
- if (boldMatch) {
147
- const innerContent = boldMatch[1] ?? boldMatch[2];
148
- if (innerContent !== void 0) {
149
- tokens.push({
150
- type: "bold",
151
- children: parseInline(innerContent)
152
- });
153
- remaining = remaining.slice(boldMatch[0].length);
154
- continue;
155
- }
191
+ const emphasis = tryParseEmphasis(remaining);
192
+ if (emphasis) {
193
+ tokens.push(emphasis.token);
194
+ remaining = remaining.slice(emphasis.length);
195
+ continue;
156
196
  }
157
197
  const strikeMatch = remaining.match(/^~~(.+?)~~/);
158
198
  if (strikeMatch && strikeMatch[1] !== void 0) {
@@ -163,18 +203,6 @@ function parseInline(content) {
163
203
  remaining = remaining.slice(strikeMatch[0].length);
164
204
  continue;
165
205
  }
166
- const italicMatch = remaining.match(/^\*([^*]+)\*|^_([^_]+)_/);
167
- if (italicMatch) {
168
- const innerContent = italicMatch[1] ?? italicMatch[2];
169
- if (innerContent !== void 0) {
170
- tokens.push({
171
- type: "italic",
172
- children: parseInline(innerContent)
173
- });
174
- remaining = remaining.slice(italicMatch[0].length);
175
- continue;
176
- }
177
- }
178
206
  const nextSpecial = findNextSpecialChar(remaining);
179
207
  if (nextSpecial > 0) {
180
208
  tokens.push({ type: "text", content: remaining.slice(0, nextSpecial) });
@@ -209,17 +237,30 @@ function mergeAdjacentText(tokens) {
209
237
  }
210
238
  return result;
211
239
  }
212
- var ReferenceBasesContext = createContext(null);
240
+ function normalizeReferenceContent(content) {
241
+ return content.replace(/\s+/g, " ").trim().toLowerCase();
242
+ }
243
+ var ReferenceNumberingContext = createContext(null);
213
244
  var BlockReferenceScopeContext = createContext(null);
214
- function BlockReferenceScope({ blockId, children }) {
215
- const bases = useContext(ReferenceBasesContext);
216
- const base = (blockId ? bases?.get(blockId) : void 0) ?? { ref: 0, footnote: 0 };
245
+ function BlockReferenceScope({ children }) {
246
+ const numbering = useContext(ReferenceNumberingContext);
247
+ const local = useRef({ ref: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() });
217
248
  const counter = useRef({ ref: 0, footnote: 0 });
218
- counter.current = { ref: 0, footnote: 0 };
219
249
  const value = {
250
+ numberFor(kind, content) {
251
+ const key = normalizeReferenceContent(content);
252
+ const fromDocument = numbering?.[kind].get(key);
253
+ if (fromDocument !== void 0) return fromDocument;
254
+ const map = local.current[kind];
255
+ const existing = map.get(key);
256
+ if (existing !== void 0) return existing;
257
+ counter.current[kind] += 1;
258
+ map.set(key, counter.current[kind]);
259
+ return counter.current[kind];
260
+ },
220
261
  next(kind) {
221
262
  counter.current[kind] += 1;
222
- return base[kind] + counter.current[kind];
263
+ return counter.current[kind];
223
264
  }
224
265
  };
225
266
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value, children });
@@ -227,7 +268,7 @@ function BlockReferenceScope({ blockId, children }) {
227
268
  function useBlockReferenceScope() {
228
269
  return useContext(BlockReferenceScopeContext);
229
270
  }
230
- var INERT_SCOPE = { next: () => 0 };
271
+ var INERT_SCOPE = { numberFor: () => 0, next: () => 0 };
231
272
  function InertReferenceScope({ children }) {
232
273
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value: INERT_SCOPE, children });
233
274
  }
@@ -751,7 +792,7 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
751
792
  );
752
793
  }
753
794
  case "inline_ref": {
754
- const number = refScope?.next(token.kind) ?? 0;
795
+ const number = refScope?.numberFor(token.kind, token.content) ?? 0;
755
796
  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 });
756
797
  }
757
798
  default:
@@ -817,8 +858,32 @@ function renderReadingText(content, opts) {
817
858
  if (cursor < content.length) pieces.push(content.slice(cursor));
818
859
  return /* @__PURE__ */ jsx(Fragment, { children: pieces });
819
860
  }
861
+ function quoteBlocks(block) {
862
+ try {
863
+ const doc = parseNcm(block.content, {
864
+ preset: "ncm-full",
865
+ allowMissingFrontmatter: true,
866
+ // Attribution off, as the NUU Editor's bridge keeps it: the promoter's
867
+ // trigger is any last quote line starting with `-`, so a nested quote
868
+ // ending in a list item would lose that item to a `— Author` line.
869
+ blockOptions: { extensions: { attribution: false } }
870
+ });
871
+ const blocks = doc.blocks ?? [];
872
+ if (blocks.length === 0 || blocks.some((child) => child.type === "frontmatter")) {
873
+ return null;
874
+ }
875
+ return blocks.map((child, index) => ({ ...child, id: `${block.id}.${index + 1}`, stableId: void 0 }));
876
+ } catch {
877
+ return null;
878
+ }
879
+ }
880
+ function QuotedLines({ content }) {
881
+ const lines = content.split("\n");
882
+ 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)) });
883
+ }
820
884
  function BlockquoteBlock({ block, className }) {
821
- const lines = block.content.split("\n");
885
+ const multiline = block.content.includes("\n");
886
+ const blocks = useMemo(() => multiline ? quoteBlocks(block) : null, [block, multiline]);
822
887
  return /* @__PURE__ */ jsx(
823
888
  "blockquote",
824
889
  {
@@ -828,7 +893,9 @@ function BlockquoteBlock({ block, className }) {
828
893
  "data-block-id": block.id,
829
894
  "data-ncm-blockquote": "",
830
895
  className,
831
- 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))
896
+ children: !multiline ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : blocks === null ? /* @__PURE__ */ jsx(QuotedLines, { content: block.content }) : blocks.map(
897
+ (child) => child.type === "paragraph" ? /* @__PURE__ */ jsx(QuotedLines, { content: child.content ?? "" }, blockKey(child)) : /* @__PURE__ */ jsx(NcmBlock, { block: child }, blockKey(child))
898
+ )
832
899
  }
833
900
  );
834
901
  }
@@ -843,7 +910,8 @@ function ChartBlock({ block, className }) {
843
910
  "data-ncm-chart": "",
844
911
  "data-ncm-chart-type": block.chartType ?? "unknown",
845
912
  className: cn(
846
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
913
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
914
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
847
915
  className
848
916
  ),
849
917
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1023,7 +1091,8 @@ function DataviewBlock({ block, className }) {
1023
1091
  "data-ncm-dataview": "",
1024
1092
  "data-ncm-query-type": block.queryType,
1025
1093
  className: cn(
1026
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1094
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1095
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1027
1096
  className
1028
1097
  ),
1029
1098
  children: /* @__PURE__ */ jsx("code", { children: block.query })
@@ -1362,7 +1431,8 @@ function HtmlBlock({ block, className }) {
1362
1431
  "data-block-id": block.id,
1363
1432
  "data-ncm-html": "",
1364
1433
  className: cn(
1365
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1434
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1435
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1366
1436
  className
1367
1437
  ),
1368
1438
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1400,7 +1470,7 @@ function LatexDisplayBlock({ block, className }) {
1400
1470
  "data-ncm-type": block.type,
1401
1471
  "data-block-id": block.id,
1402
1472
  "data-ncm-latex-display": "",
1403
- className: cn("overflow-x-auto", className),
1473
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1404
1474
  dangerouslySetInnerHTML: { __html: html }
1405
1475
  }
1406
1476
  );
@@ -1415,7 +1485,7 @@ function LatexDisplayBlock({ block, className }) {
1415
1485
  "data-ncm-type": block.type,
1416
1486
  "data-block-id": block.id,
1417
1487
  "data-ncm-latex-display": "",
1418
- className: cn("overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1488
+ className: cn("overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm", className),
1419
1489
  children: /* @__PURE__ */ jsx("code", { children: block.content })
1420
1490
  }
1421
1491
  );
@@ -1535,7 +1605,7 @@ function MathBlock({ block, className }) {
1535
1605
  "data-ncm-type": block.type,
1536
1606
  "data-block-id": block.id,
1537
1607
  "data-ncm-math": "",
1538
- className: cn("overflow-x-auto", className),
1608
+ className: cn("overflow-x-auto overflow-y-hidden", className),
1539
1609
  dangerouslySetInnerHTML: { __html: html }
1540
1610
  }
1541
1611
  );
@@ -1551,7 +1621,8 @@ function MathBlock({ block, className }) {
1551
1621
  "data-block-id": block.id,
1552
1622
  "data-ncm-math": "",
1553
1623
  className: cn(
1554
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1624
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1625
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1555
1626
  className
1556
1627
  ),
1557
1628
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1569,7 +1640,8 @@ function MermaidBlock({ block, className }) {
1569
1640
  "data-ncm-mermaid": "",
1570
1641
  "data-ncm-diagram-type": block.diagramType,
1571
1642
  className: cn(
1572
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1643
+ // `overflow-y-hidden` pairs with `overflow-x-auto` on purpose — see the note in `lib/defaults.ts`.
1644
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1573
1645
  className
1574
1646
  ),
1575
1647
  children: /* @__PURE__ */ jsx("code", { children: block.content })
@@ -1630,7 +1702,7 @@ function TableBlock({ block, className }) {
1630
1702
  "data-ncm-type": block.type,
1631
1703
  "data-block-id": block.id,
1632
1704
  "data-ncm-table": "",
1633
- className: cn("w-full overflow-x-auto", className),
1705
+ className: cn("w-full overflow-x-auto overflow-y-hidden", className),
1634
1706
  children: /* @__PURE__ */ jsxs("table", { role: "table", "data-ncm-table-element": "", children: [
1635
1707
  /* @__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)) }) }),
1636
1708
  /* @__PURE__ */ jsx("tbody", { children: block.rows.map((row, rowIndex) => {
@@ -1716,7 +1788,7 @@ function FallbackBlock({ block, className }) {
1716
1788
  "data-block-id": block.id,
1717
1789
  "data-ncm-fallback": "",
1718
1790
  className: cn(
1719
- "overflow-x-auto rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1791
+ "overflow-x-auto overflow-y-hidden rounded-lg border border-border bg-muted p-4 font-mono text-sm",
1720
1792
  className
1721
1793
  )
1722
1794
  },
@@ -0,0 +1,99 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { Block } from '@nuucognition/ncm';
4
+
5
+ /**
6
+ * Numbering for inline references (`^[ref: ...]` / `^[footnote: ...]`).
7
+ *
8
+ * The two kinds are counted **independently**: refs produce `[1] [2] …` and
9
+ * footnotes produce `¹ ² …`.
10
+ *
11
+ * Numbers are assigned by a pure pre-pass over the `blocks` array and looked up
12
+ * by **token content**, never by render order. That makes the marker number a
13
+ * function of the document alone: it is stable under React re-renders,
14
+ * StrictMode double rendering, virtualised or out-of-order block rendering,
15
+ * and it gives every citation of the same source the same number.
16
+ *
17
+ * Refs additionally honour the document's own bibliography. When a paragraph
18
+ * reads `[16] Google Gemini Team. Gemini: …` and a `^[ref: Google Gemini
19
+ * Team. Gemini: …]` token carries that entry, the marker shows `[16]` — the
20
+ * number the source document uses — so the inline citation, the reference
21
+ * list, and the original paper all agree. Refs with no bibliography match are
22
+ * numbered sequentially in first-appearance order, continuing after the
23
+ * highest bibliography number so the two ranges never collide.
24
+ *
25
+ * Footnotes are numbered sequentially in first-appearance order (identical
26
+ * footnote text shares a number).
27
+ */
28
+ type ReferenceKind = "ref" | "footnote";
29
+ interface ReferenceCounts {
30
+ ref: number;
31
+ footnote: number;
32
+ }
33
+ /** Document-wide numbering: normalised token content → marker number. */
34
+ interface ReferenceNumbering {
35
+ ref: Map<string, number>;
36
+ footnote: Map<string, number>;
37
+ }
38
+ /** Collapse whitespace and case so a token and its bibliography entry compare equal. */
39
+ declare function normalizeReferenceContent(content: string): string;
40
+ /**
41
+ * Map every `[n] entry` paragraph in the document to its number. The first
42
+ * paragraph carrying a given entry text wins.
43
+ */
44
+ declare function collectBibliography(blocks: readonly Block[]): Map<string, number>;
45
+ /**
46
+ * Compute the document-wide numbering (see the module comment for the rules).
47
+ */
48
+ declare function computeReferenceNumbering(blocks: readonly Block[]): ReferenceNumbering;
49
+ /**
50
+ * Per-block count of inline references, as the *previous* numbering scheme
51
+ * exposed it. Kept for API compatibility; the marker numbers no longer derive
52
+ * from these offsets.
53
+ */
54
+ declare function computeReferenceBases(blocks: readonly Block[]): Map<string, ReferenceCounts>;
55
+ interface BlockReferenceScopeValue {
56
+ /**
57
+ * The marker number for a token with this `kind` and `content`. Pure with
58
+ * respect to the document: the same content always gets the same number.
59
+ */
60
+ numberFor(kind: ReferenceKind, content: string): number;
61
+ /**
62
+ * Allocate the next number for `kind` in render order. Legacy API — prefer
63
+ * `numberFor`, which is stable under re-renders.
64
+ */
65
+ next(kind: ReferenceKind): number;
66
+ }
67
+ interface NcmReferencesProviderProps {
68
+ blocks: readonly Block[];
69
+ children: ReactNode;
70
+ }
71
+ /** Document-root provider — runs the numbering pre-pass over `blocks`. */
72
+ declare function NcmReferencesProvider({ blocks, children }: NcmReferencesProviderProps): react.JSX.Element;
73
+ interface BlockReferenceScopeProps {
74
+ /** Block id. Accepted for API compatibility; numbering is document-wide. */
75
+ blockId?: string;
76
+ children: ReactNode;
77
+ }
78
+ /**
79
+ * Reference scope for one block (or one standalone `InlineContent` fragment).
80
+ *
81
+ * Inside an `NcmDocument` it resolves numbers from the document pre-pass.
82
+ * Standalone — no `NcmReferencesProvider` above it — it numbers the fragment's
83
+ * own tokens by content in first-request order, which is still idempotent
84
+ * under StrictMode double rendering because a repeated request for the same
85
+ * content returns the same number.
86
+ */
87
+ declare function BlockReferenceScope({ children }: BlockReferenceScopeProps): react.JSX.Element;
88
+ /** Read the active per-block reference scope, if any. */
89
+ declare function useBlockReferenceScope(): BlockReferenceScopeValue | null;
90
+ /**
91
+ * Wrap a subtree so any inline references inside it do NOT take document
92
+ * numbers — used for a reference's own popover content, which the numbering
93
+ * pre-pass deliberately does not descend into.
94
+ */
95
+ declare function InertReferenceScope({ children }: {
96
+ children: ReactNode;
97
+ }): react.JSX.Element;
98
+
99
+ export { BlockReferenceScope as B, InertReferenceScope as I, NcmReferencesProvider as N, type ReferenceCounts as R, type BlockReferenceScopeProps as a, type BlockReferenceScopeValue as b, type NcmReferencesProviderProps as c, type ReferenceKind as d, type ReferenceNumbering as e, collectBibliography as f, computeReferenceBases as g, computeReferenceNumbering as h, normalizeReferenceContent as n, useBlockReferenceScope as u };
@@ -15,7 +15,7 @@
15
15
  * 1rem body at 1.65, headings 1.75 / 1.56 / 1.39 / 1.24 / 1.1em. Switching
16
16
  * view must not resize a single glyph. Since the flow-block rhythm pass, the
17
17
  * MARGINS are part of the same contract: the editor states this file's block
18
- * gaps (1rem between siblings, per-level rem heading margins, the 1.25rem /
18
+ * gaps (1rem between siblings, per-level rem heading margins, the 1.5rem /
19
19
  * 0.125rem list geometry) per element on its side. If one side of that pair
20
20
  * moves, move the other in the same change.
21
21
  *
@@ -105,13 +105,16 @@
105
105
  }
106
106
 
107
107
  /*
108
- * Callout (admonition) — minimalist: sharp corners, 1px hairline border, a
109
- * 2px colored left edge (the blockquote's own weight), tight padding. The
110
- * type supplies only colors, through the `--ncm-callout-*` hooks; a host
111
- * assigns them per `data-ncm-admonition-type` (Onyx does, from its palette
108
+ * Callout (admonition) — a quiet card: rounded `md` corners (the NUU chrome
109
+ * step, stated as radius × 0.8 so a host's `--radius` carries through), 1px
110
+ * hairline border, a 2px colored left edge (the blockquote's own weight),
111
+ * tight padding. Rounded since (Task) 581, superseding the sharp-corner line
112
+ * of the minimalist pass — everything else from that pass stands. The type
113
+ * supplies only colors, through the `--ncm-callout-*` hooks; a host assigns
114
+ * them per `data-ncm-admonition-type` (Onyx does, from its palette
112
115
  * families). Unset, every hook falls back to the neutral hairline card, so
113
116
  * a host with no callout stylesheet — and any type a host does not cover —
114
- * renders the same minimalist shell with no color.
117
+ * renders the same quiet shell with no color.
115
118
  *
116
119
  * These rules override the component's own rounded-card utilities
117
120
  * (`rounded-lg bg-card p-4`): this file is unlayered and Tailwind utilities
@@ -122,9 +125,18 @@
122
125
  [data-ncm-document].ncm-theme-nuu [data-ncm-admonition] {
123
126
  border: 1px solid var(--ncm-callout-border, var(--border));
124
127
  border-left: 2px solid var(--ncm-callout-edge, var(--border));
125
- border-radius: 0;
128
+ border-radius: calc(var(--radius, 0.625rem) * 0.8);
126
129
  padding: 0.625rem 0.875rem;
127
130
  background-color: var(--ncm-callout-tint, transparent);
131
+ /*
132
+ * An unbroken word must stay inside the card. Hosts lay the title out as a
133
+ * flex row (glyph + text), and a flex item's min-content size is the whole
134
+ * unbreakable word — the text ran past the card edge. `anywhere`, not
135
+ * `break-word`: only `anywhere` lets the break point count toward the
136
+ * min-content size, which is what the flex layout measures. Inherited, so
137
+ * the title and every content paragraph get it. ((Task) 581)
138
+ */
139
+ overflow-wrap: anywhere;
128
140
  }
129
141
 
130
142
  /*
@@ -157,9 +169,14 @@
157
169
  }
158
170
 
159
171
  /* Lists */
172
+ /*
173
+ * 1.5rem is the ONE marker-slot width all three list kinds share — disc,
174
+ * decimal, and the task checkbox (1em box + 0.5rem gap) — so the text column
175
+ * is identical across them at every depth.
176
+ */
160
177
  [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"],
161
178
  [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] {
162
- padding-left: 1.25rem;
179
+ padding-left: 1.5rem;
163
180
  color: var(--foreground);
164
181
  line-height: 1.65;
165
182
  }
@@ -182,7 +199,7 @@
182
199
  [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ol,
183
200
  [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ul {
184
201
  margin-top: 0.125rem;
185
- padding-left: 1.25rem;
202
+ padding-left: 1.5rem;
186
203
  }
187
204
 
188
205
  [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] li::marker,
@@ -206,11 +223,14 @@
206
223
  /*
207
224
  * Task list — one spec with the editor's `ul[data-type="taskList"]` block in
208
225
  * `@nuucognition/nuu-editor` (`src/nuu-editor/nuu-editor.scss`): flush-left
209
- * rows 0.125rem apart, a 1em sharp-cornered checkbox drawn from theme tokens
210
- * (border-token box, foreground fill when checked, background-coloured check),
211
- * 0.5rem to the text, checked text dimmed to the muted tone with NO
212
- * strike-through, nested lists indented 1.5rem so they start at the text
213
- * column. If one side of that pair moves, move the other in the same change.
226
+ * rows 0.125rem apart, a 1em checkbox rounded 0.25rem (the nuu-ui Checkbox
227
+ * radius) drawn from theme tokens (border-token box, foreground fill when
228
+ * checked, background-coloured check), 0.5rem to the text — 1em + 0.5rem =
229
+ * the shared 1.5rem marker slot that lines task text up with disc and
230
+ * decimal text. Checked text dims to the muted tone with NO strike-through.
231
+ * A nested task list carries NO indent of its own: the checkbox slot IS the
232
+ * indent, the same 1.5rem step the other list kinds get from padding. If one
233
+ * side of that pair moves, move the other in the same change.
214
234
  *
215
235
  * The checkbox is drawn with `appearance: none` because the native control
216
236
  * neither themes nor matches the editor's; the input stays a real (disabled)
@@ -233,7 +253,9 @@
233
253
  }
234
254
 
235
255
  [data-ncm-document].ncm-theme-nuu [data-ncm-task-list] [data-ncm-list-nested] {
236
- margin-left: 1.5rem;
256
+ /* No margin: the nested row's own checkbox slot (1em + 0.5rem) is the
257
+ 1.5rem indent, the same step disc and decimal get from padding. */
258
+ margin-left: 0;
237
259
  margin-top: 0.125rem;
238
260
  }
239
261
 
@@ -252,7 +274,8 @@
252
274
  height: 1em;
253
275
  margin: 0.375rem 0 0;
254
276
  border: 1px solid var(--border);
255
- border-radius: 0;
277
+ /* nuu-ui's Checkbox radius (rounded-[4px]). */
278
+ border-radius: 0.25rem;
256
279
  background-color: transparent;
257
280
  position: relative;
258
281
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuucognition/ncm-ui",
3
- "version": "0.1.1-beta.0",
3
+ "version": "0.1.1-beta.2",
4
4
  "description": "React renderers for NUU Cognition Markdown (NCM) blocks — a component library that renders parsed NCM Blocks to accessible, themeable markup.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -18,8 +18,7 @@
18
18
  "*.css"
19
19
  ],
20
20
  "publishConfig": {
21
- "access": "public",
22
- "tag": "beta"
21
+ "access": "public"
23
22
  },
24
23
  "main": "./dist/index.js",
25
24
  "module": "./dist/index.js",
@@ -30,7 +29,7 @@
30
29
  "LICENSE"
31
30
  ],
32
31
  "dependencies": {
33
- "@nuucognition/mesh-core": "1.0.1-beta.1",
32
+ "@nuucognition/mesh-core": "1.0.1-beta.2",
34
33
  "clsx": "^2.1.1",
35
34
  "katex": "^0.16.21",
36
35
  "shiki": "^1.29.2",
@@ -1,63 +0,0 @@
1
- import * as react from 'react';
2
- import { ReactNode } from 'react';
3
- import { Block } from '@nuucognition/ncm';
4
-
5
- /**
6
- * Document-order numbering for inline references (`^[ref: ...]` /
7
- * `^[footnote: ...]`).
8
- *
9
- * The two kinds are counted **independently**: refs produce `[1] [2] …` and
10
- * footnotes produce `¹ ² …`, each with its own running counter across the
11
- * whole document.
12
- *
13
- * Numbering is computed from the `blocks` array — a pure pre-pass that does not
14
- * depend on React render order — so markers stay stable even if a host
15
- * virtualises blocks or renders them out of order. The pre-pass yields, per
16
- * block, the count of each kind in all *preceding* blocks (the "base"); within
17
- * a block, a `BlockReferenceScope` hands out sequential numbers in render
18
- * order. Only the within-block step relies on the block's inline content
19
- * rendering as a synchronous subtree (it always does).
20
- */
21
- type ReferenceKind = "ref" | "footnote";
22
- interface ReferenceCounts {
23
- ref: number;
24
- footnote: number;
25
- }
26
- /**
27
- * Map each block id to the number of refs/footnotes that precede it in
28
- * document order — the starting offset for that block's own markers.
29
- */
30
- declare function computeReferenceBases(blocks: readonly Block[]): Map<string, ReferenceCounts>;
31
- interface BlockReferenceScopeValue {
32
- /** Allocate the next document-order number for `kind` within this block. */
33
- next(kind: ReferenceKind): number;
34
- }
35
- interface NcmReferencesProviderProps {
36
- blocks: readonly Block[];
37
- children: ReactNode;
38
- }
39
- /** Document-root provider — runs the numbering pre-pass over `blocks`. */
40
- declare function NcmReferencesProvider({ blocks, children }: NcmReferencesProviderProps): react.JSX.Element;
41
- interface BlockReferenceScopeProps {
42
- /** Block id; resolves the base offset from the document pre-pass. */
43
- blockId?: string;
44
- children: ReactNode;
45
- }
46
- /**
47
- * Per-block numbering scope. Seeds a fresh counter from the block's base offset
48
- * on every render and hands out sequential numbers as markers render. Resetting
49
- * each render keeps numbering idempotent under React re-renders / StrictMode.
50
- */
51
- declare function BlockReferenceScope({ blockId, children }: BlockReferenceScopeProps): react.JSX.Element;
52
- /** Read the active per-block reference scope, if any. */
53
- declare function useBlockReferenceScope(): BlockReferenceScopeValue | null;
54
- /**
55
- * Wrap a subtree so any inline references inside it do NOT allocate
56
- * document-order numbers — used for a reference's own popover content, which
57
- * the numbering pre-pass deliberately does not descend into.
58
- */
59
- declare function InertReferenceScope({ children }: {
60
- children: ReactNode;
61
- }): react.JSX.Element;
62
-
63
- export { BlockReferenceScope as B, InertReferenceScope as I, NcmReferencesProvider as N, type ReferenceCounts as R, type BlockReferenceScopeProps as a, type BlockReferenceScopeValue as b, type NcmReferencesProviderProps as c, type ReferenceKind as d, computeReferenceBases as e, useBlockReferenceScope as u };