@hejiayue/x-markdown-test 0.0.1-beta.107

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.
@@ -0,0 +1,1856 @@
1
+ import { h, toValue, computed, defineComponent, toRefs, shallowRef, watch, shallowReadonly, getCurrentScope, onScopeDispose, readonly, getCurrentInstance, onMounted, unref, toRaw, ref, isRef, onUnmounted, createElementBlock, openBlock, normalizeStyle, createElementVNode, toDisplayString, normalizeClass, createTextVNode, Fragment, renderList, createCommentVNode, renderSlot, createBlock, resolveDynamicComponent, createVNode, defineAsyncComponent } from "vue";
2
+ import { find, svg, html } from "property-information";
3
+ import deepmerge from "deepmerge";
4
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
5
+ import remarkParse from "remark-parse";
6
+ import remarkRehype from "remark-rehype";
7
+ import { unified } from "unified";
8
+ import rehypeKatex from "rehype-katex";
9
+ import rehypeRaw from "rehype-raw";
10
+ import remarkBreaks from "remark-breaks";
11
+ import remarkGfm from "remark-gfm";
12
+ import remarkMath from "remark-math";
13
+ import { visit } from "unist-util-visit";
14
+ import { flow, throttle } from "lodash-es";
15
+ function render(hast, attrs, slots, customAttrs) {
16
+ const keyCounter = {};
17
+ return h(
18
+ "div",
19
+ attrs,
20
+ renderChildren(
21
+ hast.children,
22
+ { listDepth: -1, listOrdered: false, listItemIndex: -1, svg: false },
23
+ hast,
24
+ slots ?? {},
25
+ toValue(customAttrs) ?? {},
26
+ keyCounter
27
+ )
28
+ );
29
+ }
30
+ function renderChildren(nodeList, ctx, parent, slots, customAttrs, keyCounter) {
31
+ return nodeList.map((node) => {
32
+ switch (node.type) {
33
+ case "text":
34
+ return node.value;
35
+ case "raw":
36
+ return node.value;
37
+ case "root":
38
+ return renderChildren(node.children, ctx, parent, slots, customAttrs, keyCounter);
39
+ case "element": {
40
+ const { attrs, context, aliasList, vnodeProps } = getVNodeInfos(node, parent, ctx, keyCounter, customAttrs);
41
+ for (let i = aliasList.length - 1; i >= 0; i--) {
42
+ const targetSlot = slots[aliasList[i]];
43
+ if (typeof targetSlot === "function") {
44
+ return targetSlot({
45
+ ...vnodeProps,
46
+ ...attrs,
47
+ children: () => renderChildren(node.children, context, node, slots, customAttrs, keyCounter)
48
+ });
49
+ }
50
+ }
51
+ return h(node.tagName, attrs, renderChildren(node.children, context, node, slots, customAttrs, keyCounter));
52
+ }
53
+ default:
54
+ return null;
55
+ }
56
+ });
57
+ }
58
+ function getVNodeInfos(node, parent, context, keyCounter, customAttrs) {
59
+ const aliasList = [];
60
+ let attrs = {};
61
+ const vnodeProps = {};
62
+ const ctx = { ...context };
63
+ if (node.type === "element") {
64
+ aliasList.push(node.tagName);
65
+ keyCounter[node.tagName] = node.tagName in keyCounter ? keyCounter[node.tagName] + 1 : 0;
66
+ vnodeProps.key = `${node.tagName}-${keyCounter[node.tagName]}`;
67
+ node.properties = node.properties || {};
68
+ if (node.tagName === "svg") {
69
+ ctx.svg = true;
70
+ }
71
+ attrs = Object.entries(node.properties).reduce((acc, [hastKey, value]) => {
72
+ const attrInfo = find(ctx.svg ? svg : html, hastKey);
73
+ acc[attrInfo.attribute] = value;
74
+ return acc;
75
+ }, {});
76
+ switch (node.tagName) {
77
+ case "h1":
78
+ case "h2":
79
+ case "h3":
80
+ case "h4":
81
+ case "h5":
82
+ case "h6":
83
+ vnodeProps.level = Number.parseFloat(node.tagName.slice(1));
84
+ aliasList.push("heading");
85
+ break;
86
+ case "code":
87
+ vnodeProps.languageOriginal = Array.isArray(attrs.class) ? attrs.class.find((cls) => cls.startsWith("language-")) : "";
88
+ vnodeProps.language = vnodeProps.languageOriginal ? vnodeProps.languageOriginal.replace("language-", "") : "";
89
+ vnodeProps.inline = "tagName" in parent && parent.tagName !== "pre";
90
+ vnodeProps.content = node.children[0]?.value ?? "";
91
+ aliasList.push(vnodeProps.inline ? "inline-code" : "block-code");
92
+ break;
93
+ case "thead":
94
+ case "tbody":
95
+ ctx.currentContext = node.tagName;
96
+ break;
97
+ case "td":
98
+ case "th":
99
+ case "tr":
100
+ vnodeProps.isHead = context.currentContext === "thead";
101
+ break;
102
+ case "ul":
103
+ case "ol":
104
+ ctx.listDepth = context.listDepth + 1;
105
+ ctx.listOrdered = node.tagName === "ol";
106
+ ctx.listItemIndex = -1;
107
+ vnodeProps.ordered = ctx.listOrdered;
108
+ vnodeProps.depth = ctx.listDepth;
109
+ aliasList.push("list");
110
+ break;
111
+ case "li":
112
+ ctx.listItemIndex++;
113
+ vnodeProps.ordered = ctx.listOrdered;
114
+ vnodeProps.depth = ctx.listDepth;
115
+ vnodeProps.index = ctx.listItemIndex;
116
+ aliasList.push("list-item");
117
+ break;
118
+ case "slot":
119
+ if (typeof node.properties["slot-name"] === "string") {
120
+ aliasList.push(`${node.properties["slot-name"]}`);
121
+ delete node.properties["slot-name"];
122
+ }
123
+ break;
124
+ }
125
+ attrs = computeAttrs(
126
+ node,
127
+ aliasList,
128
+ vnodeProps,
129
+ { ...attrs },
130
+ // TODO: fix this
131
+ customAttrs
132
+ );
133
+ }
134
+ return {
135
+ attrs,
136
+ context: ctx,
137
+ aliasList,
138
+ vnodeProps
139
+ };
140
+ }
141
+ function computeAttrs(node, aliasList, vnodeProps, attrs, customAttrs) {
142
+ const result = {
143
+ ...attrs
144
+ };
145
+ for (let i = aliasList.length - 1; i >= 0; i--) {
146
+ const name = aliasList[i];
147
+ if (name in customAttrs) {
148
+ const customAttr = customAttrs[name];
149
+ return {
150
+ ...result,
151
+ ...typeof customAttr === "function" ? customAttr(node, { ...attrs, ...vnodeProps }) : customAttr
152
+ };
153
+ }
154
+ }
155
+ return result;
156
+ }
157
+ function useMarkdownProcessor(options) {
158
+ const processor = computed(() => {
159
+ return createProcessor({
160
+ prePlugins: [remarkParse, ...toValue(options?.remarkPlugins) ?? []],
161
+ rehypePlugins: toValue(options?.rehypePlugins),
162
+ rehypeOptions: toValue(options?.rehypeOptions),
163
+ sanitize: toValue(options?.sanitize),
164
+ sanitizeOptions: toValue(options?.sanitizeOptions)
165
+ });
166
+ });
167
+ return { processor };
168
+ }
169
+ function createProcessor(options) {
170
+ return unified().use(options?.prePlugins ?? []).use(remarkRehype, {
171
+ allowDangerousHtml: true,
172
+ ...options?.rehypeOptions || {}
173
+ }).use(options?.rehypePlugins ?? []).use(
174
+ options?.sanitize ? [
175
+ [
176
+ rehypeSanitize,
177
+ deepmerge(
178
+ defaultSchema,
179
+ options?.sanitizeOptions?.sanitizeOptions || {},
180
+ options?.sanitizeOptions?.mergeOptions || {}
181
+ )
182
+ ]
183
+ ] : []
184
+ );
185
+ }
186
+ const sharedProps = {
187
+ markdown: {
188
+ type: String,
189
+ default: ""
190
+ },
191
+ customAttrs: {
192
+ type: Object,
193
+ default: () => ({})
194
+ },
195
+ remarkPlugins: {
196
+ type: Array,
197
+ default: () => []
198
+ },
199
+ rehypePlugins: {
200
+ type: Array,
201
+ default: () => []
202
+ },
203
+ rehypeOptions: {
204
+ type: Object,
205
+ default: () => ({})
206
+ },
207
+ sanitize: {
208
+ type: Boolean,
209
+ default: false
210
+ },
211
+ sanitizeOptions: {
212
+ type: Object,
213
+ default: () => ({})
214
+ }
215
+ };
216
+ const vueMarkdownImpl = defineComponent({
217
+ name: "VueMarkdown",
218
+ props: sharedProps,
219
+ setup(props, { slots, attrs }) {
220
+ const { markdown, remarkPlugins, rehypePlugins, rehypeOptions, sanitize, sanitizeOptions, customAttrs } = toRefs(props);
221
+ const { processor } = useMarkdownProcessor({
222
+ remarkPlugins,
223
+ rehypePlugins,
224
+ rehypeOptions,
225
+ sanitize,
226
+ sanitizeOptions
227
+ });
228
+ return () => {
229
+ const mdast = processor.value.parse(markdown.value);
230
+ const hast = processor.value.runSync(mdast);
231
+ return render(hast, attrs, slots, customAttrs.value);
232
+ };
233
+ }
234
+ });
235
+ const vueMarkdownAsyncImpl = defineComponent({
236
+ name: "VueMarkdownAsync",
237
+ props: sharedProps,
238
+ async setup(props, { slots, attrs }) {
239
+ const { markdown, remarkPlugins, rehypePlugins, rehypeOptions, sanitize, sanitizeOptions, customAttrs } = toRefs(props);
240
+ const { processor } = useMarkdownProcessor({
241
+ remarkPlugins,
242
+ rehypePlugins,
243
+ rehypeOptions,
244
+ sanitize,
245
+ sanitizeOptions
246
+ });
247
+ const hast = shallowRef(null);
248
+ const process = async () => {
249
+ const mdast = processor.value.parse(markdown.value);
250
+ hast.value = await processor.value.run(mdast);
251
+ };
252
+ watch(() => [markdown.value, processor.value], process, { flush: "sync" });
253
+ await process();
254
+ return () => {
255
+ return hast.value ? render(hast.value, attrs, slots, customAttrs.value) : null;
256
+ };
257
+ }
258
+ });
259
+ const VueMarkdown = vueMarkdownImpl;
260
+ const VueMarkdownAsync = vueMarkdownAsyncImpl;
261
+ function tryOnScopeDispose(fn, failSilently) {
262
+ if (getCurrentScope()) {
263
+ onScopeDispose(fn, failSilently);
264
+ return true;
265
+ }
266
+ return false;
267
+ }
268
+ const isClient = typeof window !== "undefined" && typeof document !== "undefined";
269
+ const toString = Object.prototype.toString;
270
+ const isObject = (val) => toString.call(val) === "[object Object]";
271
+ function createSingletonPromise(fn) {
272
+ let _promise;
273
+ function wrapper() {
274
+ if (!_promise) _promise = fn();
275
+ return _promise;
276
+ }
277
+ wrapper.reset = async () => {
278
+ const _prev = _promise;
279
+ _promise = void 0;
280
+ if (_prev) await _prev;
281
+ };
282
+ return wrapper;
283
+ }
284
+ function toArray(value) {
285
+ return Array.isArray(value) ? value : [value];
286
+ }
287
+ function useTimeoutFn(cb, interval, options = {}) {
288
+ const { immediate = true, immediateCallback = false } = options;
289
+ const isPending = shallowRef(false);
290
+ let timer;
291
+ function clear() {
292
+ if (timer) {
293
+ clearTimeout(timer);
294
+ timer = void 0;
295
+ }
296
+ }
297
+ function stop() {
298
+ isPending.value = false;
299
+ clear();
300
+ }
301
+ function start(...args) {
302
+ if (immediateCallback) cb();
303
+ clear();
304
+ isPending.value = true;
305
+ timer = setTimeout(() => {
306
+ isPending.value = false;
307
+ timer = void 0;
308
+ cb(...args);
309
+ }, toValue(interval));
310
+ }
311
+ if (immediate) {
312
+ isPending.value = true;
313
+ if (isClient) start();
314
+ }
315
+ tryOnScopeDispose(stop);
316
+ return {
317
+ isPending: shallowReadonly(isPending),
318
+ start,
319
+ stop
320
+ };
321
+ }
322
+ function watchImmediate(source, cb, options) {
323
+ return watch(source, cb, {
324
+ ...options,
325
+ immediate: true
326
+ });
327
+ }
328
+ const defaultWindow = isClient ? window : void 0;
329
+ const defaultNavigator = isClient ? window.navigator : void 0;
330
+ function unrefElement(elRef) {
331
+ var _$el;
332
+ const plain = toValue(elRef);
333
+ return (_$el = plain === null || plain === void 0 ? void 0 : plain.$el) !== null && _$el !== void 0 ? _$el : plain;
334
+ }
335
+ function useEventListener(...args) {
336
+ const register = (el, event, listener, options) => {
337
+ el.addEventListener(event, listener, options);
338
+ return () => el.removeEventListener(event, listener, options);
339
+ };
340
+ const firstParamTargets = computed(() => {
341
+ const test = toArray(toValue(args[0])).filter((e) => e != null);
342
+ return test.every((e) => typeof e !== "string") ? test : void 0;
343
+ });
344
+ return watchImmediate(() => {
345
+ var _firstParamTargets$va, _firstParamTargets$va2;
346
+ return [
347
+ (_firstParamTargets$va = (_firstParamTargets$va2 = firstParamTargets.value) === null || _firstParamTargets$va2 === void 0 ? void 0 : _firstParamTargets$va2.map((e) => unrefElement(e))) !== null && _firstParamTargets$va !== void 0 ? _firstParamTargets$va : [defaultWindow].filter((e) => e != null),
348
+ toArray(toValue(firstParamTargets.value ? args[1] : args[0])),
349
+ toArray(unref(firstParamTargets.value ? args[2] : args[1])),
350
+ toValue(firstParamTargets.value ? args[3] : args[2])
351
+ ];
352
+ }, ([raw_targets, raw_events, raw_listeners, raw_options], _, onCleanup) => {
353
+ if (!(raw_targets === null || raw_targets === void 0 ? void 0 : raw_targets.length) || !(raw_events === null || raw_events === void 0 ? void 0 : raw_events.length) || !(raw_listeners === null || raw_listeners === void 0 ? void 0 : raw_listeners.length)) return;
354
+ const optionsClone = isObject(raw_options) ? { ...raw_options } : raw_options;
355
+ const cleanups = raw_targets.flatMap((el) => raw_events.flatMap((event) => raw_listeners.map((listener) => register(el, event, listener, optionsClone))));
356
+ onCleanup(() => {
357
+ cleanups.forEach((fn) => fn());
358
+ });
359
+ }, { flush: "post" });
360
+ }
361
+ // @__NO_SIDE_EFFECTS__
362
+ function useMounted() {
363
+ const isMounted = shallowRef(false);
364
+ const instance = getCurrentInstance();
365
+ if (instance) onMounted(() => {
366
+ isMounted.value = true;
367
+ }, instance);
368
+ return isMounted;
369
+ }
370
+ // @__NO_SIDE_EFFECTS__
371
+ function useSupported(callback) {
372
+ const isMounted = /* @__PURE__ */ useMounted();
373
+ return computed(() => {
374
+ isMounted.value;
375
+ return Boolean(callback());
376
+ });
377
+ }
378
+ // @__NO_SIDE_EFFECTS__
379
+ function usePermission(permissionDesc, options = {}) {
380
+ const { controls = false, navigator: navigator$1 = defaultNavigator } = options;
381
+ const isSupported = /* @__PURE__ */ useSupported(() => navigator$1 && "permissions" in navigator$1);
382
+ const permissionStatus = shallowRef();
383
+ const desc = typeof permissionDesc === "string" ? { name: permissionDesc } : permissionDesc;
384
+ const state = shallowRef();
385
+ const update = () => {
386
+ var _permissionStatus$val, _permissionStatus$val2;
387
+ state.value = (_permissionStatus$val = (_permissionStatus$val2 = permissionStatus.value) === null || _permissionStatus$val2 === void 0 ? void 0 : _permissionStatus$val2.state) !== null && _permissionStatus$val !== void 0 ? _permissionStatus$val : "prompt";
388
+ };
389
+ useEventListener(permissionStatus, "change", update, { passive: true });
390
+ const query = createSingletonPromise(async () => {
391
+ if (!isSupported.value) return;
392
+ if (!permissionStatus.value) try {
393
+ permissionStatus.value = await navigator$1.permissions.query(desc);
394
+ } catch (_unused) {
395
+ permissionStatus.value = void 0;
396
+ } finally {
397
+ update();
398
+ }
399
+ if (controls) return toRaw(permissionStatus.value);
400
+ });
401
+ query();
402
+ if (controls) return {
403
+ state,
404
+ isSupported,
405
+ query
406
+ };
407
+ else return state;
408
+ }
409
+ function useClipboard(options = {}) {
410
+ const { navigator: navigator$1 = defaultNavigator, read = false, source, copiedDuring = 1500, legacy = false } = options;
411
+ const isClipboardApiSupported = /* @__PURE__ */ useSupported(() => navigator$1 && "clipboard" in navigator$1);
412
+ const permissionRead = /* @__PURE__ */ usePermission("clipboard-read");
413
+ const permissionWrite = /* @__PURE__ */ usePermission("clipboard-write");
414
+ const isSupported = computed(() => isClipboardApiSupported.value || legacy);
415
+ const text = shallowRef("");
416
+ const copied = shallowRef(false);
417
+ const timeout = useTimeoutFn(() => copied.value = false, copiedDuring, { immediate: false });
418
+ async function updateText() {
419
+ let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionRead.value));
420
+ if (!useLegacy) try {
421
+ text.value = await navigator$1.clipboard.readText();
422
+ } catch (_unused) {
423
+ useLegacy = true;
424
+ }
425
+ if (useLegacy) text.value = legacyRead();
426
+ }
427
+ if (isSupported.value && read) useEventListener(["copy", "cut"], updateText, { passive: true });
428
+ async function copy(value = toValue(source)) {
429
+ if (isSupported.value && value != null) {
430
+ let useLegacy = !(isClipboardApiSupported.value && isAllowed(permissionWrite.value));
431
+ if (!useLegacy) try {
432
+ await navigator$1.clipboard.writeText(value);
433
+ } catch (_unused2) {
434
+ useLegacy = true;
435
+ }
436
+ if (useLegacy) legacyCopy(value);
437
+ text.value = value;
438
+ copied.value = true;
439
+ timeout.start();
440
+ }
441
+ }
442
+ function legacyCopy(value) {
443
+ const ta = document.createElement("textarea");
444
+ ta.value = value;
445
+ ta.style.position = "absolute";
446
+ ta.style.opacity = "0";
447
+ ta.setAttribute("readonly", "");
448
+ document.body.appendChild(ta);
449
+ ta.select();
450
+ document.execCommand("copy");
451
+ ta.remove();
452
+ }
453
+ function legacyRead() {
454
+ var _document$getSelectio, _document, _document$getSelectio2;
455
+ return (_document$getSelectio = (_document = document) === null || _document === void 0 || (_document$getSelectio2 = _document.getSelection) === null || _document$getSelectio2 === void 0 || (_document$getSelectio2 = _document$getSelectio2.call(_document)) === null || _document$getSelectio2 === void 0 ? void 0 : _document$getSelectio2.toString()) !== null && _document$getSelectio !== void 0 ? _document$getSelectio : "";
456
+ }
457
+ function isAllowed(status) {
458
+ return status === "granted" || status === "prompt";
459
+ }
460
+ return {
461
+ isSupported,
462
+ text: readonly(text),
463
+ copied: readonly(copied),
464
+ copy
465
+ };
466
+ }
467
+ const SHIKI_PKG = "shiki";
468
+ const SHIKI_STREAM_PKG = "shiki-stream";
469
+ let shikiModulePromise = null;
470
+ let shikiStreamModulePromise = null;
471
+ let hasShownDependencyHint = false;
472
+ const showDependencyHint = () => {
473
+ if (hasShownDependencyHint) return;
474
+ hasShownDependencyHint = true;
475
+ console.log(
476
+ "%c[x-markdown]%c 代码高亮功能已降级为纯文本模式",
477
+ "font-weight: bold; color: #0066cc;",
478
+ "color: #666;"
479
+ );
480
+ console.log(
481
+ "%c如需语法高亮功能,请安装以下依赖:",
482
+ "color: #666; font-weight: bold;"
483
+ );
484
+ console.log(
485
+ "%c pnpm add shiki shiki-stream",
486
+ "color: #00aa00; font-family: monospace;"
487
+ );
488
+ console.log(
489
+ "%c安装后请重启开发服务器",
490
+ "color: #999; font-size: 12px;"
491
+ );
492
+ };
493
+ const loadShiki = () => {
494
+ if (!shikiModulePromise) {
495
+ shikiModulePromise = import(SHIKI_PKG).catch(() => {
496
+ showDependencyHint();
497
+ return null;
498
+ });
499
+ }
500
+ return shikiModulePromise;
501
+ };
502
+ const loadShikiStream = () => {
503
+ if (!shikiStreamModulePromise) {
504
+ shikiStreamModulePromise = import(SHIKI_STREAM_PKG).catch(() => {
505
+ showDependencyHint();
506
+ return null;
507
+ });
508
+ }
509
+ return shikiStreamModulePromise;
510
+ };
511
+ const tokensToLineTokens = (tokens) => {
512
+ if (!tokens.length) return [[]];
513
+ const lines = [[]];
514
+ let currentLine = lines[0];
515
+ const startNewLine = () => {
516
+ currentLine = [];
517
+ lines.push(currentLine);
518
+ };
519
+ tokens.forEach((token) => {
520
+ const content = token.content ?? "";
521
+ if (content === "\n") {
522
+ startNewLine();
523
+ return;
524
+ }
525
+ if (!content.includes("\n")) {
526
+ currentLine.push(token);
527
+ return;
528
+ }
529
+ const segments = content.split("\n");
530
+ segments.forEach((segment, index) => {
531
+ if (segment) {
532
+ currentLine.push({
533
+ ...token,
534
+ content: segment
535
+ });
536
+ }
537
+ if (index < segments.length - 1) {
538
+ startNewLine();
539
+ }
540
+ });
541
+ });
542
+ return lines.length === 0 ? [[]] : lines;
543
+ };
544
+ const createPreStyle = (bg, fg) => {
545
+ if (!bg && !fg) return void 0;
546
+ return {
547
+ backgroundColor: bg,
548
+ color: fg
549
+ };
550
+ };
551
+ function useHighlight(text, options) {
552
+ const streaming = ref();
553
+ const isLoading = ref(false);
554
+ const error = ref(null);
555
+ let tokenizer = null;
556
+ let previousText = "";
557
+ let highlighter = null;
558
+ let currentUsedLang = "";
559
+ let lastRequestedLang = "";
560
+ const effectiveTheme = computed(() => {
561
+ const theme = isRef(options.theme) ? options.theme.value : options.theme;
562
+ return theme || "slack-dark";
563
+ });
564
+ const effectiveLanguage = computed(() => {
565
+ return toValue(options.language) || "text";
566
+ });
567
+ const lines = computed(() => streaming.value?.lines || [[]]);
568
+ const preStyle = computed(() => streaming.value?.preStyle);
569
+ const updateTokens = async (nextText, forceReset = false) => {
570
+ if (!tokenizer) return;
571
+ if (forceReset) {
572
+ tokenizer.clear();
573
+ previousText = "";
574
+ }
575
+ const canAppend = !forceReset && nextText.startsWith(previousText);
576
+ let chunk = nextText;
577
+ if (canAppend) {
578
+ chunk = nextText.slice(previousText.length);
579
+ } else if (!forceReset) {
580
+ tokenizer.clear();
581
+ }
582
+ previousText = nextText;
583
+ if (!chunk) {
584
+ const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable];
585
+ streaming.value = {
586
+ colorReplacements: options.colorReplacements,
587
+ lines: mergedTokens.length ? tokensToLineTokens(mergedTokens) : [[]],
588
+ preStyle: streaming.value?.preStyle
589
+ };
590
+ return;
591
+ }
592
+ try {
593
+ await tokenizer.enqueue(chunk);
594
+ const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable];
595
+ streaming.value = {
596
+ colorReplacements: options.colorReplacements,
597
+ lines: tokensToLineTokens(mergedTokens),
598
+ preStyle: streaming.value?.preStyle
599
+ };
600
+ } catch (err) {
601
+ console.error("[x-markdown] Streaming highlighting failed:", err);
602
+ error.value = err;
603
+ }
604
+ };
605
+ const initHighlighter = async () => {
606
+ isLoading.value = true;
607
+ error.value = null;
608
+ let currentLang = effectiveLanguage.value;
609
+ const currentTheme = effectiveTheme.value;
610
+ try {
611
+ const mod = await loadShiki();
612
+ if (!mod) {
613
+ console.warn("[x-markdown] Shiki not available, falling back to plain text mode");
614
+ streaming.value = {
615
+ colorReplacements: options.colorReplacements,
616
+ lines: [[{ content: text.value }]],
617
+ preStyle: void 0
618
+ };
619
+ return;
620
+ }
621
+ highlighter = await mod.getSingletonHighlighter({
622
+ langs: [],
623
+ themes: [currentTheme]
624
+ });
625
+ lastRequestedLang = currentLang;
626
+ try {
627
+ await highlighter.loadLanguage(currentLang);
628
+ currentUsedLang = currentLang;
629
+ } catch {
630
+ console.warn(`[x-markdown] Failed to load language: ${currentLang}, falling back to plaintext`);
631
+ currentLang = "plaintext";
632
+ currentUsedLang = "plaintext";
633
+ }
634
+ const StreamMod = await loadShikiStream();
635
+ if (!StreamMod) {
636
+ console.warn("[x-markdown] shiki-stream not available, using non-streaming mode");
637
+ const tokens = highlighter.codeToThemedTokens(text.value, currentLang, currentTheme);
638
+ streaming.value = {
639
+ colorReplacements: options.colorReplacements,
640
+ lines: tokensToLineTokens(tokens),
641
+ preStyle: createPreStyle(
642
+ highlighter.getTheme(currentTheme)?.bg,
643
+ highlighter.getTheme(currentTheme)?.fg
644
+ )
645
+ };
646
+ return;
647
+ }
648
+ const ShikiStreamTokenizer = StreamMod.ShikiStreamTokenizer || StreamMod.default;
649
+ tokenizer = new ShikiStreamTokenizer({
650
+ highlighter,
651
+ lang: currentLang,
652
+ theme: currentTheme
653
+ });
654
+ previousText = "";
655
+ const themeInfo = highlighter.getTheme(currentTheme);
656
+ const preStyleValue = createPreStyle(themeInfo?.bg, themeInfo?.fg);
657
+ if (text.value) {
658
+ await updateTokens(text.value, true);
659
+ if (streaming.value) {
660
+ streaming.value.preStyle = preStyleValue;
661
+ }
662
+ } else {
663
+ streaming.value = {
664
+ colorReplacements: options.colorReplacements,
665
+ lines: [[]],
666
+ preStyle: preStyleValue
667
+ };
668
+ }
669
+ } catch (err) {
670
+ console.error("[x-markdown] Highlighter initialization failed:", err);
671
+ error.value = err;
672
+ streaming.value = {
673
+ colorReplacements: options.colorReplacements,
674
+ lines: [[{ content: text.value }]],
675
+ preStyle: void 0
676
+ };
677
+ } finally {
678
+ isLoading.value = false;
679
+ }
680
+ };
681
+ watch(
682
+ () => [effectiveLanguage.value, effectiveTheme.value],
683
+ async ([newLang]) => {
684
+ const requestedLang = newLang;
685
+ if (highlighter && currentUsedLang === "plaintext" && requestedLang !== lastRequestedLang && requestedLang !== "plaintext") {
686
+ try {
687
+ await highlighter.loadLanguage(requestedLang);
688
+ initHighlighter();
689
+ return;
690
+ } catch {
691
+ lastRequestedLang = requestedLang;
692
+ return;
693
+ }
694
+ }
695
+ initHighlighter();
696
+ },
697
+ { immediate: true }
698
+ );
699
+ watch(text, async (newText) => {
700
+ const requestedLang = effectiveLanguage.value;
701
+ if (highlighter && currentUsedLang === "plaintext" && requestedLang !== lastRequestedLang && requestedLang !== "plaintext") {
702
+ try {
703
+ await highlighter.loadLanguage(requestedLang);
704
+ await initHighlighter();
705
+ return;
706
+ } catch {
707
+ lastRequestedLang = requestedLang;
708
+ }
709
+ }
710
+ if (tokenizer) {
711
+ updateTokens(newText);
712
+ } else if (!highlighter) {
713
+ streaming.value = {
714
+ colorReplacements: options.colorReplacements,
715
+ lines: [[{ content: newText }]],
716
+ preStyle: streaming.value?.preStyle
717
+ };
718
+ }
719
+ });
720
+ onUnmounted(() => {
721
+ tokenizer?.clear();
722
+ tokenizer = null;
723
+ previousText = "";
724
+ });
725
+ return {
726
+ streaming,
727
+ lines,
728
+ preStyle,
729
+ isLoading,
730
+ error
731
+ };
732
+ }
733
+ const _hoisted_1$1 = { class: "x-md-syntax-code-block" };
734
+ const _hoisted_2$1 = { class: "x-md-code-content" };
735
+ const _hoisted_3$1 = { key: 0 };
736
+ const SHIKI_CORE_PKG$1 = "@shikijs/core";
737
+ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
738
+ ...{
739
+ name: "SyntaxCodeBlock"
740
+ },
741
+ __name: "SyntaxCodeBlock",
742
+ props: {
743
+ code: {},
744
+ language: {},
745
+ lightTheme: { default: "vitesse-light" },
746
+ darkTheme: { default: "vitesse-dark" },
747
+ isDark: { type: Boolean, default: false },
748
+ colorReplacements: {},
749
+ codeMaxHeight: {},
750
+ enableAnimate: { type: Boolean, default: false }
751
+ },
752
+ setup(__props, { expose: __expose }) {
753
+ let getTokenStyleObjectFn = null;
754
+ onMounted(async () => {
755
+ const mod = await Function(`return import('${SHIKI_CORE_PKG$1}')`)().catch(() => {
756
+ return { getTokenStyleObject: () => ({}) };
757
+ });
758
+ getTokenStyleObjectFn = mod.getTokenStyleObject;
759
+ });
760
+ const props = __props;
761
+ const code = computed(() => props.code.trim());
762
+ const language = computed(() => props.language || "text");
763
+ const actualTheme = computed(() => props.isDark ? props.darkTheme : props.lightTheme);
764
+ const { lines, preStyle } = useHighlight(code, {
765
+ language,
766
+ theme: actualTheme,
767
+ colorReplacements: props.colorReplacements
768
+ });
769
+ const applyColorReplacement = (color, replacements) => {
770
+ if (!replacements) return color;
771
+ return replacements[color.toLowerCase()] || color;
772
+ };
773
+ const normalizeStyleKeys = (style) => {
774
+ const normalized = {};
775
+ Object.entries(style).forEach(([key, value]) => {
776
+ const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
777
+ normalized[camelKey] = value;
778
+ });
779
+ return normalized;
780
+ };
781
+ const getTokenStyle = (token) => {
782
+ if (token.htmlStyle) {
783
+ const baseStyle2 = normalizeStyleKeys(token.htmlStyle);
784
+ if (!props.colorReplacements) return baseStyle2;
785
+ const style2 = { ...baseStyle2 };
786
+ if (style2.color && typeof style2.color === "string") {
787
+ style2.color = applyColorReplacement(style2.color, props.colorReplacements);
788
+ }
789
+ if (style2.backgroundColor && typeof style2.backgroundColor === "string") {
790
+ style2.backgroundColor = applyColorReplacement(style2.backgroundColor, props.colorReplacements);
791
+ }
792
+ return style2;
793
+ }
794
+ if (!getTokenStyleObjectFn) {
795
+ return {};
796
+ }
797
+ const rawStyle = getTokenStyleObjectFn(token);
798
+ const baseStyle = normalizeStyleKeys(rawStyle);
799
+ if (!props.colorReplacements) return baseStyle;
800
+ const style = { ...baseStyle };
801
+ if (style.color && typeof style.color === "string") {
802
+ style.color = applyColorReplacement(style.color, props.colorReplacements);
803
+ }
804
+ if (style.backgroundColor && typeof style.backgroundColor === "string") {
805
+ style.backgroundColor = applyColorReplacement(style.backgroundColor, props.colorReplacements);
806
+ }
807
+ return style;
808
+ };
809
+ const showFallback = computed(() => !lines.value?.length);
810
+ const codeContainerStyle = computed(() => ({
811
+ ...preStyle.value,
812
+ maxHeight: props.codeMaxHeight
813
+ }));
814
+ __expose({
815
+ lines,
816
+ code,
817
+ language,
818
+ actualTheme
819
+ });
820
+ return (_ctx, _cache) => {
821
+ return openBlock(), createElementBlock("div", _hoisted_1$1, [
822
+ showFallback.value ? (openBlock(), createElementBlock("pre", {
823
+ key: 0,
824
+ style: normalizeStyle(codeContainerStyle.value)
825
+ }, [
826
+ createElementVNode("code", null, toDisplayString(code.value), 1)
827
+ ], 4)) : (openBlock(), createElementBlock("pre", {
828
+ key: 1,
829
+ class: normalizeClass(["shiki", actualTheme.value]),
830
+ style: normalizeStyle(codeContainerStyle.value),
831
+ tabindex: "0"
832
+ }, [
833
+ _cache[4] || (_cache[4] = createTextVNode(" ", -1)),
834
+ createElementVNode("code", _hoisted_2$1, [
835
+ _cache[2] || (_cache[2] = createTextVNode("\n ", -1)),
836
+ (openBlock(true), createElementBlock(Fragment, null, renderList(unref(lines), (line, i) => {
837
+ return openBlock(), createElementBlock("span", {
838
+ key: i,
839
+ class: "x-md-code-line"
840
+ }, [
841
+ _cache[0] || (_cache[0] = createTextVNode("\n ", -1)),
842
+ !line.length ? (openBlock(), createElementBlock("span", _hoisted_3$1, " ")) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(line, (token, j) => {
843
+ return openBlock(), createElementBlock("span", {
844
+ key: j,
845
+ style: normalizeStyle(getTokenStyle(token)),
846
+ class: normalizeClass({ "x-md-animated-word": props.enableAnimate })
847
+ }, toDisplayString(token.content), 7);
848
+ }), 128)),
849
+ _cache[1] || (_cache[1] = createTextVNode("\n ", -1))
850
+ ]);
851
+ }), 128)),
852
+ _cache[3] || (_cache[3] = createTextVNode("\n ", -1))
853
+ ]),
854
+ _cache[5] || (_cache[5] = createTextVNode("\n ", -1))
855
+ ], 6))
856
+ ]);
857
+ };
858
+ }
859
+ });
860
+ const _export_sfc = (sfc, props) => {
861
+ const target = sfc.__vccOpts || sfc;
862
+ for (const [key, val] of props) {
863
+ target[key] = val;
864
+ }
865
+ return target;
866
+ };
867
+ const SyntaxCodeBlock = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["__scopeId", "data-v-7cbaa1d1"]]);
868
+ const _hoisted_1 = { class: "x-md-code-header" };
869
+ const _hoisted_2 = { class: "x-md-code-header__left" };
870
+ const _hoisted_3 = ["title"];
871
+ const _hoisted_4 = { class: "x-md-code-lang" };
872
+ const _hoisted_5 = { class: "x-md-code-header__right" };
873
+ const _hoisted_6 = ["title", "disabled", "onClick"];
874
+ const _hoisted_7 = {
875
+ key: 0,
876
+ class: "x-md-copy-icon",
877
+ width: "16",
878
+ height: "16",
879
+ xmlns: "http://www.w3.org/2000/svg",
880
+ viewBox: "0 0 1024 1024"
881
+ };
882
+ const _hoisted_8 = {
883
+ key: 1,
884
+ class: "x-md-copy-icon",
885
+ width: "16",
886
+ height: "16",
887
+ xmlns: "http://www.w3.org/2000/svg",
888
+ viewBox: "0 0 1024 1024"
889
+ };
890
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
891
+ ...{
892
+ name: "CodeBlock"
893
+ },
894
+ __name: "index",
895
+ props: {
896
+ code: {},
897
+ language: {},
898
+ lightTheme: { default: "vitesse-light" },
899
+ darkTheme: { default: "vitesse-dark" },
900
+ isDark: { type: Boolean, default: false },
901
+ colorReplacements: {},
902
+ codeMaxHeight: {},
903
+ showCodeBlockHeader: { type: Boolean, default: true },
904
+ enableAnimate: { type: Boolean, default: false },
905
+ codeBlockActions: { default: void 0 },
906
+ stickyCodeBlockHeader: { type: Boolean, default: true }
907
+ },
908
+ setup(__props, { expose: __expose }) {
909
+ const { copy, copied } = useClipboard({ copiedDuring: 2e3 });
910
+ const collapsed = ref(false);
911
+ const syntaxCodeBlockRef = ref(null);
912
+ const toggleCollapse = () => {
913
+ collapsed.value = !collapsed.value;
914
+ };
915
+ const props = __props;
916
+ const code = computed(() => props.code.trim());
917
+ const language = computed(() => props.language || "text");
918
+ const normalizedActions = computed(() => {
919
+ return props.codeBlockActions || [];
920
+ });
921
+ const filteredActions = computed(() => {
922
+ return normalizedActions.value.filter((action) => {
923
+ if (!action.show) return true;
924
+ return action.show(slotProps.value);
925
+ });
926
+ });
927
+ const slotProps = computed(() => ({
928
+ language: language.value,
929
+ code: code.value,
930
+ copy,
931
+ copied: copied.value,
932
+ collapsed: collapsed.value,
933
+ toggleCollapse
934
+ }));
935
+ function renderActionIcon(action) {
936
+ if (!action.icon) return null;
937
+ if (typeof action.icon === "string") {
938
+ return h("span", {
939
+ class: "x-md-action-icon",
940
+ innerHTML: action.icon
941
+ });
942
+ }
943
+ if (typeof action.icon === "function") {
944
+ try {
945
+ const result = action.icon(slotProps.value);
946
+ if (result && typeof result === "object" && "__v_isVNode" in result) {
947
+ return result;
948
+ }
949
+ } catch {
950
+ }
951
+ return h(action.icon);
952
+ }
953
+ return h(action.icon);
954
+ }
955
+ function handleActionClick(action) {
956
+ if (action.disabled) return;
957
+ action.onClick?.(slotProps.value);
958
+ }
959
+ __expose({
960
+ copy,
961
+ copied,
962
+ collapsed,
963
+ toggleCollapse,
964
+ syntaxCodeBlockRef
965
+ });
966
+ return (_ctx, _cache) => {
967
+ return openBlock(), createElementBlock("div", {
968
+ class: normalizeClass(["x-md-code-block", { "x-md-code-block--dark": props.isDark }])
969
+ }, [
970
+ __props.showCodeBlockHeader ? (openBlock(), createElementBlock("div", {
971
+ key: 0,
972
+ class: normalizeClass(["x-md-code-header-wrapper", [{ "x-md-code-header-wrapper--sticky": props.stickyCodeBlockHeader }, { "x-md-code-header-wrapper--collapsed": collapsed.value }]])
973
+ }, [
974
+ createElementVNode("div", _hoisted_1, [
975
+ renderSlot(_ctx.$slots, "codeHeader", {
976
+ language: language.value,
977
+ code: code.value,
978
+ copy: unref(copy),
979
+ copied: unref(copied),
980
+ collapsed: collapsed.value,
981
+ toggleCollapse
982
+ }, () => [
983
+ createElementVNode("div", _hoisted_2, [
984
+ createElementVNode("button", {
985
+ class: normalizeClass(["x-md-collapse-btn", { "x-md-collapse-btn--collapsed": collapsed.value }]),
986
+ onClick: toggleCollapse,
987
+ title: collapsed.value ? "展开代码" : "折叠代码"
988
+ }, [..._cache[1] || (_cache[1] = [
989
+ createElementVNode("svg", {
990
+ class: "x-md-collapse-icon",
991
+ viewBox: "0 0 24 24",
992
+ width: "14",
993
+ height: "14",
994
+ fill: "none",
995
+ stroke: "currentColor",
996
+ "stroke-width": "2",
997
+ "stroke-linecap": "round",
998
+ "stroke-linejoin": "round"
999
+ }, [
1000
+ createElementVNode("polyline", { points: "6 9 12 15 18 9" })
1001
+ ], -1)
1002
+ ])], 10, _hoisted_3),
1003
+ createElementVNode("span", _hoisted_4, toDisplayString(language.value), 1)
1004
+ ]),
1005
+ createElementVNode("div", _hoisted_5, [
1006
+ renderSlot(_ctx.$slots, "codeActions", {
1007
+ code: code.value,
1008
+ copy: unref(copy),
1009
+ copied: unref(copied)
1010
+ }, () => [
1011
+ (openBlock(true), createElementBlock(Fragment, null, renderList(filteredActions.value, (action) => {
1012
+ return openBlock(), createElementBlock("button", {
1013
+ key: action.key,
1014
+ class: normalizeClass(["x-md-action-btn", [action.class, { "x-md-action-btn--disabled": action.disabled }]]),
1015
+ style: normalizeStyle(action.style),
1016
+ title: action.title,
1017
+ disabled: action.disabled,
1018
+ onClick: ($event) => handleActionClick(action)
1019
+ }, [
1020
+ action.icon ? (openBlock(), createBlock(resolveDynamicComponent(renderActionIcon(action)), { key: 0 })) : createCommentVNode("", true)
1021
+ ], 14, _hoisted_6);
1022
+ }), 128)),
1023
+ createElementVNode("button", {
1024
+ class: normalizeClass(["x-md-copy-btn", { "x-md-copy-btn--copied": unref(copied) }]),
1025
+ onClick: _cache[0] || (_cache[0] = ($event) => unref(copy)(code.value))
1026
+ }, [
1027
+ unref(copied) ? (openBlock(), createElementBlock("svg", _hoisted_7, [..._cache[2] || (_cache[2] = [
1028
+ createElementVNode("path", {
1029
+ fill: "currentColor",
1030
+ d: "M406.656 706.944 195.84 496.256a32 32 0 1 0-45.248 45.248l256 256 512-512a32 32 0 0 0-45.248-45.248L406.592 706.944z"
1031
+ }, null, -1)
1032
+ ])])) : (openBlock(), createElementBlock("svg", _hoisted_8, [..._cache[3] || (_cache[3] = [
1033
+ createElementVNode("path", {
1034
+ fill: "currentColor",
1035
+ d: "M768 832a128 128 0 0 1-128 128H192A128 128 0 0 1 64 832V384a128 128 0 0 1 128-128v64a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64z"
1036
+ }, null, -1),
1037
+ createElementVNode("path", {
1038
+ fill: "currentColor",
1039
+ d: "M384 128a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64zm0-64h448a128 128 0 0 1 128 128v448a128 128 0 0 1-128 128H384a128 128 0 0 1-128-128V192A128 128 0 0 1 384 64"
1040
+ }, null, -1)
1041
+ ])]))
1042
+ ], 2)
1043
+ ], true)
1044
+ ])
1045
+ ], true)
1046
+ ])
1047
+ ], 2)) : createCommentVNode("", true),
1048
+ createElementVNode("div", {
1049
+ class: normalizeClass(["x-md-code-body", { "x-md-code-body--collapsed": collapsed.value }])
1050
+ }, [
1051
+ createVNode(SyntaxCodeBlock, {
1052
+ ref_key: "syntaxCodeBlockRef",
1053
+ ref: syntaxCodeBlockRef,
1054
+ code: code.value,
1055
+ language: language.value,
1056
+ "light-theme": props.lightTheme,
1057
+ "dark-theme": props.darkTheme,
1058
+ "is-dark": props.isDark,
1059
+ "color-replacements": props.colorReplacements,
1060
+ "code-max-height": props.codeMaxHeight,
1061
+ "enable-animate": props.enableAnimate
1062
+ }, null, 8, ["code", "language", "light-theme", "dark-theme", "is-dark", "color-replacements", "code-max-height", "enable-animate"])
1063
+ ], 2)
1064
+ ], 2);
1065
+ };
1066
+ }
1067
+ });
1068
+ const CodeBlock = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-8b0fb0b9"]]);
1069
+ const SHIKI_CORE_PKG = "@shikijs/core";
1070
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
1071
+ __name: "index",
1072
+ props: {
1073
+ raw: { default: () => ({}) },
1074
+ isDark: { type: Boolean, default: false },
1075
+ shikiTheme: { default: () => ["vitesse-light", "vitesse-dark"] },
1076
+ enableAnimate: { type: Boolean, default: false }
1077
+ },
1078
+ setup(__props) {
1079
+ let getTokenStyleObjectFn = null;
1080
+ onMounted(async () => {
1081
+ const mod = await Function(`return import('${SHIKI_CORE_PKG}')`)().catch(() => {
1082
+ return { getTokenStyleObject: () => ({}) };
1083
+ });
1084
+ getTokenStyleObjectFn = mod.getTokenStyleObject;
1085
+ });
1086
+ const props = __props;
1087
+ const content = computed(() => props.raw?.content ?? "");
1088
+ const language = computed(() => props.raw?.language || "ts");
1089
+ const actualTheme = computed(() => props.isDark ? props.shikiTheme[1] : props.shikiTheme[0]);
1090
+ const { lines, preStyle } = useHighlight(content, {
1091
+ language,
1092
+ theme: actualTheme
1093
+ });
1094
+ const flatTokens = computed(() => lines.value.flat());
1095
+ const codeStyle = computed(() => preStyle.value || {});
1096
+ const normalizeStyleKeys = (style) => {
1097
+ const normalized = {};
1098
+ Object.entries(style).forEach(([key, value]) => {
1099
+ const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
1100
+ normalized[camelKey] = value;
1101
+ });
1102
+ return normalized;
1103
+ };
1104
+ const getTokenStyle = (token) => {
1105
+ if (token.htmlStyle) {
1106
+ return normalizeStyleKeys(token.htmlStyle);
1107
+ }
1108
+ if (!getTokenStyleObjectFn) {
1109
+ return {};
1110
+ }
1111
+ const rawStyle = getTokenStyleObjectFn(token);
1112
+ return normalizeStyleKeys(rawStyle);
1113
+ };
1114
+ return (_ctx, _cache) => {
1115
+ return openBlock(), createElementBlock("div", {
1116
+ class: normalizeClass(["x-md-inline-code", {
1117
+ "x-md-inline-code--dark": props.isDark,
1118
+ "x-md-animated-word": props.enableAnimate
1119
+ }])
1120
+ }, [
1121
+ createElementVNode("code", {
1122
+ style: normalizeStyle(codeStyle.value)
1123
+ }, [
1124
+ !flatTokens.value.length ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [
1125
+ createTextVNode(toDisplayString(content.value), 1)
1126
+ ], 64)) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(flatTokens.value, (token, i) => {
1127
+ return openBlock(), createElementBlock("span", {
1128
+ key: i,
1129
+ style: normalizeStyle(getTokenStyle(token)),
1130
+ class: normalizeClass({ "x-md-animated-word": props.enableAnimate })
1131
+ }, toDisplayString(token.content), 7);
1132
+ }), 128))
1133
+ ], 4)
1134
+ ], 2);
1135
+ };
1136
+ }
1137
+ });
1138
+ const CodeLine = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-97166d5b"]]);
1139
+ const Mermaid = defineAsyncComponent(() => import("./index-BjeepIV6.js"));
1140
+ const _sfc_main = defineComponent({
1141
+ props: {
1142
+ raw: { type: Object, default: () => ({}) },
1143
+ codeXRender: { type: Object, default: () => ({}) },
1144
+ isDark: { type: Boolean, default: false },
1145
+ shikiTheme: {
1146
+ type: Array,
1147
+ default: () => ["vitesse-light", "vitesse-dark"]
1148
+ },
1149
+ showCodeBlockHeader: { type: Boolean, default: true },
1150
+ stickyCodeBlockHeader: { type: Boolean, default: true },
1151
+ codeMaxHeight: { type: String, default: void 0 },
1152
+ enableAnimate: { type: Boolean, default: false },
1153
+ codeBlockActions: { type: Array, default: void 0 },
1154
+ mermaidActions: { type: Array, default: void 0 },
1155
+ mermaidConfig: { type: Object, default: void 0 }
1156
+ },
1157
+ setup(props, { slots }) {
1158
+ const { codeXRender } = props;
1159
+ return () => {
1160
+ if (props.raw.inline) {
1161
+ if (codeXRender && codeXRender.inline) {
1162
+ const renderer = codeXRender.inline;
1163
+ if (typeof renderer === "function") {
1164
+ return renderer(props);
1165
+ }
1166
+ return h(renderer, props);
1167
+ }
1168
+ return h(CodeLine, {
1169
+ raw: props.raw,
1170
+ isDark: props.isDark,
1171
+ shikiTheme: props.shikiTheme,
1172
+ enableAnimate: props.enableAnimate
1173
+ });
1174
+ }
1175
+ const { language } = props.raw;
1176
+ if (codeXRender && codeXRender[language]) {
1177
+ const renderer = codeXRender[language];
1178
+ if (typeof renderer === "function") {
1179
+ return renderer(props);
1180
+ }
1181
+ return h(renderer, props);
1182
+ }
1183
+ if (language === "mermaid") {
1184
+ const mermaidSlots = {};
1185
+ Object.keys(slots).forEach((key) => {
1186
+ if (key.startsWith("mermaid")) {
1187
+ mermaidSlots[key] = slots[key];
1188
+ }
1189
+ });
1190
+ return h(
1191
+ Mermaid,
1192
+ {
1193
+ raw: props.raw,
1194
+ isDark: props.isDark,
1195
+ shikiTheme: props.shikiTheme,
1196
+ mermaidActions: props.mermaidActions,
1197
+ config: props.mermaidConfig
1198
+ },
1199
+ mermaidSlots
1200
+ );
1201
+ }
1202
+ return h(
1203
+ CodeBlock,
1204
+ {
1205
+ code: props.raw.content || "",
1206
+ language: props.raw.language || "text",
1207
+ isDark: props.isDark,
1208
+ lightTheme: props.shikiTheme[0],
1209
+ darkTheme: props.shikiTheme[1],
1210
+ showCodeBlockHeader: props.showCodeBlockHeader,
1211
+ stickyCodeBlockHeader: props.stickyCodeBlockHeader,
1212
+ codeMaxHeight: props.codeMaxHeight,
1213
+ enableAnimate: props.enableAnimate,
1214
+ codeBlockActions: props.codeBlockActions
1215
+ },
1216
+ slots
1217
+ );
1218
+ };
1219
+ }
1220
+ });
1221
+ function useComponents(props) {
1222
+ const components = {
1223
+ code: (raw) => h(_sfc_main, {
1224
+ raw,
1225
+ codeXRender: props?.codeXRender,
1226
+ isDark: props?.isDark,
1227
+ shikiTheme: props?.shikiTheme,
1228
+ enableAnimate: props?.enableAnimate,
1229
+ showCodeBlockHeader: props?.showCodeBlockHeader,
1230
+ stickyCodeBlockHeader: props?.stickyCodeBlockHeader,
1231
+ codeMaxHeight: props?.codeMaxHeight,
1232
+ codeBlockActions: props?.codeBlockActions,
1233
+ mermaidActions: props?.mermaidActions,
1234
+ mermaidConfig: props?.mermaidConfig
1235
+ })
1236
+ };
1237
+ return components;
1238
+ }
1239
+ function useProcessMarkdown(markdown) {
1240
+ return preprocessLaTeX(markdown);
1241
+ }
1242
+ function preprocessLaTeX(markdown) {
1243
+ if (typeof markdown !== "string") return markdown;
1244
+ const codeBlockRegex = /```[\s\S]*?```/g;
1245
+ const codeBlocks = markdown.match(codeBlockRegex) || [];
1246
+ const escapeReplacement = (str) => str.replace(/\$/g, "_ELX_DOLLAR_");
1247
+ let processedMarkdown = markdown.replace(codeBlockRegex, "ELX_CODE_BLOCK_PLACEHOLDER");
1248
+ processedMarkdown = flow([
1249
+ (str) => str.replace(/\\\[(.*?)\\\]/g, (_, equation) => `$$${equation}$$`),
1250
+ (str) => str.replace(/\\\[([\s\S]*?)\\\]/g, (_, equation) => `$$${equation}$$`),
1251
+ (str) => str.replace(/\\\((.*?)\\\)/g, (_, equation) => `$$${equation}$$`),
1252
+ (str) => str.replace(/(^|[^\\])\$(.+?)\$/g, (_, prefix, equation) => `${prefix}$${equation}$`)
1253
+ ])(processedMarkdown);
1254
+ codeBlocks.forEach((block) => {
1255
+ processedMarkdown = processedMarkdown.replace("ELX_CODE_BLOCK_PLACEHOLDER", escapeReplacement(block));
1256
+ });
1257
+ processedMarkdown = processedMarkdown.replace(/_ELX_DOLLAR_/g, "$");
1258
+ return processedMarkdown;
1259
+ }
1260
+ function downloadSvgAsPng(svg2) {
1261
+ if (!svg2) return;
1262
+ try {
1263
+ const svgDataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg2)}`;
1264
+ const img = new Image();
1265
+ img.onload = () => {
1266
+ try {
1267
+ const canvas = document.createElement("canvas");
1268
+ const ctx = canvas.getContext("2d", { willReadFrequently: false });
1269
+ if (!ctx) return;
1270
+ const scale = 2;
1271
+ canvas.width = img.width * scale;
1272
+ canvas.height = img.height * scale;
1273
+ ctx.imageSmoothingEnabled = true;
1274
+ ctx.imageSmoothingQuality = "high";
1275
+ ctx.fillStyle = "#ffffff";
1276
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
1277
+ ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
1278
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-");
1279
+ try {
1280
+ canvas.toBlob(
1281
+ (blob) => {
1282
+ if (!blob) return;
1283
+ const url = URL.createObjectURL(blob);
1284
+ const link = document.createElement("a");
1285
+ link.href = url;
1286
+ link.download = `mermaid-diagram-${timestamp}.png`;
1287
+ document.body.appendChild(link);
1288
+ link.click();
1289
+ document.body.removeChild(link);
1290
+ URL.revokeObjectURL(url);
1291
+ },
1292
+ "image/png",
1293
+ 0.95
1294
+ );
1295
+ } catch (toBlobError) {
1296
+ console.error("Failed to convert canvas to blob:", toBlobError);
1297
+ try {
1298
+ const dataUrl = canvas.toDataURL("image/png", 0.95);
1299
+ const link = document.createElement("a");
1300
+ link.href = dataUrl;
1301
+ link.download = `mermaid-diagram-${timestamp}.png`;
1302
+ document.body.appendChild(link);
1303
+ link.click();
1304
+ document.body.removeChild(link);
1305
+ } catch (dataUrlError) {
1306
+ console.error("Failed to convert canvas to data URL:", dataUrlError);
1307
+ }
1308
+ }
1309
+ } catch (canvasError) {
1310
+ console.error("Canvas operation failed:", canvasError);
1311
+ }
1312
+ };
1313
+ img.onerror = (error) => {
1314
+ console.error("Failed to load image:", error);
1315
+ };
1316
+ img.src = svgDataUrl;
1317
+ } catch (error) {
1318
+ console.error("Failed to download SVG:", error);
1319
+ }
1320
+ }
1321
+ const MERMAID_PKG = "mermaid";
1322
+ let mermaidPromise = null;
1323
+ let hasShownMermaidHint = false;
1324
+ const showMermaidHint = () => {
1325
+ if (hasShownMermaidHint) return;
1326
+ hasShownMermaidHint = true;
1327
+ console.log(
1328
+ "%c[x-markdown]%c Mermaid 图表功能已降级为代码块显示",
1329
+ "font-weight: bold; color: #9333ea;",
1330
+ "color: #666;"
1331
+ );
1332
+ console.log(
1333
+ "%c如需 Mermaid 图表渲染功能,请安装:",
1334
+ "color: #666; font-weight: bold;"
1335
+ );
1336
+ console.log(
1337
+ "%c pnpm add mermaid",
1338
+ "color: #9333ea; font-family: monospace;"
1339
+ );
1340
+ console.log(
1341
+ "%c安装后请重启开发服务器",
1342
+ "color: #999; font-size: 12px;"
1343
+ );
1344
+ };
1345
+ async function loadMermaid() {
1346
+ if (typeof window === "undefined") return null;
1347
+ if (!mermaidPromise) {
1348
+ mermaidPromise = import(MERMAID_PKG).then((m) => m.default).catch(() => {
1349
+ showMermaidHint();
1350
+ return null;
1351
+ });
1352
+ }
1353
+ return mermaidPromise;
1354
+ }
1355
+ const renderQueue = [];
1356
+ let isProcessingQueue = false;
1357
+ async function processRenderQueue() {
1358
+ if (isProcessingQueue) return;
1359
+ isProcessingQueue = true;
1360
+ while (renderQueue.length > 0) {
1361
+ const task = renderQueue.shift();
1362
+ if (task) {
1363
+ try {
1364
+ await task();
1365
+ } catch (err) {
1366
+ console.error("Mermaid render queue error:", err);
1367
+ }
1368
+ }
1369
+ }
1370
+ isProcessingQueue = false;
1371
+ }
1372
+ function addToRenderQueue(task) {
1373
+ renderQueue.push(task);
1374
+ processRenderQueue();
1375
+ }
1376
+ function useMermaid(content, options = {}) {
1377
+ const optionsRef = computed(() => typeof options === "object" && "value" in options ? options.value : options);
1378
+ const mermaidConfig = computed(() => ({
1379
+ suppressErrorRendering: true,
1380
+ startOnLoad: false,
1381
+ securityLevel: "loose",
1382
+ theme: optionsRef.value.theme || "default",
1383
+ ...optionsRef.value.config || {}
1384
+ }));
1385
+ const data = ref("");
1386
+ const error = ref(null);
1387
+ const isLoading = ref(false);
1388
+ let isUnmounted = false;
1389
+ const getRenderContainer = () => {
1390
+ const containerOption = optionsRef.value.container;
1391
+ if (containerOption) {
1392
+ return typeof containerOption === "object" && "value" in containerOption ? containerOption.value : containerOption;
1393
+ }
1394
+ return null;
1395
+ };
1396
+ const throttledRender = throttle(
1397
+ () => {
1398
+ const contentValue = typeof content === "string" ? content : content.value;
1399
+ if (!contentValue?.trim()) {
1400
+ data.value = "";
1401
+ error.value = null;
1402
+ isLoading.value = false;
1403
+ return;
1404
+ }
1405
+ isLoading.value = true;
1406
+ addToRenderQueue(async () => {
1407
+ if (isUnmounted) return;
1408
+ try {
1409
+ const mermaidInstance = await loadMermaid();
1410
+ if (!mermaidInstance) {
1411
+ data.value = contentValue;
1412
+ error.value = null;
1413
+ isLoading.value = false;
1414
+ return;
1415
+ }
1416
+ mermaidInstance.initialize(mermaidConfig.value);
1417
+ const isValid = await mermaidInstance.parse(contentValue.trim());
1418
+ if (!isValid) {
1419
+ data.value = "";
1420
+ error.value = new Error("Mermaid parse error: Invalid syntax");
1421
+ isLoading.value = false;
1422
+ return;
1423
+ }
1424
+ const renderId = `${optionsRef.value.id || "mermaid"}-${Math.random().toString(36).substring(2, 11)}`;
1425
+ const container = getRenderContainer();
1426
+ if (!container) {
1427
+ isLoading.value = false;
1428
+ return;
1429
+ }
1430
+ const { svg: svg2 } = await mermaidInstance.render(renderId, contentValue, container);
1431
+ data.value = svg2;
1432
+ error.value = null;
1433
+ isLoading.value = false;
1434
+ } catch (err) {
1435
+ data.value = "";
1436
+ error.value = err;
1437
+ isLoading.value = false;
1438
+ }
1439
+ });
1440
+ },
1441
+ 100,
1442
+ { leading: false, trailing: true }
1443
+ );
1444
+ watch(
1445
+ [() => typeof content === "string" ? content : content.value, () => mermaidConfig.value],
1446
+ () => {
1447
+ throttledRender();
1448
+ },
1449
+ { immediate: true }
1450
+ );
1451
+ onUnmounted(() => {
1452
+ isUnmounted = true;
1453
+ });
1454
+ return {
1455
+ data,
1456
+ error,
1457
+ isLoading
1458
+ };
1459
+ }
1460
+ function useMermaidZoom(options) {
1461
+ const { container } = options;
1462
+ const scale = ref(1);
1463
+ const posX = ref(0);
1464
+ const posY = ref(0);
1465
+ const isDragging = ref(false);
1466
+ let removeEvents = null;
1467
+ const getSvg = () => container.value?.querySelector(".syntax-mermaid__content svg");
1468
+ const updateTransform = (svg2) => {
1469
+ svg2.style.transformOrigin = "center center";
1470
+ svg2.style.transform = `translate(${posX.value}px, ${posY.value}px) scale(${scale.value})`;
1471
+ };
1472
+ const resetState = () => {
1473
+ scale.value = 1;
1474
+ posX.value = 0;
1475
+ posY.value = 0;
1476
+ isDragging.value = false;
1477
+ };
1478
+ const addInteractionEvents = (containerEl) => {
1479
+ let startX = 0;
1480
+ let startY = 0;
1481
+ let isInteractingWithMermaid = false;
1482
+ const onStart = (clientX, clientY) => {
1483
+ isDragging.value = true;
1484
+ startX = clientX - posX.value;
1485
+ startY = clientY - posY.value;
1486
+ document.body.style.userSelect = "none";
1487
+ };
1488
+ const onMove = (clientX, clientY) => {
1489
+ if (isDragging.value && isInteractingWithMermaid) {
1490
+ posX.value = clientX - startX;
1491
+ posY.value = clientY - startY;
1492
+ const svg2 = getSvg();
1493
+ if (svg2) {
1494
+ updateTransform(svg2);
1495
+ }
1496
+ }
1497
+ };
1498
+ const onEnd = () => {
1499
+ isDragging.value = false;
1500
+ isInteractingWithMermaid = false;
1501
+ document.body.style.userSelect = "";
1502
+ };
1503
+ const onMouseDown = (e) => {
1504
+ if (e.button !== 0) return;
1505
+ if (e.target === containerEl || containerEl.contains(e.target)) {
1506
+ e.preventDefault();
1507
+ isInteractingWithMermaid = true;
1508
+ onStart(e.clientX, e.clientY);
1509
+ }
1510
+ };
1511
+ const onMouseMove = (e) => {
1512
+ if (isInteractingWithMermaid) {
1513
+ onMove(e.clientX, e.clientY);
1514
+ }
1515
+ };
1516
+ const handleWheelZoom = (e) => {
1517
+ const svg2 = getSvg();
1518
+ if (!svg2) return;
1519
+ const containerRect = containerEl.getBoundingClientRect();
1520
+ const svgRect = svg2.getBoundingClientRect();
1521
+ const mouseX = e.clientX - containerRect.left;
1522
+ const mouseY = e.clientY - containerRect.top;
1523
+ const svgCenterX = svgRect.left - containerRect.left + svgRect.width / 2;
1524
+ const svgCenterY = svgRect.top - containerRect.top + svgRect.height / 2;
1525
+ const offsetX = (mouseX - svgCenterX - posX.value) / scale.value;
1526
+ const offsetY = (mouseY - svgCenterY - posY.value) / scale.value;
1527
+ const delta = e.deltaY > 0 ? -0.05 : 0.05;
1528
+ const newScale = Math.min(Math.max(scale.value + delta, 0.1), 10);
1529
+ if (newScale === scale.value) return;
1530
+ scale.value = newScale;
1531
+ posX.value = mouseX - svgCenterX - offsetX * scale.value;
1532
+ posY.value = mouseY - svgCenterY - offsetY * scale.value;
1533
+ updateTransform(svg2);
1534
+ };
1535
+ const throttledWheelZoom = throttle(handleWheelZoom, 20, { leading: true, trailing: true });
1536
+ const onWheel = (e) => {
1537
+ if (e.target === containerEl || containerEl.contains(e.target)) {
1538
+ e.preventDefault();
1539
+ throttledWheelZoom(e);
1540
+ }
1541
+ };
1542
+ const onTouchStart = (e) => {
1543
+ if (e.target === containerEl || containerEl.contains(e.target)) {
1544
+ if (e.touches.length === 1) {
1545
+ e.preventDefault();
1546
+ isInteractingWithMermaid = true;
1547
+ onStart(e.touches[0].clientX, e.touches[0].clientY);
1548
+ }
1549
+ }
1550
+ };
1551
+ const onTouchMove = (e) => {
1552
+ if (isInteractingWithMermaid) {
1553
+ e.preventDefault();
1554
+ onMove(e.touches[0].clientX, e.touches[0].clientY);
1555
+ }
1556
+ };
1557
+ containerEl.addEventListener("mousedown", onMouseDown);
1558
+ document.addEventListener("mousemove", onMouseMove);
1559
+ document.addEventListener("mouseup", onEnd);
1560
+ containerEl.addEventListener("wheel", onWheel, { passive: false });
1561
+ containerEl.addEventListener("touchstart", onTouchStart, { passive: false });
1562
+ containerEl.addEventListener("touchmove", onTouchMove, { passive: false });
1563
+ document.addEventListener("touchend", onEnd);
1564
+ return () => {
1565
+ containerEl.removeEventListener("mousedown", onMouseDown);
1566
+ document.removeEventListener("mousemove", onMouseMove);
1567
+ document.removeEventListener("mouseup", onEnd);
1568
+ containerEl.removeEventListener("wheel", onWheel);
1569
+ containerEl.removeEventListener("touchstart", onTouchStart);
1570
+ containerEl.removeEventListener("touchmove", onTouchMove);
1571
+ document.removeEventListener("touchend", onEnd);
1572
+ document.body.style.userSelect = "";
1573
+ };
1574
+ };
1575
+ const zoomIn = () => {
1576
+ const svg2 = getSvg();
1577
+ if (svg2) {
1578
+ scale.value = Math.min(scale.value + 0.2, 10);
1579
+ updateTransform(svg2);
1580
+ }
1581
+ };
1582
+ const zoomOut = () => {
1583
+ const svg2 = getSvg();
1584
+ if (svg2) {
1585
+ scale.value = Math.max(scale.value - 0.2, 0.1);
1586
+ updateTransform(svg2);
1587
+ }
1588
+ };
1589
+ const reset = () => {
1590
+ const svg2 = getSvg();
1591
+ if (svg2) {
1592
+ resetState();
1593
+ updateTransform(svg2);
1594
+ }
1595
+ };
1596
+ const fullscreen = () => {
1597
+ if (!container.value) return;
1598
+ if (document.fullscreenElement) {
1599
+ document.exitFullscreen();
1600
+ } else {
1601
+ container.value.requestFullscreen?.();
1602
+ }
1603
+ };
1604
+ const initialize = () => {
1605
+ if (!container.value) return;
1606
+ resetState();
1607
+ removeEvents = addInteractionEvents(container.value);
1608
+ const svg2 = getSvg();
1609
+ if (svg2) {
1610
+ updateTransform(svg2);
1611
+ }
1612
+ };
1613
+ const destroy = () => {
1614
+ removeEvents?.();
1615
+ removeEvents = null;
1616
+ resetState();
1617
+ };
1618
+ watch(
1619
+ () => container.value,
1620
+ () => {
1621
+ destroy();
1622
+ resetState();
1623
+ }
1624
+ );
1625
+ onUnmounted(destroy);
1626
+ return {
1627
+ zoomIn,
1628
+ zoomOut,
1629
+ reset,
1630
+ fullscreen,
1631
+ destroy,
1632
+ initialize
1633
+ };
1634
+ }
1635
+ function rehypeAnimatedPlugin() {
1636
+ return (tree) => {
1637
+ visit(tree, "element", (node) => {
1638
+ if (["p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "strong", "th", "td"].includes(node.tagName) && node.children) {
1639
+ const newChildren = [];
1640
+ for (const child of node.children) {
1641
+ if (child.type === "text") {
1642
+ const segmenter = new Intl.Segmenter("zh", { granularity: "word" });
1643
+ const segments = segmenter.segment(child.value);
1644
+ const words = [...segments].map((segment) => segment.segment).filter(Boolean);
1645
+ words.forEach((word) => {
1646
+ newChildren.push({
1647
+ children: [{ type: "text", value: word }],
1648
+ properties: {
1649
+ className: "x-md-animated-word"
1650
+ },
1651
+ tagName: "span",
1652
+ type: "element"
1653
+ });
1654
+ });
1655
+ } else {
1656
+ newChildren.push(child);
1657
+ }
1658
+ }
1659
+ node.children = newChildren;
1660
+ }
1661
+ });
1662
+ };
1663
+ }
1664
+ function usePlugins(props) {
1665
+ const {
1666
+ allowHtml,
1667
+ enableAnimate,
1668
+ enableLatex,
1669
+ enableBreaks,
1670
+ enableGfm,
1671
+ rehypePlugins,
1672
+ remarkPlugins,
1673
+ rehypePluginsAhead,
1674
+ remarkPluginsAhead
1675
+ } = toRefs(props);
1676
+ const rehype = computed(() => {
1677
+ return [
1678
+ ...rehypePluginsAhead.value,
1679
+ allowHtml.value && rehypeRaw,
1680
+ enableLatex.value && rehypeKatex,
1681
+ enableAnimate.value && rehypeAnimatedPlugin,
1682
+ ...rehypePlugins.value
1683
+ ].filter(Boolean);
1684
+ });
1685
+ const remark = computed(() => {
1686
+ const base = [
1687
+ enableLatex.value && remarkMath,
1688
+ enableGfm.value !== false && [remarkGfm, { singleTilde: false }],
1689
+ enableBreaks.value && remarkBreaks
1690
+ ].filter(Boolean);
1691
+ return [
1692
+ ...remarkPluginsAhead.value,
1693
+ ...base,
1694
+ ...remarkPlugins.value
1695
+ ];
1696
+ });
1697
+ return {
1698
+ rehypePlugins: rehype,
1699
+ remarkPlugins: remark
1700
+ };
1701
+ }
1702
+ const themeMap = {
1703
+ light: "vitesse-light",
1704
+ dark: "vitesse-dark"
1705
+ };
1706
+ function useTheme(options = {}) {
1707
+ const { lightTheme = themeMap.light, darkTheme = themeMap.dark } = options;
1708
+ const internalMode = ref(isRef(options.mode) ? options.mode.value : options.mode || "auto");
1709
+ const mode = computed({
1710
+ get: () => isRef(options.mode) ? options.mode.value : internalMode.value,
1711
+ set: (val) => {
1712
+ internalMode.value = val;
1713
+ }
1714
+ });
1715
+ const systemIsDark = ref(false);
1716
+ if (typeof window !== "undefined") {
1717
+ const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
1718
+ systemIsDark.value = mediaQuery.matches;
1719
+ mediaQuery.addEventListener("change", (e) => {
1720
+ systemIsDark.value = e.matches;
1721
+ });
1722
+ }
1723
+ const isDark = computed(() => {
1724
+ if (mode.value === "auto") {
1725
+ return systemIsDark.value;
1726
+ }
1727
+ return mode.value === "dark";
1728
+ });
1729
+ const actualTheme = computed(() => {
1730
+ const customTheme = isRef(options.theme) ? options.theme.value : options.theme;
1731
+ if (customTheme) return customTheme;
1732
+ return isDark.value ? darkTheme : lightTheme;
1733
+ });
1734
+ const setMode = (newMode) => {
1735
+ internalMode.value = newMode;
1736
+ };
1737
+ const toggleMode = () => {
1738
+ const modes = ["light", "dark", "auto"];
1739
+ const currentIndex = modes.indexOf(mode.value);
1740
+ internalMode.value = modes[(currentIndex + 1) % modes.length];
1741
+ };
1742
+ return {
1743
+ mode,
1744
+ isDark,
1745
+ actualTheme,
1746
+ setMode,
1747
+ toggleMode
1748
+ };
1749
+ }
1750
+ const markdownRendererProps = {
1751
+ markdown: { type: String, default: "" },
1752
+ allowHtml: { type: Boolean, default: false },
1753
+ enableLatex: { type: Boolean, default: true },
1754
+ enableAnimate: { type: Boolean, default: false },
1755
+ enableBreaks: { type: Boolean, default: true },
1756
+ enableGfm: { type: Boolean, default: true },
1757
+ isDark: { type: Boolean, default: false },
1758
+ shikiTheme: {
1759
+ type: Array,
1760
+ default: () => ["vitesse-light", "vitesse-dark"]
1761
+ },
1762
+ showCodeBlockHeader: { type: Boolean, default: true },
1763
+ stickyCodeBlockHeader: { type: Boolean, default: false },
1764
+ codeMaxHeight: { type: String, default: void 0 },
1765
+ codeBlockActions: { type: Array, default: void 0 },
1766
+ mermaidActions: { type: Array, default: void 0 },
1767
+ mermaidConfig: { type: Object, default: void 0 },
1768
+ codeXRender: { type: Object, default: () => ({}) },
1769
+ customAttrs: { type: Object, default: () => ({}) },
1770
+ remarkPlugins: { type: Array, default: () => [] },
1771
+ remarkPluginsAhead: { type: Array, default: () => [] },
1772
+ rehypePlugins: { type: Array, default: () => [] },
1773
+ rehypePluginsAhead: { type: Array, default: () => [] },
1774
+ rehypeOptions: { type: Object, default: () => ({}) },
1775
+ sanitize: { type: Boolean, default: false },
1776
+ sanitizeOptions: { type: Object, default: () => ({}) }
1777
+ };
1778
+ function createMarkdownRenderer(name, coreComponent) {
1779
+ return defineComponent({
1780
+ name,
1781
+ props: markdownRendererProps,
1782
+ setup(props, { slots, attrs }) {
1783
+ const { rehypePlugins, remarkPlugins } = usePlugins(props);
1784
+ const components = useComponents(props);
1785
+ const markdown = computed(() => {
1786
+ return props.markdown;
1787
+ });
1788
+ const renderProps = computed(() => ({
1789
+ // markdown 内容
1790
+ markdown: markdown.value,
1791
+ // 自定义属性对象
1792
+ customAttrs: props.customAttrs,
1793
+ // remark 插件列表
1794
+ remarkPlugins: toValue(remarkPlugins),
1795
+ // rehype 插件列表
1796
+ rehypePlugins: toValue(rehypePlugins),
1797
+ // rehype 配置项
1798
+ rehypeOptions: props.rehypeOptions,
1799
+ // 是否启用内容清洗
1800
+ sanitize: props.sanitize,
1801
+ // 清洗选项
1802
+ sanitizeOptions: props.sanitizeOptions
1803
+ }));
1804
+ return () => h(
1805
+ "div",
1806
+ {
1807
+ // 外层容器 class,支持深色模式
1808
+ class: ["x-md-renderer", { "is-dark": props.isDark }],
1809
+ // 外层容器样式,根据深色模式设置背景色和文字颜色
1810
+ style: {
1811
+ backgroundColor: props.isDark ? "#1e1e1e" : "#ffffff",
1812
+ color: props.isDark ? "#e5e5e5" : "#333333",
1813
+ padding: "16px"
1814
+ },
1815
+ // 透传外部传入的 attrs
1816
+ ...attrs
1817
+ },
1818
+ // 渲染核心组件
1819
+ h(
1820
+ coreComponent,
1821
+ { ...renderProps.value, class: "x-md-core" },
1822
+ {
1823
+ // 合并自定义组件和外部插槽
1824
+ ...components,
1825
+ ...slots
1826
+ }
1827
+ )
1828
+ );
1829
+ }
1830
+ });
1831
+ }
1832
+ const MarkdownRenderer = createMarkdownRenderer("MarkdownRenderer", VueMarkdown);
1833
+ const MarkdownRendererAsync = createMarkdownRenderer("MarkdownRendererAsync", VueMarkdownAsync);
1834
+ export {
1835
+ CodeBlock as C,
1836
+ MarkdownRenderer as M,
1837
+ VueMarkdown as V,
1838
+ useMermaidZoom as a,
1839
+ useClipboard as b,
1840
+ VueMarkdownAsync as c,
1841
+ downloadSvgAsPng as d,
1842
+ MarkdownRendererAsync as e,
1843
+ renderChildren as f,
1844
+ getVNodeInfos as g,
1845
+ createProcessor as h,
1846
+ useMarkdownProcessor as i,
1847
+ useComponents as j,
1848
+ useHighlight as k,
1849
+ useProcessMarkdown as l,
1850
+ usePlugins as m,
1851
+ useTheme as n,
1852
+ preprocessLaTeX as p,
1853
+ render as r,
1854
+ useMermaid as u
1855
+ };
1856
+ //# sourceMappingURL=index-Ys7-7uFi.js.map