@wingleeio/mugen-markdown 0.3.0 → 0.4.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/dist/index.cjs CHANGED
@@ -227,120 +227,6 @@ function resolveTheme(theme) {
227
227
  return resolved;
228
228
  }
229
229
  //#endregion
230
- //#region src/inline.ts
231
- /** A base format for body text at a given size/weight/colour. */
232
- function baseFormat(theme, opts = {}) {
233
- return {
234
- family: theme.fontFamily,
235
- monoFamily: theme.monoFamily,
236
- size: opts.size ?? theme.fontSize,
237
- weight: opts.weight ?? 400,
238
- italic: false,
239
- mono: false,
240
- underline: false,
241
- strike: false,
242
- color: opts.color ?? (theme.color !== "inherit" ? theme.color : void 0)
243
- };
244
- }
245
- /** Compose a measurable `Font` shorthand from a format. */
246
- function composeFont(fmt) {
247
- const family = fmt.mono ? fmt.monoFamily : fmt.family;
248
- return `${fmt.italic ? "italic " : ""}${fmt.weight} ${fmt.size}px ${family}`;
249
- }
250
- function pushRun(out, text, fmt) {
251
- if (text.length === 0) return;
252
- const run = {
253
- text,
254
- font: composeFont(fmt)
255
- };
256
- if (fmt.color != null) run.color = fmt.color;
257
- if (fmt.background != null) run.background = fmt.background;
258
- const decoration = [fmt.underline ? "underline" : "", fmt.strike ? "line-through" : ""].filter(Boolean).join(" ");
259
- if (decoration) run.decoration = decoration;
260
- if (fmt.href != null) {
261
- run.href = fmt.href;
262
- run.as = "a";
263
- } else if (fmt.mono) run.as = "code";
264
- out.push(run);
265
- }
266
- /**
267
- * Flatten phrasing content into styled runs. Recursive over the inline marks;
268
- * the result feeds a single `<RichText>` so the whole paragraph wraps as one
269
- * flow and measures exactly.
270
- */
271
- function flattenInline(nodes, fmt, theme, out) {
272
- for (const node of nodes) switch (node.type) {
273
- case "text":
274
- pushRun(out, node.value, fmt);
275
- break;
276
- case "strong":
277
- flattenInline(node.children, {
278
- ...fmt,
279
- weight: theme.strongWeight
280
- }, theme, out);
281
- break;
282
- case "emphasis":
283
- flattenInline(node.children, {
284
- ...fmt,
285
- italic: theme.emphasisItalic ? true : fmt.italic
286
- }, theme, out);
287
- break;
288
- case "delete":
289
- flattenInline(node.children, {
290
- ...fmt,
291
- strike: true
292
- }, theme, out);
293
- break;
294
- case "inlineCode":
295
- pushRun(out, node.value, {
296
- ...fmt,
297
- mono: true,
298
- size: Math.round(fmt.size * theme.inlineCode.sizeScale),
299
- color: theme.inlineCode.color !== "inherit" ? theme.inlineCode.color : fmt.color,
300
- background: theme.inlineCode.background
301
- });
302
- break;
303
- case "link":
304
- flattenInline(node.children, {
305
- ...fmt,
306
- href: node.url,
307
- color: theme.link.color,
308
- underline: theme.link.underline ? true : fmt.underline
309
- }, theme, out);
310
- break;
311
- case "linkReference":
312
- flattenInline(node.children, fmt, theme, out);
313
- break;
314
- case "break":
315
- out.push({
316
- text: "",
317
- break: true
318
- });
319
- break;
320
- case "image":
321
- if (node.alt) pushRun(out, node.alt, {
322
- ...fmt,
323
- color: theme.image.color
324
- });
325
- break;
326
- case "imageReference":
327
- if (node.alt) pushRun(out, node.alt, {
328
- ...fmt,
329
- color: theme.image.color
330
- });
331
- break;
332
- case "footnoteReference":
333
- pushRun(out, `[${node.label ?? node.identifier}]`, {
334
- ...fmt,
335
- color: theme.link.color
336
- });
337
- break;
338
- default:
339
- if ("children" in node && Array.isArray(node.children)) flattenInline(node.children, fmt, theme, out);
340
- break;
341
- }
342
- }
343
- //#endregion
344
230
  //#region src/primitives/rich-text.tsx
345
231
  function resolveRunFont(run, fallback) {
346
232
  const font = run.font ?? fallback;
@@ -348,6 +234,7 @@ function resolveRunFont(run, fallback) {
348
234
  (0, _wingleeio_mugen.assertMeasurableFont)(font);
349
235
  return font;
350
236
  }
237
+ const BOX_PLACEHOLDER = "‌";
351
238
  /** Split runs into hard-break-delimited segments, each a list of rich-inline items. */
352
239
  function segmentItems(runs, fallback) {
353
240
  const segments = [];
@@ -358,9 +245,19 @@ function segmentItems(runs, fallback) {
358
245
  cur = [];
359
246
  continue;
360
247
  }
361
- if (run.text.length === 0) continue;
248
+ if (run.advance != null) {
249
+ cur.push({
250
+ text: BOX_PLACEHOLDER,
251
+ font: resolveRunFont(run, fallback),
252
+ extraWidth: Math.max(0, run.advance),
253
+ break: "never"
254
+ });
255
+ continue;
256
+ }
257
+ const text = run.text ?? "";
258
+ if (text.length === 0) continue;
362
259
  const item = {
363
- text: run.text,
260
+ text,
364
261
  font: resolveRunFont(run, fallback)
365
262
  };
366
263
  if (run.letterSpacing != null) item.letterSpacing = run.letterSpacing;
@@ -425,6 +322,15 @@ function renderRichText(props) {
425
322
  };
426
323
  const children = props.runs.map((run, i) => {
427
324
  if (run.break) return (0, react.createElement)("br", { key: i });
325
+ if (run.advance != null) return (0, react.createElement)("span", {
326
+ key: i,
327
+ style: {
328
+ display: "inline-block",
329
+ verticalAlign: "middle",
330
+ lineHeight: 0,
331
+ whiteSpace: "nowrap"
332
+ }
333
+ }, run.content);
428
334
  const tag = run.as ?? (run.href != null ? "a" : "span");
429
335
  const elementProps = {
430
336
  key: i,
@@ -443,7 +349,7 @@ function renderRichText(props) {
443
349
  if (run.title != null) elementProps.title = run.title;
444
350
  if (run.onClick != null) elementProps.onClick = run.onClick;
445
351
  if (run.className != null) elementProps.className = run.className;
446
- return (0, react.createElement)(tag, elementProps, run.text);
352
+ return (0, react.createElement)(tag, elementProps, run.text ?? "");
447
353
  });
448
354
  return (0, react.createElement)("div", {
449
355
  className: props.className,
@@ -471,12 +377,173 @@ const RichText = (0, _wingleeio_mugen.markPrimitive)(renderRichText, {
471
377
  return max;
472
378
  }
473
379
  });
380
+ /**
381
+ * Measure a string's rendered advance in px for a given measurable font — the
382
+ * same ruler `RichText` measures with. Use it to size an inline box: a text
383
+ * "pill" reserves `measureInline(label, font) + horizontalPadding`.
384
+ */
385
+ function measureInline(text, font) {
386
+ if (text.length === 0) return 0;
387
+ (0, _wingleeio_mugen.assertMeasurableFont)(font);
388
+ return (0, _chenglou_pretext_rich_inline.measureRichInlineStats)(prepareCached([{
389
+ text,
390
+ font
391
+ }]), 1e7).maxLineWidth;
392
+ }
474
393
  /** Drop the rich-inline prepare cache (tests / memory pressure). */
475
394
  function clearRichTextCache() {
476
395
  prepCache.clear();
477
396
  cacheEpoch = -1;
478
397
  }
479
398
  //#endregion
399
+ //#region src/inline.ts
400
+ /** A base format for body text at a given size/weight/colour. */
401
+ function baseFormat(theme, opts = {}) {
402
+ return {
403
+ family: theme.fontFamily,
404
+ monoFamily: theme.monoFamily,
405
+ size: opts.size ?? theme.fontSize,
406
+ weight: opts.weight ?? 400,
407
+ italic: false,
408
+ mono: false,
409
+ underline: false,
410
+ strike: false,
411
+ color: opts.color ?? (theme.color !== "inherit" ? theme.color : void 0)
412
+ };
413
+ }
414
+ /** Compose a measurable `Font` shorthand from a format. */
415
+ function composeFont(fmt) {
416
+ const family = fmt.mono ? fmt.monoFamily : fmt.family;
417
+ return `${fmt.italic ? "italic " : ""}${fmt.weight} ${fmt.size}px ${family}`;
418
+ }
419
+ function pushRun(out, text, fmt) {
420
+ if (text.length === 0) return;
421
+ const run = {
422
+ text,
423
+ font: composeFont(fmt)
424
+ };
425
+ if (fmt.color != null) run.color = fmt.color;
426
+ if (fmt.background != null) run.background = fmt.background;
427
+ const decoration = [fmt.underline ? "underline" : "", fmt.strike ? "line-through" : ""].filter(Boolean).join(" ");
428
+ if (decoration) run.decoration = decoration;
429
+ if (fmt.href != null) {
430
+ run.href = fmt.href;
431
+ run.as = "a";
432
+ } else if (fmt.mono) run.as = "code";
433
+ out.push(run);
434
+ }
435
+ /** Build the context handed to an inline override. */
436
+ function makeInlineCtx(fmt, theme, inline) {
437
+ return {
438
+ theme,
439
+ fmt,
440
+ font: (overrides) => composeFont(overrides ? {
441
+ ...fmt,
442
+ ...overrides
443
+ } : fmt),
444
+ measure: (text, font) => measureInline(text, font),
445
+ runs: (nodes, fmtOverrides) => {
446
+ const sub = [];
447
+ flattenInline(nodes, fmtOverrides ? {
448
+ ...fmt,
449
+ ...fmtOverrides
450
+ } : fmt, theme, sub, inline);
451
+ return sub;
452
+ }
453
+ };
454
+ }
455
+ /**
456
+ * Flatten phrasing content into styled runs. Recursive over the inline marks;
457
+ * the result feeds a single `<RichText>` so the whole paragraph wraps as one
458
+ * flow and measures exactly. An `inline` override map can replace how any node
459
+ * type flattens — returning its own runs (e.g. a measured inline box) or `null`
460
+ * to fall through to the default.
461
+ */
462
+ function flattenInline(nodes, fmt, theme, out, inline) {
463
+ for (const node of nodes) {
464
+ if (inline != null) {
465
+ const override = inline[node.type];
466
+ if (override != null) {
467
+ const produced = override(node, makeInlineCtx(fmt, theme, inline));
468
+ if (produced != null) {
469
+ for (const run of produced) out.push(run);
470
+ continue;
471
+ }
472
+ }
473
+ }
474
+ switch (node.type) {
475
+ case "text":
476
+ pushRun(out, node.value, fmt);
477
+ break;
478
+ case "strong":
479
+ flattenInline(node.children, {
480
+ ...fmt,
481
+ weight: theme.strongWeight
482
+ }, theme, out, inline);
483
+ break;
484
+ case "emphasis":
485
+ flattenInline(node.children, {
486
+ ...fmt,
487
+ italic: theme.emphasisItalic ? true : fmt.italic
488
+ }, theme, out, inline);
489
+ break;
490
+ case "delete":
491
+ flattenInline(node.children, {
492
+ ...fmt,
493
+ strike: true
494
+ }, theme, out, inline);
495
+ break;
496
+ case "inlineCode":
497
+ pushRun(out, node.value, {
498
+ ...fmt,
499
+ mono: true,
500
+ size: Math.round(fmt.size * theme.inlineCode.sizeScale),
501
+ color: theme.inlineCode.color !== "inherit" ? theme.inlineCode.color : fmt.color,
502
+ background: theme.inlineCode.background
503
+ });
504
+ break;
505
+ case "link":
506
+ flattenInline(node.children, {
507
+ ...fmt,
508
+ href: node.url,
509
+ color: theme.link.color,
510
+ underline: theme.link.underline ? true : fmt.underline
511
+ }, theme, out, inline);
512
+ break;
513
+ case "linkReference":
514
+ flattenInline(node.children, fmt, theme, out, inline);
515
+ break;
516
+ case "break":
517
+ out.push({
518
+ text: "",
519
+ break: true
520
+ });
521
+ break;
522
+ case "image":
523
+ if (node.alt) pushRun(out, node.alt, {
524
+ ...fmt,
525
+ color: theme.image.color
526
+ });
527
+ break;
528
+ case "imageReference":
529
+ if (node.alt) pushRun(out, node.alt, {
530
+ ...fmt,
531
+ color: theme.image.color
532
+ });
533
+ break;
534
+ case "footnoteReference":
535
+ pushRun(out, `[${node.label ?? node.identifier}]`, {
536
+ ...fmt,
537
+ color: theme.link.color
538
+ });
539
+ break;
540
+ default:
541
+ if ("children" in node && Array.isArray(node.children)) flattenInline(node.children, fmt, theme, out, inline);
542
+ break;
543
+ }
544
+ }
545
+ }
546
+ //#endregion
480
547
  //#region src/highlight/languages.ts
481
548
  function words(s) {
482
549
  return new Set(s.split(" "));
@@ -1930,7 +1997,7 @@ function createContext(theme, components) {
1930
1997
  ...base
1931
1998
  };
1932
1999
  const out = [];
1933
- flattenInline(nodes, fmt, theme, out);
2000
+ flattenInline(nodes, fmt, theme, out, components.inline);
1934
2001
  return out;
1935
2002
  },
1936
2003
  inlineText(nodes, opts = {}) {
@@ -1940,7 +2007,7 @@ function createContext(theme, components) {
1940
2007
  ...opts.color != null ? { color: opts.color } : null
1941
2008
  });
1942
2009
  const out = [];
1943
- flattenInline(nodes, fmt, theme, out);
2010
+ flattenInline(nodes, fmt, theme, out, components.inline);
1944
2011
  return (0, react.createElement)(RichText, {
1945
2012
  runs: out,
1946
2013
  font: composeFont(fmt),
@@ -2295,6 +2362,7 @@ Object.defineProperty(exports, "definePrimitive", {
2295
2362
  }
2296
2363
  });
2297
2364
  exports.flattenInline = flattenInline;
2365
+ exports.measureInline = measureInline;
2298
2366
  exports.parseMarkdown = parseMarkdown;
2299
2367
  exports.profileFor = profileFor;
2300
2368
  exports.registerLanguage = registerLanguage;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { CSSProperties, JSX, ReactElement, ReactNode } from "react";
2
2
  import { ParserOptions } from "@incremark/core";
3
- import { Blockquote, Blockquote as Blockquote$1, Code, Code as Code$1, Heading, Heading as Heading$1, Html, Html as Html$1, Image, Image as Image$1, Link, List, List as List$1, ListItem, Paragraph, Paragraph as Paragraph$1, PhrasingContent, PhrasingContent as PhrasingContent$1, Root, Root as Root$1, RootContent, RootContent as RootContent$1, Table, Table as Table$1, TableCell, TableRow, ThematicBreak, ThematicBreak as ThematicBreak$1 } from "mdast";
3
+ import { Blockquote, Blockquote as Blockquote$1, Code, Code as Code$1, Delete, Emphasis, Heading, Heading as Heading$1, Html, Html as Html$1, Image, Image as Image$1, InlineCode, Link, Link as Link$1, List, List as List$1, ListItem, Paragraph, Paragraph as Paragraph$1, PhrasingContent, PhrasingContent as PhrasingContent$1, Root, Root as Root$1, RootContent, RootContent as RootContent$1, Strong, Table, Table as Table$1, TableCell, TableRow, Text as Text$1, ThematicBreak, ThematicBreak as ThematicBreak$1 } from "mdast";
4
4
  import { BoxProps, Font, Font as Font$1, HStack, HStackProps, MeasurableStyle, PrimitiveComponent, SafeClassName, Text, TextProps, VStack, VStackProps, definePrimitive } from "@wingleeio/mugen";
5
5
 
6
6
  //#region src/parse.d.ts
@@ -175,8 +175,19 @@ declare function resolveTheme(theme?: DeepPartial<MarkdownTheme>): MarkdownTheme
175
175
  * rich-inline layout) and the painted spans use the identical fonts.
176
176
  */
177
177
  interface RichTextRun {
178
- /** The run's text. Ignored when `break` is set. */
179
- text: string;
178
+ /** The run's text. Ignored when `break` or `advance` is set. */
179
+ text?: string;
180
+ /**
181
+ * Render this run as an **inline box** — the inline analogue of mugen's
182
+ * `Escape`: it reserves exactly `advance` px in the flow (via pretext's
183
+ * `extraWidth`) and paints {@link content}, whatever it is, without measuring
184
+ * its insides. The flow wraps it as one non-breaking atom. The caller owns the
185
+ * contract: `content` must render exactly `advance` px wide and no taller than
186
+ * the line (use the exported `measureInline` to size text-based boxes).
187
+ */
188
+ advance?: number;
189
+ /** The painted content of an inline box (see {@link advance}). */
190
+ content?: ReactNode;
180
191
  /** Measurable font for this run; falls back to the `<RichText font>` prop. */
181
192
  font?: Font$1;
182
193
  color?: string;
@@ -217,6 +228,12 @@ interface RichTextProps<C extends string = string> {
217
228
  * performs over the rendered spans, so the analytic height matches the paint.
218
229
  */
219
230
  declare const RichText: <C extends string = string>(props: RichTextProps<C>) => ReactElement;
231
+ /**
232
+ * Measure a string's rendered advance in px for a given measurable font — the
233
+ * same ruler `RichText` measures with. Use it to size an inline box: a text
234
+ * "pill" reserves `measureInline(label, font) + horizontalPadding`.
235
+ */
236
+ declare function measureInline(text: string, font: Font$1): number;
220
237
  /** Drop the rich-inline prepare cache (tests / memory pressure). */
221
238
  declare function clearRichTextCache(): void;
222
239
  //#endregion
@@ -252,9 +269,11 @@ declare function composeFont(fmt: InlineFormat): Font$1;
252
269
  /**
253
270
  * Flatten phrasing content into styled runs. Recursive over the inline marks;
254
271
  * the result feeds a single `<RichText>` so the whole paragraph wraps as one
255
- * flow and measures exactly.
272
+ * flow and measures exactly. An `inline` override map can replace how any node
273
+ * type flattens — returning its own runs (e.g. a measured inline box) or `null`
274
+ * to fall through to the default.
256
275
  */
257
- declare function flattenInline(nodes: readonly PhrasingContent$1[], fmt: InlineFormat, theme: MarkdownTheme, out: RichTextRun[]): void;
276
+ declare function flattenInline(nodes: readonly PhrasingContent$1[], fmt: InlineFormat, theme: MarkdownTheme, out: RichTextRun[], inline?: InlineComponents): void;
258
277
  //#endregion
259
278
  //#region src/types.d.ts
260
279
  /** Options for building a body/heading inline-text (`RichText`) element. */
@@ -315,10 +334,49 @@ interface MarkdownComponentProps<N> {
315
334
  */
316
335
  type MarkdownComponent<N> = (props: MarkdownComponentProps<N>) => ReactNode;
317
336
  /**
318
- * The overridable block-level components, keyed by mdast node type and typed to
319
- * the matching node. Inline marks (bold, italic, code, links) are styled through
320
- * the {@link MarkdownTheme} rather than as components, because inline content
321
- * must collapse into a single wrapping flow to be measured exactly.
337
+ * The context passed to an inline override. It carries the active inline format
338
+ * (so an override can match the surrounding type) and the helpers to build runs
339
+ * that stay exactly measurable.
340
+ */
341
+ interface InlineRenderContext {
342
+ readonly theme: MarkdownTheme;
343
+ /** The composed inline format at this node (family, size, weight, colour…). */
344
+ readonly fmt: InlineFormat;
345
+ /** Compose a measurable `Font` from the current format plus overrides. */
346
+ font(overrides?: Partial<InlineFormat>): Font$1;
347
+ /**
348
+ * Measure a string's rendered advance in px for a font — to size an inline
349
+ * box. A text pill reserves `measure(label, font) + horizontalPadding`.
350
+ */
351
+ measure(text: string, font: Font$1): number;
352
+ /** Default-flatten phrasing children into runs, to compose with your own. */
353
+ runs(nodes: readonly PhrasingContent$1[], fmtOverrides?: Partial<InlineFormat>): RichTextRun[];
354
+ }
355
+ /**
356
+ * An inline-node override: given the mdast node and the inline context, return
357
+ * the runs it should flatten to — styled text, an inline box (`{ advance,
358
+ * content }`), or a mix — or `null` to fall back to the default styling.
359
+ */
360
+ type InlineComponent<N> = (node: N, ctx: InlineRenderContext) => RichTextRun[] | null;
361
+ /**
362
+ * Inline-node overrides, keyed by mdast inline type. The index signature admits
363
+ * custom inline tokens (from a remark plugin) beyond the named ones.
364
+ */
365
+ interface InlineComponents {
366
+ text?: InlineComponent<Text$1>;
367
+ strong?: InlineComponent<Strong>;
368
+ emphasis?: InlineComponent<Emphasis>;
369
+ delete?: InlineComponent<Delete>;
370
+ inlineCode?: InlineComponent<InlineCode>;
371
+ link?: InlineComponent<Link$1>;
372
+ [type: string]: InlineComponent<any> | undefined;
373
+ }
374
+ /**
375
+ * The overridable components. Block-level marks are keyed by mdast node type and
376
+ * typed to the matching node. Inline marks (bold, italic, code, links) are
377
+ * styled through the {@link MarkdownTheme} by default, but `inline` lets you
378
+ * override how an inline node flattens into runs — including a measured inline
379
+ * box (`{ advance, content }`), the inline twin of mugen's `Escape`.
322
380
  */
323
381
  interface MarkdownComponents {
324
382
  paragraph?: MarkdownComponent<Paragraph$1>;
@@ -330,8 +388,13 @@ interface MarkdownComponents {
330
388
  table?: MarkdownComponent<Table$1>;
331
389
  image?: MarkdownComponent<Image$1>;
332
390
  html?: MarkdownComponent<Html$1>;
391
+ /** Inline-node overrides (see {@link InlineComponents}). */
392
+ inline?: InlineComponents;
333
393
  }
334
- type ResolvedMarkdownComponents = Required<MarkdownComponents>;
394
+ /** Block components resolve to defaults; `inline` stays optional. */
395
+ type ResolvedMarkdownComponents = Required<Omit<MarkdownComponents, 'inline'>> & {
396
+ inline?: InlineComponents;
397
+ };
335
398
  /**
336
399
  * Identity helper for authoring a typed component set with full inference and
337
400
  * `node` narrowing per key:
@@ -556,4 +619,4 @@ declare function registerLanguage(names: string | readonly string[], p: Language
556
619
  /** The profile for a fence language, or `null` when the language is unknown. */
557
620
  declare function profileFor(lang: string | undefined): LanguageProfile | null;
558
621
  //#endregion
559
- export { type Blockquote, type BoxProps, type Code, CodeBlock, type CodeBlockHeader, type CodeBlockProps, type CodeTokenColors, type DeepPartial, FadeMarkdown, type Font, HStack, type HStackProps, type Heading, type Html, type Image, type InlineFormat, type InlineTextOptions, type LanguageProfile, type Link, type List, type ListItem, Markdown, type MarkdownComponent, type MarkdownComponentProps, type MarkdownComponents, type MarkdownParseOptions, type MarkdownProps, type MarkdownRenderContext, type MarkdownTheme, type Paragraph, type PhrasingContent, type PrimitiveComponent, type RenderMarkdownOptions, type ResolvedMarkdownComponents, RichText, type RichTextProps, type RichTextRun, type Root, type RootContent, type Table, TableBlock, type TableBlockProps, type TableCell, type TableRow, Text, type TextProps, type ThematicBreak, type TokenType, VStack, type VStackProps, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
622
+ export { type Blockquote, type BoxProps, type Code, CodeBlock, type CodeBlockHeader, type CodeBlockProps, type CodeTokenColors, type DeepPartial, FadeMarkdown, type Font, HStack, type HStackProps, type Heading, type Html, type Image, type InlineComponent, type InlineComponents, type InlineFormat, type InlineRenderContext, type InlineTextOptions, type LanguageProfile, type Link, type List, type ListItem, Markdown, type MarkdownComponent, type MarkdownComponentProps, type MarkdownComponents, type MarkdownParseOptions, type MarkdownProps, type MarkdownRenderContext, type MarkdownTheme, type Paragraph, type PhrasingContent, type PrimitiveComponent, type RenderMarkdownOptions, type ResolvedMarkdownComponents, RichText, type RichTextProps, type RichTextRun, type Root, type RootContent, type Table, TableBlock, type TableBlockProps, type TableCell, type TableRow, Text, type TextProps, type ThematicBreak, type TokenType, VStack, type VStackProps, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, measureInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { CSSProperties, JSX, ReactElement, ReactNode } from "react";
2
2
  import { BoxProps, Font, Font as Font$1, HStack, HStackProps, MeasurableStyle, PrimitiveComponent, SafeClassName, Text, TextProps, VStack, VStackProps, definePrimitive } from "@wingleeio/mugen";
3
3
  import { ParserOptions } from "@incremark/core";
4
- import { Blockquote, Blockquote as Blockquote$1, Code, Code as Code$1, Heading, Heading as Heading$1, Html, Html as Html$1, Image, Image as Image$1, Link, List, List as List$1, ListItem, Paragraph, Paragraph as Paragraph$1, PhrasingContent, PhrasingContent as PhrasingContent$1, Root, Root as Root$1, RootContent, RootContent as RootContent$1, Table, Table as Table$1, TableCell, TableRow, ThematicBreak, ThematicBreak as ThematicBreak$1 } from "mdast";
4
+ import { Blockquote, Blockquote as Blockquote$1, Code, Code as Code$1, Delete, Emphasis, Heading, Heading as Heading$1, Html, Html as Html$1, Image, Image as Image$1, InlineCode, Link, Link as Link$1, List, List as List$1, ListItem, Paragraph, Paragraph as Paragraph$1, PhrasingContent, PhrasingContent as PhrasingContent$1, Root, Root as Root$1, RootContent, RootContent as RootContent$1, Strong, Table, Table as Table$1, TableCell, TableRow, Text as Text$1, ThematicBreak, ThematicBreak as ThematicBreak$1 } from "mdast";
5
5
 
6
6
  //#region src/parse.d.ts
7
7
  /**
@@ -175,8 +175,19 @@ declare function resolveTheme(theme?: DeepPartial<MarkdownTheme>): MarkdownTheme
175
175
  * rich-inline layout) and the painted spans use the identical fonts.
176
176
  */
177
177
  interface RichTextRun {
178
- /** The run's text. Ignored when `break` is set. */
179
- text: string;
178
+ /** The run's text. Ignored when `break` or `advance` is set. */
179
+ text?: string;
180
+ /**
181
+ * Render this run as an **inline box** — the inline analogue of mugen's
182
+ * `Escape`: it reserves exactly `advance` px in the flow (via pretext's
183
+ * `extraWidth`) and paints {@link content}, whatever it is, without measuring
184
+ * its insides. The flow wraps it as one non-breaking atom. The caller owns the
185
+ * contract: `content` must render exactly `advance` px wide and no taller than
186
+ * the line (use the exported `measureInline` to size text-based boxes).
187
+ */
188
+ advance?: number;
189
+ /** The painted content of an inline box (see {@link advance}). */
190
+ content?: ReactNode;
180
191
  /** Measurable font for this run; falls back to the `<RichText font>` prop. */
181
192
  font?: Font$1;
182
193
  color?: string;
@@ -217,6 +228,12 @@ interface RichTextProps<C extends string = string> {
217
228
  * performs over the rendered spans, so the analytic height matches the paint.
218
229
  */
219
230
  declare const RichText: <C extends string = string>(props: RichTextProps<C>) => ReactElement;
231
+ /**
232
+ * Measure a string's rendered advance in px for a given measurable font — the
233
+ * same ruler `RichText` measures with. Use it to size an inline box: a text
234
+ * "pill" reserves `measureInline(label, font) + horizontalPadding`.
235
+ */
236
+ declare function measureInline(text: string, font: Font$1): number;
220
237
  /** Drop the rich-inline prepare cache (tests / memory pressure). */
221
238
  declare function clearRichTextCache(): void;
222
239
  //#endregion
@@ -252,9 +269,11 @@ declare function composeFont(fmt: InlineFormat): Font$1;
252
269
  /**
253
270
  * Flatten phrasing content into styled runs. Recursive over the inline marks;
254
271
  * the result feeds a single `<RichText>` so the whole paragraph wraps as one
255
- * flow and measures exactly.
272
+ * flow and measures exactly. An `inline` override map can replace how any node
273
+ * type flattens — returning its own runs (e.g. a measured inline box) or `null`
274
+ * to fall through to the default.
256
275
  */
257
- declare function flattenInline(nodes: readonly PhrasingContent$1[], fmt: InlineFormat, theme: MarkdownTheme, out: RichTextRun[]): void;
276
+ declare function flattenInline(nodes: readonly PhrasingContent$1[], fmt: InlineFormat, theme: MarkdownTheme, out: RichTextRun[], inline?: InlineComponents): void;
258
277
  //#endregion
259
278
  //#region src/types.d.ts
260
279
  /** Options for building a body/heading inline-text (`RichText`) element. */
@@ -315,10 +334,49 @@ interface MarkdownComponentProps<N> {
315
334
  */
316
335
  type MarkdownComponent<N> = (props: MarkdownComponentProps<N>) => ReactNode;
317
336
  /**
318
- * The overridable block-level components, keyed by mdast node type and typed to
319
- * the matching node. Inline marks (bold, italic, code, links) are styled through
320
- * the {@link MarkdownTheme} rather than as components, because inline content
321
- * must collapse into a single wrapping flow to be measured exactly.
337
+ * The context passed to an inline override. It carries the active inline format
338
+ * (so an override can match the surrounding type) and the helpers to build runs
339
+ * that stay exactly measurable.
340
+ */
341
+ interface InlineRenderContext {
342
+ readonly theme: MarkdownTheme;
343
+ /** The composed inline format at this node (family, size, weight, colour…). */
344
+ readonly fmt: InlineFormat;
345
+ /** Compose a measurable `Font` from the current format plus overrides. */
346
+ font(overrides?: Partial<InlineFormat>): Font$1;
347
+ /**
348
+ * Measure a string's rendered advance in px for a font — to size an inline
349
+ * box. A text pill reserves `measure(label, font) + horizontalPadding`.
350
+ */
351
+ measure(text: string, font: Font$1): number;
352
+ /** Default-flatten phrasing children into runs, to compose with your own. */
353
+ runs(nodes: readonly PhrasingContent$1[], fmtOverrides?: Partial<InlineFormat>): RichTextRun[];
354
+ }
355
+ /**
356
+ * An inline-node override: given the mdast node and the inline context, return
357
+ * the runs it should flatten to — styled text, an inline box (`{ advance,
358
+ * content }`), or a mix — or `null` to fall back to the default styling.
359
+ */
360
+ type InlineComponent<N> = (node: N, ctx: InlineRenderContext) => RichTextRun[] | null;
361
+ /**
362
+ * Inline-node overrides, keyed by mdast inline type. The index signature admits
363
+ * custom inline tokens (from a remark plugin) beyond the named ones.
364
+ */
365
+ interface InlineComponents {
366
+ text?: InlineComponent<Text$1>;
367
+ strong?: InlineComponent<Strong>;
368
+ emphasis?: InlineComponent<Emphasis>;
369
+ delete?: InlineComponent<Delete>;
370
+ inlineCode?: InlineComponent<InlineCode>;
371
+ link?: InlineComponent<Link$1>;
372
+ [type: string]: InlineComponent<any> | undefined;
373
+ }
374
+ /**
375
+ * The overridable components. Block-level marks are keyed by mdast node type and
376
+ * typed to the matching node. Inline marks (bold, italic, code, links) are
377
+ * styled through the {@link MarkdownTheme} by default, but `inline` lets you
378
+ * override how an inline node flattens into runs — including a measured inline
379
+ * box (`{ advance, content }`), the inline twin of mugen's `Escape`.
322
380
  */
323
381
  interface MarkdownComponents {
324
382
  paragraph?: MarkdownComponent<Paragraph$1>;
@@ -330,8 +388,13 @@ interface MarkdownComponents {
330
388
  table?: MarkdownComponent<Table$1>;
331
389
  image?: MarkdownComponent<Image$1>;
332
390
  html?: MarkdownComponent<Html$1>;
391
+ /** Inline-node overrides (see {@link InlineComponents}). */
392
+ inline?: InlineComponents;
333
393
  }
334
- type ResolvedMarkdownComponents = Required<MarkdownComponents>;
394
+ /** Block components resolve to defaults; `inline` stays optional. */
395
+ type ResolvedMarkdownComponents = Required<Omit<MarkdownComponents, 'inline'>> & {
396
+ inline?: InlineComponents;
397
+ };
335
398
  /**
336
399
  * Identity helper for authoring a typed component set with full inference and
337
400
  * `node` narrowing per key:
@@ -556,4 +619,4 @@ declare function registerLanguage(names: string | readonly string[], p: Language
556
619
  /** The profile for a fence language, or `null` when the language is unknown. */
557
620
  declare function profileFor(lang: string | undefined): LanguageProfile | null;
558
621
  //#endregion
559
- export { type Blockquote, type BoxProps, type Code, CodeBlock, type CodeBlockHeader, type CodeBlockProps, type CodeTokenColors, type DeepPartial, FadeMarkdown, type Font, HStack, type HStackProps, type Heading, type Html, type Image, type InlineFormat, type InlineTextOptions, type LanguageProfile, type Link, type List, type ListItem, Markdown, type MarkdownComponent, type MarkdownComponentProps, type MarkdownComponents, type MarkdownParseOptions, type MarkdownProps, type MarkdownRenderContext, type MarkdownTheme, type Paragraph, type PhrasingContent, type PrimitiveComponent, type RenderMarkdownOptions, type ResolvedMarkdownComponents, RichText, type RichTextProps, type RichTextRun, type Root, type RootContent, type Table, TableBlock, type TableBlockProps, type TableCell, type TableRow, Text, type TextProps, type ThematicBreak, type TokenType, VStack, type VStackProps, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
622
+ export { type Blockquote, type BoxProps, type Code, CodeBlock, type CodeBlockHeader, type CodeBlockProps, type CodeTokenColors, type DeepPartial, FadeMarkdown, type Font, HStack, type HStackProps, type Heading, type Html, type Image, type InlineComponent, type InlineComponents, type InlineFormat, type InlineRenderContext, type InlineTextOptions, type LanguageProfile, type Link, type List, type ListItem, Markdown, type MarkdownComponent, type MarkdownComponentProps, type MarkdownComponents, type MarkdownParseOptions, type MarkdownProps, type MarkdownRenderContext, type MarkdownTheme, type Paragraph, type PhrasingContent, type PrimitiveComponent, type RenderMarkdownOptions, type ResolvedMarkdownComponents, RichText, type RichTextProps, type RichTextRun, type Root, type RootContent, type Table, TableBlock, type TableBlockProps, type TableCell, type TableRow, Text, type TextProps, type ThematicBreak, type TokenType, VStack, type VStackProps, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, measureInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
package/dist/index.mjs CHANGED
@@ -226,120 +226,6 @@ function resolveTheme(theme) {
226
226
  return resolved;
227
227
  }
228
228
  //#endregion
229
- //#region src/inline.ts
230
- /** A base format for body text at a given size/weight/colour. */
231
- function baseFormat(theme, opts = {}) {
232
- return {
233
- family: theme.fontFamily,
234
- monoFamily: theme.monoFamily,
235
- size: opts.size ?? theme.fontSize,
236
- weight: opts.weight ?? 400,
237
- italic: false,
238
- mono: false,
239
- underline: false,
240
- strike: false,
241
- color: opts.color ?? (theme.color !== "inherit" ? theme.color : void 0)
242
- };
243
- }
244
- /** Compose a measurable `Font` shorthand from a format. */
245
- function composeFont(fmt) {
246
- const family = fmt.mono ? fmt.monoFamily : fmt.family;
247
- return `${fmt.italic ? "italic " : ""}${fmt.weight} ${fmt.size}px ${family}`;
248
- }
249
- function pushRun(out, text, fmt) {
250
- if (text.length === 0) return;
251
- const run = {
252
- text,
253
- font: composeFont(fmt)
254
- };
255
- if (fmt.color != null) run.color = fmt.color;
256
- if (fmt.background != null) run.background = fmt.background;
257
- const decoration = [fmt.underline ? "underline" : "", fmt.strike ? "line-through" : ""].filter(Boolean).join(" ");
258
- if (decoration) run.decoration = decoration;
259
- if (fmt.href != null) {
260
- run.href = fmt.href;
261
- run.as = "a";
262
- } else if (fmt.mono) run.as = "code";
263
- out.push(run);
264
- }
265
- /**
266
- * Flatten phrasing content into styled runs. Recursive over the inline marks;
267
- * the result feeds a single `<RichText>` so the whole paragraph wraps as one
268
- * flow and measures exactly.
269
- */
270
- function flattenInline(nodes, fmt, theme, out) {
271
- for (const node of nodes) switch (node.type) {
272
- case "text":
273
- pushRun(out, node.value, fmt);
274
- break;
275
- case "strong":
276
- flattenInline(node.children, {
277
- ...fmt,
278
- weight: theme.strongWeight
279
- }, theme, out);
280
- break;
281
- case "emphasis":
282
- flattenInline(node.children, {
283
- ...fmt,
284
- italic: theme.emphasisItalic ? true : fmt.italic
285
- }, theme, out);
286
- break;
287
- case "delete":
288
- flattenInline(node.children, {
289
- ...fmt,
290
- strike: true
291
- }, theme, out);
292
- break;
293
- case "inlineCode":
294
- pushRun(out, node.value, {
295
- ...fmt,
296
- mono: true,
297
- size: Math.round(fmt.size * theme.inlineCode.sizeScale),
298
- color: theme.inlineCode.color !== "inherit" ? theme.inlineCode.color : fmt.color,
299
- background: theme.inlineCode.background
300
- });
301
- break;
302
- case "link":
303
- flattenInline(node.children, {
304
- ...fmt,
305
- href: node.url,
306
- color: theme.link.color,
307
- underline: theme.link.underline ? true : fmt.underline
308
- }, theme, out);
309
- break;
310
- case "linkReference":
311
- flattenInline(node.children, fmt, theme, out);
312
- break;
313
- case "break":
314
- out.push({
315
- text: "",
316
- break: true
317
- });
318
- break;
319
- case "image":
320
- if (node.alt) pushRun(out, node.alt, {
321
- ...fmt,
322
- color: theme.image.color
323
- });
324
- break;
325
- case "imageReference":
326
- if (node.alt) pushRun(out, node.alt, {
327
- ...fmt,
328
- color: theme.image.color
329
- });
330
- break;
331
- case "footnoteReference":
332
- pushRun(out, `[${node.label ?? node.identifier}]`, {
333
- ...fmt,
334
- color: theme.link.color
335
- });
336
- break;
337
- default:
338
- if ("children" in node && Array.isArray(node.children)) flattenInline(node.children, fmt, theme, out);
339
- break;
340
- }
341
- }
342
- //#endregion
343
229
  //#region src/primitives/rich-text.tsx
344
230
  function resolveRunFont(run, fallback) {
345
231
  const font = run.font ?? fallback;
@@ -347,6 +233,7 @@ function resolveRunFont(run, fallback) {
347
233
  assertMeasurableFont(font);
348
234
  return font;
349
235
  }
236
+ const BOX_PLACEHOLDER = "‌";
350
237
  /** Split runs into hard-break-delimited segments, each a list of rich-inline items. */
351
238
  function segmentItems(runs, fallback) {
352
239
  const segments = [];
@@ -357,9 +244,19 @@ function segmentItems(runs, fallback) {
357
244
  cur = [];
358
245
  continue;
359
246
  }
360
- if (run.text.length === 0) continue;
247
+ if (run.advance != null) {
248
+ cur.push({
249
+ text: BOX_PLACEHOLDER,
250
+ font: resolveRunFont(run, fallback),
251
+ extraWidth: Math.max(0, run.advance),
252
+ break: "never"
253
+ });
254
+ continue;
255
+ }
256
+ const text = run.text ?? "";
257
+ if (text.length === 0) continue;
361
258
  const item = {
362
- text: run.text,
259
+ text,
363
260
  font: resolveRunFont(run, fallback)
364
261
  };
365
262
  if (run.letterSpacing != null) item.letterSpacing = run.letterSpacing;
@@ -424,6 +321,15 @@ function renderRichText(props) {
424
321
  };
425
322
  const children = props.runs.map((run, i) => {
426
323
  if (run.break) return createElement("br", { key: i });
324
+ if (run.advance != null) return createElement("span", {
325
+ key: i,
326
+ style: {
327
+ display: "inline-block",
328
+ verticalAlign: "middle",
329
+ lineHeight: 0,
330
+ whiteSpace: "nowrap"
331
+ }
332
+ }, run.content);
427
333
  const tag = run.as ?? (run.href != null ? "a" : "span");
428
334
  const elementProps = {
429
335
  key: i,
@@ -442,7 +348,7 @@ function renderRichText(props) {
442
348
  if (run.title != null) elementProps.title = run.title;
443
349
  if (run.onClick != null) elementProps.onClick = run.onClick;
444
350
  if (run.className != null) elementProps.className = run.className;
445
- return createElement(tag, elementProps, run.text);
351
+ return createElement(tag, elementProps, run.text ?? "");
446
352
  });
447
353
  return createElement("div", {
448
354
  className: props.className,
@@ -470,12 +376,173 @@ const RichText = markPrimitive(renderRichText, {
470
376
  return max;
471
377
  }
472
378
  });
379
+ /**
380
+ * Measure a string's rendered advance in px for a given measurable font — the
381
+ * same ruler `RichText` measures with. Use it to size an inline box: a text
382
+ * "pill" reserves `measureInline(label, font) + horizontalPadding`.
383
+ */
384
+ function measureInline(text, font) {
385
+ if (text.length === 0) return 0;
386
+ assertMeasurableFont(font);
387
+ return measureRichInlineStats(prepareCached([{
388
+ text,
389
+ font
390
+ }]), 1e7).maxLineWidth;
391
+ }
473
392
  /** Drop the rich-inline prepare cache (tests / memory pressure). */
474
393
  function clearRichTextCache() {
475
394
  prepCache.clear();
476
395
  cacheEpoch = -1;
477
396
  }
478
397
  //#endregion
398
+ //#region src/inline.ts
399
+ /** A base format for body text at a given size/weight/colour. */
400
+ function baseFormat(theme, opts = {}) {
401
+ return {
402
+ family: theme.fontFamily,
403
+ monoFamily: theme.monoFamily,
404
+ size: opts.size ?? theme.fontSize,
405
+ weight: opts.weight ?? 400,
406
+ italic: false,
407
+ mono: false,
408
+ underline: false,
409
+ strike: false,
410
+ color: opts.color ?? (theme.color !== "inherit" ? theme.color : void 0)
411
+ };
412
+ }
413
+ /** Compose a measurable `Font` shorthand from a format. */
414
+ function composeFont(fmt) {
415
+ const family = fmt.mono ? fmt.monoFamily : fmt.family;
416
+ return `${fmt.italic ? "italic " : ""}${fmt.weight} ${fmt.size}px ${family}`;
417
+ }
418
+ function pushRun(out, text, fmt) {
419
+ if (text.length === 0) return;
420
+ const run = {
421
+ text,
422
+ font: composeFont(fmt)
423
+ };
424
+ if (fmt.color != null) run.color = fmt.color;
425
+ if (fmt.background != null) run.background = fmt.background;
426
+ const decoration = [fmt.underline ? "underline" : "", fmt.strike ? "line-through" : ""].filter(Boolean).join(" ");
427
+ if (decoration) run.decoration = decoration;
428
+ if (fmt.href != null) {
429
+ run.href = fmt.href;
430
+ run.as = "a";
431
+ } else if (fmt.mono) run.as = "code";
432
+ out.push(run);
433
+ }
434
+ /** Build the context handed to an inline override. */
435
+ function makeInlineCtx(fmt, theme, inline) {
436
+ return {
437
+ theme,
438
+ fmt,
439
+ font: (overrides) => composeFont(overrides ? {
440
+ ...fmt,
441
+ ...overrides
442
+ } : fmt),
443
+ measure: (text, font) => measureInline(text, font),
444
+ runs: (nodes, fmtOverrides) => {
445
+ const sub = [];
446
+ flattenInline(nodes, fmtOverrides ? {
447
+ ...fmt,
448
+ ...fmtOverrides
449
+ } : fmt, theme, sub, inline);
450
+ return sub;
451
+ }
452
+ };
453
+ }
454
+ /**
455
+ * Flatten phrasing content into styled runs. Recursive over the inline marks;
456
+ * the result feeds a single `<RichText>` so the whole paragraph wraps as one
457
+ * flow and measures exactly. An `inline` override map can replace how any node
458
+ * type flattens — returning its own runs (e.g. a measured inline box) or `null`
459
+ * to fall through to the default.
460
+ */
461
+ function flattenInline(nodes, fmt, theme, out, inline) {
462
+ for (const node of nodes) {
463
+ if (inline != null) {
464
+ const override = inline[node.type];
465
+ if (override != null) {
466
+ const produced = override(node, makeInlineCtx(fmt, theme, inline));
467
+ if (produced != null) {
468
+ for (const run of produced) out.push(run);
469
+ continue;
470
+ }
471
+ }
472
+ }
473
+ switch (node.type) {
474
+ case "text":
475
+ pushRun(out, node.value, fmt);
476
+ break;
477
+ case "strong":
478
+ flattenInline(node.children, {
479
+ ...fmt,
480
+ weight: theme.strongWeight
481
+ }, theme, out, inline);
482
+ break;
483
+ case "emphasis":
484
+ flattenInline(node.children, {
485
+ ...fmt,
486
+ italic: theme.emphasisItalic ? true : fmt.italic
487
+ }, theme, out, inline);
488
+ break;
489
+ case "delete":
490
+ flattenInline(node.children, {
491
+ ...fmt,
492
+ strike: true
493
+ }, theme, out, inline);
494
+ break;
495
+ case "inlineCode":
496
+ pushRun(out, node.value, {
497
+ ...fmt,
498
+ mono: true,
499
+ size: Math.round(fmt.size * theme.inlineCode.sizeScale),
500
+ color: theme.inlineCode.color !== "inherit" ? theme.inlineCode.color : fmt.color,
501
+ background: theme.inlineCode.background
502
+ });
503
+ break;
504
+ case "link":
505
+ flattenInline(node.children, {
506
+ ...fmt,
507
+ href: node.url,
508
+ color: theme.link.color,
509
+ underline: theme.link.underline ? true : fmt.underline
510
+ }, theme, out, inline);
511
+ break;
512
+ case "linkReference":
513
+ flattenInline(node.children, fmt, theme, out, inline);
514
+ break;
515
+ case "break":
516
+ out.push({
517
+ text: "",
518
+ break: true
519
+ });
520
+ break;
521
+ case "image":
522
+ if (node.alt) pushRun(out, node.alt, {
523
+ ...fmt,
524
+ color: theme.image.color
525
+ });
526
+ break;
527
+ case "imageReference":
528
+ if (node.alt) pushRun(out, node.alt, {
529
+ ...fmt,
530
+ color: theme.image.color
531
+ });
532
+ break;
533
+ case "footnoteReference":
534
+ pushRun(out, `[${node.label ?? node.identifier}]`, {
535
+ ...fmt,
536
+ color: theme.link.color
537
+ });
538
+ break;
539
+ default:
540
+ if ("children" in node && Array.isArray(node.children)) flattenInline(node.children, fmt, theme, out, inline);
541
+ break;
542
+ }
543
+ }
544
+ }
545
+ //#endregion
479
546
  //#region src/highlight/languages.ts
480
547
  function words(s) {
481
548
  return new Set(s.split(" "));
@@ -1929,7 +1996,7 @@ function createContext(theme, components) {
1929
1996
  ...base
1930
1997
  };
1931
1998
  const out = [];
1932
- flattenInline(nodes, fmt, theme, out);
1999
+ flattenInline(nodes, fmt, theme, out, components.inline);
1933
2000
  return out;
1934
2001
  },
1935
2002
  inlineText(nodes, opts = {}) {
@@ -1939,7 +2006,7 @@ function createContext(theme, components) {
1939
2006
  ...opts.color != null ? { color: opts.color } : null
1940
2007
  });
1941
2008
  const out = [];
1942
- flattenInline(nodes, fmt, theme, out);
2009
+ flattenInline(nodes, fmt, theme, out, components.inline);
1943
2010
  return createElement(RichText, {
1944
2011
  runs: out,
1945
2012
  font: composeFont(fmt),
@@ -2256,4 +2323,4 @@ function defineMarkdownComponents(components) {
2256
2323
  return components;
2257
2324
  }
2258
2325
  //#endregion
2259
- export { CodeBlock, FadeMarkdown, HStack, Markdown, RichText, TableBlock, Text, VStack, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
2326
+ export { CodeBlock, FadeMarkdown, HStack, Markdown, RichText, TableBlock, Text, VStack, baseFormat, clearParseCache, clearRichTextCache, composeFont, defaultComponents, defaultTheme, defaultTokenColors, defineMarkdownComponents, definePrimitive, flattenInline, measureInline, parseMarkdown, profileFor, registerLanguage, renderMarkdown, resolveTheme };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wingleeio/mugen-markdown",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Measurable markdown for mugen — incremark-parsed, rendered with mugen primitives so the virtualizer's tree walker computes exact row heights.",
5
5
  "license": "MIT",
6
6
  "author": "Wing Lee <contact@winglee.io>",