officeparser 7.7.0 → 7.8.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 +3 -2
- package/dist/defaults.js +2 -0
- package/dist/generators/HtmlGenerator.js +19 -3
- package/dist/generators/MarkdownGenerator.d.ts +1 -0
- package/dist/generators/MarkdownGenerator.js +58 -14
- package/dist/officeparser.browser.d.ts +52 -2
- package/dist/officeparser.browser.iife.js +167 -161
- package/dist/officeparser.browser.mjs +167 -161
- package/dist/officeparser.browser.slim.d.ts +52 -2
- package/dist/officeparser.browser.slim.iife.js +158 -152
- package/dist/officeparser.browser.slim.mjs +158 -152
- package/dist/parsers/HtmlParser.js +37 -2
- package/dist/parsers/MarkdownParser.js +113 -3
- package/dist/sbom.cdx.json +92 -92
- package/dist/types.d.ts +52 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -758,7 +758,7 @@ Definition List Node (type: 'definitionList')
|
|
|
758
758
|
```
|
|
759
759
|
|
|
760
760
|
- `admonition` round-trips through both Markdown (`> [!NOTE]` / `:::note ... :::`) and HTML (`<div class="admonition admonition-note" data-type="note">`)
|
|
761
|
-
- `embed`
|
|
761
|
+
- `embed` models YouTube videos and generic iframes. Markdown form is selected by `mdConfig.dialect.embeds`: `'html'` (default; the `<div data-youtube-video>` / `<iframe>` block), `'directive'` (a `::youtube[…]{…}` / `::embed[…]{…}` leaf directive), `'link'`, or `'thumbnail'` (YouTube-only clickable preview). A generic iframe is captured only under `htmlParserConfig.preserveIframes` (the trust input) and can be emitted as an inert click-to-load placeholder via `htmlConfig.gatedEmbeds`. The `'directive'` form is an editor round-trip format, not GitHub-rendered
|
|
762
762
|
- Abbreviations (`*[HTML]: Hypertext Markup Language`) are stored as `TextMetadata.abbreviationTitle` on the abbreviated text node rather than as a separate node type
|
|
763
763
|
|
|
764
764
|
---
|
|
@@ -783,6 +783,7 @@ idempotent and `.md → AST → HTML → AST → .md` survives unchanged.
|
|
|
783
783
|
| Highlight | `==text==` | `TextMetadata.backgroundColor` |
|
|
784
784
|
| Link/image titles | `[text](url "Title")` / `` | `TextMetadata.title` / `ImageMetadata.title` |
|
|
785
785
|
| Inline/block math | `$E=mc^2$` / `` $$...$$ `` | `type: 'code'`, `CodeMetadata.math` (`'inline' \| 'block'`) |
|
|
786
|
+
| Embeds | `::youtube[Label]{id=… width=… align=…}` / `::embed[Label]{src=… …}` (leaf directive; see `mdConfig.dialect.embeds`) | `type: 'embed'`, `EmbedMetadata` |
|
|
786
787
|
| Frontmatter arrays | `tags: [a, b]` or `tags: ["a","b"]` | Real array in `metadata.customProperties`/`nativeProperties` |
|
|
787
788
|
| MDX components (import-only) | `<Component prop="x">...</Component>` | Stripped; inner Markdown is kept. Never generated back. |
|
|
788
789
|
|
|
@@ -988,7 +989,7 @@ Pass as the second argument to `parseOffice(file, config)`.
|
|
|
988
989
|
| `fileType` | `SupportedFileType \| null` | `null` | **Required for text-based binary data** (`'md'`, `'html'`, `'csv'`) as these lack magic bytes. |
|
|
989
990
|
| `csvDelimiter` | `string` | `','` | Input delimiter when parsing CSV files |
|
|
990
991
|
| `decompressionLimits` | `DecompressionLimits` | `{ maxUncompressedBytes: 512MB, maxZipEntries: 10000, maxTableCells: 1000000 }` | **New**: Limits applied during ZIP extraction (and ODF cell expansion) to protect against excessive memory and resource usage |
|
|
991
|
-
| `htmlParserConfig` | `HtmlParserConfig` | `{}` | HTML/XHTML/EPUB parsing options. `preserveAttributes` (`boolean`, default `false`): keep generic source attributes no typed field consumed on `node.htmlAttributes`. `preserveIframes` (`boolean \| string[]`, default `false`): preserve non-YouTube `<iframe>` embeds (otherwise dropped) as `embed` nodes — `true` for any, or a hostname allowlist; the src is scheme-checked on generation |
|
|
992
|
+
| `htmlParserConfig` | `HtmlParserConfig` | `{}` | HTML/XHTML/EPUB parsing options. `preserveAttributes` (`boolean`, default `false`): keep generic source attributes no typed field consumed on `node.htmlAttributes`. `preserveIframes` (`boolean \| string[]`, default `false`): preserve non-YouTube `<iframe>` embeds (otherwise dropped) as `embed` nodes — `true` for any, or a hostname allowlist; the src is scheme-checked on generation. `embedFolkForms` (`boolean`, default `false`): opt in to importing ambiguous folk embed forms (Obsidian ``, thumbnail-link) as YouTube embeds |
|
|
992
993
|
| `pdfWorkerSrc` | `string` | CDN (jsDelivr) | Path/URL to `pdf.worker.min.mjs` (required in browser) |
|
|
993
994
|
| `onWarning` | `(issue: OfficeIssue) => void` | — | Callback for non-fatal parsing issues |
|
|
994
995
|
| `abortSignal` | `AbortSignal \| null` | `null` | Optional signal to cancel parsing (rejects with AbortError) |
|
package/dist/defaults.js
CHANGED
|
@@ -41,6 +41,7 @@ const DEFAULT_OCR_CONFIG = {
|
|
|
41
41
|
const DEFAULT_HTML_PARSER_CONFIG = {
|
|
42
42
|
preserveAttributes: false,
|
|
43
43
|
preserveIframes: false,
|
|
44
|
+
embedFolkForms: false,
|
|
44
45
|
};
|
|
45
46
|
/**
|
|
46
47
|
* Default configuration for the OfficeParser.
|
|
@@ -89,6 +90,7 @@ const DEFAULT_HTML_GENERATOR_CONFIG = {
|
|
|
89
90
|
bodyEnd: '',
|
|
90
91
|
},
|
|
91
92
|
sourceAttributes: false,
|
|
93
|
+
gatedEmbeds: false,
|
|
92
94
|
};
|
|
93
95
|
/**
|
|
94
96
|
* Default configuration for PDF generation.
|
|
@@ -527,8 +527,11 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
527
527
|
// Non-list node closes all active lists
|
|
528
528
|
closeListsToLevel(-1);
|
|
529
529
|
let result = await this.processNodeRecursive(node, this.nodeProcessor.bind(this), override);
|
|
530
|
-
// Add
|
|
531
|
-
|
|
530
|
+
// Add a blank line after BLOCK nodes for readable HTML source. Inline nodes (a
|
|
531
|
+
// paragraph's text/link runs) must concatenate with no separator: adding `\n\n`
|
|
532
|
+
// around an inline <a> put a blank line inside the <p>, which reparsed as a stray
|
|
533
|
+
// space before the following punctuation (`[video](url) .`).
|
|
534
|
+
if (node.type !== 'text' && !result.endsWith('\n\n')) {
|
|
532
535
|
if (result.endsWith('\n'))
|
|
533
536
|
result += '\n';
|
|
534
537
|
else
|
|
@@ -1145,6 +1148,15 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
1145
1148
|
return '';
|
|
1146
1149
|
const w = meta?.width ? ` width="${this.escape(meta.width)}"` : '';
|
|
1147
1150
|
const h = meta?.height ? ` height="${this.escape(meta.height)}"` : '';
|
|
1151
|
+
if (this.config.htmlConfig.gatedEmbeds) {
|
|
1152
|
+
// Inert, never-auto-loading placeholder: the editor renders click-to-load from
|
|
1153
|
+
// it, and HtmlParser reads it back to the same embed node. src already sanitized.
|
|
1154
|
+
const a = meta?.align ? ` data-embed-align="${this.escape(meta.align)}"` : '';
|
|
1155
|
+
const l = meta?.label ? ` data-embed-label="${this.escape(meta.label)}"` : '';
|
|
1156
|
+
const dw = meta?.width ? ` data-embed-width="${this.escape(meta.width)}"` : '';
|
|
1157
|
+
const dh = meta?.height ? ` data-embed-height="${this.escape(meta.height)}"` : '';
|
|
1158
|
+
return `${extraAnchors}<div data-embed-gated data-embed-src="${src}"${dw}${dh}${a}${l}${idAttr}${mappedAttrs}${styleAttr}></div>`;
|
|
1159
|
+
}
|
|
1148
1160
|
return `${extraAnchors}<iframe src="${src}"${w}${h}${idAttr}${mappedAttrs}${styleAttr}></iframe>`;
|
|
1149
1161
|
}
|
|
1150
1162
|
// Match the attribute-driven Youtube wrapper shape so a loaded embed re-hydrates
|
|
@@ -1157,7 +1169,11 @@ class HtmlGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
1157
1169
|
const iframe = id
|
|
1158
1170
|
? `<iframe src="https://www.youtube.com/embed/${this.escape(id)}" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>`
|
|
1159
1171
|
: '';
|
|
1160
|
-
|
|
1172
|
+
// Carry the human label so it survives AST -> editor-HTML -> AST, at parity with the
|
|
1173
|
+
// generic gated path's data-embed-label. Only emitted when a label exists (from a
|
|
1174
|
+
// `::youtube[Label]` directive), so unlabeled youtube embeds are byte-identical.
|
|
1175
|
+
const ytLabel = meta?.label ? ` data-embed-label="${this.escape(meta.label)}"` : '';
|
|
1176
|
+
return `${extraAnchors}<div data-youtube-video="${this.escape(id)}" data-width="${this.escape(width)}" data-align="${this.escape(align)}"${ytLabel} class="youtube-embed"${idAttr}${mappedAttrs} style="width: ${(0, sanitize_js_1.sanitizeCssValue)(width)}; margin-left: ${ml}; margin-right: ${mr};">${iframe}</div>`;
|
|
1161
1177
|
}
|
|
1162
1178
|
case 'admonition': {
|
|
1163
1179
|
// Match the attribute-driven admonition wrapper so a loaded admonition
|
|
@@ -47,6 +47,7 @@ export declare class MarkdownGenerator extends BaseGenerator<'md'> {
|
|
|
47
47
|
private collectedAbbreviations;
|
|
48
48
|
private resolvedDialect;
|
|
49
49
|
private resolvedFallbackToHtml;
|
|
50
|
+
private resolvedEmbeds;
|
|
50
51
|
constructor(ast: OfficeParserAST, config?: GeneratorConfig<'md'>);
|
|
51
52
|
/**
|
|
52
53
|
* Renders anchor tags if HTML fallback is allowed.
|
|
@@ -160,10 +160,19 @@ class MarkdownGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
160
160
|
collectedAbbreviations = new Map();
|
|
161
161
|
resolvedDialect;
|
|
162
162
|
resolvedFallbackToHtml;
|
|
163
|
+
resolvedEmbeds;
|
|
163
164
|
constructor(ast, config) {
|
|
164
165
|
super('md', ast, config);
|
|
165
166
|
this.resolvedDialect = resolveDialect(this.config.mdConfig.dialect);
|
|
166
167
|
this.resolvedFallbackToHtml = resolveFallbackToHtml(this.config.mdConfig.fallbackToHtml);
|
|
168
|
+
// `dialect.embeds` is the authority for embed form. It lives in a different config object
|
|
169
|
+
// than the deprecated `fallbackToHtml.embeds` boolean, and an explicit boolean `false` must
|
|
170
|
+
// still win over the preset default, so it is resolved here rather than in `resolveDialect`:
|
|
171
|
+
// an embeds value set on the dialect OBJECT wins; otherwise the boolean maps (`true`/unset ->
|
|
172
|
+
// `'html'`, `false` -> `'link'`); otherwise the default `'html'`.
|
|
173
|
+
const dialectCfg = this.config.mdConfig.dialect;
|
|
174
|
+
const explicitEmbeds = (dialectCfg && typeof dialectCfg === 'object') ? dialectCfg.embeds : undefined;
|
|
175
|
+
this.resolvedEmbeds = explicitEmbeds ?? (this.resolvedFallbackToHtml.embeds ? 'html' : 'link');
|
|
167
176
|
}
|
|
168
177
|
/**
|
|
169
178
|
* Renders anchor tags if HTML fallback is allowed.
|
|
@@ -603,32 +612,67 @@ class MarkdownGenerator extends BaseGenerator_js_1.BaseGenerator {
|
|
|
603
612
|
return `> **Note:** ${childrenOutput.trim()}\n\n`;
|
|
604
613
|
}
|
|
605
614
|
case 'embed': {
|
|
606
|
-
// Markdown has no native embed syntax.
|
|
607
|
-
//
|
|
608
|
-
//
|
|
615
|
+
// Markdown has no native embed syntax. `this.resolvedEmbeds` (from
|
|
616
|
+
// `dialect.embeds`, honoring the deprecated `fallbackToHtml.embeds` boolean)
|
|
617
|
+
// selects the form: 'html' (the single-line block this library has always
|
|
618
|
+
// emitted and re-recognises), 'directive' (a remark-directive leaf), 'link'
|
|
619
|
+
// (a plain link), 'thumbnail' (YouTube-only clickable preview).
|
|
609
620
|
const meta = node.metadata;
|
|
621
|
+
const mode = this.resolvedEmbeds;
|
|
622
|
+
// A directive label sits inside `::name[...]`; strip the `[]`/newline chars that
|
|
623
|
+
// would break out of it. An attribute value sits inside `{...}`; percent-encode
|
|
624
|
+
// the space/brace chars that would break out (widths/aligns/ids never contain
|
|
625
|
+
// them, but a src can).
|
|
626
|
+
const dirLabel = (meta?.label || '').replace(/[[\]\r\n]+/g, ' ').trim();
|
|
627
|
+
const dirUrl = (u) => (0, sanitize_js_1.sanitizeMarkdownUrl)(u).replace(/[{}\s]/g, c => '%' + c.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0'));
|
|
628
|
+
const attrList = (pairs) => {
|
|
629
|
+
const kv = pairs.filter(([, v]) => v !== undefined && v !== '').map(([k, v]) => `${k}=${v}`);
|
|
630
|
+
return kv.length ? `{${kv.join(' ')}}` : '';
|
|
631
|
+
};
|
|
610
632
|
if (meta?.embedType === 'iframe') {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
633
|
+
const rawUrl = meta?.url || '';
|
|
634
|
+
if (mode === 'directive') {
|
|
635
|
+
const src = dirUrl(rawUrl);
|
|
636
|
+
if (!src)
|
|
637
|
+
return '';
|
|
638
|
+
const lbl = dirLabel ? `[${dirLabel}]` : '';
|
|
639
|
+
return `::embed${lbl}${attrList([['src', src], ['width', meta?.width], ['height', meta?.height], ['align', meta?.align]])}\n\n`;
|
|
640
|
+
}
|
|
641
|
+
if (mode === 'html') {
|
|
642
|
+
// sanitizeUrl scheme-checks and HTML-escapes the src (hostile schemes drop
|
|
643
|
+
// the node). The single-line <iframe> is what MarkdownParser recognises on
|
|
644
|
+
// reimport, gated there on preserveIframes.
|
|
645
|
+
const safe = (0, sanitize_js_1.sanitizeUrl)(rawUrl);
|
|
646
|
+
if (!safe)
|
|
647
|
+
return '';
|
|
618
648
|
const w = meta?.width ? ` width="${(0, sanitize_js_1.escapeHtml)(meta.width)}"` : '';
|
|
619
649
|
const h = meta?.height ? ` height="${(0, sanitize_js_1.escapeHtml)(meta.height)}"` : '';
|
|
620
650
|
return `\n<iframe src="${safe}"${w}${h}></iframe>\n\n`;
|
|
621
651
|
}
|
|
622
|
-
|
|
652
|
+
// 'link' and 'thumbnail' (thumbnail is YouTube-only, so a generic iframe
|
|
653
|
+
// degrades to a link) both emit a plain link.
|
|
654
|
+
const safe = (0, sanitize_js_1.sanitizeMarkdownUrl)(rawUrl);
|
|
655
|
+
return safe ? `[${meta?.label || 'Embed'}](${safe})\n\n` : '';
|
|
623
656
|
}
|
|
624
657
|
const id = meta?.videoId || '';
|
|
625
|
-
if (
|
|
658
|
+
if (mode === 'directive') {
|
|
659
|
+
const lbl = dirLabel ? `[${dirLabel}]` : '';
|
|
660
|
+
return `::youtube${lbl}${attrList([['id', id], ['width', meta?.width], ['align', meta?.align]])}\n\n`;
|
|
661
|
+
}
|
|
662
|
+
if (mode === 'html') {
|
|
626
663
|
const width = meta?.width ? ` data-width="${(0, sanitize_js_1.escapeHtml)(meta.width)}"` : '';
|
|
627
664
|
const align = meta?.align ? ` data-align="${(0, sanitize_js_1.escapeHtml)(meta.align)}"` : '';
|
|
628
|
-
|
|
665
|
+
const lbl = meta?.label ? ` data-embed-label="${(0, sanitize_js_1.escapeHtml)(meta.label)}"` : '';
|
|
666
|
+
return `\n<div data-youtube-video="${(0, sanitize_js_1.escapeHtml)(id)}"${width}${align}${lbl}></div>\n\n`;
|
|
667
|
+
}
|
|
668
|
+
if (mode === 'thumbnail' && id) {
|
|
669
|
+
const watch = (0, sanitize_js_1.sanitizeMarkdownUrl)(`https://www.youtube.com/watch?v=${id}`);
|
|
670
|
+
const thumb = (0, sanitize_js_1.sanitizeMarkdownUrl)(`https://img.youtube.com/vi/${id}/hqdefault.jpg`);
|
|
671
|
+
return `[](${watch})\n\n`;
|
|
629
672
|
}
|
|
673
|
+
// 'link' (and 'thumbnail' with no id): a plain link.
|
|
630
674
|
const url = meta?.url || (id ? `https://youtu.be/${id}` : '');
|
|
631
|
-
return url ? `[YouTube](${(0, sanitize_js_1.sanitizeMarkdownUrl)(url)})\n\n` : '';
|
|
675
|
+
return url ? `[${meta?.label || 'YouTube'}](${(0, sanitize_js_1.sanitizeMarkdownUrl)(url)})\n\n` : '';
|
|
632
676
|
}
|
|
633
677
|
case 'admonition': {
|
|
634
678
|
const meta = node.metadata;
|
|
@@ -415,6 +415,18 @@ export interface HtmlParserConfig {
|
|
|
415
415
|
* Defaults to false.
|
|
416
416
|
*/
|
|
417
417
|
preserveIframes?: boolean | string[];
|
|
418
|
+
/**
|
|
419
|
+
* Import ambiguous "folk" embed forms in Markdown as embeds: a standalone Obsidian-style image
|
|
420
|
+
* whose URL is a YouTube link (``), and the clickable
|
|
421
|
+
* thumbnail-link (`[](watch-url)`). Both become a
|
|
422
|
+
* `embedType: 'youtube'` embed (rendered from the validated id, so it is safe). Off by default:
|
|
423
|
+
* auto-upgrading an image/link to an embed is a heuristic that could mangle a genuinely-intended
|
|
424
|
+
* image link, so a consumer opts in. The unambiguous forms (`<div data-youtube-video>`, a bare
|
|
425
|
+
* YouTube `<iframe>`, the `::youtube` directive) are always recognized, independent of this flag.
|
|
426
|
+
*
|
|
427
|
+
* Defaults to false.
|
|
428
|
+
*/
|
|
429
|
+
embedFolkForms?: boolean;
|
|
418
430
|
}
|
|
419
431
|
/**
|
|
420
432
|
* Maps an input format string to its corresponding format-specific parser configuration, mirroring
|
|
@@ -883,6 +895,15 @@ export interface HtmlGeneratorConfig {
|
|
|
883
895
|
* `HtmlParser` reads every shape this emits, so output stays self-round-trippable.
|
|
884
896
|
*/
|
|
885
897
|
sourceAttributes?: boolean;
|
|
898
|
+
/**
|
|
899
|
+
* Emit a generic (non-YouTube) iframe embed as a gated placeholder,
|
|
900
|
+
* `<div data-embed-gated data-embed-src="…" …>`, instead of a live `<iframe>`. The gated shape
|
|
901
|
+
* never auto-loads its src: an editor renders a click-to-load placeholder from it, and
|
|
902
|
+
* `HtmlParser` reads it back to the same `embed` node. The src is scheme-checked (`sanitizeUrl`)
|
|
903
|
+
* on emit. Off by default; the default output (a live `<iframe>`) is unchanged. YouTube embeds
|
|
904
|
+
* are unaffected (they already render from a validated id).
|
|
905
|
+
*/
|
|
906
|
+
gatedEmbeds?: boolean;
|
|
886
907
|
}
|
|
887
908
|
/**
|
|
888
909
|
* Configuration options for PDF generation.
|
|
@@ -1056,6 +1077,20 @@ export type CitationSyntax = "at" | "none";
|
|
|
1056
1077
|
export type WikilinkSyntax = "double-bracket" | "none";
|
|
1057
1078
|
/** `{width=50%}` attribute lists (`'brace'`), or `'none'`. */
|
|
1058
1079
|
export type AttributeListSyntax = "brace" | "none";
|
|
1080
|
+
/**
|
|
1081
|
+
* How an `embed` node is written to Markdown:
|
|
1082
|
+
* - `'html'` (default): the single-line `<div data-youtube-video="ID">` / `<iframe src=...>` block
|
|
1083
|
+
* this library has always emitted. Round-trips through officeParser, but renders as an invisible
|
|
1084
|
+
* empty box on GitHub.
|
|
1085
|
+
* - `'directive'`: a remark-directive leaf, `::youtube[Label]{id=... width=... align=...}` /
|
|
1086
|
+
* `::embed[Label]{src=... width=... height=... align=...}`. Round-trips within an editor that
|
|
1087
|
+
* understands it; renders verbatim (not just the label) on GitHub, so it is an editor format, not
|
|
1088
|
+
* a GitHub-interop one.
|
|
1089
|
+
* - `'link'`: a plain `[YouTube](url)` / `[Embed](url)`.
|
|
1090
|
+
* - `'thumbnail'`: a YouTube-only clickable thumbnail `[](watch)`,
|
|
1091
|
+
* the best GitHub degrade; a non-YouTube embed falls back to `'link'`.
|
|
1092
|
+
*/
|
|
1093
|
+
export type EmbedSyntax = "html" | "directive" | "link" | "thumbnail";
|
|
1059
1094
|
/**
|
|
1060
1095
|
* @deprecated Legacy flavor names for `MarkdownDialectConfig.admonitions`. Use the syntax names
|
|
1061
1096
|
* instead: `'github'` -> `'blockquote'`, `'gitlab'` -> `'fence'`, `'pandoc'` -> `'fence-attribute'`.
|
|
@@ -1142,6 +1177,13 @@ export interface MarkdownDialectConfig {
|
|
|
1142
1177
|
/** Table syntax: native GFM pipe tables, or forced HTML `<table>` (required for strict
|
|
1143
1178
|
* CommonMark, which has no table syntax of its own). */
|
|
1144
1179
|
tables?: "native" | "html";
|
|
1180
|
+
/**
|
|
1181
|
+
* How an `embed` node is written to Markdown (`'html'` | `'directive'` | `'link'` |
|
|
1182
|
+
* `'thumbnail'`; see `EmbedSyntax`). This is the authority for embed form. When omitted, the
|
|
1183
|
+
* deprecated `fallbackToHtml.embeds` boolean is honored (`true`/unset maps to `'html'`, `false`
|
|
1184
|
+
* to `'link'`), then the default `'html'`.
|
|
1185
|
+
*/
|
|
1186
|
+
embeds?: EmbedSyntax;
|
|
1145
1187
|
}
|
|
1146
1188
|
/**
|
|
1147
1189
|
* Granular control over when the Markdown generator falls back to raw HTML tags for features
|
|
@@ -1158,7 +1200,12 @@ export interface FallbackToHtmlConfig {
|
|
|
1158
1200
|
anchors?: boolean;
|
|
1159
1201
|
/** Nested-table and merged-cell (colspan/rowspan) HTML `<table>` fallback. */
|
|
1160
1202
|
tables?: boolean;
|
|
1161
|
-
/**
|
|
1203
|
+
/**
|
|
1204
|
+
* YouTube embed `<div data-youtube-video>` vs. a plain link.
|
|
1205
|
+
* @deprecated Use `mdConfig.dialect.embeds` (`EmbedSyntax`) instead, which also selects the
|
|
1206
|
+
* `'directive'` and `'thumbnail'` forms. When `dialect.embeds` is unset this boolean is still
|
|
1207
|
+
* honored (`true` maps to `'html'`, `false` to `'link'`); it will be removed in the next major.
|
|
1208
|
+
*/
|
|
1162
1209
|
embeds?: boolean;
|
|
1163
1210
|
/** Multi-line table cell content joined with `<br>` instead of a space. */
|
|
1164
1211
|
cellLineBreaks?: boolean;
|
|
@@ -1778,10 +1825,13 @@ export interface EmbedMetadata {
|
|
|
1778
1825
|
url?: string;
|
|
1779
1826
|
/** Display width, as a CSS length or percentage. */
|
|
1780
1827
|
width?: string;
|
|
1781
|
-
/** Display height, as a CSS length or percentage
|
|
1828
|
+
/** Display height, as a CSS length or percentage. */
|
|
1782
1829
|
height?: string;
|
|
1783
1830
|
/** Layout alignment of the embed. */
|
|
1784
1831
|
align?: "left" | "center" | "right";
|
|
1832
|
+
/** Human-readable label for the embed (e.g. the `[Label]` of a `::youtube[Label]{...}` leaf
|
|
1833
|
+
* directive, or a gated embed's caption). Purely descriptive; never a trust or render input. */
|
|
1834
|
+
label?: string;
|
|
1785
1835
|
}
|
|
1786
1836
|
/**
|
|
1787
1837
|
* Metadata for an admonition/alert node (e.g. GitHub's `> [!NOTE]` or GLFM's `:::note`).
|