@nuucognition/ncm-ui 0.1.0-dev.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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/blocks/admonition/index.d.ts +13 -0
  4. package/dist/blocks/admonition/index.js +1713 -0
  5. package/dist/blocks/aside/index.d.ts +13 -0
  6. package/dist/blocks/aside/index.js +1713 -0
  7. package/dist/blocks/attribution/index.d.ts +13 -0
  8. package/dist/blocks/attribution/index.js +32 -0
  9. package/dist/blocks/blockquote/index.d.ts +15 -0
  10. package/dist/blocks/blockquote/index.js +609 -0
  11. package/dist/blocks/chart/index.d.ts +14 -0
  12. package/dist/blocks/chart/index.js +29 -0
  13. package/dist/blocks/code/index.d.ts +15 -0
  14. package/dist/blocks/code/index.js +163 -0
  15. package/dist/blocks/container/index.d.ts +17 -0
  16. package/dist/blocks/container/index.js +633 -0
  17. package/dist/blocks/custom-code/index.d.ts +15 -0
  18. package/dist/blocks/custom-code/index.js +31 -0
  19. package/dist/blocks/dataview/index.d.ts +14 -0
  20. package/dist/blocks/dataview/index.js +29 -0
  21. package/dist/blocks/definition/index.d.ts +13 -0
  22. package/dist/blocks/definition/index.js +1713 -0
  23. package/dist/blocks/embed/index.d.ts +12 -0
  24. package/dist/blocks/embed/index.js +46 -0
  25. package/dist/blocks/embedding/index.d.ts +13 -0
  26. package/dist/blocks/embedding/index.js +763 -0
  27. package/dist/blocks/figure/index.d.ts +13 -0
  28. package/dist/blocks/figure/index.js +656 -0
  29. package/dist/blocks/footnote/index.d.ts +13 -0
  30. package/dist/blocks/footnote/index.js +1713 -0
  31. package/dist/blocks/frontmatter/index.d.ts +16 -0
  32. package/dist/blocks/frontmatter/index.js +38 -0
  33. package/dist/blocks/header/index.d.ts +9 -0
  34. package/dist/blocks/header/index.js +610 -0
  35. package/dist/blocks/horizontal-rule/index.d.ts +9 -0
  36. package/dist/blocks/horizontal-rule/index.js +19 -0
  37. package/dist/blocks/html/index.d.ts +31 -0
  38. package/dist/blocks/html/index.js +52 -0
  39. package/dist/blocks/index.d.ts +32 -0
  40. package/dist/blocks/index.js +1736 -0
  41. package/dist/blocks/latex/index.d.ts +9 -0
  42. package/dist/blocks/latex/index.js +107 -0
  43. package/dist/blocks/link/index.d.ts +14 -0
  44. package/dist/blocks/link/index.js +27 -0
  45. package/dist/blocks/list/index.d.ts +9 -0
  46. package/dist/blocks/list/index.js +675 -0
  47. package/dist/blocks/math/index.d.ts +13 -0
  48. package/dist/blocks/math/index.js +88 -0
  49. package/dist/blocks/mermaid/index.d.ts +14 -0
  50. package/dist/blocks/mermaid/index.js +29 -0
  51. package/dist/blocks/page-breaker/index.d.ts +16 -0
  52. package/dist/blocks/page-breaker/index.js +21 -0
  53. package/dist/blocks/paragraph/index.d.ts +9 -0
  54. package/dist/blocks/paragraph/index.js +608 -0
  55. package/dist/blocks/spoiler/index.d.ts +13 -0
  56. package/dist/blocks/spoiler/index.js +1713 -0
  57. package/dist/blocks/table/index.d.ts +9 -0
  58. package/dist/blocks/table/index.js +619 -0
  59. package/dist/blocks/task-list/index.d.ts +13 -0
  60. package/dist/blocks/task-list/index.js +670 -0
  61. package/dist/components/index.d.ts +599 -0
  62. package/dist/components/index.js +1840 -0
  63. package/dist/components/ncm-block.d.ts +28 -0
  64. package/dist/components/ncm-block.js +1712 -0
  65. package/dist/components/ncm-document.d.ts +48 -0
  66. package/dist/components/ncm-document.js +1839 -0
  67. package/dist/context/index.d.ts +7 -0
  68. package/dist/context/index.js +284 -0
  69. package/dist/hooks/index.d.ts +7 -0
  70. package/dist/hooks/index.js +1731 -0
  71. package/dist/hooks/use-block-renderer.d.ts +28 -0
  72. package/dist/hooks/use-block-renderer.js +1731 -0
  73. package/dist/hooks/use-ncm-config.d.ts +6 -0
  74. package/dist/hooks/use-ncm-config.js +11 -0
  75. package/dist/index.d.ts +43 -0
  76. package/dist/index.js +1887 -0
  77. package/dist/inline/index.d.ts +108 -0
  78. package/dist/inline/index.js +598 -0
  79. package/dist/inline-DwUYUmk4.d.ts +75 -0
  80. package/dist/lib/defaults.d.ts +24 -0
  81. package/dist/lib/defaults.js +1713 -0
  82. package/dist/lib/index.d.ts +2 -0
  83. package/dist/lib/index.js +10 -0
  84. package/dist/lib/katex.d.ts +3 -0
  85. package/dist/lib/katex.js +45 -0
  86. package/dist/lib/utils.d.ts +8 -0
  87. package/dist/lib/utils.js +10 -0
  88. package/dist/references-Bs5wJybr.d.ts +63 -0
  89. package/dist/renderers-C5f41FCy.d.ts +99 -0
  90. package/dist/styles/fonts/OpenDyslexic3-Bold.woff +0 -0
  91. package/dist/styles/fonts/OpenDyslexic3-Bold.woff2 +0 -0
  92. package/dist/styles/fonts/OpenDyslexic3-Regular.woff +0 -0
  93. package/dist/styles/fonts/OpenDyslexic3-Regular.woff2 +0 -0
  94. package/dist/styles/nuu.css +364 -0
  95. package/dist/styles/paper.css +332 -0
  96. package/dist/types/index.d.ts +4 -0
  97. package/dist/types/index.js +2 -0
  98. package/dist/use-ncm-config-D0doMYTH.d.ts +114 -0
  99. package/package.json +99 -0
@@ -0,0 +1,2 @@
1
+ export { cn } from './utils.js';
2
+ import 'clsx';
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ // src/lib/utils.ts
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ export { cn };
@@ -0,0 +1,3 @@
1
+ declare function useKatexHtml(content: string, displayMode: boolean, enabled?: boolean): string | null;
2
+
3
+ export { useKatexHtml };
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import { useState, useEffect, useMemo } from 'react';
3
+
4
+ var katexPromise = null;
5
+ function loadKatex() {
6
+ if (!katexPromise) {
7
+ katexPromise = Promise.all([
8
+ import('katex'),
9
+ import('katex/dist/katex.min.css')
10
+ ]).then(([module]) => module.default).catch(() => null);
11
+ }
12
+ return katexPromise;
13
+ }
14
+ function useKatexHtml(content, displayMode, enabled = true) {
15
+ const [katexModule, setKatexModule] = useState(null);
16
+ useEffect(() => {
17
+ if (!enabled) {
18
+ return;
19
+ }
20
+ let active = true;
21
+ loadKatex().then((loaded) => {
22
+ if (active) {
23
+ setKatexModule(loaded);
24
+ }
25
+ });
26
+ return () => {
27
+ active = false;
28
+ };
29
+ }, [enabled]);
30
+ return useMemo(() => {
31
+ if (!enabled || !katexModule) {
32
+ return null;
33
+ }
34
+ try {
35
+ return katexModule.renderToString(content, {
36
+ throwOnError: false,
37
+ displayMode
38
+ });
39
+ } catch {
40
+ return null;
41
+ }
42
+ }, [content, displayMode, enabled, katexModule]);
43
+ }
44
+
45
+ export { useKatexHtml };
@@ -0,0 +1,8 @@
1
+ import { ClassValue } from 'clsx';
2
+
3
+ /**
4
+ * Merge Tailwind CSS classes with clsx
5
+ */
6
+ declare function cn(...inputs: ClassValue[]): string;
7
+
8
+ export { cn };
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ // src/lib/utils.ts
6
+ function cn(...inputs) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ export { cn };
@@ -0,0 +1,63 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
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_runtime.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_runtime.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_runtime.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 };
@@ -0,0 +1,99 @@
1
+ import { ComponentType } from 'react';
2
+ import { Block, ParagraphBlock, HeaderBlock, BlockquoteBlock, CodeBlock, ContainerBlock, FrontmatterBlock, HtmlBlock, ListBlock, TableBlock, EmbedBlock, EmbeddingBlock, LatexDisplayBlock, HorizontalRuleBlock, PageBreakerBlock, LinkBlock, FigureBlock, AdmonitionBlock, FootnoteBlock, TaskListBlock, DefinitionBlock, SpoilerBlock, AsideBlock, AttributionBlock, MermaidBlock, DataviewBlock, MathBlock, ChartBlock, CustomCodeBlock, EmbedType } from '@nuucognition/ncm';
3
+
4
+ /**
5
+ * Base props for all block renderers
6
+ */
7
+ interface BlockRendererProps<T extends Block = Block> {
8
+ block: T;
9
+ className?: string;
10
+ }
11
+ /**
12
+ * Type for a block renderer component
13
+ */
14
+ type BlockRenderer<T extends Block = Block> = ComponentType<BlockRendererProps<T>>;
15
+ /**
16
+ * Registry of block renderers by type.
17
+ *
18
+ * Every key (except the language-keyed `custom_code` sub-registry and
19
+ * `fallback`) corresponds 1:1 to a `block.type` the parser can emit. All keys
20
+ * are optional here so consumers can pass partial overrides; the shipped
21
+ * `defaultRenderers` is typed as {@link CompleteBlockRendererRegistry}, which
22
+ * requires coverage for every parser block type.
23
+ */
24
+ interface BlockRendererRegistry {
25
+ paragraph?: BlockRenderer<ParagraphBlock>;
26
+ header?: BlockRenderer<HeaderBlock>;
27
+ blockquote?: BlockRenderer<BlockquoteBlock>;
28
+ code?: BlockRenderer<CodeBlock>;
29
+ container?: BlockRenderer<ContainerBlock>;
30
+ frontmatter?: BlockRenderer<FrontmatterBlock>;
31
+ html?: BlockRenderer<HtmlBlock>;
32
+ list_ordered?: BlockRenderer<ListBlock>;
33
+ list_unordered?: BlockRenderer<ListBlock>;
34
+ table?: BlockRenderer<TableBlock>;
35
+ embed?: BlockRenderer<EmbedBlock>;
36
+ embedding?: BlockRenderer<EmbeddingBlock>;
37
+ latex_display?: BlockRenderer<LatexDisplayBlock>;
38
+ horizontal_rule?: BlockRenderer<HorizontalRuleBlock>;
39
+ page_breaker?: BlockRenderer<PageBreakerBlock>;
40
+ link?: BlockRenderer<LinkBlock>;
41
+ figure?: BlockRenderer<FigureBlock>;
42
+ admonition?: BlockRenderer<AdmonitionBlock>;
43
+ footnote?: BlockRenderer<FootnoteBlock>;
44
+ task_list?: BlockRenderer<TaskListBlock>;
45
+ definition?: BlockRenderer<DefinitionBlock>;
46
+ spoiler?: BlockRenderer<SpoilerBlock>;
47
+ aside?: BlockRenderer<AsideBlock>;
48
+ attribution?: BlockRenderer<AttributionBlock>;
49
+ mermaid?: BlockRenderer<MermaidBlock>;
50
+ dataview?: BlockRenderer<DataviewBlock>;
51
+ math?: BlockRenderer<MathBlock>;
52
+ chart?: BlockRenderer<ChartBlock>;
53
+ /** Custom code renderers keyed by language identifier */
54
+ custom_code?: Record<string, BlockRenderer<CustomCodeBlock>>;
55
+ /** Fallback renderer for unknown block types */
56
+ fallback?: BlockRenderer<Block>;
57
+ }
58
+ /**
59
+ * Every concrete `block.type` dispatched through the keyed registry — i.e. all
60
+ * parser block types except the language-keyed `custom_code`. Derived from the
61
+ * parser's `Block` union, so adding a block type upstream automatically widens
62
+ * this and breaks {@link CompleteBlockRendererRegistry} until a renderer exists.
63
+ */
64
+ type KeyedBlockType = Exclude<Block["type"], "custom_code">;
65
+ /**
66
+ * The `Block` union member whose discriminant covers literal `K`. Distributes
67
+ * over `Block` and matches on `K extends T` (not `Block extends { type: K }`),
68
+ * so block types with a union discriminant resolve correctly — e.g. `ListBlock`
69
+ * (`"list_ordered" | "list_unordered"`) is found for either list key.
70
+ */
71
+ type BlockOfType<K extends string, B = Block> = B extends {
72
+ type: infer T;
73
+ } ? K extends T ? B : never : never;
74
+ /**
75
+ * A registry that MUST provide a renderer for every keyed block type, plus the
76
+ * `custom_code` sub-registry. `defaultRenderers` is typed as this — the
77
+ * compile-time guard that keeps the block→UI mapping 1:1: omit a renderer for
78
+ * any parser block type and `tsc` fails.
79
+ */
80
+ type CompleteBlockRendererRegistry = {
81
+ [K in KeyedBlockType]-?: BlockRenderer<BlockOfType<K> & Block>;
82
+ } & {
83
+ custom_code: Record<string, BlockRenderer<CustomCodeBlock>>;
84
+ fallback: BlockRenderer<Block>;
85
+ };
86
+ /**
87
+ * Props for embedding sub-renderers
88
+ */
89
+ interface EmbeddingRendererProps {
90
+ block: EmbeddingBlock;
91
+ className?: string;
92
+ resolvedUrl: string;
93
+ }
94
+ /**
95
+ * Registry of embedding renderers by embed type
96
+ */
97
+ type EmbeddingRendererRegistry = Partial<Record<EmbedType, ComponentType<EmbeddingRendererProps>>>;
98
+
99
+ export type { BlockRenderer as B, CompleteBlockRendererRegistry as C, EmbeddingRendererProps as E, KeyedBlockType as K, BlockRendererProps as a, BlockRendererRegistry as b, EmbeddingRendererRegistry as c };
@@ -0,0 +1,364 @@
1
+ /*
2
+ * NCM UI — NUU theme
3
+ *
4
+ * Styles NCM blocks against the @nuucognition/ui token system.
5
+ * Inherits --foreground, --background, --muted, --border, etc. from the host.
6
+ * Light/dark switches automatically when the host toggles `.dark` on the root.
7
+ *
8
+ * Selectors target the data attributes emitted by ncm-ui block components.
9
+ */
10
+
11
+ [data-ncm-document].ncm-theme-nuu {
12
+ font-family: var(--font-sans);
13
+ font-size: calc(0.875rem * var(--surf-reading-scale, 1));
14
+ line-height: 1.55;
15
+ color: var(--foreground);
16
+ }
17
+
18
+ [data-ncm-document].ncm-theme-nuu > * + * {
19
+ margin-top: 1rem;
20
+ }
21
+
22
+ /* Headings — Geist Sans, tracking-tight, normal weight on h1–h3 */
23
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"] {
24
+ font-family: var(--font-heading);
25
+ color: var(--foreground);
26
+ scroll-margin-top: 5rem;
27
+ margin-bottom: 0;
28
+ }
29
+
30
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="1"] {
31
+ font-size: calc(1.875rem * var(--surf-reading-scale, 1));
32
+ line-height: 2.25rem;
33
+ letter-spacing: -0.025em;
34
+ font-weight: 400;
35
+ margin-top: 1.5rem;
36
+ }
37
+
38
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="1"]:first-child {
39
+ margin-top: 0;
40
+ }
41
+
42
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="2"] {
43
+ font-size: calc(1.5rem * var(--surf-reading-scale, 1));
44
+ line-height: 2rem;
45
+ letter-spacing: -0.025em;
46
+ font-weight: 400;
47
+ margin-top: 1.25rem;
48
+ }
49
+
50
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="3"] {
51
+ font-size: calc(1.25rem * var(--surf-reading-scale, 1));
52
+ line-height: 1.75rem;
53
+ letter-spacing: -0.025em;
54
+ font-weight: 400;
55
+ margin-top: 1rem;
56
+ }
57
+
58
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="4"] {
59
+ font-size: calc(0.875rem * var(--surf-reading-scale, 1));
60
+ line-height: 1.25rem;
61
+ font-weight: 500;
62
+ margin-top: 0.75rem;
63
+ }
64
+
65
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="5"] {
66
+ font-size: calc(0.75rem * var(--surf-reading-scale, 1));
67
+ line-height: 1rem;
68
+ font-weight: 500;
69
+ letter-spacing: 0.05em;
70
+ text-transform: uppercase;
71
+ margin-top: 0.75rem;
72
+ }
73
+
74
+ /* Paragraph */
75
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="paragraph"] {
76
+ color: var(--foreground);
77
+ line-height: 1.55;
78
+ }
79
+
80
+ /* Blockquote */
81
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="blockquote"] {
82
+ border-left: 2px solid var(--border);
83
+ padding-left: 0.875rem;
84
+ color: color-mix(in srgb, var(--foreground) 80%, transparent);
85
+ line-height: 1.55;
86
+ }
87
+
88
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="blockquote"] p + p {
89
+ margin-top: 0.25rem;
90
+ }
91
+
92
+ /* Lists */
93
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"],
94
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] {
95
+ padding-left: 1.25rem;
96
+ color: var(--foreground);
97
+ line-height: 1.55;
98
+ }
99
+
100
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] {
101
+ list-style-type: decimal;
102
+ }
103
+
104
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] {
105
+ list-style-type: disc;
106
+ }
107
+
108
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] li + li,
109
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] li + li {
110
+ margin-top: 0.125rem;
111
+ }
112
+
113
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] ol,
114
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] ul,
115
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ol,
116
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ul {
117
+ margin-top: 0.125rem;
118
+ padding-left: 1.25rem;
119
+ }
120
+
121
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] li::marker,
122
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] li::marker {
123
+ color: var(--muted-foreground);
124
+ }
125
+
126
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] ol {
127
+ list-style-type: decimal;
128
+ }
129
+
130
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] ul,
131
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ul {
132
+ list-style-type: disc;
133
+ }
134
+
135
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] ol {
136
+ list-style-type: decimal;
137
+ }
138
+
139
+ /* Tables */
140
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] {
141
+ width: 100%;
142
+ overflow-x: auto;
143
+ }
144
+
145
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] table {
146
+ width: 100%;
147
+ border-collapse: collapse;
148
+ font-size: calc(0.75rem * var(--surf-reading-scale, 1));
149
+ }
150
+
151
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] th {
152
+ border: 1px solid var(--border);
153
+ padding: 0.5rem 0.75rem;
154
+ text-align: left;
155
+ font-weight: 500;
156
+ background-color: var(--muted);
157
+ color: var(--foreground);
158
+ }
159
+
160
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] td {
161
+ border: 1px solid var(--border);
162
+ padding: 0.5rem 0.75rem;
163
+ text-align: left;
164
+ color: var(--foreground);
165
+ }
166
+
167
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] tbody tr:nth-child(even) td {
168
+ background-color: color-mix(in srgb, var(--muted) 50%, transparent);
169
+ }
170
+
171
+ /* Code (block) */
172
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="code"] {
173
+ position: relative;
174
+ }
175
+
176
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="code"] pre {
177
+ overflow-x: auto;
178
+ border: 1px solid var(--border);
179
+ border-radius: var(--radius, 0.625rem);
180
+ padding: 0.75rem;
181
+ background-color: var(--muted);
182
+ color: var(--foreground);
183
+ font-family: var(--font-mono);
184
+ font-size: calc(0.875rem * var(--surf-reading-scale, 1));
185
+ line-height: 1.6;
186
+ }
187
+
188
+ /* Inline code */
189
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="code"] {
190
+ font-family: var(--font-mono);
191
+ font-size: calc(0.75rem * var(--surf-reading-scale, 1));
192
+ padding: 0.125rem 0.25rem;
193
+ border-radius: 0.25rem;
194
+ background-color: var(--muted);
195
+ color: var(--foreground);
196
+ }
197
+
198
+ /* Links */
199
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="link"],
200
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_link"][data-ncm-wikilink-status="resolved"],
201
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_embed"]:not([data-ncm-wikiembed-status]) {
202
+ color: var(--brand, var(--primary));
203
+ text-decoration: underline;
204
+ text-underline-offset: 4px;
205
+ font-weight: 500;
206
+ }
207
+
208
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_link"][data-ncm-wikilink-status="missing"],
209
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_embed"][data-ncm-wikiembed-status="missing"] {
210
+ color: var(--muted-foreground);
211
+ text-decoration: underline dashed;
212
+ text-underline-offset: 4px;
213
+ }
214
+
215
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_link"][data-ncm-wikilink-status="ambiguous"],
216
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_embed"][data-ncm-wikiembed-status="ambiguous"] {
217
+ color: var(--fire-text, var(--destructive));
218
+ }
219
+
220
+ /* Horizontal rule */
221
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="horizontal_rule"] {
222
+ border: 0;
223
+ border-top: 1px solid var(--border);
224
+ margin-top: 1.25rem;
225
+ margin-bottom: 1.25rem;
226
+ }
227
+
228
+ /* Embeddings */
229
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="embedding"] img,
230
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="embedding"] video {
231
+ max-width: 100%;
232
+ border-radius: var(--radius, 0.625rem);
233
+ }
234
+
235
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="embedding"] figcaption {
236
+ margin-top: 0.5rem;
237
+ text-align: left;
238
+ font-size: 0.75rem;
239
+ color: var(--muted-foreground);
240
+ }
241
+
242
+ /* LaTeX display */
243
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="latex_display"] {
244
+ overflow-x: auto;
245
+ color: var(--foreground);
246
+ }
247
+
248
+ /* Custom code (fallback for promoted blocks without renderer) */
249
+ [data-ncm-document].ncm-theme-nuu [data-ncm-type="custom_code"] {
250
+ border: 1px dashed var(--border);
251
+ border-radius: var(--radius, 0.625rem);
252
+ padding: 0.75rem;
253
+ background-color: var(--muted);
254
+ color: var(--foreground);
255
+ font-family: var(--font-mono);
256
+ font-size: 0.875rem;
257
+ }
258
+
259
+ /* OpenDyslexic3 — newest, refined letterforms of the OpenDyslexic family
260
+ * (the original v2 that @fontsource/opendyslexic ships has visibly different
261
+ * glyph shapes). Fonts are bundled locally under ./fonts/ so the bundle is
262
+ * self-contained — no CDN dependency.
263
+ *
264
+ * Spacing matches opendyslexic.org's own About page CSS:
265
+ * body { font-family: OpenDyslexic3; letter-spacing: 0.07rem }
266
+ * article { line-height: 1.75rem }
267
+ * — weight stays at 400 (Regular), small letter-spacing bump separates the
268
+ * chunky glyphs, generous line-height gives them room to breathe. */
269
+ @font-face {
270
+ font-family: "OpenDyslexic3";
271
+ src: url("./fonts/OpenDyslexic3-Regular.woff2") format("woff2"),
272
+ url("./fonts/OpenDyslexic3-Regular.woff") format("woff");
273
+ font-weight: 400;
274
+ font-style: normal;
275
+ font-display: swap;
276
+ }
277
+
278
+ @font-face {
279
+ font-family: "OpenDyslexic3";
280
+ src: url("./fonts/OpenDyslexic3-Bold.woff2") format("woff2"),
281
+ url("./fonts/OpenDyslexic3-Bold.woff") format("woff");
282
+ font-weight: 700;
283
+ font-style: normal;
284
+ font-display: swap;
285
+ }
286
+
287
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"],
288
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] [data-ncm-type="header"],
289
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] [data-ncm-type="paragraph"],
290
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] [data-ncm-type="list"],
291
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] [data-ncm-type="blockquote"],
292
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] [data-ncm-type="table"] {
293
+ font-family: "OpenDyslexic3", var(--font-sans);
294
+ }
295
+
296
+ [data-ncm-document].ncm-theme-nuu[data-ncm-dyslexic-font="true"] {
297
+ letter-spacing: 0.07rem;
298
+ line-height: 1.75;
299
+ }
300
+
301
+ /* Inline references — ^[ref: …] (citation, [1]) and ^[footnote: …] (¹) */
302
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="reference"],
303
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="footnote"] {
304
+ position: relative;
305
+ white-space: nowrap;
306
+ }
307
+
308
+ [data-ncm-document].ncm-theme-nuu [data-ncm-ref-marker] {
309
+ cursor: pointer;
310
+ color: var(--brand, var(--primary));
311
+ font-weight: 500;
312
+ border-radius: 0.1875rem;
313
+ outline: none;
314
+ }
315
+
316
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="reference"] [data-ncm-ref-marker] {
317
+ font-size: 0.85em;
318
+ padding: 0 0.0625rem;
319
+ }
320
+
321
+ [data-ncm-document].ncm-theme-nuu sup[data-ncm-ref-marker] {
322
+ font-size: 0.7em;
323
+ line-height: 0;
324
+ padding-left: 0.0625rem;
325
+ }
326
+
327
+ [data-ncm-document].ncm-theme-nuu [data-ncm-ref-marker]:hover,
328
+ [data-ncm-document].ncm-theme-nuu [data-ncm-ref-marker]:focus-visible {
329
+ text-decoration: underline;
330
+ text-underline-offset: 2px;
331
+ }
332
+
333
+ /* Popover — content revealed on marker hover/focus */
334
+ [data-ncm-document].ncm-theme-nuu [data-ncm-ref-popover] {
335
+ position: absolute;
336
+ left: 0;
337
+ top: 1.6em;
338
+ z-index: 20;
339
+ width: max-content;
340
+ max-width: min(22rem, 80vw);
341
+ padding: 0.5rem 0.625rem;
342
+ border: 1px solid var(--border);
343
+ border-radius: var(--radius, 0.5rem);
344
+ background: var(--popover, var(--background, var(--muted)));
345
+ color: var(--popover-foreground, var(--foreground));
346
+ box-shadow: 0 4px 12px rgb(0 0 0 / 0.12);
347
+ font-size: calc(0.8125rem * var(--surf-reading-scale, 1));
348
+ font-weight: 400;
349
+ line-height: 1.5;
350
+ white-space: normal;
351
+ opacity: 0;
352
+ visibility: hidden;
353
+ transition: opacity 0.12s ease, visibility 0.12s ease;
354
+ pointer-events: none;
355
+ }
356
+
357
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="reference"]:hover [data-ncm-ref-popover],
358
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="reference"]:focus-within [data-ncm-ref-popover],
359
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="footnote"]:hover [data-ncm-ref-popover],
360
+ [data-ncm-document].ncm-theme-nuu [data-ncm-token="footnote"]:focus-within [data-ncm-ref-popover] {
361
+ opacity: 1;
362
+ visibility: visible;
363
+ pointer-events: auto;
364
+ }