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

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.
@@ -51,6 +51,52 @@ function tryParseInlineRef(input) {
51
51
  }
52
52
  return { token: { type: "inline_ref", kind: "footnote", content: inner.trim() }, length };
53
53
  }
54
+ function runLength(input, index, char) {
55
+ let end = index;
56
+ while (end < input.length && input[end] === char) end++;
57
+ return end - index;
58
+ }
59
+ function tryParseEmphasis(input) {
60
+ const char = input[0];
61
+ if (char !== "*" && char !== "_") return null;
62
+ const open = Math.min(3, runLength(input, 0, char));
63
+ const need = open;
64
+ let i = open;
65
+ while (i < input.length) {
66
+ const ch = input[i];
67
+ if (ch === "`") {
68
+ const close = input.indexOf("`", i + 1);
69
+ if (close === -1) break;
70
+ i = close + 1;
71
+ continue;
72
+ }
73
+ if (ch !== char) {
74
+ i++;
75
+ continue;
76
+ }
77
+ const run = runLength(input, i, char);
78
+ const closes = need === 1 ? run % 2 === 1 : run >= need;
79
+ if (!closes) {
80
+ i += run;
81
+ continue;
82
+ }
83
+ const extra = run - need;
84
+ const inner = input.slice(open, i + extra);
85
+ if (inner.length === 0) return null;
86
+ const length = i + run;
87
+ const children = parseInline(inner);
88
+ let token;
89
+ if (need === 3) {
90
+ token = { type: "bold", children: [{ type: "italic", children }] };
91
+ } else if (need === 2) {
92
+ token = { type: "bold", children };
93
+ } else {
94
+ token = { type: "italic", children };
95
+ }
96
+ return { token, length };
97
+ }
98
+ return null;
99
+ }
54
100
  function parseInline(content) {
55
101
  if (!content) return [];
56
102
  const tokens = [];
@@ -122,17 +168,11 @@ function parseInline(content) {
122
168
  remaining = remaining.slice(linkMatch[0].length);
123
169
  continue;
124
170
  }
125
- const boldMatch = remaining.match(/^\*\*(.+?)\*\*|^__(.+?)__/);
126
- if (boldMatch) {
127
- const innerContent = boldMatch[1] ?? boldMatch[2];
128
- if (innerContent !== void 0) {
129
- tokens.push({
130
- type: "bold",
131
- children: parseInline(innerContent)
132
- });
133
- remaining = remaining.slice(boldMatch[0].length);
134
- continue;
135
- }
171
+ const emphasis = tryParseEmphasis(remaining);
172
+ if (emphasis) {
173
+ tokens.push(emphasis.token);
174
+ remaining = remaining.slice(emphasis.length);
175
+ continue;
136
176
  }
137
177
  const strikeMatch = remaining.match(/^~~(.+?)~~/);
138
178
  if (strikeMatch && strikeMatch[1] !== void 0) {
@@ -143,18 +183,6 @@ function parseInline(content) {
143
183
  remaining = remaining.slice(strikeMatch[0].length);
144
184
  continue;
145
185
  }
146
- const italicMatch = remaining.match(/^\*([^*]+)\*|^_([^_]+)_/);
147
- if (italicMatch) {
148
- const innerContent = italicMatch[1] ?? italicMatch[2];
149
- if (innerContent !== void 0) {
150
- tokens.push({
151
- type: "italic",
152
- children: parseInline(innerContent)
153
- });
154
- remaining = remaining.slice(italicMatch[0].length);
155
- continue;
156
- }
157
- }
158
186
  const nextSpecial = findNextSpecialChar(remaining);
159
187
  if (nextSpecial > 0) {
160
188
  tokens.push({ type: "text", content: remaining.slice(0, nextSpecial) });
@@ -265,17 +293,30 @@ function ReferenceMarker({ kind, number, children }) {
265
293
  }
266
294
  );
267
295
  }
268
- var ReferenceBasesContext = createContext(null);
296
+ function normalizeReferenceContent(content) {
297
+ return content.replace(/\s+/g, " ").trim().toLowerCase();
298
+ }
299
+ var ReferenceNumberingContext = createContext(null);
269
300
  var BlockReferenceScopeContext = createContext(null);
270
- function BlockReferenceScope({ blockId, children }) {
271
- const bases = useContext(ReferenceBasesContext);
272
- const base = (blockId ? bases?.get(blockId) : void 0) ?? { ref: 0, footnote: 0 };
301
+ function BlockReferenceScope({ children }) {
302
+ const numbering = useContext(ReferenceNumberingContext);
303
+ const local = useRef({ ref: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() });
273
304
  const counter = useRef({ ref: 0, footnote: 0 });
274
- counter.current = { ref: 0, footnote: 0 };
275
305
  const value = {
306
+ numberFor(kind, content) {
307
+ const key = normalizeReferenceContent(content);
308
+ const fromDocument = numbering?.[kind].get(key);
309
+ if (fromDocument !== void 0) return fromDocument;
310
+ const map = local.current[kind];
311
+ const existing = map.get(key);
312
+ if (existing !== void 0) return existing;
313
+ counter.current[kind] += 1;
314
+ map.set(key, counter.current[kind]);
315
+ return counter.current[kind];
316
+ },
276
317
  next(kind) {
277
318
  counter.current[kind] += 1;
278
- return base[kind] + counter.current[kind];
319
+ return counter.current[kind];
279
320
  }
280
321
  };
281
322
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value, children });
@@ -283,7 +324,7 @@ function BlockReferenceScope({ blockId, children }) {
283
324
  function useBlockReferenceScope() {
284
325
  return useContext(BlockReferenceScopeContext);
285
326
  }
286
- var INERT_SCOPE = { next: () => 0 };
327
+ var INERT_SCOPE = { numberFor: () => 0, next: () => 0 };
287
328
  function InertReferenceScope({ children }) {
288
329
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value: INERT_SCOPE, children });
289
330
  }
@@ -574,7 +615,7 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
574
615
  );
575
616
  }
576
617
  case "inline_ref": {
577
- const number = refScope?.next(token.kind) ?? 0;
618
+ const number = refScope?.numberFor(token.kind, token.content) ?? 0;
578
619
  return /* @__PURE__ */ jsx(ReferenceMarker, { kind: token.kind, number, children: token.content ? /* @__PURE__ */ jsx(InertReferenceScope, { children: /* @__PURE__ */ jsx(InlineContent, { content: token.content, disableBionic: true, disableBeeline: true }) }) : null });
579
620
  }
580
621
  default:
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { serializeInlineContent, isTypedNestedList } from '@nuucognition/ncm';
2
+ import { serializeInlineContent, isTypedNestedList, parseNcm } from '@nuucognition/ncm';
3
3
  import { createContext, createElement, useMemo, useContext, useRef, useState, useEffect, useId, useCallback, Component } from 'react';
4
4
  import { extractExtension, embedTypeFromExtension } from '@nuucognition/mesh-core';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
@@ -48,6 +48,52 @@ function tryParseInlineRef(input) {
48
48
  }
49
49
  return { token: { type: "inline_ref", kind: "footnote", content: inner.trim() }, length };
50
50
  }
51
+ function runLength(input, index, char) {
52
+ let end = index;
53
+ while (end < input.length && input[end] === char) end++;
54
+ return end - index;
55
+ }
56
+ function tryParseEmphasis(input) {
57
+ const char = input[0];
58
+ if (char !== "*" && char !== "_") return null;
59
+ const open = Math.min(3, runLength(input, 0, char));
60
+ const need = open;
61
+ let i = open;
62
+ while (i < input.length) {
63
+ const ch = input[i];
64
+ if (ch === "`") {
65
+ const close = input.indexOf("`", i + 1);
66
+ if (close === -1) break;
67
+ i = close + 1;
68
+ continue;
69
+ }
70
+ if (ch !== char) {
71
+ i++;
72
+ continue;
73
+ }
74
+ const run = runLength(input, i, char);
75
+ const closes = need === 1 ? run % 2 === 1 : run >= need;
76
+ if (!closes) {
77
+ i += run;
78
+ continue;
79
+ }
80
+ const extra = run - need;
81
+ const inner = input.slice(open, i + extra);
82
+ if (inner.length === 0) return null;
83
+ const length = i + run;
84
+ const children = parseInline(inner);
85
+ let token;
86
+ if (need === 3) {
87
+ token = { type: "bold", children: [{ type: "italic", children }] };
88
+ } else if (need === 2) {
89
+ token = { type: "bold", children };
90
+ } else {
91
+ token = { type: "italic", children };
92
+ }
93
+ return { token, length };
94
+ }
95
+ return null;
96
+ }
51
97
  function parseInline(content) {
52
98
  if (!content) return [];
53
99
  const tokens = [];
@@ -119,17 +165,11 @@ function parseInline(content) {
119
165
  remaining = remaining.slice(linkMatch[0].length);
120
166
  continue;
121
167
  }
122
- const boldMatch = remaining.match(/^\*\*(.+?)\*\*|^__(.+?)__/);
123
- if (boldMatch) {
124
- const innerContent = boldMatch[1] ?? boldMatch[2];
125
- if (innerContent !== void 0) {
126
- tokens.push({
127
- type: "bold",
128
- children: parseInline(innerContent)
129
- });
130
- remaining = remaining.slice(boldMatch[0].length);
131
- continue;
132
- }
168
+ const emphasis = tryParseEmphasis(remaining);
169
+ if (emphasis) {
170
+ tokens.push(emphasis.token);
171
+ remaining = remaining.slice(emphasis.length);
172
+ continue;
133
173
  }
134
174
  const strikeMatch = remaining.match(/^~~(.+?)~~/);
135
175
  if (strikeMatch && strikeMatch[1] !== void 0) {
@@ -140,18 +180,6 @@ function parseInline(content) {
140
180
  remaining = remaining.slice(strikeMatch[0].length);
141
181
  continue;
142
182
  }
143
- const italicMatch = remaining.match(/^\*([^*]+)\*|^_([^_]+)_/);
144
- if (italicMatch) {
145
- const innerContent = italicMatch[1] ?? italicMatch[2];
146
- if (innerContent !== void 0) {
147
- tokens.push({
148
- type: "italic",
149
- children: parseInline(innerContent)
150
- });
151
- remaining = remaining.slice(italicMatch[0].length);
152
- continue;
153
- }
154
- }
155
183
  const nextSpecial = findNextSpecialChar(remaining);
156
184
  if (nextSpecial > 0) {
157
185
  tokens.push({ type: "text", content: remaining.slice(0, nextSpecial) });
@@ -262,17 +290,30 @@ function ReferenceMarker({ kind, number, children }) {
262
290
  }
263
291
  );
264
292
  }
265
- var ReferenceBasesContext = createContext(null);
293
+ function normalizeReferenceContent(content) {
294
+ return content.replace(/\s+/g, " ").trim().toLowerCase();
295
+ }
296
+ var ReferenceNumberingContext = createContext(null);
266
297
  var BlockReferenceScopeContext = createContext(null);
267
- function BlockReferenceScope({ blockId, children }) {
268
- const bases = useContext(ReferenceBasesContext);
269
- const base = (blockId ? bases?.get(blockId) : void 0) ?? { ref: 0, footnote: 0 };
298
+ function BlockReferenceScope({ children }) {
299
+ const numbering = useContext(ReferenceNumberingContext);
300
+ const local = useRef({ ref: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() });
270
301
  const counter = useRef({ ref: 0, footnote: 0 });
271
- counter.current = { ref: 0, footnote: 0 };
272
302
  const value = {
303
+ numberFor(kind, content) {
304
+ const key = normalizeReferenceContent(content);
305
+ const fromDocument = numbering?.[kind].get(key);
306
+ if (fromDocument !== void 0) return fromDocument;
307
+ const map = local.current[kind];
308
+ const existing = map.get(key);
309
+ if (existing !== void 0) return existing;
310
+ counter.current[kind] += 1;
311
+ map.set(key, counter.current[kind]);
312
+ return counter.current[kind];
313
+ },
273
314
  next(kind) {
274
315
  counter.current[kind] += 1;
275
- return base[kind] + counter.current[kind];
316
+ return counter.current[kind];
276
317
  }
277
318
  };
278
319
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value, children });
@@ -280,7 +321,7 @@ function BlockReferenceScope({ blockId, children }) {
280
321
  function useBlockReferenceScope() {
281
322
  return useContext(BlockReferenceScopeContext);
282
323
  }
283
- var INERT_SCOPE = { next: () => 0 };
324
+ var INERT_SCOPE = { numberFor: () => 0, next: () => 0 };
284
325
  function InertReferenceScope({ children }) {
285
326
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value: INERT_SCOPE, children });
286
327
  }
@@ -571,7 +612,7 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
571
612
  );
572
613
  }
573
614
  case "inline_ref": {
574
- const number = refScope?.next(token.kind) ?? 0;
615
+ const number = refScope?.numberFor(token.kind, token.content) ?? 0;
575
616
  return /* @__PURE__ */ jsx(ReferenceMarker, { kind: token.kind, number, children: token.content ? /* @__PURE__ */ jsx(InertReferenceScope, { children: /* @__PURE__ */ jsx(InlineContent, { content: token.content, disableBionic: true, disableBeeline: true }) }) : null });
576
617
  }
577
618
  default:
@@ -710,8 +751,32 @@ function AttributionBlock({ block, className }) {
710
751
  }
711
752
  );
712
753
  }
754
+ function quoteBlocks(block) {
755
+ try {
756
+ const doc = parseNcm(block.content, {
757
+ preset: "ncm-full",
758
+ allowMissingFrontmatter: true,
759
+ // Attribution off, as the NUU Editor's bridge keeps it: the promoter's
760
+ // trigger is any last quote line starting with `-`, so a nested quote
761
+ // ending in a list item would lose that item to a `— Author` line.
762
+ blockOptions: { extensions: { attribution: false } }
763
+ });
764
+ const blocks = doc.blocks ?? [];
765
+ if (blocks.length === 0 || blocks.some((child) => child.type === "frontmatter")) {
766
+ return null;
767
+ }
768
+ return blocks.map((child, index) => ({ ...child, id: `${block.id}.${index + 1}`, stableId: void 0 }));
769
+ } catch {
770
+ return null;
771
+ }
772
+ }
773
+ function QuotedLines({ content }) {
774
+ const lines = content.split("\n");
775
+ return /* @__PURE__ */ jsx(Fragment, { children: lines.map((line, index) => /* @__PURE__ */ jsx("p", { children: line.trim().length > 0 ? /* @__PURE__ */ jsx(InlineContent, { content: line }) : /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xA0" }) }, index)) });
776
+ }
713
777
  function BlockquoteBlock({ block, className }) {
714
- const lines = block.content.split("\n");
778
+ const multiline = block.content.includes("\n");
779
+ const blocks = useMemo(() => multiline ? quoteBlocks(block) : null, [block, multiline]);
715
780
  return /* @__PURE__ */ jsx(
716
781
  "blockquote",
717
782
  {
@@ -721,7 +786,9 @@ function BlockquoteBlock({ block, className }) {
721
786
  "data-block-id": block.id,
722
787
  "data-ncm-blockquote": "",
723
788
  className,
724
- 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))
789
+ children: !multiline ? /* @__PURE__ */ jsx(InlineContent, { content: block.content }) : blocks === null ? /* @__PURE__ */ jsx(QuotedLines, { content: block.content }) : blocks.map(
790
+ (child) => child.type === "paragraph" ? /* @__PURE__ */ jsx(QuotedLines, { content: child.content ?? "" }, blockKey(child)) : /* @__PURE__ */ jsx(NcmBlock, { block: child }, blockKey(child))
791
+ )
725
792
  }
726
793
  );
727
794
  }
@@ -925,6 +992,48 @@ function DataviewBlock({ block, className }) {
925
992
  }
926
993
  );
927
994
  }
995
+ function fileNameOf(src) {
996
+ let path = src.split(/[?#]/)[0] ?? "";
997
+ if (/^https?:\/\//i.test(src)) {
998
+ try {
999
+ path = new URL(src).pathname;
1000
+ } catch {
1001
+ return "";
1002
+ }
1003
+ }
1004
+ return path.slice(path.lastIndexOf("/") + 1);
1005
+ }
1006
+ function isExtensionless(src) {
1007
+ const name = fileNameOf(src);
1008
+ const dot = name.lastIndexOf(".");
1009
+ return dot <= 0 || dot === name.length - 1;
1010
+ }
1011
+ function EmbedPictureOrLink({
1012
+ src,
1013
+ alt,
1014
+ title,
1015
+ imgClassName,
1016
+ linkAttrs
1017
+ }) {
1018
+ const [failedSrc, setFailedSrc] = useState(null);
1019
+ useEffect(() => {
1020
+ setFailedSrc(null);
1021
+ }, [src]);
1022
+ if (failedSrc === src) {
1023
+ return /* @__PURE__ */ jsx("a", { href: src, title, ...linkAttrs, children: alt || src });
1024
+ }
1025
+ return /* @__PURE__ */ jsx(
1026
+ "img",
1027
+ {
1028
+ "data-ncm-embed-picture": "",
1029
+ src,
1030
+ alt,
1031
+ title,
1032
+ className: imgClassName,
1033
+ onError: () => setFailedSrc(src)
1034
+ }
1035
+ );
1036
+ }
928
1037
  function toEmbeddingConfigType(embedType) {
929
1038
  switch (embedType) {
930
1039
  case "image":
@@ -953,6 +1062,9 @@ function EmbedBlock({ block, className }) {
953
1062
  if (block.embedType === "image") {
954
1063
  return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className: cn(config.embeddings?.imageClassName, className), children: /* @__PURE__ */ jsx("img", { src: resolvedUrl, alt: block.alt, title: block.title ?? void 0 }) });
955
1064
  }
1065
+ if (block.embedType === "file" && isExtensionless(block.src)) {
1066
+ return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className: cn(config.embeddings?.imageClassName, className), children: /* @__PURE__ */ jsx(EmbedPictureOrLink, { src: resolvedUrl, alt: block.alt, title: block.title ?? void 0 }) });
1067
+ }
956
1068
  return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className, children: /* @__PURE__ */ jsx("a", { href: resolvedUrl, title: block.title ?? void 0, children: block.alt || block.src }) });
957
1069
  }
958
1070
  var embedFrameStyle = {
@@ -1129,7 +1241,19 @@ function FigureBlock({ block, className }) {
1129
1241
  "data-ncm-figure": "",
1130
1242
  className: cn("space-y-2", className),
1131
1243
  children: [
1132
- embed.embedType === "image" ? /* @__PURE__ */ jsx("img", { src: embed.src, alt: embed.alt, title: embed.title ?? void 0 }) : /* @__PURE__ */ jsx(
1244
+ embed.embedType === "image" ? /* @__PURE__ */ jsx("img", { src: embed.src, alt: embed.alt, title: embed.title ?? void 0 }) : embed.embedType === "file" && isExtensionless(embed.src) ? (
1245
+ // Same rule as the standalone embed block: an image spelling with an
1246
+ // extension-less destination is a picture until it fails to load.
1247
+ /* @__PURE__ */ jsx(
1248
+ EmbedPictureOrLink,
1249
+ {
1250
+ src: embed.src,
1251
+ alt: embed.alt,
1252
+ title: embed.title ?? void 0,
1253
+ linkAttrs: { "data-ncm-embed-type": embed.embedType }
1254
+ }
1255
+ )
1256
+ ) : /* @__PURE__ */ jsx(
1133
1257
  "a",
1134
1258
  {
1135
1259
  href: embed.src,
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { createContext, useContext } from 'react';
2
+ import { createContext, useContext, useState, useEffect } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
  import { clsx } from 'clsx';
5
5
  import { twMerge } from 'tailwind-merge';
@@ -12,6 +12,48 @@ function useNcmConfig() {
12
12
  function cn(...inputs) {
13
13
  return twMerge(clsx(inputs));
14
14
  }
15
+ function fileNameOf(src) {
16
+ let path = src.split(/[?#]/)[0] ?? "";
17
+ if (/^https?:\/\//i.test(src)) {
18
+ try {
19
+ path = new URL(src).pathname;
20
+ } catch {
21
+ return "";
22
+ }
23
+ }
24
+ return path.slice(path.lastIndexOf("/") + 1);
25
+ }
26
+ function isExtensionless(src) {
27
+ const name = fileNameOf(src);
28
+ const dot = name.lastIndexOf(".");
29
+ return dot <= 0 || dot === name.length - 1;
30
+ }
31
+ function EmbedPictureOrLink({
32
+ src,
33
+ alt,
34
+ title,
35
+ imgClassName,
36
+ linkAttrs
37
+ }) {
38
+ const [failedSrc, setFailedSrc] = useState(null);
39
+ useEffect(() => {
40
+ setFailedSrc(null);
41
+ }, [src]);
42
+ if (failedSrc === src) {
43
+ return /* @__PURE__ */ jsx("a", { href: src, title, ...linkAttrs, children: alt || src });
44
+ }
45
+ return /* @__PURE__ */ jsx(
46
+ "img",
47
+ {
48
+ "data-ncm-embed-picture": "",
49
+ src,
50
+ alt,
51
+ title,
52
+ className: imgClassName,
53
+ onError: () => setFailedSrc(src)
54
+ }
55
+ );
56
+ }
15
57
  function toEmbeddingConfigType(embedType) {
16
58
  switch (embedType) {
17
59
  case "image":
@@ -40,6 +82,9 @@ function EmbedBlock({ block, className }) {
40
82
  if (block.embedType === "image") {
41
83
  return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className: cn(config.embeddings?.imageClassName, className), children: /* @__PURE__ */ jsx("img", { src: resolvedUrl, alt: block.alt, title: block.title ?? void 0 }) });
42
84
  }
85
+ if (block.embedType === "file" && isExtensionless(block.src)) {
86
+ return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className: cn(config.embeddings?.imageClassName, className), children: /* @__PURE__ */ jsx(EmbedPictureOrLink, { src: resolvedUrl, alt: block.alt, title: block.title ?? void 0 }) });
87
+ }
43
88
  return /* @__PURE__ */ jsx("figure", { ...dataAttrs(block), className, children: /* @__PURE__ */ jsx("a", { href: resolvedUrl, title: block.title ?? void 0, children: block.alt || block.src }) });
44
89
  }
45
90
 
@@ -52,6 +52,52 @@ function tryParseInlineRef(input) {
52
52
  }
53
53
  return { token: { type: "inline_ref", kind: "footnote", content: inner.trim() }, length };
54
54
  }
55
+ function runLength(input, index, char) {
56
+ let end = index;
57
+ while (end < input.length && input[end] === char) end++;
58
+ return end - index;
59
+ }
60
+ function tryParseEmphasis(input) {
61
+ const char = input[0];
62
+ if (char !== "*" && char !== "_") return null;
63
+ const open = Math.min(3, runLength(input, 0, char));
64
+ const need = open;
65
+ let i = open;
66
+ while (i < input.length) {
67
+ const ch = input[i];
68
+ if (ch === "`") {
69
+ const close = input.indexOf("`", i + 1);
70
+ if (close === -1) break;
71
+ i = close + 1;
72
+ continue;
73
+ }
74
+ if (ch !== char) {
75
+ i++;
76
+ continue;
77
+ }
78
+ const run = runLength(input, i, char);
79
+ const closes = need === 1 ? run % 2 === 1 : run >= need;
80
+ if (!closes) {
81
+ i += run;
82
+ continue;
83
+ }
84
+ const extra = run - need;
85
+ const inner = input.slice(open, i + extra);
86
+ if (inner.length === 0) return null;
87
+ const length = i + run;
88
+ const children = parseInline(inner);
89
+ let token;
90
+ if (need === 3) {
91
+ token = { type: "bold", children: [{ type: "italic", children }] };
92
+ } else if (need === 2) {
93
+ token = { type: "bold", children };
94
+ } else {
95
+ token = { type: "italic", children };
96
+ }
97
+ return { token, length };
98
+ }
99
+ return null;
100
+ }
55
101
  function parseInline(content) {
56
102
  if (!content) return [];
57
103
  const tokens = [];
@@ -123,17 +169,11 @@ function parseInline(content) {
123
169
  remaining = remaining.slice(linkMatch[0].length);
124
170
  continue;
125
171
  }
126
- const boldMatch = remaining.match(/^\*\*(.+?)\*\*|^__(.+?)__/);
127
- if (boldMatch) {
128
- const innerContent = boldMatch[1] ?? boldMatch[2];
129
- if (innerContent !== void 0) {
130
- tokens.push({
131
- type: "bold",
132
- children: parseInline(innerContent)
133
- });
134
- remaining = remaining.slice(boldMatch[0].length);
135
- continue;
136
- }
172
+ const emphasis = tryParseEmphasis(remaining);
173
+ if (emphasis) {
174
+ tokens.push(emphasis.token);
175
+ remaining = remaining.slice(emphasis.length);
176
+ continue;
137
177
  }
138
178
  const strikeMatch = remaining.match(/^~~(.+?)~~/);
139
179
  if (strikeMatch && strikeMatch[1] !== void 0) {
@@ -144,18 +184,6 @@ function parseInline(content) {
144
184
  remaining = remaining.slice(strikeMatch[0].length);
145
185
  continue;
146
186
  }
147
- const italicMatch = remaining.match(/^\*([^*]+)\*|^_([^_]+)_/);
148
- if (italicMatch) {
149
- const innerContent = italicMatch[1] ?? italicMatch[2];
150
- if (innerContent !== void 0) {
151
- tokens.push({
152
- type: "italic",
153
- children: parseInline(innerContent)
154
- });
155
- remaining = remaining.slice(italicMatch[0].length);
156
- continue;
157
- }
158
- }
159
187
  const nextSpecial = findNextSpecialChar(remaining);
160
188
  if (nextSpecial > 0) {
161
189
  tokens.push({ type: "text", content: remaining.slice(0, nextSpecial) });
@@ -262,17 +290,30 @@ function ReferenceMarker({ kind, number, children }) {
262
290
  }
263
291
  );
264
292
  }
265
- var ReferenceBasesContext = createContext(null);
293
+ function normalizeReferenceContent(content) {
294
+ return content.replace(/\s+/g, " ").trim().toLowerCase();
295
+ }
296
+ var ReferenceNumberingContext = createContext(null);
266
297
  var BlockReferenceScopeContext = createContext(null);
267
- function BlockReferenceScope({ blockId, children }) {
268
- const bases = useContext(ReferenceBasesContext);
269
- const base = (blockId ? bases?.get(blockId) : void 0) ?? { ref: 0, footnote: 0 };
298
+ function BlockReferenceScope({ children }) {
299
+ const numbering = useContext(ReferenceNumberingContext);
300
+ const local = useRef({ ref: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() });
270
301
  const counter = useRef({ ref: 0, footnote: 0 });
271
- counter.current = { ref: 0, footnote: 0 };
272
302
  const value = {
303
+ numberFor(kind, content) {
304
+ const key = normalizeReferenceContent(content);
305
+ const fromDocument = numbering?.[kind].get(key);
306
+ if (fromDocument !== void 0) return fromDocument;
307
+ const map = local.current[kind];
308
+ const existing = map.get(key);
309
+ if (existing !== void 0) return existing;
310
+ counter.current[kind] += 1;
311
+ map.set(key, counter.current[kind]);
312
+ return counter.current[kind];
313
+ },
273
314
  next(kind) {
274
315
  counter.current[kind] += 1;
275
- return base[kind] + counter.current[kind];
316
+ return counter.current[kind];
276
317
  }
277
318
  };
278
319
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value, children });
@@ -280,7 +321,7 @@ function BlockReferenceScope({ blockId, children }) {
280
321
  function useBlockReferenceScope() {
281
322
  return useContext(BlockReferenceScopeContext);
282
323
  }
283
- var INERT_SCOPE = { next: () => 0 };
324
+ var INERT_SCOPE = { numberFor: () => 0, next: () => 0 };
284
325
  function InertReferenceScope({ children }) {
285
326
  return /* @__PURE__ */ jsx(BlockReferenceScopeContext.Provider, { value: INERT_SCOPE, children });
286
327
  }
@@ -571,7 +612,7 @@ function TokenRenderer({ token, disableBionic, disableBeeline }) {
571
612
  );
572
613
  }
573
614
  case "inline_ref": {
574
- const number = refScope?.next(token.kind) ?? 0;
615
+ const number = refScope?.numberFor(token.kind, token.content) ?? 0;
575
616
  return /* @__PURE__ */ jsx(ReferenceMarker, { kind: token.kind, number, children: token.content ? /* @__PURE__ */ jsx(InertReferenceScope, { children: /* @__PURE__ */ jsx(InlineContent, { content: token.content, disableBionic: true, disableBeeline: true }) }) : null });
576
617
  }
577
618
  default:
@@ -5,7 +5,9 @@ import { a as BlockRendererProps } from '../../renderers-C5f41FCy.js';
5
5
  type FigureBlockProps = BlockRendererProps<FigureBlock$1>;
6
6
  /**
7
7
  * Figure renderer — an embed plus caption, promoted from `embed + paragraph`
8
- * (no blank line). Images render inline; other embed types render as a link.
8
+ * (no blank line). Images render inline, as does a `file` embed whose
9
+ * destination has no extension (see `embed-picture.tsx`); other embed types
10
+ * render as a link.
9
11
  */
10
12
  declare function FigureBlock({ block, className }: FigureBlockProps): react.JSX.Element;
11
13