@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/README.md
CHANGED
|
@@ -102,6 +102,81 @@ import { NcmDocument, SanitizedHtmlBlock } from "@nuucognition/ncm-ui"
|
|
|
102
102
|
markup. Keep `HtmlBlock` as the default for untrusted documents. Use
|
|
103
103
|
`SanitizedHtmlBlock` only when the surface intentionally supports HTML.
|
|
104
104
|
|
|
105
|
+
## Embeds
|
|
106
|
+
|
|
107
|
+
`![[target]]` and `` render through `EmbeddingsConfig` on `NcmConfig`.
|
|
108
|
+
Everything below is optional; a config that omits a key gets the behaviour the
|
|
109
|
+
package has always had.
|
|
110
|
+
|
|
111
|
+
| Key | Effect |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `resolveUrl(target, embedType)` | Turn an authored target into a fetchable URL. |
|
|
114
|
+
| `renderers` | Per-embed-type block renderers. |
|
|
115
|
+
| `imageClassName` / `videoClassName` / `audioClassName` | Class names on the media elements. |
|
|
116
|
+
| `renderImageError` | Draw a failed image instead of leaving the browser's broken-image box. |
|
|
117
|
+
| `onEmbedClick` | Make a non-media embed a real control instead of an `<a href>`. |
|
|
118
|
+
|
|
119
|
+
### Failed images
|
|
120
|
+
|
|
121
|
+
Whether an `<img>` actually loads is only learned by trying, and what to say
|
|
122
|
+
about the failure is the host's vocabulary — a mesh embedder can retry against
|
|
123
|
+
its own blob cache; a static-site embedder has nothing to retry.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
<NcmProvider config={{
|
|
127
|
+
embeddings: {
|
|
128
|
+
renderImageError: ({ target, retry }) => (
|
|
129
|
+
<button type="button" onClick={() => { myCache.refresh(target); retry() }}>
|
|
130
|
+
{target} didn’t load — retry
|
|
131
|
+
</button>
|
|
132
|
+
),
|
|
133
|
+
},
|
|
134
|
+
}}>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Opt-in, strictly.** With no renderer supplied the `<img>` carries no `onError`
|
|
138
|
+
handler at all, so the emitted DOM is identical to what it has always been — not
|
|
139
|
+
merely visually identical. `retry` re-keys the element, so it is a real second
|
|
140
|
+
request rather than the browser replaying its cached failure.
|
|
141
|
+
|
|
142
|
+
### Non-media embeds
|
|
143
|
+
|
|
144
|
+
An `![[target]]` with no player — an embedded document, or any unrecognised type
|
|
145
|
+
— renders as `<a href={resolveUrl(...)}>`. That is only honest when the target
|
|
146
|
+
really has a URL. In a title-addressed mesh a document does not: the host
|
|
147
|
+
navigates in its own state, so the href ends up being the raw target.
|
|
148
|
+
|
|
149
|
+
Supply `onEmbedClick` and the same content renders as a `<button>` instead,
|
|
150
|
+
claiming nothing about URLs — the shape `inline.onWikiLinkClick` already
|
|
151
|
+
produces for a wikilink with no href.
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
<NcmProvider config={{
|
|
155
|
+
embeddings: {
|
|
156
|
+
onEmbedClick: (target, result) => openDocument(result?.resolved?.title ?? target),
|
|
157
|
+
},
|
|
158
|
+
}}>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Reference states in the shipped themes
|
|
162
|
+
|
|
163
|
+
`nuu.css` and `paper.css` paint three states of a wikilink or embed, and they
|
|
164
|
+
mean the same thing in both themes:
|
|
165
|
+
|
|
166
|
+
| State | Treatment | Why |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| resolved | link colour, solid underline | it goes somewhere |
|
|
169
|
+
| `missing` | muted foreground, **dashed** | not an error — a link to a document that is not in this mesh is very often a plan. Matches Obsidian, where an unresolved link is the link colour faded, never red. |
|
|
170
|
+
| `ambiguous` | warning amber, **dotted** | several targets match; something to decide, nothing has failed |
|
|
171
|
+
|
|
172
|
+
Red is reserved for genuine failure — an embed whose bytes did not arrive.
|
|
173
|
+
|
|
174
|
+
> **Changed in the 513 batch:** `ambiguous` used to be painted with
|
|
175
|
+
> `--fire-text`/`--destructive`, i.e. red, in both themes. That is a deliberate
|
|
176
|
+
> visual change for every embedder. It was made because the editor surface in
|
|
177
|
+
> the same product paints red for a *missing* link, so one hue carried two
|
|
178
|
+
> different facts depending on which mode a reader was in.
|
|
179
|
+
|
|
105
180
|
## Math And Code Rendering
|
|
106
181
|
|
|
107
182
|
KaTeX is lazy-loaded and memoized, so documents without math do not pay the
|
|
@@ -116,8 +191,23 @@ pnpm --filter @nuucognition/ncm-ui lint
|
|
|
116
191
|
pnpm --filter @nuucognition/ncm-ui typecheck
|
|
117
192
|
pnpm --filter @nuucognition/ncm-ui build
|
|
118
193
|
pnpm --filter @nuucognition/ncm-ui test
|
|
194
|
+
pnpm --filter @nuucognition/ncm-ui exec tsx scripts/render-compat.mjs
|
|
119
195
|
```
|
|
120
196
|
|
|
197
|
+
`render-compat.mjs` is the **published-API compatibility gate**. This package
|
|
198
|
+
has backward-compatibility obligations, so an added optional callback has one
|
|
199
|
+
test that matters: a consumer that does not pass it must get the same *bytes*.
|
|
200
|
+
The script renders fixed fixtures under a bare config, under the shape a real
|
|
201
|
+
host passes today, and under a config with every option supplied, and compares
|
|
202
|
+
against hand-written expected markup. The expectations are literal strings on
|
|
203
|
+
purpose — a snapshot a worker can re-bless is not a gate. It also mounts into
|
|
204
|
+
jsdom and dispatches a real `error` event, because a check written only against
|
|
205
|
+
server markup would report the failure path working while it was unreachable.
|
|
206
|
+
|
|
207
|
+
Note that `vitest.config.ts` pins `NODE_ENV=test`. React's production build does
|
|
208
|
+
not export `act`, so a shell that exports `NODE_ENV=production` makes four tests
|
|
209
|
+
fail for reasons that have nothing to do with this package.
|
|
210
|
+
|
|
121
211
|
The root NCM gate runs both `@nuucognition/ncm-tests` and `@nuucognition/ncm-ui`:
|
|
122
212
|
|
|
123
213
|
```bash
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { AdmonitionBlock as AdmonitionBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type AdmonitionBlockProps = BlockRendererProps<AdmonitionBlock$1>;
|
|
7
6
|
/**
|
|
8
7
|
* Admonition (callout) renderer. Collapsible admonitions render as `<details>`;
|
|
9
8
|
* static ones as a titled region. Child blocks render through `NcmBlock`.
|
|
10
9
|
*/
|
|
11
|
-
declare function AdmonitionBlock({ block, className }: AdmonitionBlockProps):
|
|
10
|
+
declare function AdmonitionBlock({ block, className }: AdmonitionBlockProps): react.JSX.Element;
|
|
12
11
|
|
|
13
12
|
export { AdmonitionBlock, type AdmonitionBlockProps };
|
|
@@ -506,9 +506,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
506
506
|
case "image": {
|
|
507
507
|
const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
|
|
508
508
|
return /* @__PURE__ */ jsx(
|
|
509
|
-
|
|
509
|
+
EmbedImage,
|
|
510
510
|
{
|
|
511
|
-
"data-ncm-token": "image",
|
|
511
|
+
attrs: { "data-ncm-token": "image" },
|
|
512
|
+
target: token.src,
|
|
512
513
|
src,
|
|
513
514
|
alt: token.alt,
|
|
514
515
|
title: token.title
|
|
@@ -542,10 +543,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
542
543
|
}
|
|
543
544
|
if (embedType === "image") {
|
|
544
545
|
return /* @__PURE__ */ jsx(
|
|
545
|
-
|
|
546
|
+
EmbedImage,
|
|
546
547
|
{
|
|
547
|
-
|
|
548
|
-
|
|
548
|
+
attrs: {
|
|
549
|
+
"data-ncm-token": "wiki_embed",
|
|
550
|
+
"data-ncm-wikiembed-type": embedType
|
|
551
|
+
},
|
|
552
|
+
target: token.target,
|
|
549
553
|
src: url,
|
|
550
554
|
alt: token.display ?? token.target,
|
|
551
555
|
className: config.embeddings?.imageClassName
|
|
@@ -576,6 +580,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
576
580
|
}
|
|
577
581
|
);
|
|
578
582
|
}
|
|
583
|
+
const onEmbedClick = config.embeddings?.onEmbedClick;
|
|
584
|
+
if (onEmbedClick) {
|
|
585
|
+
const handleClick = (e) => {
|
|
586
|
+
onEmbedClick(token.target, result, e);
|
|
587
|
+
};
|
|
588
|
+
return /* @__PURE__ */ jsx(
|
|
589
|
+
"button",
|
|
590
|
+
{
|
|
591
|
+
type: "button",
|
|
592
|
+
"data-ncm-token": "wiki_embed",
|
|
593
|
+
"data-ncm-wikiembed-type": embedType,
|
|
594
|
+
onClick: handleClick,
|
|
595
|
+
children: token.display ?? token.target
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
}
|
|
579
599
|
return /* @__PURE__ */ jsx(
|
|
580
600
|
"a",
|
|
581
601
|
{
|
|
@@ -594,6 +614,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
594
614
|
return null;
|
|
595
615
|
}
|
|
596
616
|
}
|
|
617
|
+
function EmbedImage({ attrs, target, src, alt, title, className }) {
|
|
618
|
+
const config = useNcmConfig();
|
|
619
|
+
const Fallback = config.embeddings?.renderImageError;
|
|
620
|
+
const [failedSrc, setFailedSrc] = useState(null);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
setFailedSrc(null);
|
|
623
|
+
}, [src]);
|
|
624
|
+
if (!Fallback) {
|
|
625
|
+
return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
|
|
626
|
+
}
|
|
627
|
+
if (failedSrc === src) {
|
|
628
|
+
return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
|
|
629
|
+
}
|
|
630
|
+
return /* @__PURE__ */ jsx(
|
|
631
|
+
"img",
|
|
632
|
+
{
|
|
633
|
+
...attrs,
|
|
634
|
+
src,
|
|
635
|
+
alt,
|
|
636
|
+
title,
|
|
637
|
+
className,
|
|
638
|
+
onError: () => setFailedSrc(src)
|
|
639
|
+
}
|
|
640
|
+
);
|
|
641
|
+
}
|
|
597
642
|
function renderReadingText(content, opts) {
|
|
598
643
|
if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
|
|
599
644
|
const { bionic = false, beeline = false } = opts;
|
|
@@ -629,7 +674,7 @@ function renderReadingText(content, opts) {
|
|
|
629
674
|
return /* @__PURE__ */ jsx(Fragment, { children: pieces });
|
|
630
675
|
}
|
|
631
676
|
function BlockquoteBlock({ block, className }) {
|
|
632
|
-
const lines = block.content.split("\n")
|
|
677
|
+
const lines = block.content.split("\n");
|
|
633
678
|
return /* @__PURE__ */ jsx(
|
|
634
679
|
"blockquote",
|
|
635
680
|
{
|
|
@@ -639,7 +684,7 @@ function BlockquoteBlock({ block, className }) {
|
|
|
639
684
|
"data-block-id": block.id,
|
|
640
685
|
"data-ncm-blockquote": "",
|
|
641
686
|
className,
|
|
642
|
-
children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
|
|
687
|
+
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))
|
|
643
688
|
}
|
|
644
689
|
);
|
|
645
690
|
}
|
|
@@ -779,7 +824,7 @@ function CodeBlock({ block, className }) {
|
|
|
779
824
|
"data-ncm-language": block.language,
|
|
780
825
|
className: cn("relative", className),
|
|
781
826
|
children: [
|
|
782
|
-
codeConfig.copyButton
|
|
827
|
+
codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
|
|
783
828
|
/* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
|
|
784
829
|
languageLabel
|
|
785
830
|
]
|
|
@@ -798,7 +843,7 @@ function CodeBlock({ block, className }) {
|
|
|
798
843
|
"data-ncm-language": block.language,
|
|
799
844
|
className: cn("relative", className),
|
|
800
845
|
children: [
|
|
801
|
-
codeConfig?.copyButton
|
|
846
|
+
codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
|
|
802
847
|
/* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
803
848
|
codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
|
|
804
849
|
/* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
|
|
@@ -1250,10 +1295,12 @@ function LinkBlock({ block, className }) {
|
|
|
1250
1295
|
function ListBlock({ block, className }) {
|
|
1251
1296
|
const isOrdered = block.type === "list_ordered";
|
|
1252
1297
|
const Tag = isOrdered ? "ol" : "ul";
|
|
1298
|
+
const start = isOrdered && typeof block.marker === "number" && block.marker !== 1 ? block.marker : void 0;
|
|
1253
1299
|
return /* @__PURE__ */ jsx(
|
|
1254
1300
|
Tag,
|
|
1255
1301
|
{
|
|
1256
1302
|
role: "list",
|
|
1303
|
+
start,
|
|
1257
1304
|
"data-ncm-block": block.id,
|
|
1258
1305
|
"data-ncm-stable": block.stableId,
|
|
1259
1306
|
"data-ncm-type": block.type,
|
|
@@ -1265,6 +1312,9 @@ function ListBlock({ block, className }) {
|
|
|
1265
1312
|
}
|
|
1266
1313
|
);
|
|
1267
1314
|
}
|
|
1315
|
+
function ItemContent({ content }) {
|
|
1316
|
+
return content.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" });
|
|
1317
|
+
}
|
|
1268
1318
|
function nestedItemIdKey(id) {
|
|
1269
1319
|
if (!id) {
|
|
1270
1320
|
return void 0;
|
|
@@ -1281,7 +1331,7 @@ function ListItems({ items, itemIds, ordered }) {
|
|
|
1281
1331
|
return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, index) => {
|
|
1282
1332
|
const key = nestedItemIdKey(itemIds?.[index]) ?? `item-${index}`;
|
|
1283
1333
|
if (typeof item === "string") {
|
|
1284
|
-
return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(
|
|
1334
|
+
return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(ItemContent, { content: item }) }, key);
|
|
1285
1335
|
}
|
|
1286
1336
|
if (isTypedNestedList(item)) {
|
|
1287
1337
|
return /* @__PURE__ */ jsx(NestedTypedList, { list: item }, key);
|
|
@@ -1291,7 +1341,7 @@ function ListItems({ items, itemIds, ordered }) {
|
|
|
1291
1341
|
const content = typeof first === "string" ? first : "";
|
|
1292
1342
|
const nestedContent = processNestedItems(rest, ordered);
|
|
1293
1343
|
return /* @__PURE__ */ jsxs("li", { "data-ncm-list-item": "", children: [
|
|
1294
|
-
/* @__PURE__ */ jsx(
|
|
1344
|
+
/* @__PURE__ */ jsx(ItemContent, { content }),
|
|
1295
1345
|
nestedContent
|
|
1296
1346
|
] }, key);
|
|
1297
1347
|
}
|
|
@@ -1450,6 +1500,7 @@ function TableBlock({ block, className }) {
|
|
|
1450
1500
|
function TaskItems({ items }) {
|
|
1451
1501
|
return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
|
|
1452
1502
|
const status = item.checked ? "Completed" : "Incomplete";
|
|
1503
|
+
const text = serializeInlineContent(item.content);
|
|
1453
1504
|
return /* @__PURE__ */ jsxs(
|
|
1454
1505
|
"li",
|
|
1455
1506
|
{
|
|
@@ -1476,7 +1527,7 @@ function TaskItems({ items }) {
|
|
|
1476
1527
|
":",
|
|
1477
1528
|
" "
|
|
1478
1529
|
] }),
|
|
1479
|
-
/* @__PURE__ */ jsx(InlineContent, { content:
|
|
1530
|
+
text.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: text }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" })
|
|
1480
1531
|
] })
|
|
1481
1532
|
] }),
|
|
1482
1533
|
item.children ? /* @__PURE__ */ jsx("ul", { role: "list", "data-ncm-list-nested": "", className: "ml-6 list-none", children: /* @__PURE__ */ jsx(TaskItems, { items: item.children.items }) }) : null
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { AsideBlock as AsideBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type AsideBlockProps = BlockRendererProps<AsideBlock$1>;
|
|
7
6
|
/**
|
|
8
7
|
* Aside renderer — parenthetical / sidebar content wrapped in `(( ))`. Child
|
|
9
8
|
* blocks render through `NcmBlock`.
|
|
10
9
|
*/
|
|
11
|
-
declare function AsideBlock({ block, className }: AsideBlockProps):
|
|
10
|
+
declare function AsideBlock({ block, className }: AsideBlockProps): react.JSX.Element;
|
|
12
11
|
|
|
13
12
|
export { AsideBlock, type AsideBlockProps };
|
|
@@ -531,9 +531,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
531
531
|
case "image": {
|
|
532
532
|
const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
|
|
533
533
|
return /* @__PURE__ */ jsx(
|
|
534
|
-
|
|
534
|
+
EmbedImage,
|
|
535
535
|
{
|
|
536
|
-
"data-ncm-token": "image",
|
|
536
|
+
attrs: { "data-ncm-token": "image" },
|
|
537
|
+
target: token.src,
|
|
537
538
|
src,
|
|
538
539
|
alt: token.alt,
|
|
539
540
|
title: token.title
|
|
@@ -567,10 +568,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
567
568
|
}
|
|
568
569
|
if (embedType === "image") {
|
|
569
570
|
return /* @__PURE__ */ jsx(
|
|
570
|
-
|
|
571
|
+
EmbedImage,
|
|
571
572
|
{
|
|
572
|
-
|
|
573
|
-
|
|
573
|
+
attrs: {
|
|
574
|
+
"data-ncm-token": "wiki_embed",
|
|
575
|
+
"data-ncm-wikiembed-type": embedType
|
|
576
|
+
},
|
|
577
|
+
target: token.target,
|
|
574
578
|
src: url,
|
|
575
579
|
alt: token.display ?? token.target,
|
|
576
580
|
className: config.embeddings?.imageClassName
|
|
@@ -601,6 +605,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
601
605
|
}
|
|
602
606
|
);
|
|
603
607
|
}
|
|
608
|
+
const onEmbedClick = config.embeddings?.onEmbedClick;
|
|
609
|
+
if (onEmbedClick) {
|
|
610
|
+
const handleClick = (e) => {
|
|
611
|
+
onEmbedClick(token.target, result, e);
|
|
612
|
+
};
|
|
613
|
+
return /* @__PURE__ */ jsx(
|
|
614
|
+
"button",
|
|
615
|
+
{
|
|
616
|
+
type: "button",
|
|
617
|
+
"data-ncm-token": "wiki_embed",
|
|
618
|
+
"data-ncm-wikiembed-type": embedType,
|
|
619
|
+
onClick: handleClick,
|
|
620
|
+
children: token.display ?? token.target
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
}
|
|
604
624
|
return /* @__PURE__ */ jsx(
|
|
605
625
|
"a",
|
|
606
626
|
{
|
|
@@ -619,6 +639,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
619
639
|
return null;
|
|
620
640
|
}
|
|
621
641
|
}
|
|
642
|
+
function EmbedImage({ attrs, target, src, alt, title, className }) {
|
|
643
|
+
const config = useNcmConfig();
|
|
644
|
+
const Fallback = config.embeddings?.renderImageError;
|
|
645
|
+
const [failedSrc, setFailedSrc] = useState(null);
|
|
646
|
+
useEffect(() => {
|
|
647
|
+
setFailedSrc(null);
|
|
648
|
+
}, [src]);
|
|
649
|
+
if (!Fallback) {
|
|
650
|
+
return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
|
|
651
|
+
}
|
|
652
|
+
if (failedSrc === src) {
|
|
653
|
+
return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
|
|
654
|
+
}
|
|
655
|
+
return /* @__PURE__ */ jsx(
|
|
656
|
+
"img",
|
|
657
|
+
{
|
|
658
|
+
...attrs,
|
|
659
|
+
src,
|
|
660
|
+
alt,
|
|
661
|
+
title,
|
|
662
|
+
className,
|
|
663
|
+
onError: () => setFailedSrc(src)
|
|
664
|
+
}
|
|
665
|
+
);
|
|
666
|
+
}
|
|
622
667
|
function renderReadingText(content, opts) {
|
|
623
668
|
if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
|
|
624
669
|
const { bionic = false, beeline = false } = opts;
|
|
@@ -654,7 +699,7 @@ function renderReadingText(content, opts) {
|
|
|
654
699
|
return /* @__PURE__ */ jsx(Fragment, { children: pieces });
|
|
655
700
|
}
|
|
656
701
|
function BlockquoteBlock({ block, className }) {
|
|
657
|
-
const lines = block.content.split("\n")
|
|
702
|
+
const lines = block.content.split("\n");
|
|
658
703
|
return /* @__PURE__ */ jsx(
|
|
659
704
|
"blockquote",
|
|
660
705
|
{
|
|
@@ -664,7 +709,7 @@ function BlockquoteBlock({ block, className }) {
|
|
|
664
709
|
"data-block-id": block.id,
|
|
665
710
|
"data-ncm-blockquote": "",
|
|
666
711
|
className,
|
|
667
|
-
children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
|
|
712
|
+
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))
|
|
668
713
|
}
|
|
669
714
|
);
|
|
670
715
|
}
|
|
@@ -804,7 +849,7 @@ function CodeBlock({ block, className }) {
|
|
|
804
849
|
"data-ncm-language": block.language,
|
|
805
850
|
className: cn("relative", className),
|
|
806
851
|
children: [
|
|
807
|
-
codeConfig.copyButton
|
|
852
|
+
codeConfig.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
|
|
808
853
|
/* @__PURE__ */ jsx(CustomRenderer, { code: block.content, language: block.language }),
|
|
809
854
|
languageLabel
|
|
810
855
|
]
|
|
@@ -823,7 +868,7 @@ function CodeBlock({ block, className }) {
|
|
|
823
868
|
"data-ncm-language": block.language,
|
|
824
869
|
className: cn("relative", className),
|
|
825
870
|
children: [
|
|
826
|
-
codeConfig?.copyButton
|
|
871
|
+
codeConfig?.copyButton === true && /* @__PURE__ */ jsx(CopyButton, { content: block.content }),
|
|
827
872
|
/* @__PURE__ */ jsx("pre", { "data-ncm-code-pre": "", children: /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
828
873
|
codeConfig?.lineNumbers && /* @__PURE__ */ jsx(LineNumbers, { count: lineCount }),
|
|
829
874
|
/* @__PURE__ */ jsx("code", { "data-ncm-code": "", className: "flex-1", children: /* @__PURE__ */ jsx(DefaultShikiRenderer, { code: block.content, language: block.language }) })
|
|
@@ -1275,10 +1320,12 @@ function LinkBlock({ block, className }) {
|
|
|
1275
1320
|
function ListBlock({ block, className }) {
|
|
1276
1321
|
const isOrdered = block.type === "list_ordered";
|
|
1277
1322
|
const Tag = isOrdered ? "ol" : "ul";
|
|
1323
|
+
const start = isOrdered && typeof block.marker === "number" && block.marker !== 1 ? block.marker : void 0;
|
|
1278
1324
|
return /* @__PURE__ */ jsx(
|
|
1279
1325
|
Tag,
|
|
1280
1326
|
{
|
|
1281
1327
|
role: "list",
|
|
1328
|
+
start,
|
|
1282
1329
|
"data-ncm-block": block.id,
|
|
1283
1330
|
"data-ncm-stable": block.stableId,
|
|
1284
1331
|
"data-ncm-type": block.type,
|
|
@@ -1290,6 +1337,9 @@ function ListBlock({ block, className }) {
|
|
|
1290
1337
|
}
|
|
1291
1338
|
);
|
|
1292
1339
|
}
|
|
1340
|
+
function ItemContent({ content }) {
|
|
1341
|
+
return content.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" });
|
|
1342
|
+
}
|
|
1293
1343
|
function nestedItemIdKey(id) {
|
|
1294
1344
|
if (!id) {
|
|
1295
1345
|
return void 0;
|
|
@@ -1306,7 +1356,7 @@ function ListItems({ items, itemIds, ordered }) {
|
|
|
1306
1356
|
return /* @__PURE__ */ jsx(Fragment, { children: items.map((item, index) => {
|
|
1307
1357
|
const key = nestedItemIdKey(itemIds?.[index]) ?? `item-${index}`;
|
|
1308
1358
|
if (typeof item === "string") {
|
|
1309
|
-
return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(
|
|
1359
|
+
return /* @__PURE__ */ jsx("li", { "data-ncm-list-item": "", children: /* @__PURE__ */ jsx(ItemContent, { content: item }) }, key);
|
|
1310
1360
|
}
|
|
1311
1361
|
if (isTypedNestedList(item)) {
|
|
1312
1362
|
return /* @__PURE__ */ jsx(NestedTypedList, { list: item }, key);
|
|
@@ -1316,7 +1366,7 @@ function ListItems({ items, itemIds, ordered }) {
|
|
|
1316
1366
|
const content = typeof first === "string" ? first : "";
|
|
1317
1367
|
const nestedContent = processNestedItems(rest, ordered);
|
|
1318
1368
|
return /* @__PURE__ */ jsxs("li", { "data-ncm-list-item": "", children: [
|
|
1319
|
-
/* @__PURE__ */ jsx(
|
|
1369
|
+
/* @__PURE__ */ jsx(ItemContent, { content }),
|
|
1320
1370
|
nestedContent
|
|
1321
1371
|
] }, key);
|
|
1322
1372
|
}
|
|
@@ -1475,6 +1525,7 @@ function TableBlock({ block, className }) {
|
|
|
1475
1525
|
function TaskItems({ items }) {
|
|
1476
1526
|
return /* @__PURE__ */ jsx(Fragment, { children: items.map((item) => {
|
|
1477
1527
|
const status = item.checked ? "Completed" : "Incomplete";
|
|
1528
|
+
const text = serializeInlineContent(item.content);
|
|
1478
1529
|
return /* @__PURE__ */ jsxs(
|
|
1479
1530
|
"li",
|
|
1480
1531
|
{
|
|
@@ -1501,7 +1552,7 @@ function TaskItems({ items }) {
|
|
|
1501
1552
|
":",
|
|
1502
1553
|
" "
|
|
1503
1554
|
] }),
|
|
1504
|
-
/* @__PURE__ */ jsx(InlineContent, { content:
|
|
1555
|
+
text.length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: text }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" })
|
|
1505
1556
|
] })
|
|
1506
1557
|
] }),
|
|
1507
1558
|
item.children ? /* @__PURE__ */ jsx("ul", { role: "list", "data-ncm-list-nested": "", className: "ml-6 list-none", children: /* @__PURE__ */ jsx(TaskItems, { items: item.children.items }) }) : null
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { AttributionBlock as AttributionBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type AttributionBlockProps = BlockRendererProps<AttributionBlock$1>;
|
|
7
6
|
/**
|
|
8
7
|
* Attribution renderer — a `— Source` line extracted from a blockquote, with
|
|
9
8
|
* optional date and URL.
|
|
10
9
|
*/
|
|
11
|
-
declare function AttributionBlock({ block, className }: AttributionBlockProps):
|
|
10
|
+
declare function AttributionBlock({ block, className }: AttributionBlockProps): react.JSX.Element;
|
|
12
11
|
|
|
13
12
|
export { AttributionBlock, type AttributionBlockProps };
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { BlockquoteBlock as BlockquoteBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type BlockquoteBlockProps = BlockRendererProps<BlockquoteBlock$1>;
|
|
7
6
|
/**
|
|
8
7
|
* Blockquote block renderer with multiline support.
|
|
9
8
|
*
|
|
10
9
|
* Splits content by newlines and renders each line as a separate paragraph
|
|
11
|
-
* to properly display multiline blockquotes.
|
|
10
|
+
* to properly display multiline blockquotes. An empty line (a bare `>` in
|
|
11
|
+
* source) renders one non-breaking space, so it keeps a full text line of
|
|
12
|
+
* height instead of collapsing — the same guard the list renderers use.
|
|
12
13
|
*/
|
|
13
|
-
declare function BlockquoteBlock({ block, className }: BlockquoteBlockProps):
|
|
14
|
+
declare function BlockquoteBlock({ block, className }: BlockquoteBlockProps): react.JSX.Element;
|
|
14
15
|
|
|
15
16
|
export { BlockquoteBlock, type BlockquoteBlockProps };
|
|
@@ -468,9 +468,10 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
468
468
|
case "image": {
|
|
469
469
|
const src = config.inline?.resolveAssetPath?.(token.src) ?? token.src;
|
|
470
470
|
return /* @__PURE__ */ jsx(
|
|
471
|
-
|
|
471
|
+
EmbedImage,
|
|
472
472
|
{
|
|
473
|
-
"data-ncm-token": "image",
|
|
473
|
+
attrs: { "data-ncm-token": "image" },
|
|
474
|
+
target: token.src,
|
|
474
475
|
src,
|
|
475
476
|
alt: token.alt,
|
|
476
477
|
title: token.title
|
|
@@ -504,10 +505,13 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
504
505
|
}
|
|
505
506
|
if (embedType === "image") {
|
|
506
507
|
return /* @__PURE__ */ jsx(
|
|
507
|
-
|
|
508
|
+
EmbedImage,
|
|
508
509
|
{
|
|
509
|
-
|
|
510
|
-
|
|
510
|
+
attrs: {
|
|
511
|
+
"data-ncm-token": "wiki_embed",
|
|
512
|
+
"data-ncm-wikiembed-type": embedType
|
|
513
|
+
},
|
|
514
|
+
target: token.target,
|
|
511
515
|
src: url,
|
|
512
516
|
alt: token.display ?? token.target,
|
|
513
517
|
className: config.embeddings?.imageClassName
|
|
@@ -538,6 +542,22 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
538
542
|
}
|
|
539
543
|
);
|
|
540
544
|
}
|
|
545
|
+
const onEmbedClick = config.embeddings?.onEmbedClick;
|
|
546
|
+
if (onEmbedClick) {
|
|
547
|
+
const handleClick = (e) => {
|
|
548
|
+
onEmbedClick(token.target, result, e);
|
|
549
|
+
};
|
|
550
|
+
return /* @__PURE__ */ jsx(
|
|
551
|
+
"button",
|
|
552
|
+
{
|
|
553
|
+
type: "button",
|
|
554
|
+
"data-ncm-token": "wiki_embed",
|
|
555
|
+
"data-ncm-wikiembed-type": embedType,
|
|
556
|
+
onClick: handleClick,
|
|
557
|
+
children: token.display ?? token.target
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
}
|
|
541
561
|
return /* @__PURE__ */ jsx(
|
|
542
562
|
"a",
|
|
543
563
|
{
|
|
@@ -556,6 +576,31 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
|
|
|
556
576
|
return null;
|
|
557
577
|
}
|
|
558
578
|
}
|
|
579
|
+
function EmbedImage({ attrs, target, src, alt, title, className }) {
|
|
580
|
+
const config = useNcmConfig();
|
|
581
|
+
const Fallback = config.embeddings?.renderImageError;
|
|
582
|
+
const [failedSrc, setFailedSrc] = useState(null);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
setFailedSrc(null);
|
|
585
|
+
}, [src]);
|
|
586
|
+
if (!Fallback) {
|
|
587
|
+
return /* @__PURE__ */ jsx("img", { ...attrs, src, alt, title, className });
|
|
588
|
+
}
|
|
589
|
+
if (failedSrc === src) {
|
|
590
|
+
return /* @__PURE__ */ jsx(Fallback, { target, src, alt, retry: () => setFailedSrc(null) });
|
|
591
|
+
}
|
|
592
|
+
return /* @__PURE__ */ jsx(
|
|
593
|
+
"img",
|
|
594
|
+
{
|
|
595
|
+
...attrs,
|
|
596
|
+
src,
|
|
597
|
+
alt,
|
|
598
|
+
title,
|
|
599
|
+
className,
|
|
600
|
+
onError: () => setFailedSrc(src)
|
|
601
|
+
}
|
|
602
|
+
);
|
|
603
|
+
}
|
|
559
604
|
function renderReadingText(content, opts) {
|
|
560
605
|
if (!content) return /* @__PURE__ */ jsx(Fragment, { children: content });
|
|
561
606
|
const { bionic = false, beeline = false } = opts;
|
|
@@ -591,7 +636,7 @@ function renderReadingText(content, opts) {
|
|
|
591
636
|
return /* @__PURE__ */ jsx(Fragment, { children: pieces });
|
|
592
637
|
}
|
|
593
638
|
function BlockquoteBlock({ block, className }) {
|
|
594
|
-
const lines = block.content.split("\n")
|
|
639
|
+
const lines = block.content.split("\n");
|
|
595
640
|
return /* @__PURE__ */ jsx(
|
|
596
641
|
"blockquote",
|
|
597
642
|
{
|
|
@@ -601,7 +646,7 @@ function BlockquoteBlock({ block, className }) {
|
|
|
601
646
|
"data-block-id": block.id,
|
|
602
647
|
"data-ncm-blockquote": "",
|
|
603
648
|
className,
|
|
604
|
-
children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(InlineContent, { content: line }) }, index))
|
|
649
|
+
children: lines.length <= 1 ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: line.trim().length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: line }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" }) }, index))
|
|
605
650
|
}
|
|
606
651
|
);
|
|
607
652
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { ChartBlock as ChartBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type ChartBlockProps = BlockRendererProps<ChartBlock$1>;
|
|
7
6
|
/**
|
|
@@ -9,6 +8,6 @@ type ChartBlockProps = BlockRendererProps<ChartBlock$1>;
|
|
|
9
8
|
* charting runtime is bundled here. `data-ncm-chart-type` exposes the detected
|
|
10
9
|
* chart kind; override the `chart` renderer key to render a live chart.
|
|
11
10
|
*/
|
|
12
|
-
declare function ChartBlock({ block, className }: ChartBlockProps):
|
|
11
|
+
declare function ChartBlock({ block, className }: ChartBlockProps): react.JSX.Element;
|
|
13
12
|
|
|
14
13
|
export { ChartBlock, type ChartBlockProps };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as react from 'react';
|
|
2
2
|
import { CodeBlock as CodeBlock$1 } from '@nuucognition/ncm';
|
|
3
3
|
import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
|
|
4
|
-
import 'react';
|
|
5
4
|
|
|
6
5
|
type CodeBlockProps = BlockRendererProps<CodeBlock$1>;
|
|
7
6
|
/**
|
|
@@ -10,6 +9,6 @@ type CodeBlockProps = BlockRendererProps<CodeBlock$1>;
|
|
|
10
9
|
* Highlights code by default using Shiki's web bundle (github-light/github-dark).
|
|
11
10
|
* Consumers can override with a custom renderer via NcmProvider config.code.renderer.
|
|
12
11
|
*/
|
|
13
|
-
declare function CodeBlock({ block, className }: CodeBlockProps):
|
|
12
|
+
declare function CodeBlock({ block, className }: CodeBlockProps): react.JSX.Element;
|
|
14
13
|
|
|
15
14
|
export { CodeBlock, type CodeBlockProps };
|