@scribdown/markdown-renderer 0.1.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.js ADDED
@@ -0,0 +1,3073 @@
1
+ // src/index.ts
2
+ import {
3
+ CONTENT_WIDTH_STORAGE_KEY,
4
+ PROJECT_HOMEPAGE_URL,
5
+ SCRIBDOWN_MARKDOWN_CLASS_NAME,
6
+ SCRIBDOWN_TOOLBAR_BTN_CLASS_NAME,
7
+ SCRIBDOWN_TOOLBAR_CLASS_NAME,
8
+ SCRIBDOWN_TOOLBAR_MENU_CLASS_NAME,
9
+ SCRIBDOWN_TOOLBAR_MENU_GROUP_CLASS_NAME,
10
+ SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME,
11
+ SCRIBDOWN_TOOLBAR_MENU_SUB_ITEM_CLASS_NAME,
12
+ SCRIBDOWN_TOOLBAR_TOC_PANEL_CLASS_NAME,
13
+ SCRIBDOWN_TOOLBAR_CURRENT_CLASS_NAME,
14
+ SCRIBDOWN_TOC_HOST_CLASS_NAME,
15
+ SCRIBDOWN_CONTENT_AREA_CLASS_NAME,
16
+ SCRIBDOWN_CONTENT_SCROLL_CLASS_NAME,
17
+ SCRIBDOWN_THIN_SCROLLBAR_CLASS_NAME,
18
+ SOURCE_LINE_DATA_ATTRIBUTE
19
+ } from "@scribdown/shared";
20
+ import DOMPurify from "dompurify";
21
+ import rehypeRaw from "rehype-raw";
22
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
23
+ import rehypeStringify from "rehype-stringify";
24
+ import remarkGfm from "remark-gfm";
25
+ import remarkParse from "remark-parse";
26
+ import remarkRehype from "remark-rehype";
27
+ import githubLightTheme from "@shikijs/themes/github-light";
28
+ import { createHighlighterCore } from "shiki/core";
29
+ import { createOnigurumaEngine } from "shiki/engine/oniguruma";
30
+ import { unified } from "unified";
31
+
32
+ // src/code-highlighter-langs.ts
33
+ var CODE_HIGHLIGHTER_EAGER_LOADERS = [
34
+ () => import("@shikijs/langs/bash"),
35
+ () => import("@shikijs/langs/css"),
36
+ () => import("@shikijs/langs/diff"),
37
+ () => import("@shikijs/langs/html"),
38
+ () => import("@shikijs/langs/javascript"),
39
+ () => import("@shikijs/langs/json"),
40
+ () => import("@shikijs/langs/markdown"),
41
+ () => import("@shikijs/langs/typescript"),
42
+ () => import("@shikijs/langs/yaml")
43
+ ];
44
+ var CODE_HIGHLIGHTER_LAZY_LANGS = {
45
+ // 前端框架与扩展
46
+ jsx: () => import("@shikijs/langs/jsx"),
47
+ tsx: () => import("@shikijs/langs/tsx"),
48
+ vue: () => import("@shikijs/langs/vue"),
49
+ svelte: () => import("@shikijs/langs/svelte"),
50
+ astro: () => import("@shikijs/langs/astro"),
51
+ mdx: () => import("@shikijs/langs/mdx"),
52
+ // 样式
53
+ scss: () => import("@shikijs/langs/scss"),
54
+ sass: () => import("@shikijs/langs/sass"),
55
+ less: () => import("@shikijs/langs/less"),
56
+ postcss: () => import("@shikijs/langs/postcss"),
57
+ stylus: () => import("@shikijs/langs/stylus"),
58
+ // 查询 / 数据 / 标记
59
+ graphql: () => import("@shikijs/langs/graphql"),
60
+ gql: () => import("@shikijs/langs/graphql"),
61
+ sql: () => import("@shikijs/langs/sql"),
62
+ xml: () => import("@shikijs/langs/xml"),
63
+ regexp: () => import("@shikijs/langs/regexp"),
64
+ regex: () => import("@shikijs/langs/regexp"),
65
+ // 系统语言
66
+ c: () => import("@shikijs/langs/c"),
67
+ cpp: () => import("@shikijs/langs/cpp"),
68
+ "c++": () => import("@shikijs/langs/cpp"),
69
+ csharp: () => import("@shikijs/langs/csharp"),
70
+ cs: () => import("@shikijs/langs/csharp"),
71
+ java: () => import("@shikijs/langs/java"),
72
+ go: () => import("@shikijs/langs/go"),
73
+ rust: () => import("@shikijs/langs/rust"),
74
+ rs: () => import("@shikijs/langs/rust"),
75
+ swift: () => import("@shikijs/langs/swift"),
76
+ kotlin: () => import("@shikijs/langs/kotlin"),
77
+ kt: () => import("@shikijs/langs/kotlin"),
78
+ scala: () => import("@shikijs/langs/scala"),
79
+ "objective-c": () => import("@shikijs/langs/objective-c"),
80
+ objc: () => import("@shikijs/langs/objective-c"),
81
+ "objective-cpp": () => import("@shikijs/langs/objective-cpp"),
82
+ // 脚本语言
83
+ python: () => import("@shikijs/langs/python"),
84
+ py: () => import("@shikijs/langs/python"),
85
+ ruby: () => import("@shikijs/langs/ruby"),
86
+ rb: () => import("@shikijs/langs/ruby"),
87
+ perl: () => import("@shikijs/langs/perl"),
88
+ lua: () => import("@shikijs/langs/lua"),
89
+ powershell: () => import("@shikijs/langs/powershell"),
90
+ ps: () => import("@shikijs/langs/powershell"),
91
+ ps1: () => import("@shikijs/langs/powershell"),
92
+ fish: () => import("@shikijs/langs/fish"),
93
+ php: () => import("@shikijs/langs/php"),
94
+ r: () => import("@shikijs/langs/r"),
95
+ // 配置 / 数据
96
+ toml: () => import("@shikijs/langs/toml"),
97
+ ini: () => import("@shikijs/langs/ini"),
98
+ dockerfile: () => import("@shikijs/langs/dockerfile"),
99
+ docker: () => import("@shikijs/langs/dockerfile"),
100
+ makefile: () => import("@shikijs/langs/make"),
101
+ make: () => import("@shikijs/langs/make"),
102
+ cmake: () => import("@shikijs/langs/cmake"),
103
+ nginx: () => import("@shikijs/langs/nginx"),
104
+ properties: () => import("@shikijs/langs/properties"),
105
+ dotenv: () => import("@shikijs/langs/dotenv"),
106
+ hcl: () => import("@shikijs/langs/hcl"),
107
+ terraform: () => import("@shikijs/langs/terraform"),
108
+ tf: () => import("@shikijs/langs/terraform"),
109
+ nix: () => import("@shikijs/langs/nix"),
110
+ json5: () => import("@shikijs/langs/json5"),
111
+ jsonc: () => import("@shikijs/langs/jsonc"),
112
+ csv: () => import("@shikijs/langs/csv"),
113
+ tsv: () => import("@shikijs/langs/tsv"),
114
+ // 函数式 / 其他
115
+ dart: () => import("@shikijs/langs/dart"),
116
+ elixir: () => import("@shikijs/langs/elixir"),
117
+ erlang: () => import("@shikijs/langs/erlang"),
118
+ haskell: () => import("@shikijs/langs/haskell"),
119
+ hs: () => import("@shikijs/langs/haskell"),
120
+ clojure: () => import("@shikijs/langs/clojure"),
121
+ clj: () => import("@shikijs/langs/clojure"),
122
+ ocaml: () => import("@shikijs/langs/ocaml"),
123
+ fsharp: () => import("@shikijs/langs/fsharp"),
124
+ vb: () => import("@shikijs/langs/vb"),
125
+ asm: () => import("@shikijs/langs/asm"),
126
+ llvm: () => import("@shikijs/langs/llvm"),
127
+ solidity: () => import("@shikijs/langs/solidity"),
128
+ prisma: () => import("@shikijs/langs/prisma"),
129
+ proto: () => import("@shikijs/langs/proto"),
130
+ // 文档标记
131
+ latex: () => import("@shikijs/langs/latex"),
132
+ tex: () => import("@shikijs/langs/latex"),
133
+ asciidoc: () => import("@shikijs/langs/asciidoc"),
134
+ adoc: () => import("@shikijs/langs/asciidoc"),
135
+ // 着色器 / 二进制
136
+ glsl: () => import("@shikijs/langs/glsl"),
137
+ wasm: () => import("@shikijs/langs/wasm"),
138
+ wat: () => import("@shikijs/langs/wasm")
139
+ };
140
+
141
+ // src/index.ts
142
+ var SOURCE_LINE_HAST_PROPERTY = SOURCE_LINE_DATA_ATTRIBUTE.replace(
143
+ /-([a-z])/gu,
144
+ (_match, letter) => letter.toUpperCase()
145
+ );
146
+ var TOC_MARKER_PATTERN = /^\s*\[toc]\s*$/i;
147
+ var TOC_CLASS_NAME = "scribdown-toc";
148
+ var FRAME_CLASS_NAME = "scribdown-frame";
149
+ var DETAILS_CLASS_NAME = "scribdown-details";
150
+ var TOC_SUMMARY_CLASS_NAME = "scribdown-toc-summary";
151
+ var TOC_NAV_CLASS_NAME = "scribdown-toc-nav";
152
+ var TOC_LIST_CLASS_NAME = "scribdown-toc-list";
153
+ var TOC_LIST_NESTED_CLASS_NAME = "scribdown-toc-list--nested";
154
+ var TOC_ITEM_CLASS_PREFIX = "scribdown-toc-item";
155
+ var TOC_ITEM_BRANCH_CLASS_NAME = "scribdown-toc-item--branch";
156
+ var TOC_ITEM_COLLAPSED_CLASS_NAME = "scribdown-toc-item--collapsed";
157
+ var TOC_LINK_CLASS_NAME = "scribdown-toc-link";
158
+ var TOC_LINK_ACTIVE_CLASS_NAME = "scribdown-toc-link--active";
159
+ var TOC_TOGGLE_CLASS_NAME = "scribdown-toc-toggle";
160
+ var TOC_TOGGLE_ARIA_LABEL = "\u5C55\u5F00\u6216\u6298\u53E0\u5B50\u76EE\u5F55";
161
+ var TOC_TOGGLE_HYDRATED_DATA_KEY = "scribdownTocToggleHydrated";
162
+ var TOC_LINK_HYDRATED_DATA_KEY = "scribdownTocLinkHydrated";
163
+ var TOC_ARIA_LABEL = "\u76EE\u5F55";
164
+ var TOC_SUMMARY_TEXT = "\u76EE\u5F55";
165
+ var EMPTY_HEADING_SLUG_PREFIX = "section";
166
+ var HEADING_MARK_CLASS_NAME = "scribdown-heading-mark";
167
+ var IMAGE_FIGURE_CLASS_NAME = "scribdown-image-figure";
168
+ var IMAGE_FRAME_CLASS_NAME = "scribdown-image-frame";
169
+ var IMAGE_ELEMENT_CLASS_NAME = "scribdown-image";
170
+ var IMAGE_CAPTION_CLASS_NAME = "scribdown-image-caption";
171
+ var IMAGE_FRAME_FAILED_CLASS_NAME = "scribdown-image-frame--failed";
172
+ var IMAGE_FRAME_LOADED_CLASS_NAME = "scribdown-image-frame--loaded";
173
+ var IMAGE_FALLBACK_CLASS_NAME = "scribdown-image-fallback";
174
+ var IMAGE_FALLBACK_ICON_CLASS_NAME = "scribdown-image-fallback-icon";
175
+ var IMAGE_FALLBACK_TEXT_CLASS_NAME = "scribdown-image-fallback-text";
176
+ var IMAGE_FALLBACK_SOURCE_CLASS_NAME = "scribdown-image-fallback-source";
177
+ var IMAGE_HYDRATED_DATA_KEY = "scribdownImageHydrated";
178
+ var VIDEO_FIGURE_CLASS_NAME = "scribdown-video-figure";
179
+ var VIDEO_FRAME_CLASS_NAME = "scribdown-video-frame";
180
+ var VIDEO_ELEMENT_CLASS_NAME = "scribdown-video";
181
+ var VIDEO_FRAME_FAILED_CLASS_NAME = "scribdown-video-frame--failed";
182
+ var VIDEO_FRAME_LOADED_CLASS_NAME = "scribdown-video-frame--loaded";
183
+ var VIDEO_FALLBACK_CLASS_NAME = "scribdown-video-fallback";
184
+ var VIDEO_FALLBACK_ICON_CLASS_NAME = "scribdown-video-fallback-icon";
185
+ var VIDEO_FALLBACK_TEXT_CLASS_NAME = "scribdown-video-fallback-text";
186
+ var VIDEO_FALLBACK_SOURCE_CLASS_NAME = "scribdown-video-fallback-source";
187
+ var VIDEO_HYDRATED_DATA_KEY = "scribdownVideoHydrated";
188
+ var VIDEO_FALLBACK_DEFAULT_TEXT = "\u89C6\u9891\u52A0\u8F7D\u5931\u8D25";
189
+ var MERMAID_LANGUAGE_ID = "mermaid";
190
+ var MERMAID_FIGURE_CLASS_NAME = "scribdown-mermaid";
191
+ var MERMAID_CHROME_CLASS_NAME = "scribdown-mermaid__chrome";
192
+ var MERMAID_LABEL_CLASS_NAME = "scribdown-mermaid__label";
193
+ var MERMAID_BODY_CLASS_NAME = "scribdown-mermaid__body";
194
+ var MERMAID_CANVAS_CLASS_NAME = "scribdown-mermaid__canvas";
195
+ var MERMAID_FALLBACK_CLASS_NAME = "scribdown-mermaid__fallback";
196
+ var MERMAID_FALLBACK_ICON_CLASS_NAME = "scribdown-mermaid__fallback-icon";
197
+ var MERMAID_FALLBACK_TEXT_CLASS_NAME = "scribdown-mermaid__fallback-text";
198
+ var MERMAID_FALLBACK_SOURCE_CLASS_NAME = "scribdown-mermaid__fallback-source";
199
+ var MERMAID_FIGURE_LOADING_CLASS_NAME = "scribdown-mermaid--loading";
200
+ var MERMAID_FIGURE_FAILED_CLASS_NAME = "scribdown-mermaid--failed";
201
+ var MERMAID_FIGURE_LOADED_CLASS_NAME = "scribdown-mermaid--loaded";
202
+ var MERMAID_HYDRATED_DATA_KEY = "scribdownMermaidHydrated";
203
+ var MERMAID_RENDER_STARTED_DATA_KEY = "scribdownMermaidRenderStarted";
204
+ var MERMAID_SOURCE_DATA_KEY = "scribdownMermaidSourceText";
205
+ var MERMAID_LABEL_TEXT = "Mermaid";
206
+ var MERMAID_FALLBACK_DEFAULT_TEXT = "\u56FE\u8868\u6E32\u67D3\u5931\u8D25";
207
+ var MERMAID_RENDER_ID_PREFIX = "scribdown-mermaid-";
208
+ var mermaidRenderIdCounter = 0;
209
+ var MERMAID_FULLSCREEN_BUTTON_CLASS_NAME = "scribdown-mermaid__fullscreen";
210
+ var MERMAID_FULLSCREEN_BUTTON_ARIA_LABEL = "\u5168\u5C4F\u67E5\u770B\u56FE\u8868";
211
+ var MERMAID_VIEWER_DIALOG_CLASS_NAME = "scribdown-mermaid-viewer";
212
+ var MERMAID_VIEWER_ZOOMED_CLASS_NAME = "scribdown-mermaid-viewer--zoomed";
213
+ var MERMAID_VIEWER_DRAGGING_CLASS_NAME = "scribdown-mermaid-viewer--dragging";
214
+ var MERMAID_VIEWER_CHROME_CLASS_NAME = "scribdown-mermaid-viewer__chrome";
215
+ var MERMAID_VIEWER_CAPTION_CLASS_NAME = "scribdown-mermaid-viewer__caption";
216
+ var MERMAID_VIEWER_CONTROLS_CLASS_NAME = "scribdown-mermaid-viewer__controls";
217
+ var MERMAID_VIEWER_BUTTON_CLASS_NAME = "scribdown-mermaid-viewer__button";
218
+ var MERMAID_VIEWER_CLOSE_BUTTON_CLASS_NAME = "scribdown-mermaid-viewer__button--close";
219
+ var MERMAID_VIEWER_VIEWPORT_CLASS_NAME = "scribdown-mermaid-viewer__viewport";
220
+ var MERMAID_VIEWER_CANVAS_CLASS_NAME = "scribdown-mermaid-viewer__canvas";
221
+ var MERMAID_VIEWER_ZOOM_VALUE_CLASS_NAME = "scribdown-mermaid-viewer__zoom-value";
222
+ var MERMAID_FULLSCREEN_AVAILABLE_DATA_KEY = "scribdownMermaidFullscreenReady";
223
+ var MERMAID_VIEWER_SOURCE_DATA_KEY = "scribdownMermaidSource";
224
+ var mermaidViewerStateByDocument = /* @__PURE__ */ new WeakMap();
225
+ var mermaidViewerStateByDialogElement = /* @__PURE__ */ new WeakMap();
226
+ var IMAGE_VIEWER_DIALOG_CLASS_NAME = "scribdown-image-viewer";
227
+ var IMAGE_VIEWER_ZOOMED_CLASS_NAME = "scribdown-image-viewer--zoomed";
228
+ var IMAGE_VIEWER_DRAGGING_CLASS_NAME = "scribdown-image-viewer--dragging";
229
+ var IMAGE_VIEWER_CAPTION_GROUP_CLASS_NAME = "scribdown-image-viewer__caption-group";
230
+ var IMAGE_VIEWER_HINT_CLASS_NAME = "scribdown-image-viewer__hint";
231
+ var IMAGE_VIEWER_HINT_TEXT = "\u5FEB\u6377\u952E\uFF1A+/= \u653E\u5927 \xB7 - \u7F29\u5C0F \xB7 0 \u91CD\u7F6E \xB7 Esc \u5173\u95ED \xB7 \u9F20\u6807\u62D6\u62FD\u53EF\u5E73\u79FB \xB7 Ctrl/\u2318 + \u6EDA\u8F6E\u7F29\u653E";
232
+ var IMAGE_VIEWER_CHROME_CLASS_NAME = "scribdown-image-viewer__chrome";
233
+ var IMAGE_VIEWER_CONTROLS_CLASS_NAME = "scribdown-image-viewer__controls";
234
+ var IMAGE_VIEWER_BUTTON_CLASS_NAME = "scribdown-image-viewer__button";
235
+ var IMAGE_VIEWER_CLOSE_BUTTON_CLASS_NAME = "scribdown-image-viewer__button--close";
236
+ var IMAGE_VIEWER_VIEWPORT_CLASS_NAME = "scribdown-image-viewer__viewport";
237
+ var IMAGE_VIEWER_IMAGE_CLASS_NAME = "scribdown-image-viewer__image";
238
+ var IMAGE_VIEWER_CAPTION_CLASS_NAME = "scribdown-image-viewer__caption";
239
+ var IMAGE_VIEWER_ZOOM_VALUE_CLASS_NAME = "scribdown-image-viewer__zoom-value";
240
+ var IMAGE_VIEWER_DEFAULT_ZOOM = 1;
241
+ var IMAGE_VIEWER_MIN_ZOOM = 0.25;
242
+ var IMAGE_VIEWER_MAX_ZOOM = 4;
243
+ var IMAGE_VIEWER_ZOOM_STEP = 0.25;
244
+ var IMAGE_VIEWER_WHEEL_ZOOM_FACTOR = 0.01;
245
+ var IMAGE_VIEWER_WHEEL_ZOOM_MAX_DELTA = 0.1;
246
+ var IMAGE_VIEWER_FIT_RATIO = 0.92;
247
+ var IMAGE_VIEWER_FALLBACK_LABEL = "\u67E5\u770B\u5168\u56FE";
248
+ var IMAGE_VIEWER_CLOSE_TEXT = "x";
249
+ var IMAGE_VIEWER_ZOOM_IN_TEXT = "+";
250
+ var IMAGE_VIEWER_ZOOM_OUT_TEXT = "-";
251
+ var IMAGE_VIEWER_RESET_TEXT = "1:1";
252
+ var imageViewerStateByDocument = /* @__PURE__ */ new WeakMap();
253
+ var imageViewerStateByDialogElement = /* @__PURE__ */ new WeakMap();
254
+ var HIGHLIGHT_MARKER_PATTERN = /==([^=\n]+?)==/g;
255
+ var DEFINITION_LIST_PARAGRAPH_PATTERN = /^\s*([^\n:][^\n]*)\n\s*:\s+([^\n]+)\s*$/u;
256
+ var CODE_BLOCK_CLASS_NAME = "scribdown-code-block";
257
+ var CODE_BLOCK_CHROME_CLASS_NAME = "scribdown-code-block__chrome";
258
+ var CODE_BLOCK_BODY_CLASS_NAME = "scribdown-code-block__body";
259
+ var CODE_BLOCK_LANG_CLASS_NAME = "scribdown-code-block__lang";
260
+ var CODE_BLOCK_COPY_CLASS_NAME = "scribdown-code-block__copy";
261
+ var CODE_BLOCK_COPY_ICON_CLASS_NAME = "scribdown-code-block__copy-icon";
262
+ var CODE_BLOCK_COPY_ICON_COPY_CLASS_NAME = "scribdown-code-block__copy-icon--copy";
263
+ var CODE_BLOCK_COPY_ICON_CHECK_CLASS_NAME = "scribdown-code-block__copy-icon--check";
264
+ var CODE_BLOCK_LINE_CLASS_NAME = "scribdown-code-block__line";
265
+ var CODE_BLOCK_GUTTER_CLASS_NAME = "scribdown-code-block__gutter";
266
+ var CODE_BLOCK_GUTTER_LINE_CLASS_NAME = "scribdown-code-block__gutter-line";
267
+ var CODE_BLOCK_EMPTY_LINE_PLACEHOLDER = "\u200B";
268
+ var CODE_BLOCK_HYDRATED_DATA_KEY = "scribdownCodeBlockHydrated";
269
+ var CODE_LANGUAGE_DISPLAY_NAMES = {
270
+ bash: "Bash",
271
+ c: "C",
272
+ cpp: "C++",
273
+ csharp: "C#",
274
+ css: "CSS",
275
+ diff: "Diff",
276
+ go: "Go",
277
+ graphql: "GraphQL",
278
+ html: "HTML",
279
+ java: "Java",
280
+ javascript: "JavaScript",
281
+ js: "JavaScript",
282
+ json: "JSON",
283
+ jsx: "JSX",
284
+ kotlin: "Kotlin",
285
+ less: "Less",
286
+ markdown: "Markdown",
287
+ md: "Markdown",
288
+ php: "PHP",
289
+ python: "Python",
290
+ py: "Python",
291
+ ruby: "Ruby",
292
+ rust: "Rust",
293
+ rs: "Rust",
294
+ sass: "Sass",
295
+ scss: "SCSS",
296
+ sh: "Shell",
297
+ shell: "Shell",
298
+ sql: "SQL",
299
+ swift: "Swift",
300
+ toml: "TOML",
301
+ ts: "TypeScript",
302
+ tsx: "TSX",
303
+ typescript: "TypeScript",
304
+ vue: "Vue",
305
+ xml: "XML",
306
+ yaml: "YAML",
307
+ yml: "YAML",
308
+ zsh: "Zsh"
309
+ };
310
+ var CODE_LANGUAGE_DEFAULT_LABEL = "Text";
311
+ var CODE_BLOCK_COPY_RESTORE_DELAY_MS = 1600;
312
+ var CODE_BLOCK_COPY_ARIA_LABEL = "\u590D\u5236\u4EE3\u7801";
313
+ var CODE_BLOCK_COPY_ARIA_LABEL_COPIED = "\u5DF2\u590D\u5236";
314
+ var CODE_HIGHLIGHTER_THEME = "github-light";
315
+ var codeHighlighterPromise;
316
+ var codeHighlighterLazyLoadPromises = /* @__PURE__ */ new Map();
317
+ var CODE_BLOCK_HIGHLIGHT_PATTERN = /<pre([^>]*)><code class="language-([\w-]+)"([^>]*)>([\s\S]*?)<\/code><\/pre>/g;
318
+ var SHIKI_CODE_INNER_PATTERN = /<code[^>]*>([\s\S]*?)<\/code>/u;
319
+ async function renderMarkdown(markdownText) {
320
+ const processor = unified().use(remarkParse).use(remarkGfm).use(remarkHighlightMark).use(remarkDefinitionLists).use(remarkTableOfContents).use(remarkImageFigures).use(remarkSourceLine).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeRaw).use(rehypeFrameClass).use(rehypeVideoFigures).use(rehypeSanitize, createScribdownSanitizeSchema()).use(rehypeStringify);
321
+ const renderedHtml = String(await processor.process(markdownText));
322
+ const sanitizedHtml = sanitizeHtmlWithDomPurify(renderedHtml);
323
+ return highlightMarkdownCodeBlocks(sanitizedHtml);
324
+ }
325
+ async function getCodeHighlighter() {
326
+ if (!codeHighlighterPromise) {
327
+ codeHighlighterPromise = createHighlighterCore({
328
+ themes: [githubLightTheme],
329
+ // eager loaders 通过动态 import 拆为独立 chunk,初始化时并发拉取;
330
+ // 主 bundle 不再内联 grammar JSON,体积更小,多个 grammar 也可并行加载。
331
+ langs: CODE_HIGHLIGHTER_EAGER_LOADERS,
332
+ // oniguruma 引擎 + wasm;shiki/wasm 内部走 base64 内联,扩展环境无需额外资源声明。
333
+ engine: createOnigurumaEngine(import("shiki/wasm"))
334
+ });
335
+ }
336
+ return codeHighlighterPromise;
337
+ }
338
+ async function highlightMarkdownCodeBlocks(html) {
339
+ if (!CODE_BLOCK_HIGHLIGHT_PATTERN.test(html)) {
340
+ return html;
341
+ }
342
+ CODE_BLOCK_HIGHLIGHT_PATTERN.lastIndex = 0;
343
+ const highlighter = await getCodeHighlighter();
344
+ let resultHtml = "";
345
+ let lastIndex = 0;
346
+ for (const match of html.matchAll(CODE_BLOCK_HIGHLIGHT_PATTERN)) {
347
+ const [matched, preAttributes, rawLanguage, codeAttributes, encodedCode] = match;
348
+ const matchStart = match.index ?? 0;
349
+ resultHtml += html.slice(lastIndex, matchStart);
350
+ resultHtml += await highlightSingleCodeBlock(highlighter, {
351
+ rawLanguage,
352
+ encodedCode,
353
+ matchedHtml: matched,
354
+ preAttributes,
355
+ codeAttributes
356
+ });
357
+ lastIndex = matchStart + matched.length;
358
+ }
359
+ resultHtml += html.slice(lastIndex);
360
+ return resultHtml;
361
+ }
362
+ async function highlightSingleCodeBlock(highlighter, input) {
363
+ const { rawLanguage, encodedCode, matchedHtml, preAttributes, codeAttributes } = input;
364
+ const normalizedLanguage = rawLanguage.toLowerCase();
365
+ if (normalizedLanguage === MERMAID_LANGUAGE_ID) {
366
+ return matchedHtml;
367
+ }
368
+ const resolvedLanguage = await ensureHighlighterLanguage(highlighter, normalizedLanguage);
369
+ const codeText = decodeHtmlEntities(encodedCode);
370
+ try {
371
+ const highlightedHtml = highlighter.codeToHtml(codeText, {
372
+ lang: resolvedLanguage,
373
+ theme: CODE_HIGHLIGHTER_THEME
374
+ });
375
+ const innerMatch = highlightedHtml.match(SHIKI_CODE_INNER_PATTERN);
376
+ const innerHtml = innerMatch?.[1] ?? encodedCode;
377
+ return `<pre${preAttributes}><code class="language-${rawLanguage}"${codeAttributes}>${innerHtml}</code></pre>`;
378
+ } catch {
379
+ return matchedHtml;
380
+ }
381
+ }
382
+ async function ensureHighlighterLanguage(highlighter, normalizedLanguage) {
383
+ if (normalizedLanguage.length === 0) {
384
+ return "text";
385
+ }
386
+ if (highlighter.getLoadedLanguages().includes(normalizedLanguage)) {
387
+ return normalizedLanguage;
388
+ }
389
+ const lazyLoader = CODE_HIGHLIGHTER_LAZY_LANGS[normalizedLanguage];
390
+ if (!lazyLoader) {
391
+ return "text";
392
+ }
393
+ let pending = codeHighlighterLazyLoadPromises.get(normalizedLanguage);
394
+ if (!pending) {
395
+ pending = (async () => {
396
+ const grammarModule = await lazyLoader();
397
+ await highlighter.loadLanguage(grammarModule.default);
398
+ })();
399
+ codeHighlighterLazyLoadPromises.set(normalizedLanguage, pending);
400
+ }
401
+ try {
402
+ await pending;
403
+ return normalizedLanguage;
404
+ } catch {
405
+ return "text";
406
+ }
407
+ }
408
+ var HTML_NAMED_ENTITIES = {
409
+ lt: "<",
410
+ gt: ">",
411
+ quot: '"',
412
+ apos: "'",
413
+ amp: "&"
414
+ };
415
+ var HTML_ENTITY_PATTERN = /&(?:(lt|gt|quot|apos|amp)|#x([0-9a-fA-F]+)|#(\d+));/g;
416
+ function decodeHtmlEntities(encodedText) {
417
+ return encodedText.replace(
418
+ HTML_ENTITY_PATTERN,
419
+ (matched, namedEntity, hexCodePoint, decimalCodePoint) => {
420
+ if (namedEntity) {
421
+ return HTML_NAMED_ENTITIES[namedEntity] ?? matched;
422
+ }
423
+ if (hexCodePoint) {
424
+ return safeFromCodePoint(parseInt(hexCodePoint, 16), matched);
425
+ }
426
+ if (decimalCodePoint) {
427
+ return safeFromCodePoint(parseInt(decimalCodePoint, 10), matched);
428
+ }
429
+ return matched;
430
+ }
431
+ );
432
+ }
433
+ function safeFromCodePoint(codePoint, fallback) {
434
+ if (!Number.isFinite(codePoint) || codePoint < 0 || codePoint > 1114111) {
435
+ return fallback;
436
+ }
437
+ try {
438
+ return String.fromCodePoint(codePoint);
439
+ } catch {
440
+ return fallback;
441
+ }
442
+ }
443
+ function sanitizeHtmlWithDomPurify(unsafeHtml) {
444
+ const domPurify = DOMPurify;
445
+ if (typeof domPurify.sanitize !== "function") {
446
+ return unsafeHtml;
447
+ }
448
+ return domPurify.sanitize(unsafeHtml);
449
+ }
450
+ function hydrateMarkdownImages(rootElement) {
451
+ const imageElements = rootElement.querySelectorAll(
452
+ `img.${IMAGE_ELEMENT_CLASS_NAME}`
453
+ );
454
+ imageElements.forEach(bindMarkdownImageState);
455
+ }
456
+ function hydrateMarkdownVideos(rootElement) {
457
+ const videoElements = rootElement.querySelectorAll(
458
+ `video.${VIDEO_ELEMENT_CLASS_NAME}`
459
+ );
460
+ videoElements.forEach(bindMarkdownVideoState);
461
+ }
462
+ function hydrateCodeBlocks(rootElement) {
463
+ const preElements = rootElement.querySelectorAll("pre");
464
+ preElements.forEach(decorateCodeBlock);
465
+ }
466
+ var mermaidLoaderPromise;
467
+ function hydrateMermaidBlocks(rootElement) {
468
+ const mermaidCodeElements = rootElement.querySelectorAll(
469
+ `pre > code.language-${MERMAID_LANGUAGE_ID}`
470
+ );
471
+ mermaidCodeElements.forEach((codeElement) => {
472
+ const preElement = codeElement.parentElement;
473
+ if (!preElement) {
474
+ return;
475
+ }
476
+ if (preElement.dataset[CODE_BLOCK_HYDRATED_DATA_KEY] === "true") {
477
+ return;
478
+ }
479
+ decorateMermaidBlock(preElement, codeElement);
480
+ });
481
+ kickOffPendingMermaidRenders(rootElement);
482
+ }
483
+ function kickOffPendingMermaidRenders(rootElement) {
484
+ const pendingFigures = rootElement.querySelectorAll(
485
+ `.${MERMAID_FIGURE_CLASS_NAME}.${MERMAID_FIGURE_LOADING_CLASS_NAME}`
486
+ );
487
+ pendingFigures.forEach((figureElement) => {
488
+ if (!figureElement.isConnected) {
489
+ return;
490
+ }
491
+ if (figureElement.dataset[MERMAID_RENDER_STARTED_DATA_KEY] === "true") {
492
+ return;
493
+ }
494
+ const canvasElement = figureElement.querySelector(
495
+ `.${MERMAID_CANVAS_CLASS_NAME}`
496
+ );
497
+ const mermaidSource = figureElement.dataset[MERMAID_SOURCE_DATA_KEY] ?? "";
498
+ if (!canvasElement || mermaidSource.length === 0) {
499
+ return;
500
+ }
501
+ figureElement.dataset[MERMAID_RENDER_STARTED_DATA_KEY] = "true";
502
+ void renderMermaidIntoCanvas(figureElement, canvasElement, mermaidSource);
503
+ });
504
+ }
505
+ function decorateMermaidBlock(preElement, codeElement) {
506
+ if (preElement.dataset[MERMAID_HYDRATED_DATA_KEY] === "true") {
507
+ return;
508
+ }
509
+ preElement.dataset[MERMAID_HYDRATED_DATA_KEY] = "true";
510
+ const ownerDocument = preElement.ownerDocument;
511
+ const mermaidSource = (codeElement.textContent ?? "").replace(/\n+$/u, "");
512
+ const figureElement = ownerDocument.createElement("figure");
513
+ figureElement.className = `${MERMAID_FIGURE_CLASS_NAME} ${MERMAID_FIGURE_LOADING_CLASS_NAME}`;
514
+ const chromeElement = ownerDocument.createElement("div");
515
+ chromeElement.className = MERMAID_CHROME_CLASS_NAME;
516
+ const labelElement = ownerDocument.createElement("span");
517
+ labelElement.className = MERMAID_LABEL_CLASS_NAME;
518
+ labelElement.textContent = MERMAID_LABEL_TEXT;
519
+ chromeElement.append(labelElement);
520
+ const bodyElement = ownerDocument.createElement("div");
521
+ bodyElement.className = MERMAID_BODY_CLASS_NAME;
522
+ const canvasElement = ownerDocument.createElement("div");
523
+ canvasElement.className = MERMAID_CANVAS_CLASS_NAME;
524
+ canvasElement.setAttribute("role", "img");
525
+ canvasElement.setAttribute("aria-label", MERMAID_LABEL_TEXT);
526
+ const fullscreenButtonElement = ownerDocument.createElement("button");
527
+ fullscreenButtonElement.type = "button";
528
+ fullscreenButtonElement.className = MERMAID_FULLSCREEN_BUTTON_CLASS_NAME;
529
+ fullscreenButtonElement.setAttribute("aria-label", MERMAID_FULLSCREEN_BUTTON_ARIA_LABEL);
530
+ fullscreenButtonElement.disabled = true;
531
+ fullscreenButtonElement.innerHTML = MERMAID_FULLSCREEN_ICON_SVG;
532
+ fullscreenButtonElement.addEventListener("click", handleMermaidFullscreenButtonClick);
533
+ bodyElement.append(canvasElement, fullscreenButtonElement);
534
+ figureElement.append(chromeElement, bodyElement);
535
+ const sourceLine = codeElement.getAttribute(SOURCE_LINE_DATA_ATTRIBUTE);
536
+ if (sourceLine !== null) {
537
+ figureElement.setAttribute(SOURCE_LINE_DATA_ATTRIBUTE, sourceLine);
538
+ }
539
+ figureElement.dataset[MERMAID_SOURCE_DATA_KEY] = mermaidSource;
540
+ preElement.replaceWith(figureElement);
541
+ }
542
+ async function loadMermaid() {
543
+ if (typeof window === "undefined" || typeof document === "undefined") {
544
+ return void 0;
545
+ }
546
+ if (!mermaidLoaderPromise) {
547
+ mermaidLoaderPromise = (async () => {
548
+ const mermaidModule = await import("mermaid");
549
+ const mermaidApi = mermaidModule.default;
550
+ mermaidApi.initialize({
551
+ startOnLoad: false,
552
+ securityLevel: "strict",
553
+ theme: "default",
554
+ flowchart: { useMaxWidth: false },
555
+ sequence: { useMaxWidth: false },
556
+ class: { useMaxWidth: false },
557
+ state: { useMaxWidth: false },
558
+ gantt: { useMaxWidth: false },
559
+ er: { useMaxWidth: false },
560
+ journey: { useMaxWidth: false },
561
+ pie: { useMaxWidth: false },
562
+ requirement: { useMaxWidth: false },
563
+ c4: { useMaxWidth: false },
564
+ mindmap: { useMaxWidth: false },
565
+ timeline: { useMaxWidth: false },
566
+ gitGraph: { useMaxWidth: false },
567
+ quadrantChart: { useMaxWidth: false },
568
+ xyChart: { useMaxWidth: false },
569
+ sankey: { useMaxWidth: false },
570
+ block: { useMaxWidth: false }
571
+ });
572
+ return mermaidApi;
573
+ })().catch((loadError) => {
574
+ mermaidLoaderPromise = void 0;
575
+ throw loadError;
576
+ });
577
+ }
578
+ return mermaidLoaderPromise;
579
+ }
580
+ async function renderMermaidIntoCanvas(figureElement, canvasElement, mermaidSource) {
581
+ try {
582
+ const mermaidApi = await loadMermaid();
583
+ if (!mermaidApi) {
584
+ return;
585
+ }
586
+ mermaidRenderIdCounter += 1;
587
+ const renderId = `${MERMAID_RENDER_ID_PREFIX}${mermaidRenderIdCounter}`;
588
+ const { svg, bindFunctions } = await mermaidApi.render(renderId, mermaidSource);
589
+ canvasElement.innerHTML = svg;
590
+ bindFunctions?.(canvasElement);
591
+ figureElement.classList.remove(MERMAID_FIGURE_LOADING_CLASS_NAME);
592
+ figureElement.classList.add(MERMAID_FIGURE_LOADED_CLASS_NAME);
593
+ figureElement.dataset[MERMAID_VIEWER_SOURCE_DATA_KEY] = svg;
594
+ figureElement.dataset[MERMAID_FULLSCREEN_AVAILABLE_DATA_KEY] = "true";
595
+ const fullscreenButtonElement = figureElement.querySelector(
596
+ `.${MERMAID_FULLSCREEN_BUTTON_CLASS_NAME}`
597
+ );
598
+ if (fullscreenButtonElement) {
599
+ fullscreenButtonElement.disabled = false;
600
+ }
601
+ } catch (renderError) {
602
+ showMermaidFallback(figureElement, canvasElement, mermaidSource, renderError);
603
+ }
604
+ }
605
+ function showMermaidFallback(figureElement, canvasElement, mermaidSource, renderError) {
606
+ const ownerDocument = figureElement.ownerDocument;
607
+ figureElement.classList.remove(MERMAID_FIGURE_LOADING_CLASS_NAME);
608
+ figureElement.classList.add(MERMAID_FIGURE_FAILED_CLASS_NAME);
609
+ const fullscreenButtonElement = figureElement.querySelector(
610
+ `.${MERMAID_FULLSCREEN_BUTTON_CLASS_NAME}`
611
+ );
612
+ fullscreenButtonElement?.remove();
613
+ canvasElement.replaceChildren();
614
+ cleanupOrphanMermaidNodes(ownerDocument);
615
+ const fallbackElement = ownerDocument.createElement("div");
616
+ fallbackElement.className = MERMAID_FALLBACK_CLASS_NAME;
617
+ const iconElement = ownerDocument.createElement("span");
618
+ iconElement.className = MERMAID_FALLBACK_ICON_CLASS_NAME;
619
+ iconElement.setAttribute("aria-hidden", "true");
620
+ fallbackElement.append(iconElement);
621
+ const textElement = ownerDocument.createElement("p");
622
+ textElement.className = MERMAID_FALLBACK_TEXT_CLASS_NAME;
623
+ textElement.textContent = MERMAID_FALLBACK_DEFAULT_TEXT;
624
+ fallbackElement.append(textElement);
625
+ const errorMessage = extractErrorMessage(renderError);
626
+ if (errorMessage) {
627
+ const messageElement = ownerDocument.createElement("p");
628
+ messageElement.className = MERMAID_FALLBACK_SOURCE_CLASS_NAME;
629
+ messageElement.textContent = errorMessage;
630
+ fallbackElement.append(messageElement);
631
+ }
632
+ if (mermaidSource.length > 0) {
633
+ const sourceElement = ownerDocument.createElement("pre");
634
+ sourceElement.className = MERMAID_FALLBACK_SOURCE_CLASS_NAME;
635
+ sourceElement.textContent = mermaidSource;
636
+ fallbackElement.append(sourceElement);
637
+ }
638
+ canvasElement.replaceWith(fallbackElement);
639
+ }
640
+ function extractErrorMessage(renderError) {
641
+ if (renderError instanceof Error) {
642
+ return renderError.message;
643
+ }
644
+ if (typeof renderError === "string") {
645
+ return renderError;
646
+ }
647
+ return "";
648
+ }
649
+ function cleanupOrphanMermaidNodes(ownerDocument) {
650
+ const orphanNodes = ownerDocument.querySelectorAll(
651
+ `body > [id^="${MERMAID_RENDER_ID_PREFIX}"], body > div[id^="d"][id*="mermaid"]`
652
+ );
653
+ orphanNodes.forEach((orphanNode) => {
654
+ orphanNode.remove();
655
+ });
656
+ }
657
+ var MERMAID_FULLSCREEN_ICON_SVG = '<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true" focusable="false"><path d="M4 9 V4 H9 M15 4 H20 V9 M20 15 V20 H15 M9 20 H4 V15" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/></svg>';
658
+ function handleMermaidFullscreenButtonClick(event) {
659
+ const buttonElement = event.currentTarget;
660
+ const figureElement = buttonElement.closest(`.${MERMAID_FIGURE_CLASS_NAME}`);
661
+ if (!figureElement) {
662
+ return;
663
+ }
664
+ if (figureElement.dataset[MERMAID_FULLSCREEN_AVAILABLE_DATA_KEY] !== "true") {
665
+ return;
666
+ }
667
+ const svgSource = figureElement.dataset[MERMAID_VIEWER_SOURCE_DATA_KEY] ?? "";
668
+ if (svgSource.length === 0) {
669
+ return;
670
+ }
671
+ event.preventDefault();
672
+ event.stopPropagation();
673
+ openMarkdownMermaidViewer(figureElement.ownerDocument, svgSource);
674
+ }
675
+ function openMarkdownMermaidViewer(ownerDocument, svgSource) {
676
+ const viewerState = getOrCreateMarkdownMermaidViewerState(ownerDocument);
677
+ viewerState.canvasElement.innerHTML = svgSource;
678
+ const svgDimensions = readSvgNaturalDimensions(viewerState.canvasElement);
679
+ viewerState.naturalWidth = svgDimensions.width;
680
+ viewerState.naturalHeight = svgDimensions.height;
681
+ viewerState.viewportElement.scrollLeft = 0;
682
+ viewerState.viewportElement.scrollTop = 0;
683
+ viewerState.zoomValue = IMAGE_VIEWER_DEFAULT_ZOOM;
684
+ showMarkdownMermaidViewerDialog(viewerState);
685
+ requestMarkdownMermaidViewerLayout(viewerState);
686
+ }
687
+ function readSvgNaturalDimensions(canvasElement) {
688
+ const svgElement = canvasElement.querySelector("svg");
689
+ if (!svgElement) {
690
+ return { width: 720, height: 480 };
691
+ }
692
+ const viewBox = svgElement.viewBox?.baseVal;
693
+ if (viewBox && viewBox.width > 0 && viewBox.height > 0) {
694
+ return { width: viewBox.width, height: viewBox.height };
695
+ }
696
+ const widthAttribute = parseFloat(svgElement.getAttribute("width") ?? "");
697
+ const heightAttribute = parseFloat(svgElement.getAttribute("height") ?? "");
698
+ if (Number.isFinite(widthAttribute) && Number.isFinite(heightAttribute)) {
699
+ return {
700
+ width: widthAttribute > 0 ? widthAttribute : 720,
701
+ height: heightAttribute > 0 ? heightAttribute : 480
702
+ };
703
+ }
704
+ return { width: 720, height: 480 };
705
+ }
706
+ function getOrCreateMarkdownMermaidViewerState(ownerDocument) {
707
+ const existingViewerState = mermaidViewerStateByDocument.get(ownerDocument);
708
+ if (existingViewerState) {
709
+ return existingViewerState;
710
+ }
711
+ const createdViewerState = createMarkdownMermaidViewerState(ownerDocument);
712
+ mermaidViewerStateByDocument.set(ownerDocument, createdViewerState);
713
+ return createdViewerState;
714
+ }
715
+ function createMarkdownMermaidViewerState(ownerDocument) {
716
+ const dialogElement = ownerDocument.createElement("dialog");
717
+ const chromeElement = ownerDocument.createElement("div");
718
+ const captionElement = ownerDocument.createElement("p");
719
+ const controlsElement = ownerDocument.createElement("div");
720
+ const zoomOutButtonElement = createMarkdownMermaidViewerButton(ownerDocument, {
721
+ ariaLabel: "\u7F29\u5C0F\u56FE\u8868",
722
+ text: IMAGE_VIEWER_ZOOM_OUT_TEXT
723
+ });
724
+ const zoomValueElement = ownerDocument.createElement("span");
725
+ const zoomInButtonElement = createMarkdownMermaidViewerButton(ownerDocument, {
726
+ ariaLabel: "\u653E\u5927\u56FE\u8868",
727
+ text: IMAGE_VIEWER_ZOOM_IN_TEXT
728
+ });
729
+ const resetButtonElement = createMarkdownMermaidViewerButton(ownerDocument, {
730
+ ariaLabel: "\u91CD\u7F6E\u7F29\u653E",
731
+ text: IMAGE_VIEWER_RESET_TEXT
732
+ });
733
+ const closeButtonElement = createMarkdownMermaidViewerButton(ownerDocument, {
734
+ ariaLabel: "\u5173\u95ED\u5168\u5C4F\u67E5\u770B",
735
+ className: MERMAID_VIEWER_CLOSE_BUTTON_CLASS_NAME,
736
+ text: IMAGE_VIEWER_CLOSE_TEXT
737
+ });
738
+ const viewportElement = ownerDocument.createElement("div");
739
+ const canvasElement = ownerDocument.createElement("div");
740
+ const viewerState = {
741
+ dialogElement,
742
+ captionElement,
743
+ zoomValueElement,
744
+ zoomOutButtonElement,
745
+ zoomInButtonElement,
746
+ resetButtonElement,
747
+ closeButtonElement,
748
+ viewportElement,
749
+ canvasElement,
750
+ naturalWidth: 720,
751
+ naturalHeight: 480,
752
+ zoomValue: IMAGE_VIEWER_DEFAULT_ZOOM,
753
+ isDragging: false,
754
+ dragStartClientX: 0,
755
+ dragStartClientY: 0,
756
+ dragStartScrollLeft: 0,
757
+ dragStartScrollTop: 0
758
+ };
759
+ const ownerWindow = ownerDocument.defaultView;
760
+ dialogElement.className = MERMAID_VIEWER_DIALOG_CLASS_NAME;
761
+ dialogElement.setAttribute("aria-modal", "true");
762
+ dialogElement.setAttribute("aria-label", "\u5168\u5C4F\u67E5\u770B Mermaid \u56FE\u8868");
763
+ dialogElement.setAttribute("tabindex", "-1");
764
+ chromeElement.className = MERMAID_VIEWER_CHROME_CLASS_NAME;
765
+ captionElement.className = MERMAID_VIEWER_CAPTION_CLASS_NAME;
766
+ captionElement.textContent = MERMAID_LABEL_TEXT;
767
+ controlsElement.className = MERMAID_VIEWER_CONTROLS_CLASS_NAME;
768
+ zoomValueElement.className = MERMAID_VIEWER_ZOOM_VALUE_CLASS_NAME;
769
+ viewportElement.className = MERMAID_VIEWER_VIEWPORT_CLASS_NAME;
770
+ canvasElement.className = MERMAID_VIEWER_CANVAS_CLASS_NAME;
771
+ mermaidViewerStateByDialogElement.set(dialogElement, viewerState);
772
+ controlsElement.append(
773
+ zoomOutButtonElement,
774
+ zoomValueElement,
775
+ zoomInButtonElement,
776
+ resetButtonElement,
777
+ closeButtonElement
778
+ );
779
+ chromeElement.append(captionElement, controlsElement);
780
+ viewportElement.append(canvasElement);
781
+ dialogElement.append(chromeElement, viewportElement);
782
+ ownerDocument.body.append(dialogElement);
783
+ dialogElement.addEventListener("click", handleMarkdownMermaidViewerBackdropClick);
784
+ dialogElement.addEventListener("close", handleMarkdownMermaidViewerClose);
785
+ dialogElement.addEventListener("keydown", handleMarkdownMermaidViewerKeyDown);
786
+ viewportElement.addEventListener("wheel", handleMarkdownMermaidViewerWheel, { passive: false });
787
+ viewportElement.addEventListener("pointerdown", handleMarkdownMermaidViewerPointerDown);
788
+ viewportElement.addEventListener("pointermove", handleMarkdownMermaidViewerPointerMove);
789
+ viewportElement.addEventListener("pointerup", handleMarkdownMermaidViewerPointerUp);
790
+ viewportElement.addEventListener("pointercancel", handleMarkdownMermaidViewerPointerUp);
791
+ zoomOutButtonElement.addEventListener("click", handleMarkdownMermaidViewerZoomOutClick);
792
+ zoomInButtonElement.addEventListener("click", handleMarkdownMermaidViewerZoomInClick);
793
+ resetButtonElement.addEventListener("click", handleMarkdownMermaidViewerResetClick);
794
+ closeButtonElement.addEventListener("click", handleMarkdownMermaidViewerCloseClick);
795
+ if (ownerWindow) {
796
+ ownerWindow.addEventListener("resize", handleMarkdownMermaidViewerWindowResize);
797
+ }
798
+ updateMarkdownMermaidViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
799
+ return viewerState;
800
+ }
801
+ function createMarkdownMermaidViewerButton(ownerDocument, options) {
802
+ const buttonElement = ownerDocument.createElement("button");
803
+ buttonElement.type = "button";
804
+ buttonElement.className = options.className ? `${MERMAID_VIEWER_BUTTON_CLASS_NAME} ${options.className}` : MERMAID_VIEWER_BUTTON_CLASS_NAME;
805
+ buttonElement.textContent = options.text;
806
+ buttonElement.setAttribute("aria-label", options.ariaLabel);
807
+ return buttonElement;
808
+ }
809
+ function showMarkdownMermaidViewerDialog(viewerState) {
810
+ if (typeof viewerState.dialogElement.showModal === "function") {
811
+ if (!viewerState.dialogElement.open) {
812
+ viewerState.dialogElement.showModal();
813
+ }
814
+ } else {
815
+ viewerState.dialogElement.setAttribute("open", "");
816
+ }
817
+ viewerState.dialogElement.focus();
818
+ }
819
+ function requestMarkdownMermaidViewerLayout(viewerState) {
820
+ const ownerWindow = viewerState.dialogElement.ownerDocument.defaultView;
821
+ if (!ownerWindow) {
822
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue);
823
+ return;
824
+ }
825
+ ownerWindow.setTimeout(handleMarkdownMermaidViewerDeferredLayout, 0, viewerState);
826
+ }
827
+ function handleMarkdownMermaidViewerDeferredLayout(viewerState) {
828
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue);
829
+ }
830
+ function handleMarkdownMermaidViewerBackdropClick(event) {
831
+ if (event.target !== event.currentTarget) {
832
+ return;
833
+ }
834
+ const dialogElement = event.currentTarget;
835
+ const viewerState = mermaidViewerStateByDialogElement.get(dialogElement);
836
+ if (!viewerState) {
837
+ return;
838
+ }
839
+ closeMarkdownMermaidViewer(viewerState);
840
+ }
841
+ function handleMarkdownMermaidViewerClose(event) {
842
+ const dialogElement = event.currentTarget;
843
+ const viewerState = mermaidViewerStateByDialogElement.get(dialogElement);
844
+ if (!viewerState) {
845
+ return;
846
+ }
847
+ resetMarkdownMermaidViewerAfterClose(viewerState);
848
+ }
849
+ function resetMarkdownMermaidViewerAfterClose(viewerState) {
850
+ viewerState.canvasElement.replaceChildren();
851
+ viewerState.viewportElement.scrollLeft = 0;
852
+ viewerState.viewportElement.scrollTop = 0;
853
+ viewerState.zoomValue = IMAGE_VIEWER_DEFAULT_ZOOM;
854
+ viewerState.isDragging = false;
855
+ viewerState.dialogElement.classList.remove(MERMAID_VIEWER_DRAGGING_CLASS_NAME);
856
+ viewerState.dialogElement.classList.remove(MERMAID_VIEWER_ZOOMED_CLASS_NAME);
857
+ }
858
+ function handleMarkdownMermaidViewerKeyDown(event) {
859
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
860
+ if (!viewerState) {
861
+ return;
862
+ }
863
+ if (event.key === "+" || event.key === "=") {
864
+ event.preventDefault();
865
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue + IMAGE_VIEWER_ZOOM_STEP);
866
+ return;
867
+ }
868
+ if (event.key === "-") {
869
+ event.preventDefault();
870
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue - IMAGE_VIEWER_ZOOM_STEP);
871
+ return;
872
+ }
873
+ if (event.key === "0") {
874
+ event.preventDefault();
875
+ updateMarkdownMermaidViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
876
+ return;
877
+ }
878
+ if (event.key === "Escape") {
879
+ event.preventDefault();
880
+ closeMarkdownMermaidViewer(viewerState);
881
+ }
882
+ }
883
+ function handleMarkdownMermaidViewerWheel(event) {
884
+ if (!event.ctrlKey && !event.metaKey) {
885
+ return;
886
+ }
887
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
888
+ if (!viewerState) {
889
+ return;
890
+ }
891
+ const rawDelta = -event.deltaY * IMAGE_VIEWER_WHEEL_ZOOM_FACTOR;
892
+ const zoomDelta = Math.max(
893
+ -IMAGE_VIEWER_WHEEL_ZOOM_MAX_DELTA,
894
+ Math.min(IMAGE_VIEWER_WHEEL_ZOOM_MAX_DELTA, rawDelta)
895
+ );
896
+ event.preventDefault();
897
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue + zoomDelta, {
898
+ x: event.clientX,
899
+ y: event.clientY
900
+ });
901
+ }
902
+ function handleMarkdownMermaidViewerPointerDown(event) {
903
+ if (event.button !== 0) {
904
+ return;
905
+ }
906
+ const viewportElement = event.currentTarget;
907
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
908
+ if (!viewerState) {
909
+ return;
910
+ }
911
+ const canScrollHorizontally = viewportElement.scrollWidth > viewportElement.clientWidth;
912
+ const canScrollVertically = viewportElement.scrollHeight > viewportElement.clientHeight;
913
+ if (!canScrollHorizontally && !canScrollVertically) {
914
+ return;
915
+ }
916
+ viewerState.isDragging = true;
917
+ viewerState.dragStartClientX = event.clientX;
918
+ viewerState.dragStartClientY = event.clientY;
919
+ viewerState.dragStartScrollLeft = viewportElement.scrollLeft;
920
+ viewerState.dragStartScrollTop = viewportElement.scrollTop;
921
+ viewerState.dialogElement.classList.add(MERMAID_VIEWER_DRAGGING_CLASS_NAME);
922
+ if (typeof viewportElement.setPointerCapture === "function") {
923
+ viewportElement.setPointerCapture(event.pointerId);
924
+ }
925
+ event.preventDefault();
926
+ }
927
+ function handleMarkdownMermaidViewerPointerMove(event) {
928
+ const viewportElement = event.currentTarget;
929
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
930
+ if (!viewerState || !viewerState.isDragging) {
931
+ return;
932
+ }
933
+ const deltaX = event.clientX - viewerState.dragStartClientX;
934
+ const deltaY = event.clientY - viewerState.dragStartClientY;
935
+ viewportElement.scrollLeft = viewerState.dragStartScrollLeft - deltaX;
936
+ viewportElement.scrollTop = viewerState.dragStartScrollTop - deltaY;
937
+ }
938
+ function handleMarkdownMermaidViewerPointerUp(event) {
939
+ const viewportElement = event.currentTarget;
940
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
941
+ if (!viewerState || !viewerState.isDragging) {
942
+ return;
943
+ }
944
+ viewerState.isDragging = false;
945
+ viewerState.dialogElement.classList.remove(MERMAID_VIEWER_DRAGGING_CLASS_NAME);
946
+ if (typeof viewportElement.releasePointerCapture === "function" && viewportElement.hasPointerCapture(event.pointerId)) {
947
+ viewportElement.releasePointerCapture(event.pointerId);
948
+ }
949
+ }
950
+ function handleMarkdownMermaidViewerWindowResize(event) {
951
+ const ownerWindow = event.currentTarget;
952
+ const viewerState = mermaidViewerStateByDocument.get(ownerWindow.document);
953
+ if (!viewerState || !isMarkdownMermaidViewerOpen(viewerState)) {
954
+ return;
955
+ }
956
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue);
957
+ }
958
+ function handleMarkdownMermaidViewerZoomOutClick(event) {
959
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
960
+ if (!viewerState) {
961
+ return;
962
+ }
963
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue - IMAGE_VIEWER_ZOOM_STEP);
964
+ }
965
+ function handleMarkdownMermaidViewerZoomInClick(event) {
966
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
967
+ if (!viewerState) {
968
+ return;
969
+ }
970
+ updateMarkdownMermaidViewerZoom(viewerState, viewerState.zoomValue + IMAGE_VIEWER_ZOOM_STEP);
971
+ }
972
+ function handleMarkdownMermaidViewerResetClick(event) {
973
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
974
+ if (!viewerState) {
975
+ return;
976
+ }
977
+ updateMarkdownMermaidViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
978
+ }
979
+ function handleMarkdownMermaidViewerCloseClick(event) {
980
+ const viewerState = getMarkdownMermaidViewerStateFromEvent(event);
981
+ if (!viewerState) {
982
+ return;
983
+ }
984
+ closeMarkdownMermaidViewer(viewerState);
985
+ }
986
+ function getMarkdownMermaidViewerStateFromEvent(event) {
987
+ const targetElement = event.target;
988
+ const dialogElement = targetElement?.closest(`.${MERMAID_VIEWER_DIALOG_CLASS_NAME}`) ?? null;
989
+ if (!dialogElement) {
990
+ return void 0;
991
+ }
992
+ return mermaidViewerStateByDialogElement.get(dialogElement);
993
+ }
994
+ function closeMarkdownMermaidViewer(viewerState) {
995
+ if (typeof viewerState.dialogElement.close === "function" && viewerState.dialogElement.open) {
996
+ viewerState.dialogElement.close();
997
+ return;
998
+ }
999
+ viewerState.dialogElement.removeAttribute("open");
1000
+ resetMarkdownMermaidViewerAfterClose(viewerState);
1001
+ }
1002
+ function isMarkdownMermaidViewerOpen(viewerState) {
1003
+ return viewerState.dialogElement.open === true;
1004
+ }
1005
+ function updateMarkdownMermaidViewerZoom(viewerState, nextZoom, zoomAnchor) {
1006
+ const normalizedZoom = clampMarkdownImageViewerZoom(nextZoom);
1007
+ const focalPoint = isMarkdownMermaidViewerOpen(viewerState) ? captureMarkdownMermaidViewerFocalPoint(viewerState, zoomAnchor) : void 0;
1008
+ viewerState.zoomValue = normalizedZoom;
1009
+ viewerState.dialogElement.classList.toggle(
1010
+ MERMAID_VIEWER_ZOOMED_CLASS_NAME,
1011
+ normalizedZoom > IMAGE_VIEWER_DEFAULT_ZOOM
1012
+ );
1013
+ viewerState.zoomValueElement.textContent = `${Math.round(normalizedZoom * 100)}%`;
1014
+ viewerState.zoomOutButtonElement.disabled = normalizedZoom <= IMAGE_VIEWER_MIN_ZOOM;
1015
+ viewerState.zoomInButtonElement.disabled = normalizedZoom >= IMAGE_VIEWER_MAX_ZOOM;
1016
+ viewerState.resetButtonElement.disabled = normalizedZoom === IMAGE_VIEWER_DEFAULT_ZOOM;
1017
+ updateMarkdownMermaidViewerCanvasSize(viewerState);
1018
+ if (focalPoint) {
1019
+ applyMarkdownMermaidViewerFocalPoint(viewerState, focalPoint);
1020
+ }
1021
+ }
1022
+ function captureMarkdownMermaidViewerFocalPoint(viewerState, zoomAnchor) {
1023
+ const preCanvasRect = viewerState.canvasElement.getBoundingClientRect();
1024
+ if (preCanvasRect.width <= 0 || preCanvasRect.height <= 0) {
1025
+ return void 0;
1026
+ }
1027
+ const viewportRect = viewerState.viewportElement.getBoundingClientRect();
1028
+ const anchorClientX = zoomAnchor?.x ?? viewportRect.left + viewportRect.width / 2;
1029
+ const anchorClientY = zoomAnchor?.y ?? viewportRect.top + viewportRect.height / 2;
1030
+ const normalizedX = (anchorClientX - preCanvasRect.left) / preCanvasRect.width;
1031
+ const normalizedY = (anchorClientY - preCanvasRect.top) / preCanvasRect.height;
1032
+ return { anchorClientX, anchorClientY, normalizedX, normalizedY };
1033
+ }
1034
+ function applyMarkdownMermaidViewerFocalPoint(viewerState, focalPoint) {
1035
+ viewerState.viewportElement.scrollLeft = 0;
1036
+ viewerState.viewportElement.scrollTop = 0;
1037
+ const postCanvasRect = viewerState.canvasElement.getBoundingClientRect();
1038
+ if (postCanvasRect.width <= 0 || postCanvasRect.height <= 0) {
1039
+ return;
1040
+ }
1041
+ const targetCanvasClientLeft = focalPoint.anchorClientX - focalPoint.normalizedX * postCanvasRect.width;
1042
+ const targetCanvasClientTop = focalPoint.anchorClientY - focalPoint.normalizedY * postCanvasRect.height;
1043
+ viewerState.viewportElement.scrollLeft = postCanvasRect.left - targetCanvasClientLeft;
1044
+ viewerState.viewportElement.scrollTop = postCanvasRect.top - targetCanvasClientTop;
1045
+ }
1046
+ function updateMarkdownMermaidViewerCanvasSize(viewerState) {
1047
+ const viewportWidth = Math.max(
1048
+ viewerState.viewportElement.clientWidth * IMAGE_VIEWER_FIT_RATIO,
1049
+ 1
1050
+ );
1051
+ const viewportHeight = Math.max(
1052
+ viewerState.viewportElement.clientHeight * IMAGE_VIEWER_FIT_RATIO,
1053
+ 1
1054
+ );
1055
+ const naturalWidth = Math.max(viewerState.naturalWidth, 1);
1056
+ const naturalHeight = Math.max(viewerState.naturalHeight, 1);
1057
+ const fitScale = Math.min(1, viewportWidth / naturalWidth, viewportHeight / naturalHeight);
1058
+ const displayWidth = Math.max(1, Math.round(naturalWidth * fitScale * viewerState.zoomValue));
1059
+ const displayHeight = Math.max(1, Math.round(naturalHeight * fitScale * viewerState.zoomValue));
1060
+ viewerState.canvasElement.style.width = `${displayWidth}px`;
1061
+ viewerState.canvasElement.style.height = `${displayHeight}px`;
1062
+ const innerSvgElement = viewerState.canvasElement.querySelector("svg");
1063
+ if (innerSvgElement) {
1064
+ innerSvgElement.removeAttribute("width");
1065
+ innerSvgElement.removeAttribute("height");
1066
+ innerSvgElement.style.width = "100%";
1067
+ innerSvgElement.style.height = "100%";
1068
+ innerSvgElement.style.maxWidth = "none";
1069
+ innerSvgElement.style.display = "block";
1070
+ }
1071
+ }
1072
+ function defaultScrollToHeading(targetElement) {
1073
+ targetElement.scrollIntoView({ behavior: "smooth", block: "start" });
1074
+ }
1075
+ function hydrateMarkdown(rootElement, options = {}) {
1076
+ hydrateMarkdownImages(rootElement);
1077
+ hydrateMarkdownVideos(rootElement);
1078
+ hydrateMermaidBlocks(rootElement);
1079
+ hydrateCodeBlocks(rootElement);
1080
+ hydrateToc(rootElement, options.scrollToHeading ?? defaultScrollToHeading);
1081
+ }
1082
+ function mountMarkdownToolbar(container, options = {}) {
1083
+ const ownerDocument = container.ownerDocument;
1084
+ if (!ownerDocument) {
1085
+ return;
1086
+ }
1087
+ if (typeof ownerDocument.defaultView?.scrollTo !== "function") {
1088
+ return;
1089
+ }
1090
+ applyContentWidth(ownerDocument, loadContentWidth());
1091
+ mountPageToolbar(ownerDocument, container, options.scrollToHeading ?? defaultScrollToHeading);
1092
+ }
1093
+ function decorateCodeBlock(preElement) {
1094
+ if (preElement.parentElement?.classList.contains(CODE_BLOCK_CLASS_NAME)) {
1095
+ return;
1096
+ }
1097
+ if (preElement.dataset[CODE_BLOCK_HYDRATED_DATA_KEY] === "true") {
1098
+ return;
1099
+ }
1100
+ const codeElement = preElement.querySelector(":scope > code");
1101
+ if (!codeElement) {
1102
+ return;
1103
+ }
1104
+ preElement.dataset[CODE_BLOCK_HYDRATED_DATA_KEY] = "true";
1105
+ const ownerDocument = preElement.ownerDocument;
1106
+ const languageId = resolveCodeBlockLanguageId(codeElement);
1107
+ const languageLabel = resolveCodeBlockLanguageLabel(languageId);
1108
+ const originalCodeText = codeElement.textContent ?? "";
1109
+ const codeLineCount = rewriteCodeBlockLines(codeElement, ownerDocument);
1110
+ const chromeElement = ownerDocument.createElement("div");
1111
+ chromeElement.className = CODE_BLOCK_CHROME_CLASS_NAME;
1112
+ const langElement = ownerDocument.createElement("span");
1113
+ langElement.className = CODE_BLOCK_LANG_CLASS_NAME;
1114
+ langElement.textContent = languageLabel;
1115
+ chromeElement.append(langElement);
1116
+ const copyButtonElement = createCodeBlockCopyButton(ownerDocument);
1117
+ chromeElement.append(copyButtonElement);
1118
+ const figureElement = ownerDocument.createElement("figure");
1119
+ figureElement.className = CODE_BLOCK_CLASS_NAME;
1120
+ figureElement.dataset.scribdownCodeLang = languageId;
1121
+ const bodyElement = ownerDocument.createElement("div");
1122
+ bodyElement.className = CODE_BLOCK_BODY_CLASS_NAME;
1123
+ const gutterElement = createCodeBlockLineNumberGutter(ownerDocument, codeLineCount);
1124
+ preElement.replaceWith(figureElement);
1125
+ bodyElement.append(gutterElement, preElement);
1126
+ figureElement.append(chromeElement, bodyElement);
1127
+ const codeBlockSourceLine = codeElement.getAttribute(SOURCE_LINE_DATA_ATTRIBUTE);
1128
+ if (codeBlockSourceLine !== null) {
1129
+ figureElement.setAttribute(SOURCE_LINE_DATA_ATTRIBUTE, codeBlockSourceLine);
1130
+ codeElement.removeAttribute(SOURCE_LINE_DATA_ATTRIBUTE);
1131
+ }
1132
+ copyButtonElement.dataset.scribdownCodeSource = originalCodeText;
1133
+ copyButtonElement.addEventListener("click", handleCodeBlockCopyClick);
1134
+ }
1135
+ function resolveCodeBlockLanguageId(codeElement) {
1136
+ const languageClassName = Array.from(codeElement.classList).find(
1137
+ (className) => className.startsWith("language-")
1138
+ );
1139
+ if (!languageClassName) {
1140
+ return "";
1141
+ }
1142
+ return languageClassName.slice("language-".length).toLowerCase();
1143
+ }
1144
+ function resolveCodeBlockLanguageLabel(languageId) {
1145
+ if (!languageId) {
1146
+ return CODE_LANGUAGE_DEFAULT_LABEL;
1147
+ }
1148
+ const mappedLabel = CODE_LANGUAGE_DISPLAY_NAMES[languageId];
1149
+ if (mappedLabel) {
1150
+ return mappedLabel;
1151
+ }
1152
+ return languageId.charAt(0).toUpperCase() + languageId.slice(1);
1153
+ }
1154
+ function rewriteCodeBlockLines(codeElement, ownerDocument) {
1155
+ const shikiLineElements = codeElement.querySelectorAll(":scope > span.line");
1156
+ if (shikiLineElements.length > 0) {
1157
+ const shikiLineNodes = Array.from(shikiLineElements);
1158
+ shikiLineElements.forEach((lineElement) => {
1159
+ lineElement.classList.add(CODE_BLOCK_LINE_CLASS_NAME);
1160
+ if (lineElement.textContent === "") {
1161
+ lineElement.textContent = CODE_BLOCK_EMPTY_LINE_PLACEHOLDER;
1162
+ }
1163
+ });
1164
+ codeElement.replaceChildren(...shikiLineNodes);
1165
+ return shikiLineNodes.length;
1166
+ }
1167
+ const codeText = codeElement.textContent ?? "";
1168
+ const trimmedCodeText = codeText.replace(/\n+$/u, "");
1169
+ const codeLines = trimmedCodeText.split("\n");
1170
+ while (codeElement.firstChild) {
1171
+ codeElement.removeChild(codeElement.firstChild);
1172
+ }
1173
+ codeLines.forEach((lineText) => {
1174
+ const lineElement = ownerDocument.createElement("span");
1175
+ lineElement.className = CODE_BLOCK_LINE_CLASS_NAME;
1176
+ if (lineText.length === 0) {
1177
+ lineElement.textContent = CODE_BLOCK_EMPTY_LINE_PLACEHOLDER;
1178
+ } else {
1179
+ lineElement.textContent = lineText;
1180
+ }
1181
+ codeElement.append(lineElement);
1182
+ });
1183
+ return codeLines.length;
1184
+ }
1185
+ function createCodeBlockLineNumberGutter(ownerDocument, lineCount) {
1186
+ const gutterElement = ownerDocument.createElement("div");
1187
+ gutterElement.className = CODE_BLOCK_GUTTER_CLASS_NAME;
1188
+ for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
1189
+ const lineNumberText = String(lineIndex + 1);
1190
+ const lineNumberElement = ownerDocument.createElement("span");
1191
+ lineNumberElement.className = CODE_BLOCK_GUTTER_LINE_CLASS_NAME;
1192
+ lineNumberElement.textContent = lineNumberText;
1193
+ gutterElement.append(lineNumberElement);
1194
+ }
1195
+ return gutterElement;
1196
+ }
1197
+ var CODE_BLOCK_COPY_ICON_COPY_PATHS = '<path d="M8.4 6.8 C10.4 6.5 12.6 6.7 14.6 6.6 C16 6.6 16.5 7.2 16.6 8.6 C16.7 10.8 16.6 13.2 16.6 15.4 C16.5 16.6 15.8 17.2 14.6 17.2 C12.4 17.3 10.2 17.2 8 17.2 C6.6 17.2 6.2 16.6 6.2 15.4 C6.2 13.2 6.2 11 6.2 8.8 C6.2 7.4 6.8 6.8 8.4 6.8 Z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/><path d="M8.6 7.4 C10.6 7.2 12.6 7.4 14.4 7.3" fill="none" stroke="currentColor" stroke-width="1" stroke-linejoin="round" stroke-linecap="round" opacity="0.42"/><path d="M3.6 12.8 C3.4 11.0 3.4 9.0 3.5 7.0 C3.5 5.0 3.4 3.6 5.2 3.4 C7.2 3.2 9.2 3.4 11.2 3.4 C12.6 3.4 13.2 4.0 13.3 5.0" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round" stroke-linecap="round"/><path d="M4.4 12.4 C4.2 10.6 4.2 8.8 4.2 7.0" fill="none" stroke="currentColor" stroke-width="1" stroke-linejoin="round" stroke-linecap="round" opacity="0.42"/>';
1198
+ var CODE_BLOCK_COPY_ICON_CHECK_PATHS = '<path d="M4.2 10.6 C5.6 11.8 6.8 13.2 8.2 14.6 C10.4 11.6 13.0 8.6 16.0 5.6" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round"/><path d="M4.6 11.4 C5.6 12.4 6.8 13.6 7.8 14.6 C10.2 12.2 12.6 9.6 15.2 7.0" fill="none" stroke="currentColor" stroke-width="1" stroke-linejoin="round" stroke-linecap="round" opacity="0.42"/>';
1199
+ function createCodeBlockCopyButton(ownerDocument) {
1200
+ const copyButtonElement = ownerDocument.createElement("button");
1201
+ copyButtonElement.type = "button";
1202
+ copyButtonElement.className = CODE_BLOCK_COPY_CLASS_NAME;
1203
+ copyButtonElement.setAttribute("aria-label", CODE_BLOCK_COPY_ARIA_LABEL);
1204
+ const copyIconElement = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "svg");
1205
+ copyIconElement.setAttribute(
1206
+ "class",
1207
+ `${CODE_BLOCK_COPY_ICON_CLASS_NAME} ${CODE_BLOCK_COPY_ICON_COPY_CLASS_NAME}`
1208
+ );
1209
+ copyIconElement.setAttribute("viewBox", "0 0 20 20");
1210
+ copyIconElement.setAttribute("aria-hidden", "true");
1211
+ copyIconElement.setAttribute("focusable", "false");
1212
+ copyIconElement.innerHTML = CODE_BLOCK_COPY_ICON_COPY_PATHS;
1213
+ copyButtonElement.append(copyIconElement);
1214
+ const checkIconElement = ownerDocument.createElementNS("http://www.w3.org/2000/svg", "svg");
1215
+ checkIconElement.setAttribute(
1216
+ "class",
1217
+ `${CODE_BLOCK_COPY_ICON_CLASS_NAME} ${CODE_BLOCK_COPY_ICON_CHECK_CLASS_NAME}`
1218
+ );
1219
+ checkIconElement.setAttribute("viewBox", "0 0 20 20");
1220
+ checkIconElement.setAttribute("aria-hidden", "true");
1221
+ checkIconElement.setAttribute("focusable", "false");
1222
+ checkIconElement.innerHTML = CODE_BLOCK_COPY_ICON_CHECK_PATHS;
1223
+ copyButtonElement.append(checkIconElement);
1224
+ return copyButtonElement;
1225
+ }
1226
+ function handleCodeBlockCopyClick(event) {
1227
+ const copyButtonElement = event.currentTarget;
1228
+ const codeText = copyButtonElement.dataset.scribdownCodeSource ?? "";
1229
+ void writeCodeBlockTextToClipboard(codeText).then((isSucceeded) => {
1230
+ if (isSucceeded) {
1231
+ flashCodeBlockCopyButton(copyButtonElement);
1232
+ }
1233
+ });
1234
+ }
1235
+ async function writeCodeBlockTextToClipboard(codeText) {
1236
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
1237
+ try {
1238
+ await navigator.clipboard.writeText(codeText);
1239
+ return true;
1240
+ } catch {
1241
+ }
1242
+ }
1243
+ if (typeof document === "undefined") {
1244
+ return false;
1245
+ }
1246
+ const fallbackTextarea = document.createElement("textarea");
1247
+ fallbackTextarea.value = codeText;
1248
+ fallbackTextarea.setAttribute("readonly", "");
1249
+ fallbackTextarea.style.position = "fixed";
1250
+ fallbackTextarea.style.opacity = "0";
1251
+ fallbackTextarea.style.pointerEvents = "none";
1252
+ document.body.append(fallbackTextarea);
1253
+ fallbackTextarea.select();
1254
+ const isSucceeded = document.execCommand("copy");
1255
+ fallbackTextarea.remove();
1256
+ return isSucceeded;
1257
+ }
1258
+ function flashCodeBlockCopyButton(copyButtonElement) {
1259
+ copyButtonElement.dataset.scribdownCodeCopied = "true";
1260
+ copyButtonElement.setAttribute("aria-label", CODE_BLOCK_COPY_ARIA_LABEL_COPIED);
1261
+ window.setTimeout(() => {
1262
+ delete copyButtonElement.dataset.scribdownCodeCopied;
1263
+ copyButtonElement.setAttribute("aria-label", CODE_BLOCK_COPY_ARIA_LABEL);
1264
+ }, CODE_BLOCK_COPY_RESTORE_DELAY_MS);
1265
+ }
1266
+ function bindMarkdownImageState(imageElement) {
1267
+ updateMarkdownImageState(imageElement);
1268
+ if (imageElement.dataset[IMAGE_HYDRATED_DATA_KEY] === "true") {
1269
+ return;
1270
+ }
1271
+ imageElement.dataset[IMAGE_HYDRATED_DATA_KEY] = "true";
1272
+ imageElement.addEventListener("load", handleMarkdownImageLoad);
1273
+ imageElement.addEventListener("error", handleMarkdownImageError);
1274
+ bindMarkdownImageViewer(imageElement);
1275
+ }
1276
+ function handleMarkdownImageLoad(event) {
1277
+ const imageElement = event.currentTarget;
1278
+ updateMarkdownImageState(imageElement);
1279
+ }
1280
+ function handleMarkdownImageError(event) {
1281
+ const imageElement = event.currentTarget;
1282
+ updateMarkdownImageState(imageElement);
1283
+ }
1284
+ function updateMarkdownImageState(imageElement) {
1285
+ const frameElement = imageElement.closest(`.${IMAGE_FRAME_CLASS_NAME}`);
1286
+ if (!frameElement) {
1287
+ return;
1288
+ }
1289
+ const isFailed = imageElement.complete && imageElement.naturalWidth === 0;
1290
+ const isLoaded = imageElement.complete && imageElement.naturalWidth > 0;
1291
+ frameElement.classList.toggle(IMAGE_FRAME_FAILED_CLASS_NAME, isFailed);
1292
+ frameElement.classList.toggle(IMAGE_FRAME_LOADED_CLASS_NAME, isLoaded);
1293
+ }
1294
+ function bindMarkdownVideoState(videoElement) {
1295
+ updateMarkdownVideoState(videoElement);
1296
+ if (videoElement.dataset[VIDEO_HYDRATED_DATA_KEY] === "true") {
1297
+ return;
1298
+ }
1299
+ videoElement.dataset[VIDEO_HYDRATED_DATA_KEY] = "true";
1300
+ videoElement.addEventListener("loadeddata", handleMarkdownVideoLoaded);
1301
+ videoElement.addEventListener("error", handleMarkdownVideoError);
1302
+ videoElement.querySelectorAll("source").forEach((sourceElement) => {
1303
+ sourceElement.addEventListener("error", handleMarkdownVideoSourceError);
1304
+ });
1305
+ }
1306
+ function handleMarkdownVideoLoaded(event) {
1307
+ const videoElement = event.currentTarget;
1308
+ updateMarkdownVideoState(videoElement);
1309
+ }
1310
+ function handleMarkdownVideoError(event) {
1311
+ const videoElement = event.currentTarget;
1312
+ markMarkdownVideoFailed(videoElement);
1313
+ }
1314
+ function handleMarkdownVideoSourceError(event) {
1315
+ const sourceElement = event.currentTarget;
1316
+ const videoElement = sourceElement.closest(
1317
+ `video.${VIDEO_ELEMENT_CLASS_NAME}`
1318
+ );
1319
+ if (!videoElement) {
1320
+ return;
1321
+ }
1322
+ if (videoElement.networkState !== videoElement.NETWORK_NO_SOURCE) {
1323
+ return;
1324
+ }
1325
+ markMarkdownVideoFailed(videoElement);
1326
+ }
1327
+ function updateMarkdownVideoState(videoElement) {
1328
+ const frameElement = videoElement.closest(
1329
+ `.${VIDEO_FRAME_CLASS_NAME}`
1330
+ );
1331
+ if (!frameElement) {
1332
+ return;
1333
+ }
1334
+ const isFailed = videoElement.error !== null || videoElement.networkState === videoElement.NETWORK_NO_SOURCE;
1335
+ const isLoaded = videoElement.readyState >= videoElement.HAVE_CURRENT_DATA;
1336
+ frameElement.classList.toggle(VIDEO_FRAME_FAILED_CLASS_NAME, isFailed);
1337
+ frameElement.classList.toggle(
1338
+ VIDEO_FRAME_LOADED_CLASS_NAME,
1339
+ isLoaded && !isFailed
1340
+ );
1341
+ }
1342
+ function markMarkdownVideoFailed(videoElement) {
1343
+ const frameElement = videoElement.closest(
1344
+ `.${VIDEO_FRAME_CLASS_NAME}`
1345
+ );
1346
+ if (!frameElement) {
1347
+ return;
1348
+ }
1349
+ frameElement.classList.add(VIDEO_FRAME_FAILED_CLASS_NAME);
1350
+ frameElement.classList.remove(VIDEO_FRAME_LOADED_CLASS_NAME);
1351
+ }
1352
+ function bindMarkdownImageViewer(imageElement) {
1353
+ const linkElement = imageElement.closest("a[href]");
1354
+ if (linkElement) {
1355
+ return;
1356
+ }
1357
+ imageElement.setAttribute("role", "button");
1358
+ imageElement.setAttribute("tabindex", "0");
1359
+ imageElement.setAttribute("aria-label", createMarkdownImageViewerAriaLabel(imageElement));
1360
+ imageElement.addEventListener("click", handleMarkdownImageViewerOpenClick);
1361
+ imageElement.addEventListener("keydown", handleMarkdownImageViewerOpenKeyDown);
1362
+ }
1363
+ function handleMarkdownImageViewerOpenClick(event) {
1364
+ const imageElement = event.currentTarget;
1365
+ if (!canOpenMarkdownImageViewer(imageElement)) {
1366
+ return;
1367
+ }
1368
+ event.preventDefault();
1369
+ event.stopPropagation();
1370
+ openMarkdownImageViewer(imageElement);
1371
+ }
1372
+ function handleMarkdownImageViewerOpenKeyDown(event) {
1373
+ const isOpenKey = event.key === "Enter" || event.key === " ";
1374
+ const imageElement = event.currentTarget;
1375
+ if (!isOpenKey || !canOpenMarkdownImageViewer(imageElement)) {
1376
+ return;
1377
+ }
1378
+ event.preventDefault();
1379
+ openMarkdownImageViewer(imageElement);
1380
+ }
1381
+ function canOpenMarkdownImageViewer(imageElement) {
1382
+ const imageSource = getMarkdownImageSource(imageElement);
1383
+ return imageSource.length > 0 && imageElement.complete && imageElement.naturalWidth > 0 && imageElement.naturalHeight > 0;
1384
+ }
1385
+ function openMarkdownImageViewer(sourceImageElement) {
1386
+ const imageSource = getMarkdownImageSource(sourceImageElement);
1387
+ if (imageSource.length === 0) {
1388
+ return;
1389
+ }
1390
+ const ownerDocument = sourceImageElement.ownerDocument;
1391
+ const viewerState = getOrCreateMarkdownImageViewerState(ownerDocument);
1392
+ const imageLabel = createMarkdownImageViewerCaption(sourceImageElement, imageSource);
1393
+ viewerState.sourceImageElement = sourceImageElement;
1394
+ viewerState.naturalWidth = sourceImageElement.naturalWidth;
1395
+ viewerState.naturalHeight = sourceImageElement.naturalHeight;
1396
+ viewerState.imageElement.src = imageSource;
1397
+ viewerState.imageElement.alt = sourceImageElement.alt;
1398
+ viewerState.captionElement.textContent = imageLabel;
1399
+ viewerState.dialogElement.setAttribute(
1400
+ "aria-label",
1401
+ createMarkdownImageViewerAriaLabel(sourceImageElement)
1402
+ );
1403
+ viewerState.viewportElement.scrollLeft = 0;
1404
+ viewerState.viewportElement.scrollTop = 0;
1405
+ viewerState.zoomValue = IMAGE_VIEWER_DEFAULT_ZOOM;
1406
+ showMarkdownImageViewerDialog(viewerState);
1407
+ requestMarkdownImageViewerLayout(viewerState);
1408
+ }
1409
+ function getOrCreateMarkdownImageViewerState(ownerDocument) {
1410
+ const existingViewerState = imageViewerStateByDocument.get(ownerDocument);
1411
+ if (existingViewerState) {
1412
+ return existingViewerState;
1413
+ }
1414
+ const createdViewerState = createMarkdownImageViewerState(ownerDocument);
1415
+ imageViewerStateByDocument.set(ownerDocument, createdViewerState);
1416
+ return createdViewerState;
1417
+ }
1418
+ function createMarkdownImageViewerState(ownerDocument) {
1419
+ const dialogElement = ownerDocument.createElement("dialog");
1420
+ const chromeElement = ownerDocument.createElement("div");
1421
+ const captionGroupElement = ownerDocument.createElement("div");
1422
+ const captionElement = ownerDocument.createElement("p");
1423
+ const hintElement = ownerDocument.createElement("p");
1424
+ const controlsElement = ownerDocument.createElement("div");
1425
+ const zoomOutButtonElement = createMarkdownImageViewerButton(ownerDocument, {
1426
+ ariaLabel: "\u7F29\u5C0F\u56FE\u7247",
1427
+ text: IMAGE_VIEWER_ZOOM_OUT_TEXT
1428
+ });
1429
+ const zoomValueElement = ownerDocument.createElement("span");
1430
+ const zoomInButtonElement = createMarkdownImageViewerButton(ownerDocument, {
1431
+ ariaLabel: "\u653E\u5927\u56FE\u7247",
1432
+ text: IMAGE_VIEWER_ZOOM_IN_TEXT
1433
+ });
1434
+ const resetButtonElement = createMarkdownImageViewerButton(ownerDocument, {
1435
+ ariaLabel: "\u91CD\u7F6E\u7F29\u653E",
1436
+ text: IMAGE_VIEWER_RESET_TEXT
1437
+ });
1438
+ const closeButtonElement = createMarkdownImageViewerButton(ownerDocument, {
1439
+ ariaLabel: "\u5173\u95ED\u5168\u56FE\u67E5\u770B",
1440
+ className: IMAGE_VIEWER_CLOSE_BUTTON_CLASS_NAME,
1441
+ text: IMAGE_VIEWER_CLOSE_TEXT
1442
+ });
1443
+ const viewportElement = ownerDocument.createElement("div");
1444
+ const imageElement = ownerDocument.createElement("img");
1445
+ const viewerState = {
1446
+ captionElement,
1447
+ closeButtonElement,
1448
+ dialogElement,
1449
+ dragStartClientX: 0,
1450
+ dragStartClientY: 0,
1451
+ dragStartScrollLeft: 0,
1452
+ dragStartScrollTop: 0,
1453
+ hintElement,
1454
+ imageElement,
1455
+ isDragging: false,
1456
+ naturalHeight: 1,
1457
+ naturalWidth: 1,
1458
+ resetButtonElement,
1459
+ viewportElement,
1460
+ zoomInButtonElement,
1461
+ zoomOutButtonElement,
1462
+ zoomValue: IMAGE_VIEWER_DEFAULT_ZOOM,
1463
+ zoomValueElement
1464
+ };
1465
+ const ownerWindow = ownerDocument.defaultView;
1466
+ dialogElement.className = IMAGE_VIEWER_DIALOG_CLASS_NAME;
1467
+ dialogElement.setAttribute("aria-modal", "true");
1468
+ dialogElement.setAttribute("tabindex", "-1");
1469
+ chromeElement.className = IMAGE_VIEWER_CHROME_CLASS_NAME;
1470
+ captionGroupElement.className = IMAGE_VIEWER_CAPTION_GROUP_CLASS_NAME;
1471
+ captionElement.className = IMAGE_VIEWER_CAPTION_CLASS_NAME;
1472
+ hintElement.className = IMAGE_VIEWER_HINT_CLASS_NAME;
1473
+ hintElement.textContent = IMAGE_VIEWER_HINT_TEXT;
1474
+ controlsElement.className = IMAGE_VIEWER_CONTROLS_CLASS_NAME;
1475
+ zoomValueElement.className = IMAGE_VIEWER_ZOOM_VALUE_CLASS_NAME;
1476
+ viewportElement.className = IMAGE_VIEWER_VIEWPORT_CLASS_NAME;
1477
+ imageElement.className = IMAGE_VIEWER_IMAGE_CLASS_NAME;
1478
+ imageElement.decoding = "async";
1479
+ imageViewerStateByDialogElement.set(dialogElement, viewerState);
1480
+ controlsElement.append(
1481
+ zoomOutButtonElement,
1482
+ zoomValueElement,
1483
+ zoomInButtonElement,
1484
+ resetButtonElement,
1485
+ closeButtonElement
1486
+ );
1487
+ captionGroupElement.append(captionElement, hintElement);
1488
+ chromeElement.append(captionGroupElement, controlsElement);
1489
+ viewportElement.append(imageElement);
1490
+ dialogElement.append(chromeElement, viewportElement);
1491
+ ownerDocument.body.append(dialogElement);
1492
+ dialogElement.addEventListener("click", handleMarkdownImageViewerBackdropClick);
1493
+ dialogElement.addEventListener("close", handleMarkdownImageViewerClose);
1494
+ dialogElement.addEventListener("keydown", handleMarkdownImageViewerKeyDown);
1495
+ viewportElement.addEventListener("wheel", handleMarkdownImageViewerWheel, { passive: false });
1496
+ viewportElement.addEventListener("pointerdown", handleMarkdownImageViewerPointerDown);
1497
+ viewportElement.addEventListener("pointermove", handleMarkdownImageViewerPointerMove);
1498
+ viewportElement.addEventListener("pointerup", handleMarkdownImageViewerPointerUp);
1499
+ viewportElement.addEventListener("pointercancel", handleMarkdownImageViewerPointerUp);
1500
+ imageElement.addEventListener("load", handleMarkdownImageViewerImageLoad);
1501
+ imageElement.addEventListener("dragstart", handleMarkdownImageViewerImageDragStart);
1502
+ zoomOutButtonElement.addEventListener("click", handleMarkdownImageViewerZoomOutClick);
1503
+ zoomInButtonElement.addEventListener("click", handleMarkdownImageViewerZoomInClick);
1504
+ resetButtonElement.addEventListener("click", handleMarkdownImageViewerResetClick);
1505
+ closeButtonElement.addEventListener("click", handleMarkdownImageViewerCloseClick);
1506
+ if (ownerWindow) {
1507
+ ownerWindow.addEventListener("resize", handleMarkdownImageViewerWindowResize);
1508
+ }
1509
+ updateMarkdownImageViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
1510
+ return viewerState;
1511
+ }
1512
+ function createMarkdownImageViewerButton(ownerDocument, options) {
1513
+ const buttonElement = ownerDocument.createElement("button");
1514
+ buttonElement.type = "button";
1515
+ buttonElement.className = options.className ? `${IMAGE_VIEWER_BUTTON_CLASS_NAME} ${options.className}` : IMAGE_VIEWER_BUTTON_CLASS_NAME;
1516
+ buttonElement.textContent = options.text;
1517
+ buttonElement.setAttribute("aria-label", options.ariaLabel);
1518
+ return buttonElement;
1519
+ }
1520
+ function showMarkdownImageViewerDialog(viewerState) {
1521
+ if (typeof viewerState.dialogElement.showModal === "function") {
1522
+ if (!viewerState.dialogElement.open) {
1523
+ viewerState.dialogElement.showModal();
1524
+ }
1525
+ } else {
1526
+ viewerState.dialogElement.setAttribute("open", "");
1527
+ }
1528
+ viewerState.dialogElement.focus();
1529
+ }
1530
+ function requestMarkdownImageViewerLayout(viewerState) {
1531
+ const ownerWindow = viewerState.dialogElement.ownerDocument.defaultView;
1532
+ if (!ownerWindow) {
1533
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue);
1534
+ return;
1535
+ }
1536
+ ownerWindow.setTimeout(handleMarkdownImageViewerDeferredLayout, 0, viewerState);
1537
+ }
1538
+ function handleMarkdownImageViewerDeferredLayout(viewerState) {
1539
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue);
1540
+ }
1541
+ function handleMarkdownImageViewerBackdropClick(event) {
1542
+ if (event.target !== event.currentTarget) {
1543
+ return;
1544
+ }
1545
+ const dialogElement = event.currentTarget;
1546
+ const viewerState = imageViewerStateByDialogElement.get(dialogElement);
1547
+ if (!viewerState) {
1548
+ return;
1549
+ }
1550
+ closeMarkdownImageViewer(viewerState);
1551
+ }
1552
+ function handleMarkdownImageViewerClose(event) {
1553
+ const dialogElement = event.currentTarget;
1554
+ const viewerState = imageViewerStateByDialogElement.get(dialogElement);
1555
+ if (!viewerState) {
1556
+ return;
1557
+ }
1558
+ resetMarkdownImageViewerAfterClose(viewerState);
1559
+ }
1560
+ function handleMarkdownImageViewerKeyDown(event) {
1561
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1562
+ if (!viewerState) {
1563
+ return;
1564
+ }
1565
+ if (event.key === "+" || event.key === "=") {
1566
+ event.preventDefault();
1567
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue + IMAGE_VIEWER_ZOOM_STEP);
1568
+ return;
1569
+ }
1570
+ if (event.key === "-") {
1571
+ event.preventDefault();
1572
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue - IMAGE_VIEWER_ZOOM_STEP);
1573
+ return;
1574
+ }
1575
+ if (event.key === "0") {
1576
+ event.preventDefault();
1577
+ updateMarkdownImageViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
1578
+ return;
1579
+ }
1580
+ if (event.key === "Escape") {
1581
+ event.preventDefault();
1582
+ closeMarkdownImageViewer(viewerState);
1583
+ }
1584
+ }
1585
+ function handleMarkdownImageViewerWheel(event) {
1586
+ if (!event.ctrlKey && !event.metaKey) {
1587
+ return;
1588
+ }
1589
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1590
+ if (!viewerState) {
1591
+ return;
1592
+ }
1593
+ const rawDelta = -event.deltaY * IMAGE_VIEWER_WHEEL_ZOOM_FACTOR;
1594
+ const zoomDelta = Math.max(
1595
+ -IMAGE_VIEWER_WHEEL_ZOOM_MAX_DELTA,
1596
+ Math.min(IMAGE_VIEWER_WHEEL_ZOOM_MAX_DELTA, rawDelta)
1597
+ );
1598
+ event.preventDefault();
1599
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue + zoomDelta, {
1600
+ x: event.clientX,
1601
+ y: event.clientY
1602
+ });
1603
+ }
1604
+ function handleMarkdownImageViewerPointerDown(event) {
1605
+ if (event.button !== 0) {
1606
+ return;
1607
+ }
1608
+ const viewportElement = event.currentTarget;
1609
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1610
+ if (!viewerState) {
1611
+ return;
1612
+ }
1613
+ const canScrollHorizontally = viewportElement.scrollWidth > viewportElement.clientWidth;
1614
+ const canScrollVertically = viewportElement.scrollHeight > viewportElement.clientHeight;
1615
+ if (!canScrollHorizontally && !canScrollVertically) {
1616
+ return;
1617
+ }
1618
+ viewerState.isDragging = true;
1619
+ viewerState.dragStartClientX = event.clientX;
1620
+ viewerState.dragStartClientY = event.clientY;
1621
+ viewerState.dragStartScrollLeft = viewportElement.scrollLeft;
1622
+ viewerState.dragStartScrollTop = viewportElement.scrollTop;
1623
+ viewerState.dialogElement.classList.add(IMAGE_VIEWER_DRAGGING_CLASS_NAME);
1624
+ if (typeof viewportElement.setPointerCapture === "function") {
1625
+ viewportElement.setPointerCapture(event.pointerId);
1626
+ }
1627
+ event.preventDefault();
1628
+ }
1629
+ function handleMarkdownImageViewerPointerMove(event) {
1630
+ const viewportElement = event.currentTarget;
1631
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1632
+ if (!viewerState || !viewerState.isDragging) {
1633
+ return;
1634
+ }
1635
+ const deltaX = event.clientX - viewerState.dragStartClientX;
1636
+ const deltaY = event.clientY - viewerState.dragStartClientY;
1637
+ viewportElement.scrollLeft = viewerState.dragStartScrollLeft - deltaX;
1638
+ viewportElement.scrollTop = viewerState.dragStartScrollTop - deltaY;
1639
+ }
1640
+ function handleMarkdownImageViewerPointerUp(event) {
1641
+ const viewportElement = event.currentTarget;
1642
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1643
+ if (!viewerState || !viewerState.isDragging) {
1644
+ return;
1645
+ }
1646
+ viewerState.isDragging = false;
1647
+ viewerState.dialogElement.classList.remove(IMAGE_VIEWER_DRAGGING_CLASS_NAME);
1648
+ if (typeof viewportElement.releasePointerCapture === "function" && viewportElement.hasPointerCapture(event.pointerId)) {
1649
+ viewportElement.releasePointerCapture(event.pointerId);
1650
+ }
1651
+ }
1652
+ function handleMarkdownImageViewerImageDragStart(event) {
1653
+ event.preventDefault();
1654
+ }
1655
+ function handleMarkdownImageViewerImageLoad(event) {
1656
+ const imageElement = event.currentTarget;
1657
+ const dialogElement = imageElement.closest(
1658
+ `.${IMAGE_VIEWER_DIALOG_CLASS_NAME}`
1659
+ );
1660
+ if (!dialogElement) {
1661
+ return;
1662
+ }
1663
+ const viewerState = imageViewerStateByDialogElement.get(dialogElement);
1664
+ if (!viewerState) {
1665
+ return;
1666
+ }
1667
+ viewerState.naturalWidth = imageElement.naturalWidth || viewerState.naturalWidth;
1668
+ viewerState.naturalHeight = imageElement.naturalHeight || viewerState.naturalHeight;
1669
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue);
1670
+ }
1671
+ function handleMarkdownImageViewerWindowResize(event) {
1672
+ const ownerWindow = event.currentTarget;
1673
+ const viewerState = imageViewerStateByDocument.get(ownerWindow.document);
1674
+ if (!viewerState || !isMarkdownImageViewerOpen(viewerState)) {
1675
+ return;
1676
+ }
1677
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue);
1678
+ }
1679
+ function handleMarkdownImageViewerZoomOutClick(event) {
1680
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1681
+ if (!viewerState) {
1682
+ return;
1683
+ }
1684
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue - IMAGE_VIEWER_ZOOM_STEP);
1685
+ }
1686
+ function handleMarkdownImageViewerZoomInClick(event) {
1687
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1688
+ if (!viewerState) {
1689
+ return;
1690
+ }
1691
+ updateMarkdownImageViewerZoom(viewerState, viewerState.zoomValue + IMAGE_VIEWER_ZOOM_STEP);
1692
+ }
1693
+ function handleMarkdownImageViewerResetClick(event) {
1694
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1695
+ if (!viewerState) {
1696
+ return;
1697
+ }
1698
+ updateMarkdownImageViewerZoom(viewerState, IMAGE_VIEWER_DEFAULT_ZOOM);
1699
+ }
1700
+ function handleMarkdownImageViewerCloseClick(event) {
1701
+ const viewerState = getMarkdownImageViewerStateFromEvent(event);
1702
+ if (!viewerState) {
1703
+ return;
1704
+ }
1705
+ closeMarkdownImageViewer(viewerState);
1706
+ }
1707
+ function getMarkdownImageViewerStateFromEvent(event) {
1708
+ const currentTargetElement = event.currentTarget;
1709
+ if (!currentTargetElement) {
1710
+ return void 0;
1711
+ }
1712
+ const dialogElement = currentTargetElement.closest(
1713
+ `.${IMAGE_VIEWER_DIALOG_CLASS_NAME}`
1714
+ );
1715
+ if (!dialogElement) {
1716
+ return void 0;
1717
+ }
1718
+ return imageViewerStateByDialogElement.get(dialogElement);
1719
+ }
1720
+ function updateMarkdownImageViewerZoom(viewerState, nextZoom, zoomAnchor) {
1721
+ const normalizedZoom = clampMarkdownImageViewerZoom(nextZoom);
1722
+ const focalPoint = isMarkdownImageViewerOpen(viewerState) ? captureMarkdownImageViewerFocalPoint(viewerState, zoomAnchor) : void 0;
1723
+ viewerState.zoomValue = normalizedZoom;
1724
+ viewerState.dialogElement.classList.toggle(
1725
+ IMAGE_VIEWER_ZOOMED_CLASS_NAME,
1726
+ normalizedZoom > IMAGE_VIEWER_DEFAULT_ZOOM
1727
+ );
1728
+ viewerState.zoomValueElement.textContent = `${Math.round(normalizedZoom * 100)}%`;
1729
+ viewerState.zoomOutButtonElement.disabled = normalizedZoom <= IMAGE_VIEWER_MIN_ZOOM;
1730
+ viewerState.zoomInButtonElement.disabled = normalizedZoom >= IMAGE_VIEWER_MAX_ZOOM;
1731
+ viewerState.resetButtonElement.disabled = normalizedZoom === IMAGE_VIEWER_DEFAULT_ZOOM;
1732
+ updateMarkdownImageViewerImageSize(viewerState);
1733
+ if (focalPoint) {
1734
+ applyMarkdownImageViewerFocalPoint(viewerState, focalPoint);
1735
+ }
1736
+ }
1737
+ function captureMarkdownImageViewerFocalPoint(viewerState, zoomAnchor) {
1738
+ const preImageRect = viewerState.imageElement.getBoundingClientRect();
1739
+ if (preImageRect.width <= 0 || preImageRect.height <= 0) {
1740
+ return void 0;
1741
+ }
1742
+ const viewportRect = viewerState.viewportElement.getBoundingClientRect();
1743
+ const anchorClientX = zoomAnchor?.x ?? viewportRect.left + viewportRect.width / 2;
1744
+ const anchorClientY = zoomAnchor?.y ?? viewportRect.top + viewportRect.height / 2;
1745
+ const normalizedX = (anchorClientX - preImageRect.left) / preImageRect.width;
1746
+ const normalizedY = (anchorClientY - preImageRect.top) / preImageRect.height;
1747
+ return {
1748
+ anchorClientX,
1749
+ anchorClientY,
1750
+ normalizedX,
1751
+ normalizedY
1752
+ };
1753
+ }
1754
+ function applyMarkdownImageViewerFocalPoint(viewerState, focalPoint) {
1755
+ viewerState.viewportElement.scrollLeft = 0;
1756
+ viewerState.viewportElement.scrollTop = 0;
1757
+ const postImageRect = viewerState.imageElement.getBoundingClientRect();
1758
+ if (postImageRect.width <= 0 || postImageRect.height <= 0) {
1759
+ return;
1760
+ }
1761
+ const targetImageClientLeft = focalPoint.anchorClientX - focalPoint.normalizedX * postImageRect.width;
1762
+ const targetImageClientTop = focalPoint.anchorClientY - focalPoint.normalizedY * postImageRect.height;
1763
+ viewerState.viewportElement.scrollLeft = postImageRect.left - targetImageClientLeft;
1764
+ viewerState.viewportElement.scrollTop = postImageRect.top - targetImageClientTop;
1765
+ }
1766
+ function updateMarkdownImageViewerImageSize(viewerState) {
1767
+ const viewportWidth = Math.max(
1768
+ viewerState.viewportElement.clientWidth * IMAGE_VIEWER_FIT_RATIO,
1769
+ 1
1770
+ );
1771
+ const viewportHeight = Math.max(
1772
+ viewerState.viewportElement.clientHeight * IMAGE_VIEWER_FIT_RATIO,
1773
+ 1
1774
+ );
1775
+ const naturalWidth = Math.max(viewerState.naturalWidth, 1);
1776
+ const naturalHeight = Math.max(viewerState.naturalHeight, 1);
1777
+ const fitScale = Math.min(1, viewportWidth / naturalWidth, viewportHeight / naturalHeight);
1778
+ const displayWidth = Math.max(1, Math.round(naturalWidth * fitScale * viewerState.zoomValue));
1779
+ const displayHeight = Math.max(1, Math.round(naturalHeight * fitScale * viewerState.zoomValue));
1780
+ viewerState.imageElement.style.width = `${displayWidth}px`;
1781
+ viewerState.imageElement.style.height = `${displayHeight}px`;
1782
+ }
1783
+ function clampMarkdownImageViewerZoom(zoomValue) {
1784
+ return Math.min(IMAGE_VIEWER_MAX_ZOOM, Math.max(IMAGE_VIEWER_MIN_ZOOM, zoomValue));
1785
+ }
1786
+ function closeMarkdownImageViewer(viewerState) {
1787
+ if (typeof viewerState.dialogElement.close === "function" && viewerState.dialogElement.open) {
1788
+ viewerState.dialogElement.close();
1789
+ return;
1790
+ }
1791
+ viewerState.dialogElement.removeAttribute("open");
1792
+ resetMarkdownImageViewerAfterClose(viewerState);
1793
+ }
1794
+ function resetMarkdownImageViewerAfterClose(viewerState) {
1795
+ viewerState.dialogElement.classList.remove(IMAGE_VIEWER_ZOOMED_CLASS_NAME);
1796
+ viewerState.dialogElement.classList.remove(IMAGE_VIEWER_DRAGGING_CLASS_NAME);
1797
+ viewerState.isDragging = false;
1798
+ viewerState.imageElement.removeAttribute("src");
1799
+ viewerState.imageElement.removeAttribute("alt");
1800
+ viewerState.imageElement.removeAttribute("style");
1801
+ viewerState.captionElement.textContent = "";
1802
+ viewerState.viewportElement.scrollLeft = 0;
1803
+ viewerState.viewportElement.scrollTop = 0;
1804
+ viewerState.zoomValue = IMAGE_VIEWER_DEFAULT_ZOOM;
1805
+ if (viewerState.sourceImageElement?.isConnected) {
1806
+ viewerState.sourceImageElement.focus();
1807
+ }
1808
+ }
1809
+ function isMarkdownImageViewerOpen(viewerState) {
1810
+ return viewerState.dialogElement.open || viewerState.dialogElement.hasAttribute("open");
1811
+ }
1812
+ function getMarkdownImageSource(imageElement) {
1813
+ return imageElement.currentSrc || imageElement.getAttribute("src") || imageElement.src || "";
1814
+ }
1815
+ function createMarkdownImageViewerAriaLabel(imageElement) {
1816
+ const imageAltText = imageElement.alt.trim();
1817
+ if (imageAltText.length === 0) {
1818
+ return IMAGE_VIEWER_FALLBACK_LABEL;
1819
+ }
1820
+ return `${IMAGE_VIEWER_FALLBACK_LABEL}\uFF1A${imageAltText}`;
1821
+ }
1822
+ function createMarkdownImageViewerCaption(imageElement, imageSource) {
1823
+ const imageAltText = imageElement.alt.trim();
1824
+ const imageTitleText = imageElement.title.trim();
1825
+ return imageAltText || imageTitleText || imageSource;
1826
+ }
1827
+ function createScribdownSanitizeSchema() {
1828
+ const defaultTagNames = defaultSchema.tagNames ?? [];
1829
+ const defaultAttributes = defaultSchema.attributes ?? {};
1830
+ const detailsAttributes = [
1831
+ "open",
1832
+ ["className", DETAILS_CLASS_NAME, FRAME_CLASS_NAME, TOC_CLASS_NAME]
1833
+ ];
1834
+ const summaryAttributes = [
1835
+ ...defaultAttributes.summary ?? [],
1836
+ ["className", TOC_SUMMARY_CLASS_NAME]
1837
+ ];
1838
+ const navAttributes = ["ariaLabel", ["className", /^scribdown-toc-nav$/u]];
1839
+ const linkAttributes = [
1840
+ "ariaDescribedBy",
1841
+ "ariaLabel",
1842
+ "ariaLabelledBy",
1843
+ "dataFootnoteBackref",
1844
+ "dataFootnoteRef",
1845
+ ["className", "data-footnote-backref", TOC_LINK_CLASS_NAME],
1846
+ "href"
1847
+ ];
1848
+ const buttonAttributes = [
1849
+ "type",
1850
+ "ariaExpanded",
1851
+ "ariaLabel",
1852
+ ["className", TOC_TOGGLE_CLASS_NAME]
1853
+ ];
1854
+ const orderedListAttributes = [
1855
+ "ariaDescribedBy",
1856
+ "ariaLabel",
1857
+ "ariaLabelledBy",
1858
+ ["className", "contains-task-list", /^scribdown-toc-list(?:--nested)?$/u]
1859
+ ];
1860
+ const spanAttributes = [
1861
+ ...defaultAttributes.span ?? [],
1862
+ [
1863
+ "className",
1864
+ HEADING_MARK_CLASS_NAME,
1865
+ IMAGE_FRAME_CLASS_NAME,
1866
+ IMAGE_FALLBACK_CLASS_NAME,
1867
+ IMAGE_FALLBACK_ICON_CLASS_NAME,
1868
+ IMAGE_FALLBACK_TEXT_CLASS_NAME,
1869
+ IMAGE_FALLBACK_SOURCE_CLASS_NAME,
1870
+ VIDEO_FRAME_CLASS_NAME,
1871
+ VIDEO_FALLBACK_CLASS_NAME,
1872
+ VIDEO_FALLBACK_ICON_CLASS_NAME,
1873
+ VIDEO_FALLBACK_TEXT_CLASS_NAME,
1874
+ VIDEO_FALLBACK_SOURCE_CLASS_NAME
1875
+ ]
1876
+ ];
1877
+ const figureAttributes = [
1878
+ ["className", IMAGE_FIGURE_CLASS_NAME, VIDEO_FIGURE_CLASS_NAME]
1879
+ ];
1880
+ const figcaptionAttributes = [["className", IMAGE_CAPTION_CLASS_NAME]];
1881
+ const imageAttributes = [
1882
+ ...defaultAttributes.img ?? [],
1883
+ ["className", IMAGE_ELEMENT_CLASS_NAME]
1884
+ ];
1885
+ const videoAttributes = [
1886
+ "src",
1887
+ "controls",
1888
+ "controlsList",
1889
+ "width",
1890
+ "height",
1891
+ "poster",
1892
+ "preload",
1893
+ "playsInline",
1894
+ "muted",
1895
+ "loop",
1896
+ "autoPlay",
1897
+ "crossOrigin",
1898
+ ["className", VIDEO_ELEMENT_CLASS_NAME]
1899
+ ];
1900
+ const sourceAttributes = ["src", "type", "media"];
1901
+ const abbrAttributes = ["title"];
1902
+ const listItemAttributes = [
1903
+ "dataTocIndex",
1904
+ ["className", "task-list-item", /^scribdown-toc-item(?:--(?:branch|depth-[1-6]))?$/u]
1905
+ ];
1906
+ const wildcardAttributes = [...defaultAttributes["*"] ?? [], SOURCE_LINE_HAST_PROPERTY];
1907
+ return {
1908
+ ...defaultSchema,
1909
+ clobberPrefix: "",
1910
+ tagNames: Array.from(
1911
+ /* @__PURE__ */ new Set([
1912
+ ...defaultTagNames,
1913
+ "button",
1914
+ "details",
1915
+ "nav",
1916
+ "summary",
1917
+ "u",
1918
+ "mark",
1919
+ "sub",
1920
+ "sup",
1921
+ "kbd",
1922
+ "abbr",
1923
+ "small",
1924
+ "dl",
1925
+ "dt",
1926
+ "dd",
1927
+ "figure",
1928
+ "figcaption",
1929
+ "video",
1930
+ "source"
1931
+ ])
1932
+ ),
1933
+ attributes: {
1934
+ ...defaultAttributes,
1935
+ "*": wildcardAttributes,
1936
+ button: buttonAttributes,
1937
+ details: detailsAttributes,
1938
+ summary: summaryAttributes,
1939
+ nav: navAttributes,
1940
+ a: linkAttributes,
1941
+ ol: orderedListAttributes,
1942
+ li: listItemAttributes,
1943
+ span: spanAttributes,
1944
+ abbr: abbrAttributes,
1945
+ figure: figureAttributes,
1946
+ figcaption: figcaptionAttributes,
1947
+ img: imageAttributes,
1948
+ video: videoAttributes,
1949
+ source: sourceAttributes
1950
+ }
1951
+ };
1952
+ }
1953
+ function remarkHighlightMark() {
1954
+ return (tree) => {
1955
+ transformHighlightMarks(tree);
1956
+ };
1957
+ }
1958
+ var BLOCK_CONTAINER_NODE_TYPES = /* @__PURE__ */ new Set(["list", "listItem", "blockquote"]);
1959
+ function remarkSourceLine() {
1960
+ return (tree) => {
1961
+ const blockNodes = tree.children ?? [];
1962
+ blockNodes.forEach((blockNode) => {
1963
+ annotateSourceLine(blockNode);
1964
+ annotateNestedBlockSourceLines(blockNode);
1965
+ });
1966
+ };
1967
+ }
1968
+ function annotateSourceLine(node) {
1969
+ const startLine = node.position?.start.line;
1970
+ if (typeof startLine !== "number") {
1971
+ return;
1972
+ }
1973
+ const nodeData = node.data ?? {};
1974
+ const hProperties = nodeData.hProperties ?? {};
1975
+ hProperties[SOURCE_LINE_HAST_PROPERTY] = startLine;
1976
+ nodeData.hProperties = hProperties;
1977
+ node.data = nodeData;
1978
+ }
1979
+ function annotateNestedBlockSourceLines(node) {
1980
+ if (!BLOCK_CONTAINER_NODE_TYPES.has(node.type)) {
1981
+ return;
1982
+ }
1983
+ const childNodes = node.children ?? [];
1984
+ childNodes.forEach((childNode) => {
1985
+ annotateSourceLine(childNode);
1986
+ annotateNestedBlockSourceLines(childNode);
1987
+ });
1988
+ }
1989
+ function transformHighlightMarks(node) {
1990
+ if (!node.children) {
1991
+ return;
1992
+ }
1993
+ const nextChildren = [];
1994
+ for (let childIndex = 0; childIndex < node.children.length; childIndex += 1) {
1995
+ const childNode = node.children[childIndex];
1996
+ if (childNode.type === "text" && typeof childNode.value === "string" && childNode.value.includes("==")) {
1997
+ nextChildren.push(...splitHighlightMarks(childNode.value));
1998
+ continue;
1999
+ }
2000
+ transformHighlightMarks(childNode);
2001
+ nextChildren.push(childNode);
2002
+ }
2003
+ node.children = nextChildren;
2004
+ }
2005
+ function splitHighlightMarks(textValue) {
2006
+ const segments = [];
2007
+ let cursor = 0;
2008
+ HIGHLIGHT_MARKER_PATTERN.lastIndex = 0;
2009
+ let match = HIGHLIGHT_MARKER_PATTERN.exec(textValue);
2010
+ while (match !== null) {
2011
+ const leadingText = textValue.slice(cursor, match.index);
2012
+ if (leadingText.length > 0) {
2013
+ segments.push({ type: "text", value: leadingText });
2014
+ }
2015
+ segments.push({
2016
+ type: "highlightMark",
2017
+ data: {
2018
+ hName: "mark"
2019
+ },
2020
+ children: [{ type: "text", value: match[1] }]
2021
+ });
2022
+ cursor = match.index + match[0].length;
2023
+ match = HIGHLIGHT_MARKER_PATTERN.exec(textValue);
2024
+ }
2025
+ const trailingText = textValue.slice(cursor);
2026
+ if (trailingText.length > 0) {
2027
+ segments.push({ type: "text", value: trailingText });
2028
+ }
2029
+ return segments;
2030
+ }
2031
+ function remarkDefinitionLists() {
2032
+ return (tree) => {
2033
+ transformDefinitionLists(tree);
2034
+ };
2035
+ }
2036
+ function transformDefinitionLists(node) {
2037
+ if (!node.children) {
2038
+ return;
2039
+ }
2040
+ const childNodes = node.children;
2041
+ for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2042
+ const childNode = childNodes[childIndex];
2043
+ const definitionListNode = createDefinitionListNode(childNode);
2044
+ if (definitionListNode) {
2045
+ childNodes[childIndex] = definitionListNode;
2046
+ continue;
2047
+ }
2048
+ transformDefinitionLists(childNode);
2049
+ }
2050
+ }
2051
+ function createDefinitionListNode(node) {
2052
+ if (node.type !== "paragraph") {
2053
+ return void 0;
2054
+ }
2055
+ const paragraphText = extractNodeText(node);
2056
+ const definitionMatch = DEFINITION_LIST_PARAGRAPH_PATTERN.exec(paragraphText);
2057
+ if (!definitionMatch) {
2058
+ return void 0;
2059
+ }
2060
+ const definitionTerm = definitionMatch[1].trim();
2061
+ const definitionDescription = definitionMatch[2].trim();
2062
+ if (definitionTerm.length === 0 || definitionDescription.length === 0) {
2063
+ return void 0;
2064
+ }
2065
+ return {
2066
+ type: "definitionList",
2067
+ // 关键步骤:保留原段落源码位置,使 remarkSourceLine 能为 dl 标注 data-source-line。
2068
+ position: node.position,
2069
+ data: {
2070
+ hName: "dl"
2071
+ },
2072
+ children: [
2073
+ {
2074
+ type: "definitionTerm",
2075
+ data: {
2076
+ hName: "dt"
2077
+ },
2078
+ children: [{ type: "text", value: definitionTerm }]
2079
+ },
2080
+ {
2081
+ type: "definitionDescription",
2082
+ data: {
2083
+ hName: "dd"
2084
+ },
2085
+ children: [{ type: "text", value: definitionDescription }]
2086
+ }
2087
+ ]
2088
+ };
2089
+ }
2090
+ function remarkImageFigures() {
2091
+ return (tree) => {
2092
+ transformImageFigures(tree);
2093
+ };
2094
+ }
2095
+ function transformImageFigures(node) {
2096
+ if (!node.children) {
2097
+ return;
2098
+ }
2099
+ const childNodes = node.children;
2100
+ for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2101
+ const childNode = childNodes[childIndex];
2102
+ const imageFigureNode = createImageFigureNode(childNode);
2103
+ if (imageFigureNode) {
2104
+ childNodes[childIndex] = imageFigureNode;
2105
+ continue;
2106
+ }
2107
+ if (isImageNode(childNode)) {
2108
+ decorateImageNode(childNode);
2109
+ continue;
2110
+ }
2111
+ transformImageFigures(childNode);
2112
+ }
2113
+ }
2114
+ function createImageFigureNode(node) {
2115
+ if (node.type !== "paragraph" || !node.children || node.children.length !== 1) {
2116
+ return void 0;
2117
+ }
2118
+ const onlyChild = node.children[0];
2119
+ if (!isImageNode(onlyChild)) {
2120
+ return void 0;
2121
+ }
2122
+ decorateImageNode(onlyChild);
2123
+ return {
2124
+ type: "imageFigure",
2125
+ // 关键步骤:保留原段落源码位置,使 remarkSourceLine 能为 figure 标注 data-source-line。
2126
+ position: node.position,
2127
+ data: {
2128
+ hName: "figure",
2129
+ hProperties: {
2130
+ className: [IMAGE_FIGURE_CLASS_NAME]
2131
+ }
2132
+ },
2133
+ children: [createImageFrameNode(onlyChild), ...createImageCaptionNodes(onlyChild)]
2134
+ };
2135
+ }
2136
+ function decorateImageNode(imageNode) {
2137
+ imageNode.data = {
2138
+ ...imageNode.data,
2139
+ hProperties: {
2140
+ ...imageNode.data?.hProperties,
2141
+ className: [IMAGE_ELEMENT_CLASS_NAME]
2142
+ }
2143
+ };
2144
+ }
2145
+ function createImageFrameNode(imageNode) {
2146
+ return {
2147
+ type: "imageFrame",
2148
+ data: {
2149
+ hName: "span",
2150
+ hProperties: {
2151
+ className: [IMAGE_FRAME_CLASS_NAME]
2152
+ }
2153
+ },
2154
+ children: [imageNode, createImageFallbackNode(imageNode)]
2155
+ };
2156
+ }
2157
+ function createImageCaptionNodes(imageNode) {
2158
+ const imageTitle = typeof imageNode.title === "string" ? imageNode.title.trim() : "";
2159
+ if (imageTitle.length === 0) {
2160
+ return [];
2161
+ }
2162
+ return [
2163
+ {
2164
+ type: "imageCaption",
2165
+ data: {
2166
+ hName: "figcaption",
2167
+ hProperties: {
2168
+ className: [IMAGE_CAPTION_CLASS_NAME]
2169
+ }
2170
+ },
2171
+ children: [{ type: "text", value: imageTitle }]
2172
+ }
2173
+ ];
2174
+ }
2175
+ function createImageFallbackNode(imageNode) {
2176
+ const fallbackText = imageNode.alt?.trim() || "\u56FE\u7247\u52A0\u8F7D\u5931\u8D25";
2177
+ const fallbackSource = imageNode.url ?? imageNode.identifier ?? "";
2178
+ return {
2179
+ type: "imageFallback",
2180
+ data: {
2181
+ hName: "span",
2182
+ hProperties: {
2183
+ className: [IMAGE_FALLBACK_CLASS_NAME]
2184
+ }
2185
+ },
2186
+ children: [
2187
+ {
2188
+ type: "imageFallbackIcon",
2189
+ data: {
2190
+ hName: "span",
2191
+ hProperties: {
2192
+ className: [IMAGE_FALLBACK_ICON_CLASS_NAME]
2193
+ }
2194
+ }
2195
+ },
2196
+ {
2197
+ type: "imageFallbackText",
2198
+ data: {
2199
+ hName: "span",
2200
+ hProperties: {
2201
+ className: [IMAGE_FALLBACK_TEXT_CLASS_NAME]
2202
+ }
2203
+ },
2204
+ children: [{ type: "text", value: fallbackText }]
2205
+ },
2206
+ {
2207
+ type: "imageFallbackSource",
2208
+ data: {
2209
+ hName: "span",
2210
+ hProperties: {
2211
+ className: [IMAGE_FALLBACK_SOURCE_CLASS_NAME]
2212
+ }
2213
+ },
2214
+ children: [{ type: "text", value: fallbackSource }]
2215
+ }
2216
+ ]
2217
+ };
2218
+ }
2219
+ function isImageNode(node) {
2220
+ return node.type === "image" || node.type === "imageReference";
2221
+ }
2222
+ function remarkTableOfContents() {
2223
+ return (tree) => {
2224
+ const tocHeadings = collectTocHeadings(tree);
2225
+ replaceTocMarkers(tree, tocHeadings);
2226
+ };
2227
+ }
2228
+ function collectTocHeadings(tree) {
2229
+ const headingSlugCounts = /* @__PURE__ */ new Map();
2230
+ const tocHeadings = [];
2231
+ const headingIndexCounts = Array.from({ length: 7 }, () => 0);
2232
+ let rootHeadingDepth;
2233
+ visitMarkdownNode(tree, (node) => {
2234
+ if (node.type !== "heading") {
2235
+ return;
2236
+ }
2237
+ const headingDepth = node.depth ?? 2;
2238
+ const headingText = extractNodeText(node).trim();
2239
+ const headingId = createUniqueSlug(headingText, headingSlugCounts, tocHeadings.length + 1);
2240
+ const headingIndex = createHeadingIndex(
2241
+ headingDepth,
2242
+ headingIndexCounts,
2243
+ rootHeadingDepth ?? headingDepth
2244
+ );
2245
+ rootHeadingDepth = rootHeadingDepth ?? headingDepth;
2246
+ node.data = {
2247
+ ...node.data,
2248
+ hProperties: {
2249
+ ...node.data?.hProperties,
2250
+ id: headingId
2251
+ }
2252
+ };
2253
+ node.children = [
2254
+ {
2255
+ type: "headingMark",
2256
+ data: {
2257
+ hName: "span",
2258
+ hProperties: {
2259
+ className: [HEADING_MARK_CLASS_NAME]
2260
+ }
2261
+ },
2262
+ children: node.children ?? []
2263
+ }
2264
+ ];
2265
+ tocHeadings.push({
2266
+ depth: headingDepth,
2267
+ id: headingId,
2268
+ index: headingIndex,
2269
+ text: headingText || headingId
2270
+ });
2271
+ });
2272
+ return tocHeadings;
2273
+ }
2274
+ function visitMarkdownNode(node, visitor) {
2275
+ visitor(node);
2276
+ if (!node.children) {
2277
+ return;
2278
+ }
2279
+ const childNodes = node.children;
2280
+ for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2281
+ visitMarkdownNode(childNodes[childIndex], visitor);
2282
+ }
2283
+ }
2284
+ function replaceTocMarkers(node, tocHeadings) {
2285
+ if (!node.children) {
2286
+ return;
2287
+ }
2288
+ const childNodes = node.children;
2289
+ for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2290
+ const childNode = childNodes[childIndex];
2291
+ if (isTocMarkerParagraph(childNode)) {
2292
+ childNodes[childIndex] = createTocNode(tocHeadings, childNode);
2293
+ continue;
2294
+ }
2295
+ replaceTocMarkers(childNode, tocHeadings);
2296
+ }
2297
+ }
2298
+ function isTocMarkerParagraph(node) {
2299
+ if (node.type !== "paragraph" || !node.children || node.children.length !== 1) {
2300
+ return false;
2301
+ }
2302
+ const onlyChild = node.children[0];
2303
+ return onlyChild.type === "text" && TOC_MARKER_PATTERN.test(onlyChild.value ?? "");
2304
+ }
2305
+ function createTocNode(tocHeadings, markerNode) {
2306
+ const tocTree = createTocTree(tocHeadings);
2307
+ return {
2308
+ type: "toc",
2309
+ // 关键步骤:保留 [TOC] 标记段落的源码位置,使 remarkSourceLine 能为目录标注 data-source-line。
2310
+ position: markerNode.position,
2311
+ data: {
2312
+ hName: "details",
2313
+ hProperties: {
2314
+ // 关键步骤:目录根 opt-in 手绘边框,再由 toc.css 叠加目录自身样式。
2315
+ className: [TOC_CLASS_NAME, FRAME_CLASS_NAME]
2316
+ }
2317
+ },
2318
+ children: [
2319
+ {
2320
+ type: "tocSummary",
2321
+ data: {
2322
+ hName: "summary",
2323
+ hProperties: {
2324
+ className: [TOC_SUMMARY_CLASS_NAME]
2325
+ }
2326
+ },
2327
+ children: [
2328
+ {
2329
+ type: "text",
2330
+ value: TOC_SUMMARY_TEXT
2331
+ }
2332
+ ]
2333
+ },
2334
+ {
2335
+ type: "tocNav",
2336
+ data: {
2337
+ hName: "nav",
2338
+ hProperties: {
2339
+ ariaLabel: TOC_ARIA_LABEL,
2340
+ className: [TOC_NAV_CLASS_NAME]
2341
+ }
2342
+ },
2343
+ children: [createTocListNode(tocTree, false)]
2344
+ }
2345
+ ]
2346
+ };
2347
+ }
2348
+ function createTocTree(tocHeadings) {
2349
+ const rootItems = [];
2350
+ const itemStack = [];
2351
+ for (let headingIndex = 0; headingIndex < tocHeadings.length; headingIndex += 1) {
2352
+ const tocHeading = tocHeadings[headingIndex];
2353
+ const tocTreeItem = {
2354
+ ...tocHeading,
2355
+ children: []
2356
+ };
2357
+ while (itemStack.length > 0 && itemStack[itemStack.length - 1].depth >= tocTreeItem.depth) {
2358
+ itemStack.pop();
2359
+ }
2360
+ if (itemStack.length === 0) {
2361
+ rootItems.push(tocTreeItem);
2362
+ } else {
2363
+ itemStack[itemStack.length - 1].children.push(tocTreeItem);
2364
+ }
2365
+ itemStack.push(tocTreeItem);
2366
+ }
2367
+ return rootItems;
2368
+ }
2369
+ function createTocListNode(tocItems, isNested) {
2370
+ const tocListClassNames = isNested ? [TOC_LIST_CLASS_NAME, TOC_LIST_NESTED_CLASS_NAME] : [TOC_LIST_CLASS_NAME];
2371
+ return {
2372
+ type: "list",
2373
+ ordered: true,
2374
+ spread: false,
2375
+ data: {
2376
+ hProperties: {
2377
+ className: tocListClassNames
2378
+ }
2379
+ },
2380
+ children: tocItems.map(createTocListItem)
2381
+ };
2382
+ }
2383
+ function createTocListItem(tocItem) {
2384
+ const hasChildren = tocItem.children.length > 0;
2385
+ const tocItemClassNames = createTocItemClassNames(tocItem.depth, hasChildren);
2386
+ const itemChildren = hasChildren ? [createTocToggleNode(), createTocLinkNode(tocItem), createTocListNode(tocItem.children, true)] : [createTocLinkNode(tocItem)];
2387
+ return {
2388
+ type: "listItem",
2389
+ spread: false,
2390
+ data: {
2391
+ hProperties: {
2392
+ dataTocIndex: tocItem.index,
2393
+ className: tocItemClassNames
2394
+ }
2395
+ },
2396
+ children: itemChildren
2397
+ };
2398
+ }
2399
+ function createTocItemClassNames(depth, hasChildren) {
2400
+ return [
2401
+ TOC_ITEM_CLASS_PREFIX,
2402
+ `${TOC_ITEM_CLASS_PREFIX}--depth-${depth}`,
2403
+ ...hasChildren ? [TOC_ITEM_BRANCH_CLASS_NAME] : []
2404
+ ];
2405
+ }
2406
+ function createTocToggleNode() {
2407
+ return {
2408
+ type: "tocToggle",
2409
+ data: {
2410
+ hName: "button",
2411
+ hProperties: {
2412
+ type: "button",
2413
+ className: [TOC_TOGGLE_CLASS_NAME],
2414
+ ariaExpanded: "true",
2415
+ ariaLabel: TOC_TOGGLE_ARIA_LABEL
2416
+ }
2417
+ },
2418
+ children: []
2419
+ };
2420
+ }
2421
+ function createTocLinkNode(tocItem) {
2422
+ return {
2423
+ type: "link",
2424
+ url: `#${tocItem.id}`,
2425
+ data: {
2426
+ hProperties: {
2427
+ className: [TOC_LINK_CLASS_NAME]
2428
+ }
2429
+ },
2430
+ children: [
2431
+ {
2432
+ type: "text",
2433
+ value: tocItem.text
2434
+ }
2435
+ ]
2436
+ };
2437
+ }
2438
+ function collectTocHeadingsFromDom(rootElement) {
2439
+ const markdownContainer = rootElement instanceof Element && rootElement.matches(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`) ? rootElement : rootElement.querySelector(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`);
2440
+ if (!markdownContainer) {
2441
+ return [];
2442
+ }
2443
+ const headingElements = Array.from(
2444
+ markdownContainer.querySelectorAll(
2445
+ "h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]"
2446
+ )
2447
+ );
2448
+ const tocHeadings = [];
2449
+ const headingIndexCounts = Array.from({ length: 7 }, () => 0);
2450
+ let rootHeadingDepth;
2451
+ for (const headingElement of headingElements) {
2452
+ const headingDepth = parseInt(headingElement.tagName.slice(1), 10);
2453
+ if (!Number.isFinite(headingDepth)) {
2454
+ continue;
2455
+ }
2456
+ const headingText = headingElement.textContent?.trim() ?? "";
2457
+ const headingId = headingElement.id;
2458
+ const headingIndex = createHeadingIndex(
2459
+ headingDepth,
2460
+ headingIndexCounts,
2461
+ rootHeadingDepth ?? headingDepth
2462
+ );
2463
+ rootHeadingDepth = rootHeadingDepth ?? headingDepth;
2464
+ tocHeadings.push({
2465
+ depth: headingDepth,
2466
+ id: headingId,
2467
+ index: headingIndex,
2468
+ text: headingText || headingId
2469
+ });
2470
+ }
2471
+ return tocHeadings;
2472
+ }
2473
+ function createTocNavElement(ownerDocument, tocTreeItems) {
2474
+ const navElement = ownerDocument.createElement("nav");
2475
+ navElement.setAttribute("aria-label", TOC_ARIA_LABEL);
2476
+ navElement.className = TOC_NAV_CLASS_NAME;
2477
+ navElement.appendChild(createTocListElement(ownerDocument, tocTreeItems, false));
2478
+ return navElement;
2479
+ }
2480
+ function createTocListElement(ownerDocument, tocItems, isNested) {
2481
+ const listElement = ownerDocument.createElement("ol");
2482
+ listElement.className = isNested ? `${TOC_LIST_CLASS_NAME} ${TOC_LIST_NESTED_CLASS_NAME}` : TOC_LIST_CLASS_NAME;
2483
+ for (const tocItem of tocItems) {
2484
+ listElement.appendChild(createTocListItemElement(ownerDocument, tocItem));
2485
+ }
2486
+ return listElement;
2487
+ }
2488
+ function createTocListItemElement(ownerDocument, tocItem) {
2489
+ const hasChildren = tocItem.children.length > 0;
2490
+ const itemElement = ownerDocument.createElement("li");
2491
+ itemElement.className = createTocItemClassNames(tocItem.depth, hasChildren).join(" ");
2492
+ itemElement.dataset.tocIndex = tocItem.index;
2493
+ if (hasChildren) {
2494
+ const toggleElement = ownerDocument.createElement("button");
2495
+ toggleElement.type = "button";
2496
+ toggleElement.className = TOC_TOGGLE_CLASS_NAME;
2497
+ toggleElement.setAttribute("aria-expanded", "true");
2498
+ toggleElement.setAttribute("aria-label", TOC_TOGGLE_ARIA_LABEL);
2499
+ itemElement.appendChild(toggleElement);
2500
+ }
2501
+ const linkElement = ownerDocument.createElement("a");
2502
+ linkElement.className = TOC_LINK_CLASS_NAME;
2503
+ linkElement.href = `#${tocItem.id}`;
2504
+ linkElement.textContent = tocItem.text;
2505
+ itemElement.appendChild(linkElement);
2506
+ if (hasChildren) {
2507
+ itemElement.appendChild(createTocListElement(ownerDocument, tocItem.children, true));
2508
+ }
2509
+ return itemElement;
2510
+ }
2511
+ function hydrateToc(rootElement, scrollToHeading) {
2512
+ const toggleElements = rootElement.querySelectorAll(
2513
+ `button.${TOC_TOGGLE_CLASS_NAME}`
2514
+ );
2515
+ toggleElements.forEach((toggleElement) => {
2516
+ if (toggleElement.dataset[TOC_TOGGLE_HYDRATED_DATA_KEY] === "true") {
2517
+ return;
2518
+ }
2519
+ toggleElement.dataset[TOC_TOGGLE_HYDRATED_DATA_KEY] = "true";
2520
+ toggleElement.addEventListener("click", () => {
2521
+ const itemElement = toggleElement.closest(`.${TOC_ITEM_CLASS_PREFIX}`);
2522
+ if (!itemElement) {
2523
+ return;
2524
+ }
2525
+ const nextCollapsed = !itemElement.classList.contains(TOC_ITEM_COLLAPSED_CLASS_NAME);
2526
+ itemElement.classList.toggle(TOC_ITEM_COLLAPSED_CLASS_NAME, nextCollapsed);
2527
+ toggleElement.setAttribute("aria-expanded", String(!nextCollapsed));
2528
+ });
2529
+ });
2530
+ const linkElements = rootElement.querySelectorAll(
2531
+ `a.${TOC_LINK_CLASS_NAME}`
2532
+ );
2533
+ linkElements.forEach((linkElement) => {
2534
+ if (linkElement.dataset[TOC_LINK_HYDRATED_DATA_KEY] === "true") {
2535
+ return;
2536
+ }
2537
+ linkElement.dataset[TOC_LINK_HYDRATED_DATA_KEY] = "true";
2538
+ linkElement.addEventListener("click", (event) => {
2539
+ event.preventDefault();
2540
+ const ownerDocument = linkElement.ownerDocument;
2541
+ const rawTargetId = (linkElement.getAttribute("href") ?? "").slice(1);
2542
+ if (!rawTargetId) {
2543
+ return;
2544
+ }
2545
+ let targetElement = ownerDocument.getElementById(rawTargetId);
2546
+ if (!targetElement) {
2547
+ try {
2548
+ targetElement = ownerDocument.getElementById(decodeURIComponent(rawTargetId));
2549
+ } catch {
2550
+ targetElement = null;
2551
+ }
2552
+ }
2553
+ if (targetElement) {
2554
+ scrollToHeading(targetElement);
2555
+ }
2556
+ });
2557
+ });
2558
+ }
2559
+ function extractNodeText(node) {
2560
+ if (node.type === "text" || node.type === "inlineCode") {
2561
+ return node.value ?? "";
2562
+ }
2563
+ if (isImageNode(node)) {
2564
+ return node.alt ?? "";
2565
+ }
2566
+ if (!node.children) {
2567
+ return "";
2568
+ }
2569
+ return node.children.map(extractNodeText).join("");
2570
+ }
2571
+ function createUniqueSlug(headingText, slugCounts, fallbackIndex) {
2572
+ const normalizedSlug = normalizeSlugText(headingText);
2573
+ const fallbackSlug = `${EMPTY_HEADING_SLUG_PREFIX}-${fallbackIndex}`;
2574
+ const baseSlug = normalizedSlug || fallbackSlug;
2575
+ const usedCount = slugCounts.get(baseSlug) ?? 0;
2576
+ slugCounts.set(baseSlug, usedCount + 1);
2577
+ if (usedCount === 0) {
2578
+ return baseSlug;
2579
+ }
2580
+ return `${baseSlug}-${usedCount}`;
2581
+ }
2582
+ function createHeadingIndex(headingDepth, headingIndexCounts, rootHeadingDepth) {
2583
+ const startDepth = Math.min(rootHeadingDepth, headingDepth);
2584
+ headingIndexCounts[headingDepth] += 1;
2585
+ for (let depthIndex = headingDepth + 1; depthIndex < headingIndexCounts.length; depthIndex += 1) {
2586
+ headingIndexCounts[depthIndex] = 0;
2587
+ }
2588
+ for (let depthIndex = startDepth; depthIndex < headingDepth; depthIndex += 1) {
2589
+ if (headingIndexCounts[depthIndex] === 0) {
2590
+ headingIndexCounts[depthIndex] = 1;
2591
+ }
2592
+ }
2593
+ return headingIndexCounts.slice(startDepth, headingDepth + 1).join(".");
2594
+ }
2595
+ function normalizeSlugText(headingText) {
2596
+ return headingText.trim().toLowerCase().replace(/<[^>]+>/g, "").replace(/[^\p{L}\p{N}\s_-]+/gu, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
2597
+ }
2598
+ function rehypeFrameClass() {
2599
+ return (tree) => {
2600
+ tagContentDetails(tree);
2601
+ };
2602
+ }
2603
+ function tagContentDetails(node) {
2604
+ if (node.type === "element" && node.tagName === "details") {
2605
+ const classNames = normalizeClassNames(node.properties?.className);
2606
+ const isTocDetails = classNames.includes(TOC_CLASS_NAME);
2607
+ if (!isTocDetails) {
2608
+ addClassNames(node, [DETAILS_CLASS_NAME, FRAME_CLASS_NAME]);
2609
+ }
2610
+ }
2611
+ if (Array.isArray(node.children)) {
2612
+ for (const childNode of node.children) {
2613
+ tagContentDetails(childNode);
2614
+ }
2615
+ }
2616
+ }
2617
+ function normalizeClassNames(className) {
2618
+ if (Array.isArray(className)) {
2619
+ return className.filter((item) => typeof item === "string");
2620
+ }
2621
+ if (typeof className === "string") {
2622
+ return className.split(/\s+/u).filter(Boolean);
2623
+ }
2624
+ return [];
2625
+ }
2626
+ function addClassNames(node, classNamesToAdd) {
2627
+ const mergedClassNames = normalizeClassNames(node.properties?.className);
2628
+ for (const classNameToAdd of classNamesToAdd) {
2629
+ if (!mergedClassNames.includes(classNameToAdd)) {
2630
+ mergedClassNames.push(classNameToAdd);
2631
+ }
2632
+ }
2633
+ node.properties = { ...node.properties, className: mergedClassNames };
2634
+ }
2635
+ function rehypeVideoFigures() {
2636
+ return (tree) => {
2637
+ transformVideoFigures(tree);
2638
+ };
2639
+ }
2640
+ function transformVideoFigures(node) {
2641
+ if (!Array.isArray(node.children)) {
2642
+ return;
2643
+ }
2644
+ const childNodes = node.children;
2645
+ for (let childIndex = 0; childIndex < childNodes.length; childIndex += 1) {
2646
+ const childNode = childNodes[childIndex];
2647
+ if (isHastVideoElement(childNode)) {
2648
+ childNodes[childIndex] = createVideoFigureHast(childNode);
2649
+ continue;
2650
+ }
2651
+ const standaloneVideo = extractStandaloneParagraphVideo(childNode);
2652
+ if (standaloneVideo) {
2653
+ childNodes[childIndex] = createVideoFigureHast(standaloneVideo);
2654
+ continue;
2655
+ }
2656
+ transformVideoFigures(childNode);
2657
+ }
2658
+ }
2659
+ function isHastVideoElement(node) {
2660
+ return node.type === "element" && node.tagName === "video";
2661
+ }
2662
+ function extractStandaloneParagraphVideo(node) {
2663
+ if (node.type !== "element" || node.tagName !== "p" || !Array.isArray(node.children)) {
2664
+ return void 0;
2665
+ }
2666
+ const significantChildren = node.children.filter((childNode) => {
2667
+ if (childNode.type === "text" && typeof childNode.value === "string") {
2668
+ return childNode.value.trim().length > 0;
2669
+ }
2670
+ return true;
2671
+ });
2672
+ if (significantChildren.length === 1 && isHastVideoElement(significantChildren[0])) {
2673
+ return significantChildren[0];
2674
+ }
2675
+ return void 0;
2676
+ }
2677
+ function createVideoFigureHast(videoNode) {
2678
+ decorateHastVideoElement(videoNode);
2679
+ const sourceUrl = readHastVideoSourceUrl(videoNode);
2680
+ return {
2681
+ type: "element",
2682
+ tagName: "figure",
2683
+ properties: { className: [VIDEO_FIGURE_CLASS_NAME] },
2684
+ children: [
2685
+ {
2686
+ type: "element",
2687
+ tagName: "span",
2688
+ properties: { className: [VIDEO_FRAME_CLASS_NAME] },
2689
+ children: [videoNode, createVideoFallbackHast(sourceUrl)]
2690
+ }
2691
+ ]
2692
+ };
2693
+ }
2694
+ function decorateHastVideoElement(videoNode) {
2695
+ const properties = videoNode.properties ?? {};
2696
+ const existingClassName = properties.className;
2697
+ const classList = Array.isArray(existingClassName) ? existingClassName.map(String) : typeof existingClassName === "string" ? [existingClassName] : [];
2698
+ if (!classList.includes(VIDEO_ELEMENT_CLASS_NAME)) {
2699
+ classList.push(VIDEO_ELEMENT_CLASS_NAME);
2700
+ }
2701
+ properties.className = classList;
2702
+ videoNode.properties = properties;
2703
+ }
2704
+ function readHastVideoSourceUrl(videoNode) {
2705
+ const properties = videoNode.properties ?? {};
2706
+ if (typeof properties.src === "string") {
2707
+ return properties.src;
2708
+ }
2709
+ if (Array.isArray(videoNode.children)) {
2710
+ for (const childNode of videoNode.children) {
2711
+ if (childNode.type === "element" && childNode.tagName === "source" && typeof childNode.properties?.src === "string") {
2712
+ return childNode.properties.src;
2713
+ }
2714
+ }
2715
+ }
2716
+ return "";
2717
+ }
2718
+ function createVideoFallbackHast(sourceUrl) {
2719
+ return {
2720
+ type: "element",
2721
+ tagName: "span",
2722
+ properties: { className: [VIDEO_FALLBACK_CLASS_NAME] },
2723
+ children: [
2724
+ {
2725
+ type: "element",
2726
+ tagName: "span",
2727
+ properties: { className: [VIDEO_FALLBACK_ICON_CLASS_NAME] },
2728
+ children: []
2729
+ },
2730
+ {
2731
+ type: "element",
2732
+ tagName: "span",
2733
+ properties: { className: [VIDEO_FALLBACK_TEXT_CLASS_NAME] },
2734
+ children: [{ type: "text", value: VIDEO_FALLBACK_DEFAULT_TEXT }]
2735
+ },
2736
+ {
2737
+ type: "element",
2738
+ tagName: "span",
2739
+ properties: { className: [VIDEO_FALLBACK_SOURCE_CLASS_NAME] },
2740
+ children: [{ type: "text", value: sourceUrl }]
2741
+ }
2742
+ ]
2743
+ };
2744
+ }
2745
+ var TOOLBAR_WIDTH_PRESETS = [
2746
+ { label: "680", value: "680px" },
2747
+ { label: "840", value: "840px" },
2748
+ { label: "1080", value: "1080px" },
2749
+ { label: "100%", value: "100%" }
2750
+ ];
2751
+ var TOOLBAR_DEFAULT_WIDTH = "840px";
2752
+ function loadContentWidth() {
2753
+ try {
2754
+ return localStorage.getItem(CONTENT_WIDTH_STORAGE_KEY) ?? TOOLBAR_DEFAULT_WIDTH;
2755
+ } catch {
2756
+ return TOOLBAR_DEFAULT_WIDTH;
2757
+ }
2758
+ }
2759
+ function saveContentWidth(value) {
2760
+ try {
2761
+ localStorage.setItem(CONTENT_WIDTH_STORAGE_KEY, value);
2762
+ } catch {
2763
+ }
2764
+ }
2765
+ function applyContentWidth(ownerDocument, value) {
2766
+ ownerDocument.documentElement.style.setProperty("--scribdown-content-width", value);
2767
+ }
2768
+ function createPageToolbarBtn(ownerDocument, ariaLabel, svgContent) {
2769
+ const btn = ownerDocument.createElement("button");
2770
+ btn.type = "button";
2771
+ btn.className = SCRIBDOWN_TOOLBAR_BTN_CLASS_NAME;
2772
+ btn.setAttribute("aria-label", ariaLabel);
2773
+ btn.innerHTML = svgContent;
2774
+ return btn;
2775
+ }
2776
+ var TOOLBAR_TOC_ICON_SVG = '<svg width="16" height="16" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><line x1="3" y1="5" x2="15" y2="5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><line x1="3" y1="9" x2="11" y2="9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><line x1="3" y1="13" x2="13" y2="13" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>';
2777
+ var TOOLBAR_MORE_ICON_SVG = '<svg width="16" height="16" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="4" cy="9" r="1.4" fill="currentColor"/><circle cx="9" cy="9" r="1.4" fill="currentColor"/><circle cx="14" cy="9" r="1.4" fill="currentColor"/></svg>';
2778
+ var TOOLBAR_BACK_TOP_ICON_SVG = '<svg width="16" height="16" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M9 14V5M9 5L5 9M9 5l4 4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/><line x1="4" y1="3.5" x2="14" y2="3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>';
2779
+ var TOOLBAR_WIDTH_ICON_SVG = '<svg width="16" height="16" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M3 6v6M15 6v6" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><path d="M5.5 9H3l2.5-2.5M3 9l2.5 2.5M12.5 9H15l-2.5-2.5M15 9l-2.5 2.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>';
2780
+ var TOOLBAR_ABOUT_ICON_SVG = '<svg width="16" height="16" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><circle cx="9" cy="9" r="6.5" stroke="currentColor" stroke-width="1.6"/><path d="M9 8v4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="9" cy="5.6" r="0.9" fill="currentColor"/></svg>';
2781
+ var pageToolbarScrollspyTeardowns = /* @__PURE__ */ new WeakMap();
2782
+ function mountPageToolbar(ownerDocument, container, scrollToHeading) {
2783
+ pageToolbarScrollspyTeardowns.get(container)?.();
2784
+ pageToolbarScrollspyTeardowns.delete(container);
2785
+ container.querySelector(`:scope > .${SCRIBDOWN_TOOLBAR_CLASS_NAME}`)?.remove();
2786
+ container.querySelector(`:scope > .${SCRIBDOWN_TOOLBAR_TOC_PANEL_CLASS_NAME}`)?.remove();
2787
+ container.classList.add(SCRIBDOWN_TOC_HOST_CLASS_NAME);
2788
+ let contentArea = container.querySelector(
2789
+ `:scope > .${SCRIBDOWN_CONTENT_AREA_CLASS_NAME}`
2790
+ );
2791
+ if (!contentArea) {
2792
+ contentArea = ownerDocument.createElement("div");
2793
+ contentArea.className = SCRIBDOWN_CONTENT_AREA_CLASS_NAME;
2794
+ container.appendChild(contentArea);
2795
+ }
2796
+ let contentScroll = contentArea.querySelector(
2797
+ `:scope > .${SCRIBDOWN_CONTENT_SCROLL_CLASS_NAME}`
2798
+ );
2799
+ if (!contentScroll) {
2800
+ contentScroll = ownerDocument.createElement("div");
2801
+ contentScroll.className = `${SCRIBDOWN_CONTENT_SCROLL_CLASS_NAME} ${SCRIBDOWN_THIN_SCROLLBAR_CLASS_NAME}`;
2802
+ contentArea.appendChild(contentScroll);
2803
+ }
2804
+ const contentScrollElement = contentScroll;
2805
+ const contentAreaElement = contentArea;
2806
+ [...Array.from(container.children), ...Array.from(contentArea.children)].forEach((child) => {
2807
+ if (child === contentAreaElement || child === contentScrollElement || child.classList.contains(SCRIBDOWN_TOOLBAR_CLASS_NAME) || child.classList.contains(SCRIBDOWN_TOOLBAR_TOC_PANEL_CLASS_NAME)) {
2808
+ return;
2809
+ }
2810
+ contentScrollElement.appendChild(child);
2811
+ });
2812
+ const ownerWindow = ownerDocument.defaultView;
2813
+ const toolbar = ownerDocument.createElement("div");
2814
+ toolbar.className = SCRIBDOWN_TOOLBAR_CLASS_NAME;
2815
+ const tocPanel = ownerDocument.createElement("div");
2816
+ tocPanel.className = `${SCRIBDOWN_TOOLBAR_TOC_PANEL_CLASS_NAME} ${SCRIBDOWN_THIN_SCROLLBAR_CLASS_NAME}`;
2817
+ const tocTitle = ownerDocument.createElement("div");
2818
+ tocTitle.className = "scribdown-toolbar-toc-panel-title";
2819
+ const tocTitleText = ownerDocument.createElement("span");
2820
+ tocTitleText.textContent = "\u76EE\u5F55";
2821
+ tocTitle.appendChild(tocTitleText);
2822
+ const tocCloseBtn = ownerDocument.createElement("button");
2823
+ tocCloseBtn.type = "button";
2824
+ tocCloseBtn.className = "scribdown-toolbar-toc-panel-close";
2825
+ tocCloseBtn.setAttribute("aria-label", "\u5173\u95ED\u76EE\u5F55");
2826
+ tocCloseBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>';
2827
+ const setTocOpen = (open) => {
2828
+ tocPanel.classList.toggle("is-open", open);
2829
+ };
2830
+ tocCloseBtn.addEventListener("click", (e) => {
2831
+ e.stopPropagation();
2832
+ setTocOpen(false);
2833
+ });
2834
+ tocTitle.appendChild(tocCloseBtn);
2835
+ tocPanel.appendChild(tocTitle);
2836
+ const tocHeadings = collectTocHeadingsFromDom(container);
2837
+ if (tocHeadings.length === 0) {
2838
+ const emptyElement = ownerDocument.createElement("p");
2839
+ emptyElement.className = "scribdown-toolbar-toc-panel-empty";
2840
+ emptyElement.textContent = "\u6682\u65E0\u6807\u9898";
2841
+ tocPanel.appendChild(emptyElement);
2842
+ } else {
2843
+ const tocTree = createTocTree(tocHeadings);
2844
+ const tocNavElement = createTocNavElement(ownerDocument, tocTree);
2845
+ tocPanel.appendChild(tocNavElement);
2846
+ hydrateToc(tocNavElement, scrollToHeading);
2847
+ }
2848
+ const tocBtn = createPageToolbarBtn(ownerDocument, "\u76EE\u5F55", TOOLBAR_TOC_ICON_SVG);
2849
+ const moreBtn = createPageToolbarBtn(ownerDocument, "\u66F4\u591A", TOOLBAR_MORE_ICON_SVG);
2850
+ moreBtn.classList.add(`${SCRIBDOWN_TOOLBAR_BTN_CLASS_NAME}--more`);
2851
+ moreBtn.setAttribute("aria-haspopup", "menu");
2852
+ moreBtn.setAttribute("aria-expanded", "false");
2853
+ const menu = ownerDocument.createElement("div");
2854
+ menu.className = SCRIBDOWN_TOOLBAR_MENU_CLASS_NAME;
2855
+ menu.setAttribute("role", "menu");
2856
+ let widthIndex = TOOLBAR_WIDTH_PRESETS.findIndex((p) => p.value === loadContentWidth());
2857
+ if (widthIndex === -1) {
2858
+ widthIndex = TOOLBAR_WIDTH_PRESETS.findIndex((p) => p.value === TOOLBAR_DEFAULT_WIDTH);
2859
+ }
2860
+ const widthGroup = ownerDocument.createElement("div");
2861
+ widthGroup.className = SCRIBDOWN_TOOLBAR_MENU_GROUP_CLASS_NAME;
2862
+ const widthTrigger = ownerDocument.createElement("button");
2863
+ widthTrigger.type = "button";
2864
+ widthTrigger.className = `${SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME} ${SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME}--select`;
2865
+ widthTrigger.setAttribute("role", "menuitem");
2866
+ widthTrigger.setAttribute("aria-haspopup", "listbox");
2867
+ widthTrigger.setAttribute("aria-expanded", "false");
2868
+ const widthValueElement = ownerDocument.createElement("span");
2869
+ widthValueElement.className = "scribdown-toolbar-menu-select-value";
2870
+ widthValueElement.textContent = TOOLBAR_WIDTH_PRESETS[widthIndex].label;
2871
+ widthTrigger.innerHTML = `${TOOLBAR_WIDTH_ICON_SVG}<span class="scribdown-toolbar-menu-select-label">\u9875\u9762\u5BBD\u5EA6</span>`;
2872
+ widthTrigger.appendChild(widthValueElement);
2873
+ widthTrigger.insertAdjacentHTML(
2874
+ "beforeend",
2875
+ '<svg class="scribdown-toolbar-menu-select-chevron" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 4.5l3 3 3-3" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>'
2876
+ );
2877
+ widthGroup.appendChild(widthTrigger);
2878
+ const widthChoices = ownerDocument.createElement("div");
2879
+ widthChoices.className = "scribdown-toolbar-menu-group-choices";
2880
+ widthChoices.setAttribute("role", "listbox");
2881
+ const widthSubItems = [];
2882
+ TOOLBAR_WIDTH_PRESETS.forEach((preset, idx) => {
2883
+ const subItem = ownerDocument.createElement("button");
2884
+ subItem.type = "button";
2885
+ subItem.className = SCRIBDOWN_TOOLBAR_MENU_SUB_ITEM_CLASS_NAME;
2886
+ subItem.setAttribute("role", "option");
2887
+ subItem.setAttribute("aria-selected", String(idx === widthIndex));
2888
+ subItem.innerHTML = `<span class="scribdown-toolbar-menu-sub-item-mark" aria-hidden="true"><svg width="12" height="12" viewBox="0 0 12 12" fill="none"><path d="M2.5 6.5l2.5 2.5 4.5-5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span><span class="scribdown-toolbar-menu-sub-item-label">${preset.label}</span>`;
2889
+ subItem.addEventListener("click", () => {
2890
+ widthIndex = idx;
2891
+ applyContentWidth(ownerDocument, preset.value);
2892
+ saveContentWidth(preset.value);
2893
+ widthValueElement.textContent = preset.label;
2894
+ widthSubItems.forEach((item, i) => {
2895
+ item.setAttribute("aria-selected", String(i === idx));
2896
+ });
2897
+ closeWidthSelect();
2898
+ });
2899
+ widthChoices.appendChild(subItem);
2900
+ widthSubItems.push(subItem);
2901
+ });
2902
+ widthGroup.appendChild(widthChoices);
2903
+ const closeWidthSelect = () => {
2904
+ widthGroup.classList.remove("is-open");
2905
+ widthTrigger.setAttribute("aria-expanded", "false");
2906
+ };
2907
+ widthTrigger.addEventListener("click", () => {
2908
+ const opened = widthGroup.classList.toggle("is-open");
2909
+ widthTrigger.setAttribute("aria-expanded", String(opened));
2910
+ });
2911
+ const closeMoreMenu = () => {
2912
+ menu.classList.remove("is-open");
2913
+ moreBtn.setAttribute("aria-expanded", "false");
2914
+ closeWidthSelect();
2915
+ };
2916
+ const backTopItem = ownerDocument.createElement("button");
2917
+ backTopItem.type = "button";
2918
+ backTopItem.className = SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME;
2919
+ backTopItem.setAttribute("role", "menuitem");
2920
+ backTopItem.innerHTML = `${TOOLBAR_BACK_TOP_ICON_SVG}<span>\u56DE\u5230\u9876\u90E8</span>`;
2921
+ backTopItem.addEventListener("click", () => {
2922
+ contentScroll?.scrollTo({ top: 0, behavior: "smooth" });
2923
+ ownerWindow?.scrollTo({ top: 0, behavior: "smooth" });
2924
+ closeMoreMenu();
2925
+ });
2926
+ const aboutItem = ownerDocument.createElement("button");
2927
+ aboutItem.type = "button";
2928
+ aboutItem.className = SCRIBDOWN_TOOLBAR_MENU_ITEM_CLASS_NAME;
2929
+ aboutItem.setAttribute("role", "menuitem");
2930
+ aboutItem.innerHTML = `${TOOLBAR_ABOUT_ICON_SVG}<span>\u5173\u4E8E</span>`;
2931
+ aboutItem.addEventListener("click", () => {
2932
+ ownerWindow?.open(PROJECT_HOMEPAGE_URL, "_blank", "noopener,noreferrer");
2933
+ closeMoreMenu();
2934
+ });
2935
+ menu.appendChild(backTopItem);
2936
+ menu.appendChild(widthGroup);
2937
+ menu.appendChild(aboutItem);
2938
+ tocBtn.addEventListener("click", (e) => {
2939
+ e.stopPropagation();
2940
+ const wasOpen = tocPanel.classList.contains("is-open");
2941
+ setTocOpen(!wasOpen);
2942
+ if (!wasOpen) {
2943
+ closeMoreMenu();
2944
+ }
2945
+ });
2946
+ moreBtn.addEventListener("click", (e) => {
2947
+ e.stopPropagation();
2948
+ const opened = menu.classList.toggle("is-open");
2949
+ moreBtn.setAttribute("aria-expanded", String(opened));
2950
+ });
2951
+ ownerDocument.addEventListener("click", (e) => {
2952
+ const target = e.target;
2953
+ if (!menu.contains(target) && target !== moreBtn) {
2954
+ closeMoreMenu();
2955
+ }
2956
+ });
2957
+ const markdownContainer = container.matches(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`) ? container : container.querySelector(`.${SCRIBDOWN_MARKDOWN_CLASS_NAME}`);
2958
+ const headingElements = markdownContainer ? Array.from(
2959
+ markdownContainer.querySelectorAll(
2960
+ "h1[id],h2[id],h3[id],h4[id],h5[id],h6[id]"
2961
+ )
2962
+ ) : [];
2963
+ if (headingElements.length > 0) {
2964
+ const ACTIVE_HEADING_OFFSET = 48;
2965
+ const currentHeading = ownerDocument.createElement("div");
2966
+ currentHeading.className = SCRIBDOWN_TOOLBAR_CURRENT_CLASS_NAME;
2967
+ currentHeading.setAttribute("role", "button");
2968
+ currentHeading.tabIndex = 0;
2969
+ const currentHeadingText = ownerDocument.createElement("span");
2970
+ currentHeadingText.className = "scribdown-toolbar-current-text";
2971
+ currentHeading.appendChild(currentHeadingText);
2972
+ let lastHeadingText = "";
2973
+ let activeHeadingElement = headingElements[0];
2974
+ const tocLinkByHeading = /* @__PURE__ */ new Map();
2975
+ tocPanel.querySelectorAll(`a.${TOC_LINK_CLASS_NAME}`).forEach((linkElement) => {
2976
+ const rawTargetId = (linkElement.getAttribute("href") ?? "").slice(1);
2977
+ if (!rawTargetId) {
2978
+ return;
2979
+ }
2980
+ let targetHeading = ownerDocument.getElementById(rawTargetId);
2981
+ if (!targetHeading) {
2982
+ try {
2983
+ targetHeading = ownerDocument.getElementById(decodeURIComponent(rawTargetId));
2984
+ } catch {
2985
+ targetHeading = null;
2986
+ }
2987
+ }
2988
+ if (targetHeading instanceof HTMLHeadingElement) {
2989
+ tocLinkByHeading.set(targetHeading, linkElement);
2990
+ }
2991
+ });
2992
+ let activeTocLink;
2993
+ const setActiveTocLink = (headingElement) => {
2994
+ const nextLink = tocLinkByHeading.get(headingElement);
2995
+ if (nextLink === activeTocLink) {
2996
+ return;
2997
+ }
2998
+ if (activeTocLink) {
2999
+ activeTocLink.classList.remove(TOC_LINK_ACTIVE_CLASS_NAME);
3000
+ activeTocLink.removeAttribute("aria-current");
3001
+ }
3002
+ if (nextLink) {
3003
+ nextLink.classList.add(TOC_LINK_ACTIVE_CLASS_NAME);
3004
+ nextLink.setAttribute("aria-current", "location");
3005
+ }
3006
+ activeTocLink = nextLink;
3007
+ };
3008
+ const updateCurrentHeading = () => {
3009
+ let activeElement = headingElements[0];
3010
+ for (const headingElement of headingElements) {
3011
+ if (headingElement.getBoundingClientRect().top <= ACTIVE_HEADING_OFFSET) {
3012
+ activeElement = headingElement;
3013
+ } else {
3014
+ break;
3015
+ }
3016
+ }
3017
+ activeHeadingElement = activeElement;
3018
+ setActiveTocLink(activeElement);
3019
+ const headingText = activeElement.textContent?.trim() ?? "";
3020
+ if (headingText === lastHeadingText) {
3021
+ return;
3022
+ }
3023
+ lastHeadingText = headingText;
3024
+ currentHeadingText.textContent = headingText;
3025
+ currentHeading.setAttribute("aria-label", headingText);
3026
+ };
3027
+ const jumpToActiveHeading = () => {
3028
+ activeHeadingElement.scrollIntoView({ behavior: "smooth", block: "start" });
3029
+ };
3030
+ currentHeading.addEventListener("click", jumpToActiveHeading);
3031
+ currentHeading.addEventListener("keydown", (event) => {
3032
+ if (event.key === "Enter" || event.key === " ") {
3033
+ event.preventDefault();
3034
+ jumpToActiveHeading();
3035
+ }
3036
+ });
3037
+ let scrollRafId = 0;
3038
+ const onScrollOrResize = () => {
3039
+ if (scrollRafId) {
3040
+ return;
3041
+ }
3042
+ scrollRafId = ownerWindow?.requestAnimationFrame(() => {
3043
+ scrollRafId = 0;
3044
+ updateCurrentHeading();
3045
+ }) ?? 0;
3046
+ };
3047
+ ownerWindow?.addEventListener("scroll", onScrollOrResize, {
3048
+ passive: true,
3049
+ capture: true
3050
+ });
3051
+ ownerWindow?.addEventListener("resize", onScrollOrResize, { passive: true });
3052
+ updateCurrentHeading();
3053
+ pageToolbarScrollspyTeardowns.set(container, () => {
3054
+ if (scrollRafId) {
3055
+ ownerWindow?.cancelAnimationFrame(scrollRafId);
3056
+ }
3057
+ ownerWindow?.removeEventListener("scroll", onScrollOrResize, { capture: true });
3058
+ ownerWindow?.removeEventListener("resize", onScrollOrResize);
3059
+ });
3060
+ toolbar.appendChild(currentHeading);
3061
+ }
3062
+ toolbar.appendChild(tocBtn);
3063
+ toolbar.appendChild(moreBtn);
3064
+ toolbar.appendChild(menu);
3065
+ container.appendChild(toolbar);
3066
+ container.insertBefore(tocPanel, contentArea);
3067
+ }
3068
+ export {
3069
+ hydrateMarkdown,
3070
+ mountMarkdownToolbar,
3071
+ renderMarkdown
3072
+ };
3073
+ //# sourceMappingURL=index.js.map