@deot/docs-markdown 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -0
- package/dist/index.cjs +827 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +798 -0
- package/dist/index.style.css +1 -0
- package/package.json +36 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,827 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let vue = require("vue");
|
|
25
|
+
let _deot_docs_locale = require("@deot/docs-locale");
|
|
26
|
+
let markdown_it_chain = require("markdown-it-chain");
|
|
27
|
+
markdown_it_chain = __toESM(markdown_it_chain);
|
|
28
|
+
let markdown_it_anchor = require("markdown-it-anchor");
|
|
29
|
+
markdown_it_anchor = __toESM(markdown_it_anchor);
|
|
30
|
+
let markdown_it_container = require("markdown-it-container");
|
|
31
|
+
markdown_it_container = __toESM(markdown_it_container);
|
|
32
|
+
let markdown_it = require("markdown-it");
|
|
33
|
+
markdown_it = __toESM(markdown_it);
|
|
34
|
+
let json5 = require("json5");
|
|
35
|
+
json5 = __toESM(json5);
|
|
36
|
+
let _deot_helper_dom = require("@deot/helper-dom");
|
|
37
|
+
let _deot_vc = require("@deot/vc");
|
|
38
|
+
//#region packages/markdown/src/markdown.ts
|
|
39
|
+
var HTML_MD_SIGN = "md";
|
|
40
|
+
var PLAYGROUND = "playground";
|
|
41
|
+
var TIP = "tip";
|
|
42
|
+
var WARNING = "warning";
|
|
43
|
+
var config = new markdown_it_chain.default();
|
|
44
|
+
config.options.html(true).typographer(true).linkify(true).end().plugin("anchor").use(markdown_it_anchor.default, [{ permalink: markdown_it_anchor.default.permalink.linkInsideHeader({
|
|
45
|
+
placement: "before",
|
|
46
|
+
symbol: "#"
|
|
47
|
+
}) }]).end().plugin("container").use(($md) => {
|
|
48
|
+
const reg = new RegExp(`^${PLAYGROUND}\\s*(.*)$`);
|
|
49
|
+
$md.use(markdown_it_container.default, PLAYGROUND, { validate(params) {
|
|
50
|
+
return params.trim().match(reg);
|
|
51
|
+
} });
|
|
52
|
+
$md.use(markdown_it_container.default, TIP);
|
|
53
|
+
$md.use(markdown_it_container.default, WARNING);
|
|
54
|
+
}).end();
|
|
55
|
+
var md = config.toMd(markdown_it.default);
|
|
56
|
+
var playgroundOpen = `container_${PLAYGROUND}_open`;
|
|
57
|
+
var playgroundClose = `container_${PLAYGROUND}_close`;
|
|
58
|
+
var htmlCommentRE = /<!--([\s\S]*?)-->/g;
|
|
59
|
+
var runtimeConfigRE = /<config\s+lang\s*=\s*["']json5["']\s*>([\s\S]*?)<\/config>/i;
|
|
60
|
+
var renderPlaygroundError = (message) => `<div class="docs-playground-error">PLAYGROUND: ${md.utils.escapeHtml(message)}</div>\n`;
|
|
61
|
+
var PLAYGROUND_VIEWS = ["runtime", "files"];
|
|
62
|
+
var isPlaygroundView = (value) => typeof value === "string" && PLAYGROUND_VIEWS.includes(value);
|
|
63
|
+
var isRuntimeViewport = (viewport) => {
|
|
64
|
+
if (viewport === "auto") return true;
|
|
65
|
+
if (typeof viewport === "number") return Number.isFinite(viewport) && viewport > 0;
|
|
66
|
+
return Array.isArray(viewport) && viewport.length === 2 && viewport.every((value) => typeof value === "number" && Number.isFinite(value) && value > 0);
|
|
67
|
+
};
|
|
68
|
+
var getRuntimeViewportKey = (viewport) => Array.isArray(viewport) ? `${viewport[0]}x${viewport[1]}` : String(viewport);
|
|
69
|
+
var validateRuntimeViews = (propsData) => {
|
|
70
|
+
if ("view" in propsData) return "不支持 view 参数,请使用 views";
|
|
71
|
+
if (!("views" in propsData)) return "";
|
|
72
|
+
const views = propsData.views;
|
|
73
|
+
if (!Array.isArray(views) || !views.length) return "views 必须是非空数组";
|
|
74
|
+
const invalidView = views.find((view) => !isPlaygroundView(view));
|
|
75
|
+
if (invalidView !== void 0) return `views 不支持 ${String(invalidView)}`;
|
|
76
|
+
const duplicateView = views.find((view, viewIndex) => views.indexOf(view) !== viewIndex);
|
|
77
|
+
if (duplicateView !== void 0) return `views 不能重复声明 ${String(duplicateView)}`;
|
|
78
|
+
return "";
|
|
79
|
+
};
|
|
80
|
+
var validateRuntimeViewport = (propsData) => {
|
|
81
|
+
if ("viewport" in propsData && !isRuntimeViewport(propsData.viewport)) return "viewport 必须是 auto、正数宽度或 [宽,高]";
|
|
82
|
+
if (!("viewportOptions" in propsData)) return "";
|
|
83
|
+
const options = propsData.viewportOptions;
|
|
84
|
+
if (!Array.isArray(options)) return "viewportOptions 必须是数组";
|
|
85
|
+
const invalidIndex = options.findIndex((option) => !isRuntimeViewport(option));
|
|
86
|
+
if (invalidIndex >= 0) return `viewportOptions[${invalidIndex}] 必须是 auto、正数宽度或 [宽,高]`;
|
|
87
|
+
const keys = options.filter(isRuntimeViewport).map(getRuntimeViewportKey);
|
|
88
|
+
const duplicateIndex = keys.findIndex((key, index) => keys.indexOf(key) !== index);
|
|
89
|
+
if (duplicateIndex >= 0) return `viewportOptions 不能重复声明 ${keys[duplicateIndex]}`;
|
|
90
|
+
return "";
|
|
91
|
+
};
|
|
92
|
+
var parseRuntimeProps = (tokens) => {
|
|
93
|
+
for (const token of tokens) {
|
|
94
|
+
const sources = [];
|
|
95
|
+
if (token.type === "html_block" && token.content) sources.push(token.content);
|
|
96
|
+
if (token.type === "inline" && token.content) sources.push(token.content);
|
|
97
|
+
for (const source of sources) {
|
|
98
|
+
htmlCommentRE.lastIndex = 0;
|
|
99
|
+
let commentMatch = htmlCommentRE.exec(source);
|
|
100
|
+
while (commentMatch) {
|
|
101
|
+
const configMatch = commentMatch[1].match(runtimeConfigRE);
|
|
102
|
+
if (configMatch) try {
|
|
103
|
+
const parsed = json5.default.parse(configMatch[1]);
|
|
104
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
105
|
+
} catch {}
|
|
106
|
+
commentMatch = htmlCommentRE.exec(source);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return {};
|
|
111
|
+
};
|
|
112
|
+
var renderPlaygroundAttrs = (propsData) => `data-props="${md.utils.escapeHtml(JSON.stringify(propsData))}"`;
|
|
113
|
+
md.core.ruler.after("block", "runtime-files", (state) => {
|
|
114
|
+
for (let index = 0; index < state.tokens.length; index++) {
|
|
115
|
+
if (state.tokens[index].type !== playgroundOpen) continue;
|
|
116
|
+
let depth = 1;
|
|
117
|
+
let closeIndex = index + 1;
|
|
118
|
+
for (; closeIndex < state.tokens.length; closeIndex++) {
|
|
119
|
+
if (state.tokens[closeIndex].type === playgroundOpen) depth++;
|
|
120
|
+
if (state.tokens[closeIndex].type === playgroundClose) depth--;
|
|
121
|
+
if (depth === 0) break;
|
|
122
|
+
}
|
|
123
|
+
const innerTokens = state.tokens.slice(index + 1, closeIndex);
|
|
124
|
+
const fences = innerTokens.filter((token) => token.type === "fence");
|
|
125
|
+
const placeholder = new state.Token("html_block", "", 0);
|
|
126
|
+
placeholder.block = true;
|
|
127
|
+
const propsData = parseRuntimeProps(innerTokens);
|
|
128
|
+
const propsError = validateRuntimeViews(propsData) || validateRuntimeViewport(propsData);
|
|
129
|
+
const propsAttr = renderPlaygroundAttrs(propsData);
|
|
130
|
+
if (!fences.length) placeholder.content = renderPlaygroundError("至少需要声明一个代码文件");
|
|
131
|
+
else if (propsError) placeholder.content = renderPlaygroundError(propsError);
|
|
132
|
+
else {
|
|
133
|
+
const fileEntries = fences.map((token) => {
|
|
134
|
+
const [, filename = ""] = token.info.trim().split(/\s+/, 2);
|
|
135
|
+
return [filename, token.content];
|
|
136
|
+
});
|
|
137
|
+
if (fences.length === 1 && !fileEntries[0][0]) placeholder.content = [
|
|
138
|
+
"<div data-playground",
|
|
139
|
+
`data-code="${md.utils.escapeHtml(fences[0].content)}"`,
|
|
140
|
+
`${propsAttr}></div>\n`
|
|
141
|
+
].join(" ");
|
|
142
|
+
else {
|
|
143
|
+
const missingFilename = fileEntries.some(([filename]) => !filename);
|
|
144
|
+
const filenames = fileEntries.map(([filename]) => filename);
|
|
145
|
+
const duplicateFilename = filenames.find((filename, fileIndex) => filenames.indexOf(filename) !== fileIndex);
|
|
146
|
+
const entry = (typeof propsData.entry === "string" ? propsData.entry : "") || filenames[0];
|
|
147
|
+
if (missingFilename) placeholder.content = renderPlaygroundError("多文件模式下每个代码块都必须声明文件名");
|
|
148
|
+
else if (duplicateFilename) placeholder.content = renderPlaygroundError(`文件名 ${duplicateFilename} 重复`);
|
|
149
|
+
else if (!filenames.includes(entry)) placeholder.content = renderPlaygroundError(`入口文件 ${entry} 不存在`);
|
|
150
|
+
else {
|
|
151
|
+
const files = Object.fromEntries(fileEntries);
|
|
152
|
+
placeholder.content = [
|
|
153
|
+
"<div data-playground",
|
|
154
|
+
`data-files="${md.utils.escapeHtml(JSON.stringify(files))}"`,
|
|
155
|
+
`data-entry="${md.utils.escapeHtml(entry)}"`,
|
|
156
|
+
`${propsAttr}></div>\n`
|
|
157
|
+
].join(" ");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
state.tokens.splice(index, closeIndex - index + 1, placeholder);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
var renderAttrs = md.renderer.renderAttrs;
|
|
165
|
+
md.renderer.renderAttrs = (token) => {
|
|
166
|
+
const reg = new RegExp(`container_${PLAYGROUND}|fence|text`);
|
|
167
|
+
if (token.nesting !== -1 && token.type && !reg.test(token.type)) token.attrPush([HTML_MD_SIGN, ""]);
|
|
168
|
+
return renderAttrs(token);
|
|
169
|
+
};
|
|
170
|
+
var Markdown = md;
|
|
171
|
+
var normalizeSearchText = (value) => value.replace(/\s+/gu, " ").trim();
|
|
172
|
+
/**
|
|
173
|
+
* 从 inline token 中提取用户可见文本。HTML token 不进入索引,避免标签、
|
|
174
|
+
* 注释或嵌入代码污染搜索结果;链接仍保留其可见标题。
|
|
175
|
+
* @param token Markdown-It inline token。
|
|
176
|
+
* @returns 规范化后的可搜索文本。
|
|
177
|
+
*/
|
|
178
|
+
var getInlineSearchText = (token) => {
|
|
179
|
+
if (!token.children) return normalizeSearchText(token.content);
|
|
180
|
+
return normalizeSearchText(token.children.map((child) => {
|
|
181
|
+
if (child.type === "html_inline") return "";
|
|
182
|
+
if (child.type === "softbreak" || child.type === "hardbreak") return " ";
|
|
183
|
+
return ["text", "code_inline"].includes(child.type) ? child.content : "";
|
|
184
|
+
}).join(" "));
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* 使用页面渲染共用的 Markdown Engine 生成搜索文档。heading ID 直接读取
|
|
188
|
+
* markdown-it-anchor 处理后的 token,确保重复标题和中文标题的跳转地址
|
|
189
|
+
* 与实际页面一致。
|
|
190
|
+
* @param content Markdown 原文。
|
|
191
|
+
* @returns 文档标题、正文和按标题划分的小节。
|
|
192
|
+
*/
|
|
193
|
+
var parseMarkdownSearchSections = (content) => {
|
|
194
|
+
const tokens = md.parse(content, {});
|
|
195
|
+
const sections = [];
|
|
196
|
+
const documentParts = [];
|
|
197
|
+
let activeSection;
|
|
198
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
199
|
+
const token = tokens[index];
|
|
200
|
+
if (token.type === "heading_open") {
|
|
201
|
+
const inline = tokens[index + 1];
|
|
202
|
+
if (inline?.type !== "inline") continue;
|
|
203
|
+
const title = getInlineSearchText(inline);
|
|
204
|
+
if (!title) continue;
|
|
205
|
+
activeSection = {
|
|
206
|
+
title,
|
|
207
|
+
anchor: token.attrGet("id") || "",
|
|
208
|
+
level: Number(token.tag.slice(1)) || 1,
|
|
209
|
+
text: ""
|
|
210
|
+
};
|
|
211
|
+
sections.push(activeSection);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (token.type !== "inline" || tokens[index - 1]?.type === "heading_open") continue;
|
|
215
|
+
const text = getInlineSearchText(token);
|
|
216
|
+
if (!text) continue;
|
|
217
|
+
documentParts.push(text);
|
|
218
|
+
if (activeSection) activeSection.text = normalizeSearchText(`${activeSection.text} ${text}`);
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
title: sections.find((section) => section.level === 1)?.title || sections[0]?.title || "",
|
|
222
|
+
text: normalizeSearchText(documentParts.join(" ")),
|
|
223
|
+
sections
|
|
224
|
+
};
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region packages/markdown/src/directive.ts
|
|
228
|
+
var mountedApps = /* @__PURE__ */ new WeakMap();
|
|
229
|
+
var renderVersions = /* @__PURE__ */ new WeakMap();
|
|
230
|
+
var playgroundModule;
|
|
231
|
+
var loadPlayground = () => playgroundModule ||= import("@deot/docs-playground");
|
|
232
|
+
var cleanup = (el) => {
|
|
233
|
+
for (const app of mountedApps.get(el) || []) app.unmount();
|
|
234
|
+
mountedApps.delete(el);
|
|
235
|
+
};
|
|
236
|
+
var resolveBlockLanguage = (block) => {
|
|
237
|
+
return [...block.classList].find((className) => className.startsWith("language-"))?.slice(9) || "";
|
|
238
|
+
};
|
|
239
|
+
var render = async (el, binding) => {
|
|
240
|
+
cleanup(el);
|
|
241
|
+
const version = (renderVersions.get(el) || 0) + 1;
|
|
242
|
+
renderVersions.set(el, version);
|
|
243
|
+
el.innerHTML = binding.value.source ? Markdown.render(binding.value.source) : "";
|
|
244
|
+
const apps = [];
|
|
245
|
+
const blocks = el.querySelectorAll("pre code");
|
|
246
|
+
const playgrounds = el.querySelectorAll("div[data-playground]");
|
|
247
|
+
if (!blocks.length && !playgrounds.length) {
|
|
248
|
+
mountedApps.set(el, apps);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const DocsPlayground = await loadPlayground();
|
|
252
|
+
if (renderVersions.get(el) !== version) return;
|
|
253
|
+
blocks.forEach((block) => {
|
|
254
|
+
const pre = block.parentElement;
|
|
255
|
+
if (!pre) return;
|
|
256
|
+
const mountPoint = document.createElement("div");
|
|
257
|
+
mountPoint.className = "docs-markdown-code-preview";
|
|
258
|
+
pre.replaceWith(mountPoint);
|
|
259
|
+
const app = (0, vue.createApp)(() => (0, vue.h)(DocsPlayground.CodePreview, {
|
|
260
|
+
code: block.textContent || "",
|
|
261
|
+
language: resolveBlockLanguage(block),
|
|
262
|
+
locale: binding.value.locale
|
|
263
|
+
}));
|
|
264
|
+
app.mount(mountPoint);
|
|
265
|
+
apps.push(app);
|
|
266
|
+
});
|
|
267
|
+
playgrounds.forEach((item) => {
|
|
268
|
+
const code = item.dataset.code;
|
|
269
|
+
let files;
|
|
270
|
+
let propsData = {};
|
|
271
|
+
try {
|
|
272
|
+
propsData = JSON.parse(item.dataset.props || "{}");
|
|
273
|
+
} catch {}
|
|
274
|
+
try {
|
|
275
|
+
files = item.dataset.files ? JSON.parse(item.dataset.files) : void 0;
|
|
276
|
+
} catch {}
|
|
277
|
+
const runtimeProps = files ? {
|
|
278
|
+
files,
|
|
279
|
+
entry: item.dataset.entry
|
|
280
|
+
} : { modelValue: code };
|
|
281
|
+
const app = (0, vue.createApp)(() => (0, vue.h)(DocsPlayground.Playground, {
|
|
282
|
+
...typeof propsData === "object" ? propsData : {},
|
|
283
|
+
...runtimeProps,
|
|
284
|
+
locale: binding.value.locale
|
|
285
|
+
}));
|
|
286
|
+
app.mount(item);
|
|
287
|
+
apps.push(app);
|
|
288
|
+
});
|
|
289
|
+
mountedApps.set(el, apps);
|
|
290
|
+
};
|
|
291
|
+
var update = (el, binding) => {
|
|
292
|
+
if (binding.value.source !== binding.oldValue?.source || binding.value.locale !== binding.oldValue?.locale) render(el, binding);
|
|
293
|
+
};
|
|
294
|
+
var vMarkdown = {
|
|
295
|
+
mounted: (el, binding) => void render(el, binding),
|
|
296
|
+
updated: update,
|
|
297
|
+
beforeUnmount: (el) => {
|
|
298
|
+
renderVersions.set(el, (renderVersions.get(el) || 0) + 1);
|
|
299
|
+
cleanup(el);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region packages/markdown/src/indicator.vue?vue&type=script&setup=true&lang.ts
|
|
304
|
+
var _hoisted_1$1 = ["aria-label"];
|
|
305
|
+
var _hoisted_2 = { class: "docs-markdown-indicator__list" };
|
|
306
|
+
var _hoisted_3 = ["aria-label", "onClick"];
|
|
307
|
+
var _hoisted_4 = { class: "docs-markdown-indicator__preview-title" };
|
|
308
|
+
var _hoisted_5 = {
|
|
309
|
+
key: 0,
|
|
310
|
+
class: "docs-markdown-indicator__preview-content"
|
|
311
|
+
};
|
|
312
|
+
var MAX_ARIA_LABEL_LENGTH = 180;
|
|
313
|
+
//#endregion
|
|
314
|
+
//#region packages/markdown/src/indicator.vue
|
|
315
|
+
var indicator_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
316
|
+
__name: "indicator",
|
|
317
|
+
props: {
|
|
318
|
+
options: {},
|
|
319
|
+
target: {}
|
|
320
|
+
},
|
|
321
|
+
setup(__props) {
|
|
322
|
+
const BLOCK_SELECTOR = [
|
|
323
|
+
"h1",
|
|
324
|
+
"h2",
|
|
325
|
+
"h3",
|
|
326
|
+
"h4",
|
|
327
|
+
"h5",
|
|
328
|
+
"h6",
|
|
329
|
+
"p",
|
|
330
|
+
"li",
|
|
331
|
+
"pre",
|
|
332
|
+
"blockquote",
|
|
333
|
+
"table",
|
|
334
|
+
".tip",
|
|
335
|
+
".warning",
|
|
336
|
+
".docs-markdown-code-preview",
|
|
337
|
+
"[data-playground]"
|
|
338
|
+
].join(",");
|
|
339
|
+
const CONTAINER_SELECTOR = [
|
|
340
|
+
"li",
|
|
341
|
+
"blockquote",
|
|
342
|
+
"table",
|
|
343
|
+
".tip",
|
|
344
|
+
".warning",
|
|
345
|
+
".docs-markdown-code-preview",
|
|
346
|
+
"[data-playground]"
|
|
347
|
+
].join(",");
|
|
348
|
+
const props = __props;
|
|
349
|
+
const { lang: localeName, t } = (0, _deot_docs_locale.useLocale)();
|
|
350
|
+
const parentScroller = (0, vue.inject)("vc-scroller", null);
|
|
351
|
+
const indicatorRoot = (0, vue.ref)();
|
|
352
|
+
const viewport = (0, vue.ref)();
|
|
353
|
+
const rail = (0, vue.ref)();
|
|
354
|
+
const indicatorScroller = (0, vue.ref)();
|
|
355
|
+
const markers = (0, vue.shallowRef)([]);
|
|
356
|
+
const activeIndex = (0, vue.ref)(0);
|
|
357
|
+
const hoverIndex = (0, vue.ref)();
|
|
358
|
+
const previewTop = (0, vue.ref)(0);
|
|
359
|
+
const dragging = (0, vue.ref)(false);
|
|
360
|
+
const stickyInset = (0, vue.ref)("0px");
|
|
361
|
+
let observer;
|
|
362
|
+
let hostResizeObserver;
|
|
363
|
+
let scrollHost;
|
|
364
|
+
let usesParentScroller = false;
|
|
365
|
+
let captureTarget;
|
|
366
|
+
let refreshFrame = 0;
|
|
367
|
+
let activeFrame = 0;
|
|
368
|
+
let targetGeneration = 0;
|
|
369
|
+
const toCssLength = (value, fallback) => typeof value === "number" ? `${value}px` : value || fallback;
|
|
370
|
+
const rootStyle = (0, vue.computed)(() => ({
|
|
371
|
+
"--docs-markdown-indicator-height": toCssLength(props.options.height, "min(72vh, 600px)"),
|
|
372
|
+
"--docs-markdown-indicator-inset": stickyInset.value,
|
|
373
|
+
"--docs-markdown-indicator-top": toCssLength(props.options.top, "0px")
|
|
374
|
+
}));
|
|
375
|
+
const hoveredMarker = (0, vue.computed)(() => typeof hoverIndex.value === "number" ? markers.value[hoverIndex.value] : void 0);
|
|
376
|
+
const previewStyle = (0, vue.computed)(() => ({ top: `${previewTop.value}px` }));
|
|
377
|
+
/**
|
|
378
|
+
* 保留文档块的有效换行,同时折叠每行内部的多余空白。
|
|
379
|
+
* @param element 当前文档块。
|
|
380
|
+
* @returns 适合摘要卡展示的文本。
|
|
381
|
+
*/
|
|
382
|
+
const getBlockText = (element) => (element.textContent || "").split(/\n+/).map((line) => line.replace(/\s+/g, " ").trim()).filter(Boolean).join("\n");
|
|
383
|
+
/**
|
|
384
|
+
* 清理标题锚点产生的井号,并提供无标题内容的兜底文本。
|
|
385
|
+
* @param element 当前标题节点。
|
|
386
|
+
* @returns 单行章节标题。
|
|
387
|
+
*/
|
|
388
|
+
const getHeadingTitle = (element) => getBlockText(element).replace(/^#\s*/, "").replace(/\s+/g, " ").trim() || t("markdown.indicator.untitled");
|
|
389
|
+
/**
|
|
390
|
+
* 为无障碍名称限制长度,避免长代码块生成过大的属性值。
|
|
391
|
+
* @param title 当前章节标题。
|
|
392
|
+
* @param content 当前文档块内容。
|
|
393
|
+
* @returns 标题和内容组成的简短名称。
|
|
394
|
+
*/
|
|
395
|
+
const getAriaLabel = (title, content) => {
|
|
396
|
+
const value = content ? `${title}: ${content.replace(/\s+/g, " ")}` : title;
|
|
397
|
+
if (value.length <= MAX_ARIA_LABEL_LENGTH) return value;
|
|
398
|
+
return `${value.slice(0, MAX_ARIA_LABEL_LENGTH).trim()}…`;
|
|
399
|
+
};
|
|
400
|
+
/**
|
|
401
|
+
* 判断候选节点是否已经包含在另一个可独立定位的文档块内。
|
|
402
|
+
* @param element 当前候选节点。
|
|
403
|
+
* @returns 是否应由外层文档块统一表示。
|
|
404
|
+
*/
|
|
405
|
+
const isNestedBlock = (element) => {
|
|
406
|
+
const container = element.closest(CONTAINER_SELECTOR);
|
|
407
|
+
return Boolean(container && container !== element);
|
|
408
|
+
};
|
|
409
|
+
/**
|
|
410
|
+
* 获取 Markdown 使用的原生滚动宿主。
|
|
411
|
+
* Scroller 组件由注入上下文优先处理;仅在没有 Scroller 时才向上查找普通滚动层。
|
|
412
|
+
* @returns 当前文档使用的滚动容器。
|
|
413
|
+
*/
|
|
414
|
+
const getScrollHost = () => (0, _deot_helper_dom.getScroller)(props.target) || window;
|
|
415
|
+
/**
|
|
416
|
+
* 获取当前滚动宿主的可视区域顶部。
|
|
417
|
+
* @returns 相对于视口的顶部坐标。
|
|
418
|
+
*/
|
|
419
|
+
const getScrollHostTop = () => {
|
|
420
|
+
if (usesParentScroller) return parentScroller?.wrapper?.getBoundingClientRect().top || 0;
|
|
421
|
+
return scrollHost instanceof HTMLElement ? scrollHost.getBoundingClientRect().top : 0;
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* 判断文档是否已经到达滚动末尾。
|
|
425
|
+
* 末屏通常无法把最后一个内容块推到顶部阈值,因此需要显式选中最后一条刻度。
|
|
426
|
+
* @returns 是否位于可滚动内容末尾。
|
|
427
|
+
*/
|
|
428
|
+
const isScrollEnd = () => {
|
|
429
|
+
let clientHeight;
|
|
430
|
+
let scrollHeight;
|
|
431
|
+
let scrollTop;
|
|
432
|
+
if (usesParentScroller) {
|
|
433
|
+
clientHeight = parentScroller?.clientHeight || 0;
|
|
434
|
+
scrollHeight = parentScroller?.scrollHeight || 0;
|
|
435
|
+
scrollTop = parentScroller?.scrollTop || 0;
|
|
436
|
+
} else if (scrollHost instanceof HTMLElement) {
|
|
437
|
+
clientHeight = scrollHost.clientHeight;
|
|
438
|
+
scrollHeight = scrollHost.scrollHeight;
|
|
439
|
+
scrollTop = scrollHost.scrollTop;
|
|
440
|
+
} else {
|
|
441
|
+
clientHeight = window.innerHeight;
|
|
442
|
+
scrollHeight = document.documentElement.scrollHeight;
|
|
443
|
+
scrollTop = window.scrollY;
|
|
444
|
+
}
|
|
445
|
+
return scrollHeight > clientHeight && scrollTop + clientHeight >= scrollHeight - 1;
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* 保证当前阅读刻度处于内部 Scroller 的可视范围内。
|
|
449
|
+
* @param index 当前阅读刻度序号。
|
|
450
|
+
*/
|
|
451
|
+
const followActiveMarker = async (index) => {
|
|
452
|
+
await (0, vue.nextTick)();
|
|
453
|
+
const marker = rail.value?.querySelectorAll(".docs-markdown-indicator__marker")[index];
|
|
454
|
+
const wrapper = viewport.value?.querySelector(".vc-scroller__wrapper");
|
|
455
|
+
if (!marker || !wrapper) return;
|
|
456
|
+
const markerRect = marker.getBoundingClientRect();
|
|
457
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
458
|
+
const padding = 16;
|
|
459
|
+
if (markerRect.top >= wrapperRect.top + padding && markerRect.bottom <= wrapperRect.bottom - padding) return;
|
|
460
|
+
const markerCenter = markerRect.top + markerRect.height / 2;
|
|
461
|
+
const wrapperCenter = wrapperRect.top + wrapperRect.height / 2;
|
|
462
|
+
indicatorScroller.value?.setScrollTop(Math.max(0, wrapper.scrollTop + markerCenter - wrapperCenter));
|
|
463
|
+
};
|
|
464
|
+
/** 根据滚动位置更新当前阅读块,不触发 Markdown 内容重渲染。 */
|
|
465
|
+
const updateActiveIndex = () => {
|
|
466
|
+
activeFrame = 0;
|
|
467
|
+
if (!markers.value.length) return;
|
|
468
|
+
const threshold = getScrollHostTop() + 80;
|
|
469
|
+
let nextIndex = markers.value.length - 1;
|
|
470
|
+
if (!isScrollEnd()) {
|
|
471
|
+
nextIndex = 0;
|
|
472
|
+
for (let index = 0; index < markers.value.length; index++) {
|
|
473
|
+
if (markers.value[index].element.getBoundingClientRect().top > threshold) break;
|
|
474
|
+
nextIndex = index;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (activeIndex.value !== nextIndex) {
|
|
478
|
+
activeIndex.value = nextIndex;
|
|
479
|
+
followActiveMarker(nextIndex);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
/** 将连续滚动事件合并到浏览器的下一绘制帧。 */
|
|
483
|
+
const handleScroll = () => {
|
|
484
|
+
if (!activeFrame) activeFrame = requestAnimationFrame(updateActiveIndex);
|
|
485
|
+
};
|
|
486
|
+
/** 从最新渲染的 Markdown DOM 重建文档刻度。 */
|
|
487
|
+
const refreshMarkers = () => {
|
|
488
|
+
refreshFrame = 0;
|
|
489
|
+
const target = props.target;
|
|
490
|
+
if (!target) {
|
|
491
|
+
markers.value = [];
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const elements = [...target.querySelectorAll(BLOCK_SELECTOR)].filter((element) => !isNestedBlock(element));
|
|
495
|
+
let sectionTitle = t("markdown.indicator.document");
|
|
496
|
+
markers.value = elements.map((element, index) => {
|
|
497
|
+
const isHeading = /^H[1-6]$/.test(element.tagName);
|
|
498
|
+
if (isHeading) sectionTitle = getHeadingTitle(element);
|
|
499
|
+
const nextElement = elements[index + 1];
|
|
500
|
+
const content = isHeading && nextElement && !/^H[1-6]$/.test(nextElement.tagName) ? getBlockText(nextElement) : isHeading ? "" : getBlockText(element);
|
|
501
|
+
return {
|
|
502
|
+
ariaLabel: getAriaLabel(sectionTitle, content),
|
|
503
|
+
content,
|
|
504
|
+
element,
|
|
505
|
+
id: `${index}-${element.id || element.tagName}`,
|
|
506
|
+
title: sectionTitle
|
|
507
|
+
};
|
|
508
|
+
});
|
|
509
|
+
hoverIndex.value = void 0;
|
|
510
|
+
updateActiveIndex();
|
|
511
|
+
followActiveMarker(activeIndex.value);
|
|
512
|
+
};
|
|
513
|
+
/** 合并 MutationObserver 的密集通知,避免 Playground 挂载时重复扫描。 */
|
|
514
|
+
const scheduleRefresh = () => {
|
|
515
|
+
if (!refreshFrame) refreshFrame = requestAnimationFrame(refreshMarkers);
|
|
516
|
+
};
|
|
517
|
+
/**
|
|
518
|
+
* 读取当前滚动容器的可视高度。
|
|
519
|
+
* sticky 的百分比 top 会相对整篇文档计算,不能用来做容器垂直居中。
|
|
520
|
+
* @returns 滚动容器 clientHeight,窗口滚动时回退到 innerHeight。
|
|
521
|
+
*/
|
|
522
|
+
const getScrollHostClientHeight = () => {
|
|
523
|
+
if (usesParentScroller) return parentScroller?.wrapper?.clientHeight || parentScroller?.clientHeight || window.innerHeight;
|
|
524
|
+
if (scrollHost instanceof HTMLElement) return scrollHost.clientHeight;
|
|
525
|
+
return window.innerHeight;
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* 读取指示器可视高度:数字配置优先,否则测量已挂载节点或回退默认值。
|
|
529
|
+
* @returns 指示器高度,单位 px。
|
|
530
|
+
*/
|
|
531
|
+
const resolveIndicatorHeight = () => {
|
|
532
|
+
const height = props.options.height;
|
|
533
|
+
if (typeof height === "number" && Number.isFinite(height) && height > 0) return height;
|
|
534
|
+
const measured = viewport.value?.getBoundingClientRect().height || 0;
|
|
535
|
+
if (measured > 0) return measured;
|
|
536
|
+
return Math.min(window.innerHeight * .72, 600);
|
|
537
|
+
};
|
|
538
|
+
/** 把指示器钉在滚动容器可视区域的垂直中心。 */
|
|
539
|
+
const updateStickyInset = () => {
|
|
540
|
+
const hostHeight = getScrollHostClientHeight();
|
|
541
|
+
const indicatorHeight = resolveIndicatorHeight();
|
|
542
|
+
stickyInset.value = hostHeight > 0 ? `${Math.max(0, (hostHeight - indicatorHeight) / 2)}px` : "0px";
|
|
543
|
+
};
|
|
544
|
+
/** 跟随滚动容器和指示器自身的尺寸变化,保持垂直居中。 */
|
|
545
|
+
const observeHostSize = () => {
|
|
546
|
+
hostResizeObserver?.disconnect();
|
|
547
|
+
hostResizeObserver = void 0;
|
|
548
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
549
|
+
hostResizeObserver = new ResizeObserver(updateStickyInset);
|
|
550
|
+
const host = usesParentScroller ? parentScroller?.wrapper : scrollHost instanceof HTMLElement ? scrollHost : void 0;
|
|
551
|
+
if (host) hostResizeObserver.observe(host);
|
|
552
|
+
if (viewport.value) hostResizeObserver.observe(viewport.value);
|
|
553
|
+
}
|
|
554
|
+
updateStickyInset();
|
|
555
|
+
};
|
|
556
|
+
/** 解除旧文档的观察和滚动监听。 */
|
|
557
|
+
const cleanupTarget = () => {
|
|
558
|
+
observer?.disconnect();
|
|
559
|
+
observer = void 0;
|
|
560
|
+
hostResizeObserver?.disconnect();
|
|
561
|
+
hostResizeObserver = void 0;
|
|
562
|
+
window.removeEventListener("resize", updateStickyInset);
|
|
563
|
+
if (usesParentScroller) parentScroller?.off?.(handleScroll);
|
|
564
|
+
else if (scrollHost) scrollHost.removeEventListener("scroll", handleScroll);
|
|
565
|
+
usesParentScroller = false;
|
|
566
|
+
scrollHost = void 0;
|
|
567
|
+
};
|
|
568
|
+
/** 为当前 Markdown DOM 建立内容观察与滚动同步。 */
|
|
569
|
+
const setupTarget = async () => {
|
|
570
|
+
const generation = ++targetGeneration;
|
|
571
|
+
cleanupTarget();
|
|
572
|
+
await (0, vue.nextTick)();
|
|
573
|
+
if (generation !== targetGeneration) return;
|
|
574
|
+
if (!props.target) {
|
|
575
|
+
markers.value = [];
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
usesParentScroller = Boolean(parentScroller?.on);
|
|
579
|
+
if (usesParentScroller) {
|
|
580
|
+
parentScroller?.on?.(handleScroll);
|
|
581
|
+
scrollHost = parentScroller?.wrapper || window;
|
|
582
|
+
} else {
|
|
583
|
+
scrollHost = getScrollHost();
|
|
584
|
+
scrollHost.addEventListener("scroll", handleScroll, { passive: true });
|
|
585
|
+
}
|
|
586
|
+
window.addEventListener("resize", updateStickyInset, { passive: true });
|
|
587
|
+
observer = new MutationObserver(scheduleRefresh);
|
|
588
|
+
observer.observe(props.target, {
|
|
589
|
+
childList: true,
|
|
590
|
+
characterData: true,
|
|
591
|
+
subtree: true
|
|
592
|
+
});
|
|
593
|
+
refreshMarkers();
|
|
594
|
+
observeHostSize();
|
|
595
|
+
};
|
|
596
|
+
/**
|
|
597
|
+
* 将指示器内的纵坐标转换为最近的文档块序号。
|
|
598
|
+
* @param event 当前指针事件。
|
|
599
|
+
* @returns 距离指针最近的文档块序号。
|
|
600
|
+
*/
|
|
601
|
+
const getIndexByPointer = (event) => {
|
|
602
|
+
if (!rail.value || !markers.value.length) return 0;
|
|
603
|
+
const elements = [...rail.value.querySelectorAll(".docs-markdown-indicator__marker")];
|
|
604
|
+
let closestIndex = 0;
|
|
605
|
+
let closestDistance = Number.POSITIVE_INFINITY;
|
|
606
|
+
elements.forEach((element, index) => {
|
|
607
|
+
const bounds = element.getBoundingClientRect();
|
|
608
|
+
const distance = Math.abs(event.clientY - (bounds.top + bounds.height / 2));
|
|
609
|
+
if (distance < closestDistance) {
|
|
610
|
+
closestIndex = index;
|
|
611
|
+
closestDistance = distance;
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
return closestIndex;
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* 让摘要跟随当前刻度,同时限制在指示器可视高度内。
|
|
618
|
+
* @param index 当前刻度序号。
|
|
619
|
+
* @param clientY 指针的视口纵坐标。
|
|
620
|
+
*/
|
|
621
|
+
const updatePreviewPosition = (index, clientY) => {
|
|
622
|
+
const rootBounds = indicatorRoot.value?.getBoundingClientRect();
|
|
623
|
+
const viewportBounds = viewport.value?.getBoundingClientRect();
|
|
624
|
+
const marker = rail.value?.querySelectorAll(".docs-markdown-indicator__marker")[index];
|
|
625
|
+
if (!rootBounds || !viewportBounds || !marker) return;
|
|
626
|
+
const markerBounds = marker.getBoundingClientRect();
|
|
627
|
+
const relativeY = (clientY ?? markerBounds.top + markerBounds.height / 2) - rootBounds.top;
|
|
628
|
+
previewTop.value = Math.min(viewportBounds.height - 48, Math.max(48, relativeY));
|
|
629
|
+
};
|
|
630
|
+
/**
|
|
631
|
+
* 滚动到指定文档块;拖动使用即时定位,点击使用平滑定位。
|
|
632
|
+
* @param index 目标文档块序号。
|
|
633
|
+
* @param behavior 滚动行为。
|
|
634
|
+
*/
|
|
635
|
+
const scrollToMarker = (index, behavior) => {
|
|
636
|
+
const marker = markers.value[index];
|
|
637
|
+
if (!marker || !scrollHost) return;
|
|
638
|
+
const markerTop = marker.element.getBoundingClientRect().top;
|
|
639
|
+
if (usesParentScroller && parentScroller?.setScrollTop) parentScroller.setScrollTop((parentScroller.scrollTop || 0) + markerTop - getScrollHostTop() - 24);
|
|
640
|
+
else if (scrollHost instanceof HTMLElement) {
|
|
641
|
+
const hostTop = scrollHost.getBoundingClientRect().top;
|
|
642
|
+
scrollHost.scrollTo({
|
|
643
|
+
top: scrollHost.scrollTop + markerTop - hostTop - 24,
|
|
644
|
+
behavior
|
|
645
|
+
});
|
|
646
|
+
} else window.scrollTo({
|
|
647
|
+
top: window.scrollY + markerTop - 24,
|
|
648
|
+
behavior
|
|
649
|
+
});
|
|
650
|
+
activeIndex.value = index;
|
|
651
|
+
followActiveMarker(index);
|
|
652
|
+
};
|
|
653
|
+
/**
|
|
654
|
+
* 指针移动时展示摘要;按下拖动时同步快速浏览文档。
|
|
655
|
+
* @param event 当前指针事件。
|
|
656
|
+
*/
|
|
657
|
+
const handlePointerMove = (event) => {
|
|
658
|
+
const index = getIndexByPointer(event);
|
|
659
|
+
hoverIndex.value = index;
|
|
660
|
+
updatePreviewPosition(index, event.clientY);
|
|
661
|
+
if (dragging.value) scrollToMarker(index, "auto");
|
|
662
|
+
};
|
|
663
|
+
/**
|
|
664
|
+
* 开始拖动指示器并定位到按下位置。
|
|
665
|
+
* @param event 当前指针事件。
|
|
666
|
+
*/
|
|
667
|
+
const handlePointerDown = (event) => {
|
|
668
|
+
if (event.button !== 0) return;
|
|
669
|
+
dragging.value = props.options.draggable !== false;
|
|
670
|
+
const index = getIndexByPointer(event);
|
|
671
|
+
hoverIndex.value = index;
|
|
672
|
+
updatePreviewPosition(index, event.clientY);
|
|
673
|
+
if (dragging.value) {
|
|
674
|
+
captureTarget = event.currentTarget;
|
|
675
|
+
captureTarget.setPointerCapture?.(event.pointerId);
|
|
676
|
+
scrollToMarker(index, "auto");
|
|
677
|
+
event.preventDefault();
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
/**
|
|
681
|
+
* 结束拖动并释放指针捕获。
|
|
682
|
+
* @param event 当前指针事件。
|
|
683
|
+
*/
|
|
684
|
+
const handlePointerUp = (event) => {
|
|
685
|
+
dragging.value = false;
|
|
686
|
+
if (captureTarget?.hasPointerCapture?.(event.pointerId)) captureTarget.releasePointerCapture(event.pointerId);
|
|
687
|
+
captureTarget = void 0;
|
|
688
|
+
};
|
|
689
|
+
/** 离开指示器时隐藏摘要;拖动期间由指针捕获继续处理。 */
|
|
690
|
+
const handlePointerLeave = () => {
|
|
691
|
+
if (!dragging.value) hoverIndex.value = void 0;
|
|
692
|
+
};
|
|
693
|
+
/**
|
|
694
|
+
* 点击单条刻度时平滑定位到对应文档块。
|
|
695
|
+
* @param index 目标文档块序号。
|
|
696
|
+
*/
|
|
697
|
+
const handleClick = (index) => {
|
|
698
|
+
hoverIndex.value = index;
|
|
699
|
+
updatePreviewPosition(index);
|
|
700
|
+
scrollToMarker(index, "smooth");
|
|
701
|
+
};
|
|
702
|
+
/**
|
|
703
|
+
* 计算刻度位置、基础宽度和靠近指针时的鱼眼展开宽度。
|
|
704
|
+
* @param index 当前刻度序号。
|
|
705
|
+
* @returns 刻度的定位和宽度样式。
|
|
706
|
+
*/
|
|
707
|
+
const getMarkerStyle = (index) => {
|
|
708
|
+
const distance = typeof hoverIndex.value === "number" ? Math.abs(index - hoverIndex.value) : Number.POSITIVE_INFINITY;
|
|
709
|
+
return { width: `${distance <= 3 ? [
|
|
710
|
+
28,
|
|
711
|
+
22,
|
|
712
|
+
16,
|
|
713
|
+
10
|
|
714
|
+
][distance] : 8}px` };
|
|
715
|
+
};
|
|
716
|
+
(0, vue.watch)([() => props.target, localeName], setupTarget, { immediate: true });
|
|
717
|
+
(0, vue.watch)([() => props.options.height, viewport], () => {
|
|
718
|
+
if (viewport.value && hostResizeObserver) hostResizeObserver.observe(viewport.value);
|
|
719
|
+
updateStickyInset();
|
|
720
|
+
});
|
|
721
|
+
(0, vue.onBeforeUnmount)(() => {
|
|
722
|
+
targetGeneration++;
|
|
723
|
+
cleanupTarget();
|
|
724
|
+
if (refreshFrame) cancelAnimationFrame(refreshFrame);
|
|
725
|
+
if (activeFrame) cancelAnimationFrame(activeFrame);
|
|
726
|
+
});
|
|
727
|
+
return (_ctx, _cache) => {
|
|
728
|
+
return markers.value.length > 1 ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("aside", {
|
|
729
|
+
key: 0,
|
|
730
|
+
ref_key: "indicatorRoot",
|
|
731
|
+
ref: indicatorRoot,
|
|
732
|
+
class: (0, vue.normalizeClass)(["docs-markdown-indicator", `is-${__props.options.position || "right"}`]),
|
|
733
|
+
style: (0, vue.normalizeStyle)(rootStyle.value),
|
|
734
|
+
"aria-label": (0, vue.unref)(t)("markdown.indicator.label")
|
|
735
|
+
}, [(0, vue.createElementVNode)("div", {
|
|
736
|
+
ref_key: "viewport",
|
|
737
|
+
ref: viewport,
|
|
738
|
+
class: "docs-markdown-indicator__viewport",
|
|
739
|
+
onPointermove: handlePointerMove,
|
|
740
|
+
onPointerleave: handlePointerLeave,
|
|
741
|
+
onPointerdown: handlePointerDown,
|
|
742
|
+
onPointerup: handlePointerUp,
|
|
743
|
+
onPointercancel: handlePointerUp
|
|
744
|
+
}, [(0, vue.createVNode)((0, vue.unref)(_deot_vc.Scroller), {
|
|
745
|
+
ref_key: "indicatorScroller",
|
|
746
|
+
ref: indicatorScroller,
|
|
747
|
+
class: "docs-markdown-indicator__scroller",
|
|
748
|
+
"auto-resize": true,
|
|
749
|
+
native: false,
|
|
750
|
+
"show-bar": true,
|
|
751
|
+
height: "100%",
|
|
752
|
+
"wrapper-style": "overflow-x: hidden;"
|
|
753
|
+
}, {
|
|
754
|
+
default: (0, vue.withCtx)(() => [(0, vue.createElementVNode)("div", {
|
|
755
|
+
ref_key: "rail",
|
|
756
|
+
ref: rail,
|
|
757
|
+
class: "docs-markdown-indicator__rail"
|
|
758
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_2, [((0, vue.openBlock)(true), (0, vue.createElementBlock)(vue.Fragment, null, (0, vue.renderList)(markers.value, (marker, index) => {
|
|
759
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("button", {
|
|
760
|
+
key: marker.id,
|
|
761
|
+
type: "button",
|
|
762
|
+
class: (0, vue.normalizeClass)(["docs-markdown-indicator__marker", {
|
|
763
|
+
"is-active": index === activeIndex.value,
|
|
764
|
+
"is-hovered": index === hoverIndex.value
|
|
765
|
+
}]),
|
|
766
|
+
style: (0, vue.normalizeStyle)(getMarkerStyle(index)),
|
|
767
|
+
"aria-label": marker.ariaLabel,
|
|
768
|
+
onClick: ($event) => handleClick(index)
|
|
769
|
+
}, null, 14, _hoisted_3);
|
|
770
|
+
}), 128))])], 512)]),
|
|
771
|
+
_: 1
|
|
772
|
+
}, 512)], 544), __props.options.preview !== false && hoveredMarker.value ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", {
|
|
773
|
+
key: 0,
|
|
774
|
+
class: "docs-markdown-indicator__preview",
|
|
775
|
+
style: (0, vue.normalizeStyle)(previewStyle.value)
|
|
776
|
+
}, [(0, vue.createElementVNode)("div", _hoisted_4, (0, vue.toDisplayString)(hoveredMarker.value.title), 1), hoveredMarker.value.content ? ((0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_5, (0, vue.toDisplayString)(hoveredMarker.value.content), 1)) : (0, vue.createCommentVNode)("v-if", true)], 4)) : (0, vue.createCommentVNode)("v-if", true)], 14, _hoisted_1$1)) : (0, vue.createCommentVNode)("v-if", true);
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
//#endregion
|
|
781
|
+
//#region packages/markdown/src/wrapper.vue?vue&type=script&setup=true&lang.ts
|
|
782
|
+
var _hoisted_1 = { class: "docs-markdown" };
|
|
783
|
+
//#endregion
|
|
784
|
+
//#region packages/markdown/src/wrapper.vue
|
|
785
|
+
var wrapper_default = /* @__PURE__ */ (0, vue.defineComponent)({
|
|
786
|
+
__name: "wrapper",
|
|
787
|
+
props: {
|
|
788
|
+
indicator: {
|
|
789
|
+
type: [Boolean, Object],
|
|
790
|
+
default: true
|
|
791
|
+
},
|
|
792
|
+
locale: {},
|
|
793
|
+
modelValue: {},
|
|
794
|
+
value: {}
|
|
795
|
+
},
|
|
796
|
+
emits: ["update:modelValue", "change"],
|
|
797
|
+
setup(__props) {
|
|
798
|
+
const props = __props;
|
|
799
|
+
const content = (0, vue.ref)();
|
|
800
|
+
const inheritedLocale = (0, _deot_docs_locale.useLocale)();
|
|
801
|
+
const locale = (0, vue.computed)(() => props.locale || inheritedLocale.locale.value);
|
|
802
|
+
(0, _deot_docs_locale.provideLocale)(locale);
|
|
803
|
+
const indicatorOptions = (0, vue.computed)(() => {
|
|
804
|
+
if (props.indicator === false) return void 0;
|
|
805
|
+
return typeof props.indicator === "object" ? props.indicator : {};
|
|
806
|
+
});
|
|
807
|
+
const source = (0, vue.computed)(() => typeof props.modelValue === "string" ? props.modelValue : props.value);
|
|
808
|
+
const markdownBinding = (0, vue.computed)(() => ({
|
|
809
|
+
source: source.value,
|
|
810
|
+
locale: locale.value
|
|
811
|
+
}));
|
|
812
|
+
return (_ctx, _cache) => {
|
|
813
|
+
return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1, [indicatorOptions.value ? ((0, vue.openBlock)(), (0, vue.createBlock)(indicator_default, {
|
|
814
|
+
key: 0,
|
|
815
|
+
target: content.value,
|
|
816
|
+
options: indicatorOptions.value
|
|
817
|
+
}, null, 8, ["target", "options"])) : (0, vue.createCommentVNode)("v-if", true), (0, vue.withDirectives)((0, vue.createElementVNode)("div", {
|
|
818
|
+
ref_key: "content",
|
|
819
|
+
ref: content,
|
|
820
|
+
class: "docs-markdown-reset"
|
|
821
|
+
}, null, 512), [[(0, vue.unref)(vMarkdown), markdownBinding.value]])]);
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
//#endregion
|
|
826
|
+
exports.Markdown = wrapper_default;
|
|
827
|
+
exports.parseMarkdownSearchSections = parseMarkdownSearchSections;
|