@nuucognition/ncm-ui 0.1.0-dev.1 → 0.1.1-beta.0
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.
- package/README.md +90 -0
- package/dist/blocks/admonition/index.d.ts +2 -3
- package/dist/blocks/admonition/index.js +63 -12
- package/dist/blocks/aside/index.d.ts +2 -3
- package/dist/blocks/aside/index.js +63 -12
- package/dist/blocks/attribution/index.d.ts +2 -3
- package/dist/blocks/blockquote/index.d.ts +5 -4
- package/dist/blocks/blockquote/index.js +52 -7
- package/dist/blocks/chart/index.d.ts +2 -3
- package/dist/blocks/code/index.d.ts +2 -3
- package/dist/blocks/code/index.js +2 -2
- package/dist/blocks/container/index.d.ts +2 -3
- package/dist/blocks/container/index.js +50 -5
- package/dist/blocks/custom-code/index.d.ts +2 -3
- package/dist/blocks/dataview/index.d.ts +2 -3
- package/dist/blocks/definition/index.d.ts +2 -3
- package/dist/blocks/definition/index.js +63 -12
- package/dist/blocks/embed/index.d.ts +2 -3
- package/dist/blocks/embedding/index.d.ts +2 -3
- package/dist/blocks/embedding/index.js +50 -5
- package/dist/blocks/figure/index.d.ts +2 -3
- package/dist/blocks/figure/index.js +50 -5
- package/dist/blocks/footnote/index.d.ts +2 -3
- package/dist/blocks/footnote/index.js +63 -12
- package/dist/blocks/frontmatter/index.d.ts +2 -3
- package/dist/blocks/header/index.d.ts +2 -3
- package/dist/blocks/header/index.js +50 -5
- package/dist/blocks/horizontal-rule/index.d.ts +2 -3
- package/dist/blocks/html/index.d.ts +3 -4
- package/dist/blocks/index.d.ts +1 -2
- package/dist/blocks/index.js +63 -12
- package/dist/blocks/latex/index.d.ts +2 -3
- package/dist/blocks/link/index.d.ts +2 -3
- package/dist/blocks/list/index.d.ts +2 -3
- package/dist/blocks/list/index.js +57 -7
- package/dist/blocks/math/index.d.ts +2 -3
- package/dist/blocks/mermaid/index.d.ts +2 -3
- package/dist/blocks/page-breaker/index.d.ts +2 -3
- package/dist/blocks/paragraph/index.d.ts +2 -3
- package/dist/blocks/paragraph/index.js +50 -5
- package/dist/blocks/spoiler/index.d.ts +2 -3
- package/dist/blocks/spoiler/index.js +63 -12
- package/dist/blocks/table/index.d.ts +2 -3
- package/dist/blocks/table/index.js +50 -5
- package/dist/blocks/task-list/index.d.ts +2 -3
- package/dist/blocks/task-list/index.js +52 -6
- package/dist/components/index.d.ts +34 -4
- package/dist/components/index.js +101 -14
- package/dist/components/ncm-block.d.ts +2 -3
- package/dist/components/ncm-block.js +63 -12
- package/dist/components/ncm-document.d.ts +4 -4
- package/dist/components/ncm-document.js +63 -12
- package/dist/context/index.d.ts +2 -3
- package/dist/hooks/index.d.ts +1 -2
- package/dist/hooks/index.js +63 -12
- package/dist/hooks/use-block-renderer.js +63 -12
- package/dist/hooks/use-ncm-config.d.ts +1 -2
- package/dist/index.d.ts +3 -4
- package/dist/index.js +102 -15
- package/dist/inline/index.d.ts +4 -4
- package/dist/inline/index.js +51 -6
- package/dist/lib/defaults.js +63 -12
- package/dist/{references-Bs5wJybr.d.ts → references-BYW5pnj_.d.ts} +4 -4
- package/dist/styles/nuu.css +304 -42
- package/dist/styles/paper.css +62 -1
- package/dist/{use-ncm-config-D0doMYTH.d.ts → use-ncm-config-CnntcShj.d.ts} +55 -4
- package/package.json +5 -4
package/dist/styles/nuu.css
CHANGED
|
@@ -8,10 +8,25 @@
|
|
|
8
8
|
* Selectors target the data attributes emitted by ncm-ui block components.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/*
|
|
12
|
+
* Read mode and edit mode are ONE document at two moments, so they render at
|
|
13
|
+
* one size. The base and the heading ladder below are copied from the editor's
|
|
14
|
+
* own scale (`@nuucognition/nuu-editor`, `src/nuu-editor/nuu-editor.scss`):
|
|
15
|
+
* 1rem body at 1.65, headings 1.75 / 1.56 / 1.39 / 1.24 / 1.1em. Switching
|
|
16
|
+
* view must not resize a single glyph. Since the flow-block rhythm pass, the
|
|
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 /
|
|
19
|
+
* 0.125rem list geometry) per element on its side. If one side of that pair
|
|
20
|
+
* moves, move the other in the same change.
|
|
21
|
+
*
|
|
22
|
+
* `--surf-reading-scale` is the reader's own text-size setting and stays on
|
|
23
|
+
* the base alone; everything under it is expressed in `em`, so one multiply
|
|
24
|
+
* carries the whole document.
|
|
25
|
+
*/
|
|
11
26
|
[data-ncm-document].ncm-theme-nuu {
|
|
12
27
|
font-family: var(--font-sans);
|
|
13
|
-
font-size: calc(
|
|
14
|
-
line-height: 1.
|
|
28
|
+
font-size: calc(1rem * var(--surf-reading-scale, 1));
|
|
29
|
+
line-height: 1.65;
|
|
15
30
|
color: var(--foreground);
|
|
16
31
|
}
|
|
17
32
|
|
|
@@ -19,7 +34,7 @@
|
|
|
19
34
|
margin-top: 1rem;
|
|
20
35
|
}
|
|
21
36
|
|
|
22
|
-
/* Headings —
|
|
37
|
+
/* Headings — the editor ladder: one 1.12 ratio, h5 a clear step above body */
|
|
23
38
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"] {
|
|
24
39
|
font-family: var(--font-heading);
|
|
25
40
|
color: var(--foreground);
|
|
@@ -28,10 +43,10 @@
|
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="1"] {
|
|
31
|
-
font-size:
|
|
32
|
-
line-height:
|
|
33
|
-
letter-spacing: -0.
|
|
34
|
-
font-weight:
|
|
46
|
+
font-size: 1.75em;
|
|
47
|
+
line-height: 1.3;
|
|
48
|
+
letter-spacing: -0.01em;
|
|
49
|
+
font-weight: 700;
|
|
35
50
|
margin-top: 1.5rem;
|
|
36
51
|
}
|
|
37
52
|
|
|
@@ -40,41 +55,93 @@
|
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="2"] {
|
|
43
|
-
font-size:
|
|
44
|
-
line-height:
|
|
45
|
-
letter-spacing: -0.
|
|
46
|
-
font-weight:
|
|
58
|
+
font-size: 1.56em;
|
|
59
|
+
line-height: 1.3;
|
|
60
|
+
letter-spacing: -0.01em;
|
|
61
|
+
font-weight: 660;
|
|
47
62
|
margin-top: 1.25rem;
|
|
48
63
|
}
|
|
49
64
|
|
|
50
65
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="3"] {
|
|
51
|
-
font-size:
|
|
52
|
-
line-height: 1.
|
|
53
|
-
|
|
54
|
-
font-weight: 400;
|
|
66
|
+
font-size: 1.39em;
|
|
67
|
+
line-height: 1.3;
|
|
68
|
+
font-weight: 620;
|
|
55
69
|
margin-top: 1rem;
|
|
56
70
|
}
|
|
57
71
|
|
|
72
|
+
/* h4/h5 open at 1.25rem, above the 1rem gap that follows them: a heading must
|
|
73
|
+
sit closer to the section it introduces than to the text it ends. At the
|
|
74
|
+
old 0.75rem the binding was inverted. */
|
|
58
75
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="4"] {
|
|
59
|
-
font-size:
|
|
60
|
-
line-height: 1.
|
|
61
|
-
font-weight:
|
|
62
|
-
margin-top:
|
|
76
|
+
font-size: 1.24em;
|
|
77
|
+
line-height: 1.3;
|
|
78
|
+
font-weight: 600;
|
|
79
|
+
margin-top: 1.25rem;
|
|
63
80
|
}
|
|
64
81
|
|
|
65
82
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="header"][data-ncm-level="5"] {
|
|
66
|
-
font-size:
|
|
67
|
-
line-height:
|
|
68
|
-
font-weight:
|
|
69
|
-
|
|
70
|
-
text-transform: uppercase;
|
|
71
|
-
margin-top: 0.75rem;
|
|
83
|
+
font-size: 1.1em;
|
|
84
|
+
line-height: 1.3;
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
margin-top: 1.25rem;
|
|
72
87
|
}
|
|
73
88
|
|
|
74
89
|
/* Paragraph */
|
|
75
90
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="paragraph"] {
|
|
76
91
|
color: var(--foreground);
|
|
77
|
-
line-height: 1.
|
|
92
|
+
line-height: 1.65;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* Callout content keeps its line breaks. NCM parses a callout's quoted lines
|
|
97
|
+
* into paragraph content WITH the newlines preserved ("line one\nline two"),
|
|
98
|
+
* and default HTML whitespace collapsing was rendering them as one
|
|
99
|
+
* space-joined line — an author who wrote three lines in a callout saw one.
|
|
100
|
+
* Scoped to admonitions: everywhere else soft-wrapped source keeps its
|
|
101
|
+
* CommonMark space-join.
|
|
102
|
+
*/
|
|
103
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-admonition] [data-ncm-type="paragraph"] {
|
|
104
|
+
white-space: pre-wrap;
|
|
105
|
+
}
|
|
106
|
+
|
|
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
|
|
112
|
+
* families). Unset, every hook falls back to the neutral hairline card, so
|
|
113
|
+
* a host with no callout stylesheet — and any type a host does not cover —
|
|
114
|
+
* renders the same minimalist shell with no color.
|
|
115
|
+
*
|
|
116
|
+
* These rules override the component's own rounded-card utilities
|
|
117
|
+
* (`rounded-lg bg-card p-4`): this file is unlayered and Tailwind utilities
|
|
118
|
+
* are layered, so the unlayered declarations win regardless of specificity.
|
|
119
|
+
* The hooks are set by hosts at ANY specificity — this file deliberately
|
|
120
|
+
* never assigns them, only reads them with fallbacks.
|
|
121
|
+
*/
|
|
122
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-admonition] {
|
|
123
|
+
border: 1px solid var(--ncm-callout-border, var(--border));
|
|
124
|
+
border-left: 2px solid var(--ncm-callout-edge, var(--border));
|
|
125
|
+
border-radius: 0;
|
|
126
|
+
padding: 0.625rem 0.875rem;
|
|
127
|
+
background-color: var(--ncm-callout-tint, transparent);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* One gap for both title shapes. The static `<p>` title ships `mb-2` and the
|
|
132
|
+
* collapsible variant instead puts `mt-2` on the content that follows the
|
|
133
|
+
* `<summary>` — two different gaps for one anatomy. Zero the title's own
|
|
134
|
+
* margin and state the gap once on whatever follows the title; a collapsed
|
|
135
|
+
* `<details>` renders no content and so gets no stray bottom space.
|
|
136
|
+
*/
|
|
137
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-admonition] > [data-ncm-admonition-title] {
|
|
138
|
+
margin-bottom: 0;
|
|
139
|
+
color: var(--ncm-callout-title, var(--foreground));
|
|
140
|
+
font-weight: 600;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-admonition] > [data-ncm-admonition-title] + * {
|
|
144
|
+
margin-top: 0.375rem;
|
|
78
145
|
}
|
|
79
146
|
|
|
80
147
|
/* Blockquote */
|
|
@@ -82,7 +149,7 @@
|
|
|
82
149
|
border-left: 2px solid var(--border);
|
|
83
150
|
padding-left: 0.875rem;
|
|
84
151
|
color: color-mix(in srgb, var(--foreground) 80%, transparent);
|
|
85
|
-
line-height: 1.
|
|
152
|
+
line-height: 1.65;
|
|
86
153
|
}
|
|
87
154
|
|
|
88
155
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="blockquote"] p + p {
|
|
@@ -94,7 +161,7 @@
|
|
|
94
161
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="list_unordered"] {
|
|
95
162
|
padding-left: 1.25rem;
|
|
96
163
|
color: var(--foreground);
|
|
97
|
-
line-height: 1.
|
|
164
|
+
line-height: 1.65;
|
|
98
165
|
}
|
|
99
166
|
|
|
100
167
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="list_ordered"] {
|
|
@@ -136,6 +203,84 @@
|
|
|
136
203
|
list-style-type: decimal;
|
|
137
204
|
}
|
|
138
205
|
|
|
206
|
+
/*
|
|
207
|
+
* Task list — one spec with the editor's `ul[data-type="taskList"]` block in
|
|
208
|
+
* `@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.
|
|
214
|
+
*
|
|
215
|
+
* The checkbox is drawn with `appearance: none` because the native control
|
|
216
|
+
* neither themes nor matches the editor's; the input stays a real (disabled)
|
|
217
|
+
* checkbox for the accessibility tree. The `> li` rule neutralizes the
|
|
218
|
+
* component's own Tailwind row gap so the 0.125rem list rhythm is the one
|
|
219
|
+
* answer.
|
|
220
|
+
*/
|
|
221
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-list] {
|
|
222
|
+
padding-left: 0;
|
|
223
|
+
color: var(--foreground);
|
|
224
|
+
line-height: 1.65;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-list] > li:not(:last-child) {
|
|
228
|
+
margin-bottom: 0;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-list] li + li {
|
|
232
|
+
margin-top: 0.125rem;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-list] [data-ncm-list-nested] {
|
|
236
|
+
margin-left: 1.5rem;
|
|
237
|
+
margin-top: 0.125rem;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* Top-aligned like the editor (whose checkbox sits 0.375rem into the row):
|
|
241
|
+
on a wrapped item the box stays on the first line instead of centring
|
|
242
|
+
across the whole item. */
|
|
243
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-item] > span {
|
|
244
|
+
gap: 0.5rem;
|
|
245
|
+
align-items: flex-start;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-item] input[type="checkbox"] {
|
|
249
|
+
appearance: none;
|
|
250
|
+
-webkit-appearance: none;
|
|
251
|
+
width: 1em;
|
|
252
|
+
height: 1em;
|
|
253
|
+
margin: 0.375rem 0 0;
|
|
254
|
+
border: 1px solid var(--border);
|
|
255
|
+
border-radius: 0;
|
|
256
|
+
background-color: transparent;
|
|
257
|
+
position: relative;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-item] input[type="checkbox"]:checked {
|
|
261
|
+
background-color: var(--foreground);
|
|
262
|
+
border-color: var(--foreground);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-item] input[type="checkbox"]:checked::before {
|
|
266
|
+
content: "";
|
|
267
|
+
position: absolute;
|
|
268
|
+
left: 50%;
|
|
269
|
+
top: 50%;
|
|
270
|
+
transform: translate(-50%, -50%);
|
|
271
|
+
width: 0.75em;
|
|
272
|
+
height: 0.75em;
|
|
273
|
+
background-color: var(--background);
|
|
274
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21.4142%204.58579C22.1953%205.36683%2022.1953%206.63317%2021.4142%207.41421L10.4142%2018.4142C9.63317%2019.1953%208.36684%2019.1953%207.58579%2018.4142L2.58579%2013.4142C1.80474%2012.6332%201.80474%2011.3668%202.58579%2010.5858C3.36683%209.80474%204.63317%209.80474%205.41421%2010.5858L9%2014.1716L18.5858%204.58579C19.3668%203.80474%2020.6332%203.80474%2021.4142%204.58579Z%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E")
|
|
275
|
+
center/contain no-repeat;
|
|
276
|
+
mask: url("data:image/svg+xml,%3Csvg%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22currentColor%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill-rule%3D%22evenodd%22%20clip-rule%3D%22evenodd%22%20d%3D%22M21.4142%204.58579C22.1953%205.36683%2022.1953%206.63317%2021.4142%207.41421L10.4142%2018.4142C9.63317%2019.1953%208.36684%2019.1953%207.58579%2018.4142L2.58579%2013.4142C1.80474%2012.6332%201.80474%2011.3668%202.58579%2010.5858C3.36683%209.80474%204.63317%209.80474%205.41421%2010.5858L9%2014.1716L18.5858%204.58579C19.3668%203.80474%2020.6332%203.80474%2021.4142%204.58579Z%22%20fill%3D%22currentColor%22%2F%3E%3C%2Fsvg%3E")
|
|
277
|
+
center/contain no-repeat;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-task-item][data-ncm-checked="true"] > span > span {
|
|
281
|
+
color: var(--muted-foreground);
|
|
282
|
+
}
|
|
283
|
+
|
|
139
284
|
/* Tables */
|
|
140
285
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] {
|
|
141
286
|
width: 100%;
|
|
@@ -145,7 +290,7 @@
|
|
|
145
290
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] table {
|
|
146
291
|
width: 100%;
|
|
147
292
|
border-collapse: collapse;
|
|
148
|
-
font-size:
|
|
293
|
+
font-size: 0.857em;
|
|
149
294
|
}
|
|
150
295
|
|
|
151
296
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] th {
|
|
@@ -168,27 +313,50 @@
|
|
|
168
313
|
background-color: color-mix(in srgb, var(--muted) 50%, transparent);
|
|
169
314
|
}
|
|
170
315
|
|
|
316
|
+
/* An empty cell keeps its line box, so an entirely blank row renders at full
|
|
317
|
+
height instead of collapsing to its padding. */
|
|
318
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] th:empty::after,
|
|
319
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-type="table"] td:empty::after {
|
|
320
|
+
content: "\00a0";
|
|
321
|
+
}
|
|
322
|
+
|
|
171
323
|
/* Code (block) */
|
|
172
324
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="code"] {
|
|
173
325
|
position: relative;
|
|
174
326
|
}
|
|
175
327
|
|
|
328
|
+
/* Minimalist, matching the editor's code block at rest: bare text — no fill,
|
|
329
|
+
no visible box. The border keeps its 1px width as transparent so the text
|
|
330
|
+
sits at exactly the editor's inset (its border reveals on hover/caret). The
|
|
331
|
+
editor's `pre` rule mirrors this — move both together. */
|
|
176
332
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="code"] pre {
|
|
177
333
|
overflow-x: auto;
|
|
178
|
-
border: 1px solid
|
|
179
|
-
border-radius:
|
|
334
|
+
border: 1px solid transparent;
|
|
335
|
+
border-radius: 0;
|
|
180
336
|
padding: 0.75rem;
|
|
181
|
-
background-color:
|
|
337
|
+
background-color: transparent;
|
|
182
338
|
color: var(--foreground);
|
|
183
339
|
font-family: var(--font-mono);
|
|
184
|
-
font-size:
|
|
185
|
-
line-height: 1.
|
|
340
|
+
font-size: 0.875em;
|
|
341
|
+
line-height: 1.55;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* Parse warning (NcmBlockPreview) — the reader-mode display for a block
|
|
345
|
+
source that cannot be parsed as its block type. Minimalist: muted text,
|
|
346
|
+
hairline border, sharp corners. */
|
|
347
|
+
[data-ncm-parse-warning] {
|
|
348
|
+
border: 1px solid var(--border);
|
|
349
|
+
padding: 0.5rem 0.75rem;
|
|
350
|
+
color: var(--muted-foreground);
|
|
351
|
+
font-family: var(--font-sans);
|
|
352
|
+
font-size: 0.857em;
|
|
353
|
+
line-height: 1.5;
|
|
186
354
|
}
|
|
187
355
|
|
|
188
356
|
/* Inline code */
|
|
189
357
|
[data-ncm-document].ncm-theme-nuu [data-ncm-token="code"] {
|
|
190
358
|
font-family: var(--font-mono);
|
|
191
|
-
font-size:
|
|
359
|
+
font-size: 0.857em;
|
|
192
360
|
padding: 0.125rem 0.25rem;
|
|
193
361
|
border-radius: 0.25rem;
|
|
194
362
|
background-color: var(--muted);
|
|
@@ -212,17 +380,111 @@
|
|
|
212
380
|
text-underline-offset: 4px;
|
|
213
381
|
}
|
|
214
382
|
|
|
383
|
+
/*
|
|
384
|
+
* AMBIGUOUS IS A WARNING, NOT A FAILURE — and this rule used to say otherwise.
|
|
385
|
+
*
|
|
386
|
+
* It painted `--fire-text`/`--destructive`, i.e. RED, while the EDIT surface
|
|
387
|
+
* paints red for a MISSING link and amber for an ambiguous one. The same hue
|
|
388
|
+
* therefore carried two different facts depending on which mode the reader was
|
|
389
|
+
* in, which is worse than either surface being wrong on its own: a reader who
|
|
390
|
+
* learns what red means in one mode is actively misled by the other.
|
|
391
|
+
*
|
|
392
|
+
* The vocabulary this settles on, ruled in room `onyx-uiux` on 2026-07-31 and
|
|
393
|
+
* applied across both surfaces:
|
|
394
|
+
*
|
|
395
|
+
* missing pending — muted foreground, dashed. A link to a document that
|
|
396
|
+
* is not in this mesh is very often a PLAN, not a mistake; that is
|
|
397
|
+
* also the Obsidian benchmark, where an unresolved link is the
|
|
398
|
+
* link colour faded and never red.
|
|
399
|
+
* ambiguous warning — amber, dotted. Several targets match; the author has
|
|
400
|
+
* something to decide, but nothing has failed.
|
|
401
|
+
* red reserved for GENUINE FAILURE — in this package, an embed whose
|
|
402
|
+
* bytes did not arrive (see `renderImageError`).
|
|
403
|
+
*
|
|
404
|
+
* A DELIBERATE VISUAL CHANGE FOR EVERY EMBEDDER, not just for Onyx: an
|
|
405
|
+
* ambiguous reference stops being drawn as an error. Recorded in the README.
|
|
406
|
+
*/
|
|
215
407
|
[data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_link"][data-ncm-wikilink-status="ambiguous"],
|
|
216
408
|
[data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_embed"][data-ncm-wikiembed-status="ambiguous"] {
|
|
217
|
-
color: var(--
|
|
409
|
+
color: var(--sun-text, var(--warning, #b45309));
|
|
410
|
+
text-decoration: underline dotted;
|
|
411
|
+
text-underline-offset: 4px;
|
|
218
412
|
}
|
|
219
413
|
|
|
220
|
-
/*
|
|
414
|
+
/*
|
|
415
|
+
* Wikilink focus ring.
|
|
416
|
+
*
|
|
417
|
+
* `renderer.tsx` already emits a real <a> or <button> for an actionable
|
|
418
|
+
* wikilink, so read-mode links have ALWAYS been in the tab order — a keyboard
|
|
419
|
+
* user could reach them and had no way to see that they had. Measured on the
|
|
420
|
+
* Onyx read surface in both themes: outline-style none, box-shadow none, while
|
|
421
|
+
* the surrounding chrome buttons showed a ring. That is the whole of
|
|
422
|
+
* known-adopted issue 095#5, and it belongs here rather than in a host: an
|
|
423
|
+
* embedder gets no focus affordance for these elements from anywhere else.
|
|
424
|
+
*
|
|
425
|
+
* A NEW rule, touching no existing selector. It paints only when a host has
|
|
426
|
+
* made the link actionable AND the browser is in keyboard-focus mode, so no
|
|
427
|
+
* rendering that exists today changes.
|
|
428
|
+
*
|
|
429
|
+
* (Landed as the negotiated ncm-ui freeze exception for Task 507, approved in
|
|
430
|
+
* room `onyx-uiux` on 2026-07-31. The TSX half of the original proposal was
|
|
431
|
+
* withdrawn: the button is focusable by construction and needed nothing.)
|
|
432
|
+
*/
|
|
433
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_link"]:focus-visible {
|
|
434
|
+
outline: 2px solid var(--ring, currentColor);
|
|
435
|
+
outline-offset: 2px;
|
|
436
|
+
border-radius: 0.1875rem;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/*
|
|
440
|
+
* Pointer cursor for the ACTIONABLE wikilink elements — the <a> a host's
|
|
441
|
+
* `resolveWikiLinkHref` produces and the <button> a bare `onWikiLinkClick`
|
|
442
|
+
* produces. The anchor has the pointer from the user agent already; the button
|
|
443
|
+
* does not — Tailwind v4's preflight leaves buttons on the default arrow, so in
|
|
444
|
+
* Onyx a clickable wikilink did not read as clickable. The element selectors
|
|
445
|
+
* exclude the inert <span> a host with neither config gets, which stays on the
|
|
446
|
+
* text cursor on purpose. (Task 558.)
|
|
447
|
+
*/
|
|
448
|
+
[data-ncm-document].ncm-theme-nuu a[data-ncm-token="wiki_link"],
|
|
449
|
+
[data-ncm-document].ncm-theme-nuu button[data-ncm-token="wiki_link"] {
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/*
|
|
454
|
+
* The embed BUTTON — the element `EmbeddingsConfig.onEmbedClick` produces.
|
|
455
|
+
*
|
|
456
|
+
* Both rules below can only ever match an element that does not exist unless a
|
|
457
|
+
* host opted in, so neither can change a rendering that ships today.
|
|
458
|
+
*
|
|
459
|
+
* The reset first: a `<button>` in the middle of a sentence arrives wearing the
|
|
460
|
+
* user agent's chrome — its own background, border, padding and font — and an
|
|
461
|
+
* embedded document is a piece of the prose, not a form control. Onyx never saw
|
|
462
|
+
* this because Tailwind's preflight resets buttons app-wide; an embedder
|
|
463
|
+
* without one would have. Then the ring, the sibling of the wikilink rule
|
|
464
|
+
* directly above: the button is a tab stop by construction, so it needs to be
|
|
465
|
+
* able to say when it has been reached.
|
|
466
|
+
*/
|
|
467
|
+
[data-ncm-document].ncm-theme-nuu button[data-ncm-token="wiki_embed"] {
|
|
468
|
+
appearance: none;
|
|
469
|
+
padding: 0;
|
|
470
|
+
border: 0;
|
|
471
|
+
background: none;
|
|
472
|
+
font: inherit;
|
|
473
|
+
cursor: pointer;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
[data-ncm-document].ncm-theme-nuu [data-ncm-token="wiki_embed"]:focus-visible {
|
|
477
|
+
outline: 2px solid var(--ring, currentColor);
|
|
478
|
+
outline-offset: 2px;
|
|
479
|
+
border-radius: 0.1875rem;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/* Horizontal rule — 1em on both sides, matching the editor's hr rule. */
|
|
221
483
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="horizontal_rule"] {
|
|
222
484
|
border: 0;
|
|
223
485
|
border-top: 1px solid var(--border);
|
|
224
|
-
margin-top:
|
|
225
|
-
margin-bottom:
|
|
486
|
+
margin-top: 1em;
|
|
487
|
+
margin-bottom: 1em;
|
|
226
488
|
}
|
|
227
489
|
|
|
228
490
|
/* Embeddings */
|
|
@@ -235,7 +497,7 @@
|
|
|
235
497
|
[data-ncm-document].ncm-theme-nuu [data-ncm-type="embedding"] figcaption {
|
|
236
498
|
margin-top: 0.5rem;
|
|
237
499
|
text-align: left;
|
|
238
|
-
font-size: 0.
|
|
500
|
+
font-size: 0.857em;
|
|
239
501
|
color: var(--muted-foreground);
|
|
240
502
|
}
|
|
241
503
|
|
|
@@ -253,7 +515,7 @@
|
|
|
253
515
|
background-color: var(--muted);
|
|
254
516
|
color: var(--foreground);
|
|
255
517
|
font-family: var(--font-mono);
|
|
256
|
-
font-size:
|
|
518
|
+
font-size: 1em;
|
|
257
519
|
}
|
|
258
520
|
|
|
259
521
|
/* OpenDyslexic3 — newest, refined letterforms of the OpenDyslexic family
|
|
@@ -344,7 +606,7 @@
|
|
|
344
606
|
background: var(--popover, var(--background, var(--muted)));
|
|
345
607
|
color: var(--popover-foreground, var(--foreground));
|
|
346
608
|
box-shadow: 0 4px 12px rgb(0 0 0 / 0.12);
|
|
347
|
-
font-size:
|
|
609
|
+
font-size: 0.929em;
|
|
348
610
|
font-weight: 400;
|
|
349
611
|
line-height: 1.5;
|
|
350
612
|
white-space: normal;
|
package/dist/styles/paper.css
CHANGED
|
@@ -172,6 +172,13 @@
|
|
|
172
172
|
color: var(--muted-foreground);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/* An empty cell keeps its line box, so an entirely blank row renders at full
|
|
176
|
+
height instead of collapsing to its padding. */
|
|
177
|
+
[data-ncm-document].ncm-theme-paper [data-ncm-type="table"] th:empty::after,
|
|
178
|
+
[data-ncm-document].ncm-theme-paper [data-ncm-type="table"] td:empty::after {
|
|
179
|
+
content: "\00a0";
|
|
180
|
+
}
|
|
181
|
+
|
|
175
182
|
/* Code (block) */
|
|
176
183
|
[data-ncm-document].ncm-theme-paper [data-ncm-type="code"] {
|
|
177
184
|
position: relative;
|
|
@@ -218,9 +225,63 @@
|
|
|
218
225
|
text-underline-offset: 3px;
|
|
219
226
|
}
|
|
220
227
|
|
|
228
|
+
/*
|
|
229
|
+
* Ambiguous is a warning, not a failure — the paper sibling of the rule in
|
|
230
|
+
* `nuu.css`, where the whole vocabulary argument is written down. Same change,
|
|
231
|
+
* same reason, kept in step so the two themes cannot disagree about what a
|
|
232
|
+
* colour means. Paper's dotted underline is 1px-thin and offset 3px like every
|
|
233
|
+
* other decoration in this sheet.
|
|
234
|
+
*/
|
|
221
235
|
[data-ncm-document].ncm-theme-paper [data-ncm-token="wiki_link"][data-ncm-wikilink-status="ambiguous"],
|
|
222
236
|
[data-ncm-document].ncm-theme-paper [data-ncm-token="wiki_embed"][data-ncm-wikiembed-status="ambiguous"] {
|
|
223
|
-
color: var(--
|
|
237
|
+
color: var(--sun-text, var(--warning, #b45309));
|
|
238
|
+
text-decoration: underline dotted;
|
|
239
|
+
text-decoration-thickness: 1px;
|
|
240
|
+
text-underline-offset: 3px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
* Wikilink focus ring — the paper sibling of the rule in `nuu.css`; see that
|
|
245
|
+
* file for the full reasoning. Kept in step deliberately: a focus affordance
|
|
246
|
+
* that exists in one theme and not the other is a keyboard user's experience
|
|
247
|
+
* changing with a setting that is supposed to be about typography.
|
|
248
|
+
*
|
|
249
|
+
* `--ring` with a `currentColor` fallback, so a paper host that defines no ring
|
|
250
|
+
* token still gets a visible ring in the link's own ink rather than nothing.
|
|
251
|
+
*/
|
|
252
|
+
[data-ncm-document].ncm-theme-paper [data-ncm-token="wiki_link"]:focus-visible {
|
|
253
|
+
outline: 2px solid var(--ring, currentColor);
|
|
254
|
+
outline-offset: 2px;
|
|
255
|
+
border-radius: 0.1875rem;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/*
|
|
259
|
+
* Pointer cursor for the actionable wikilink elements — the paper sibling of
|
|
260
|
+
* the rule in `nuu.css`; that file carries the reasoning. (Task 558.)
|
|
261
|
+
*/
|
|
262
|
+
[data-ncm-document].ncm-theme-paper a[data-ncm-token="wiki_link"],
|
|
263
|
+
[data-ncm-document].ncm-theme-paper button[data-ncm-token="wiki_link"] {
|
|
264
|
+
cursor: pointer;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/*
|
|
268
|
+
* The embed button — paper siblings of the two rules in `nuu.css`; that file
|
|
269
|
+
* carries the reasoning. Both can only match an element a host opted into, so
|
|
270
|
+
* neither changes anything that renders today.
|
|
271
|
+
*/
|
|
272
|
+
[data-ncm-document].ncm-theme-paper button[data-ncm-token="wiki_embed"] {
|
|
273
|
+
appearance: none;
|
|
274
|
+
padding: 0;
|
|
275
|
+
border: 0;
|
|
276
|
+
background: none;
|
|
277
|
+
font: inherit;
|
|
278
|
+
cursor: pointer;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
[data-ncm-document].ncm-theme-paper [data-ncm-token="wiki_embed"]:focus-visible {
|
|
282
|
+
outline: 2px solid var(--ring, currentColor);
|
|
283
|
+
outline-offset: 2px;
|
|
284
|
+
border-radius: 0.1875rem;
|
|
224
285
|
}
|
|
225
286
|
|
|
226
287
|
/* Horizontal rule */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { ComponentType, ReactNode } from 'react';
|
|
3
|
-
import { MeshResolveLinkResult, MeshHydrationContext } from '@nuucognition/mesh-core';
|
|
3
|
+
import { MeshResolveEmbedResult, MeshResolveLinkResult, MeshHydrationContext } from '@nuucognition/mesh-core';
|
|
4
4
|
import { EmbedType } from '@nuucognition/ncm';
|
|
5
5
|
import { c as EmbeddingRendererRegistry, b as BlockRendererRegistry } from './renderers-C5f41FCy.js';
|
|
6
6
|
|
|
@@ -23,6 +23,28 @@ interface InlineConfig {
|
|
|
23
23
|
*/
|
|
24
24
|
referencePopover?: boolean;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* What a host draws in place of an image whose bytes never arrived.
|
|
28
|
+
*
|
|
29
|
+
* The renderer cannot answer this itself. Whether an `<img src>` actually
|
|
30
|
+
* loads is only learned by trying, and what to SAY about the failure is the
|
|
31
|
+
* host's vocabulary — a mesh embedder can offer a retry against its own blob
|
|
32
|
+
* cache, a static-site embedder has nothing to retry and should say so.
|
|
33
|
+
*/
|
|
34
|
+
interface ImageErrorProps {
|
|
35
|
+
/** The embed target exactly as the author wrote it (`logo.png`, a URL). */
|
|
36
|
+
target: string;
|
|
37
|
+
/** The URL that was handed to `<img src>` and did not load. */
|
|
38
|
+
src: string;
|
|
39
|
+
/** The alt text from the source, if the author wrote one. */
|
|
40
|
+
alt?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Re-attempt the load. The `<img>` is re-keyed, so this is a real second
|
|
43
|
+
* fetch rather than the browser's cached failure. A host that owns the URL
|
|
44
|
+
* (a blob cache) should refresh it first and then call this.
|
|
45
|
+
*/
|
|
46
|
+
retry: () => void;
|
|
47
|
+
}
|
|
26
48
|
/**
|
|
27
49
|
* Configuration for embedding rendering
|
|
28
50
|
*/
|
|
@@ -32,6 +54,34 @@ interface EmbeddingsConfig {
|
|
|
32
54
|
imageClassName?: string;
|
|
33
55
|
videoClassName?: string;
|
|
34
56
|
audioClassName?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Render a failed image instead of leaving the browser's broken-image box.
|
|
59
|
+
*
|
|
60
|
+
* OPT-IN, and strictly so: with no renderer supplied the `<img>` carries no
|
|
61
|
+
* `onError` handler at all, so the emitted DOM is identical to what every
|
|
62
|
+
* consumer gets today — not merely visually identical. Supplying one is what
|
|
63
|
+
* turns an unloadable image into a state with a name and a way out.
|
|
64
|
+
*
|
|
65
|
+
* Applies to both spellings of an inline image — `` and an
|
|
66
|
+
* `![[target]]` that resolves to one.
|
|
67
|
+
*/
|
|
68
|
+
renderImageError?: ComponentType<ImageErrorProps>;
|
|
69
|
+
/**
|
|
70
|
+
* Handle a click on a non-media embed — an embedded DOCUMENT, or any target
|
|
71
|
+
* whose type has no player.
|
|
72
|
+
*
|
|
73
|
+
* WITHOUT THIS the renderer emits `<a href={resolveUrl(...)}>`, which is only
|
|
74
|
+
* honest when the target really has a URL. A document in a title-addressed
|
|
75
|
+
* mesh does not: the host navigates in its own state, so the href ends up
|
|
76
|
+
* being the raw target, a relative URL that leads nowhere and copies as
|
|
77
|
+
* nonsense from the context menu.
|
|
78
|
+
*
|
|
79
|
+
* Supplying this renders the same content as a `<button>` instead, which
|
|
80
|
+
* claims nothing about URLs and is a real control — exactly the shape
|
|
81
|
+
* {@link InlineConfig.onWikiLinkClick} already produces for a wikilink with
|
|
82
|
+
* no href. Absent, nothing changes.
|
|
83
|
+
*/
|
|
84
|
+
onEmbedClick?: (target: string, result: MeshResolveEmbedResult | null, event: React.MouseEvent) => void;
|
|
35
85
|
}
|
|
36
86
|
/**
|
|
37
87
|
* Code rendering configuration
|
|
@@ -43,6 +93,7 @@ interface CodeConfig {
|
|
|
43
93
|
className?: string;
|
|
44
94
|
}>;
|
|
45
95
|
lineNumbers?: boolean;
|
|
96
|
+
/** Opt-in since the minimalist block pass: the button renders only on `true`. */
|
|
46
97
|
copyButton?: boolean;
|
|
47
98
|
}
|
|
48
99
|
/**
|
|
@@ -109,6 +160,6 @@ interface NcmProviderProps {
|
|
|
109
160
|
* </NcmProvider>
|
|
110
161
|
* ```
|
|
111
162
|
*/
|
|
112
|
-
declare function NcmProvider({ config, children }: NcmProviderProps):
|
|
163
|
+
declare function NcmProvider({ config, children }: NcmProviderProps): react.JSX.Element;
|
|
113
164
|
|
|
114
|
-
export { type CodeConfig as C, type EmbeddingsConfig as E, type
|
|
165
|
+
export { type CodeConfig as C, type EmbeddingsConfig as E, type ImageErrorProps as I, type NcmConfig as N, type InlineConfig as a, NcmProvider as b, type NcmProviderProps as c, useNcmConfig as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuucognition/ncm-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.0",
|
|
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,7 +18,8 @@
|
|
|
18
18
|
"*.css"
|
|
19
19
|
],
|
|
20
20
|
"publishConfig": {
|
|
21
|
-
"access": "public"
|
|
21
|
+
"access": "public",
|
|
22
|
+
"tag": "beta"
|
|
22
23
|
},
|
|
23
24
|
"main": "./dist/index.js",
|
|
24
25
|
"module": "./dist/index.js",
|
|
@@ -29,12 +30,12 @@
|
|
|
29
30
|
"LICENSE"
|
|
30
31
|
],
|
|
31
32
|
"dependencies": {
|
|
32
|
-
"@nuucognition/mesh-core": "
|
|
33
|
+
"@nuucognition/mesh-core": "1.0.1-beta.1",
|
|
33
34
|
"clsx": "^2.1.1",
|
|
34
35
|
"katex": "^0.16.21",
|
|
35
36
|
"shiki": "^1.29.2",
|
|
36
37
|
"tailwind-merge": "^3.3.1",
|
|
37
|
-
"@nuucognition/ncm": "0.1.
|
|
38
|
+
"@nuucognition/ncm": "0.1.1-beta.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@types/katex": "^0.16.7",
|