@hejiayue/x-markdown-test 0.0.1-beta.118 → 0.0.1-beta.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/x-markdown.cjs19.js +1 -1
- package/dist/x-markdown.cjs24.js +1 -1
- package/dist/x-markdown.cjs27.js +1 -1
- package/dist/x-markdown.cjs27.js.map +1 -1
- package/dist/x-markdown.cjs28.js +2 -0
- package/dist/x-markdown.cjs28.js.map +1 -0
- package/dist/x-markdown.cjs31.js +1 -1
- package/dist/x-markdown.cjs31.js.map +1 -1
- package/dist/x-markdown.cjs33.js +2 -0
- package/dist/x-markdown.cjs33.js.map +1 -0
- package/dist/x-markdown.cjs7.js +1 -1
- package/dist/x-markdown.cjs7.js.map +1 -1
- package/dist/x-markdown.cjs9.js +1 -1
- package/dist/x-markdown.cjs9.js.map +1 -1
- package/dist/x-markdown.es19.js +1 -1
- package/dist/x-markdown.es24.js +1 -1
- package/dist/x-markdown.es27.js +5 -139
- package/dist/x-markdown.es27.js.map +1 -1
- package/dist/x-markdown.es28.js +142 -0
- package/dist/x-markdown.es28.js.map +1 -0
- package/dist/x-markdown.es31.js +121 -2
- package/dist/x-markdown.es31.js.map +1 -1
- package/dist/x-markdown.es33.js +6 -0
- package/dist/x-markdown.es33.js.map +1 -0
- package/dist/x-markdown.es7.js +16 -8
- package/dist/x-markdown.es7.js.map +1 -1
- package/dist/x-markdown.es9.js +11 -9
- package/dist/x-markdown.es9.js.map +1 -1
- package/package.json +1 -1
- package/dist/x-markdown.cjs29.js +0 -2
- package/dist/x-markdown.cjs29.js.map +0 -1
- package/dist/x-markdown.cjs32.js +0 -2
- package/dist/x-markdown.cjs32.js.map +0 -1
- package/dist/x-markdown.es29.js +0 -8
- package/dist/x-markdown.es29.js.map +0 -1
- package/dist/x-markdown.es32.js +0 -125
- package/dist/x-markdown.es32.js.map +0 -1
package/dist/x-markdown.es27.js
CHANGED
|
@@ -1,142 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
const
|
|
5
|
-
key: 0,
|
|
6
|
-
class: "syntax-mermaid__loading"
|
|
7
|
-
};
|
|
8
|
-
const _hoisted_2 = ["innerHTML"];
|
|
9
|
-
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
10
|
-
__name: "SyntaxMermaid",
|
|
11
|
-
props: {
|
|
12
|
-
content: { default: "" },
|
|
13
|
-
id: { default: "mermaid-default" },
|
|
14
|
-
isDark: { type: Boolean, default: false },
|
|
15
|
-
config: { default: () => ({}) }
|
|
16
|
-
},
|
|
17
|
-
emits: ["degraded", "ready"],
|
|
18
|
-
setup(__props, { expose: __expose, emit: __emit }) {
|
|
19
|
-
const props = __props;
|
|
20
|
-
const emit = __emit;
|
|
21
|
-
const renderContainerRef = ref(null);
|
|
22
|
-
const mermaidContent = computed(() => props.content);
|
|
23
|
-
const mermaidOptions = computed(() => ({
|
|
24
|
-
id: props.id,
|
|
25
|
-
theme: props.isDark ? "dark" : "default",
|
|
26
|
-
config: props.config,
|
|
27
|
-
container: renderContainerRef.value
|
|
28
|
-
}));
|
|
29
|
-
const mermaidResult = useMermaid(mermaidContent, mermaidOptions);
|
|
30
|
-
const svg = ref("");
|
|
31
|
-
const isLoading = computed(() => mermaidResult.isLoading.value);
|
|
32
|
-
const error = computed(() => mermaidResult.error.value);
|
|
33
|
-
const containerRef = ref(null);
|
|
34
|
-
const zoomControls = useMermaidZoom({
|
|
35
|
-
container: containerRef
|
|
36
|
-
});
|
|
37
|
-
const debouncedInitialize = debounce(initializeZoom, 500);
|
|
38
|
-
function initializeZoom() {
|
|
39
|
-
nextTick(() => {
|
|
40
|
-
if (containerRef.value) {
|
|
41
|
-
zoomControls.initialize();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
watch(
|
|
46
|
-
() => mermaidResult.data.value,
|
|
47
|
-
(newSvg, oldSvg) => {
|
|
48
|
-
console.log("[SyntaxMermaid] mermaidResult.data.value changed:", {
|
|
49
|
-
oldSvg,
|
|
50
|
-
newSvg,
|
|
51
|
-
isNewSvg: !!newSvg,
|
|
52
|
-
startsWithSvg: newSvg?.trim().startsWith("<svg"),
|
|
53
|
-
preview: newSvg?.substring(0, 50)
|
|
54
|
-
});
|
|
55
|
-
if (newSvg) {
|
|
56
|
-
svg.value = newSvg;
|
|
57
|
-
debouncedInitialize();
|
|
58
|
-
if (newSvg.trim().startsWith("<svg")) {
|
|
59
|
-
console.log("[SyntaxMermaid] Emitting ready event - Mermaid is available");
|
|
60
|
-
emit("ready");
|
|
61
|
-
} else {
|
|
62
|
-
console.log("[SyntaxMermaid] Emitting degraded event - Mermaid not available");
|
|
63
|
-
emit("degraded");
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
},
|
|
67
|
-
{ immediate: true }
|
|
68
|
-
);
|
|
69
|
-
watch(svg, (newSvg) => {
|
|
70
|
-
if (newSvg) {
|
|
71
|
-
debouncedInitialize();
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
function zoomIn() {
|
|
75
|
-
zoomControls?.zoomIn();
|
|
76
|
-
}
|
|
77
|
-
function zoomOut() {
|
|
78
|
-
zoomControls?.zoomOut();
|
|
79
|
-
}
|
|
80
|
-
function reset() {
|
|
81
|
-
zoomControls?.reset();
|
|
82
|
-
}
|
|
83
|
-
function fullscreen() {
|
|
84
|
-
zoomControls?.fullscreen();
|
|
85
|
-
zoomControls?.reset();
|
|
86
|
-
}
|
|
87
|
-
function download() {
|
|
88
|
-
downloadSvgAsPng(svg.value);
|
|
89
|
-
}
|
|
90
|
-
function getSvg() {
|
|
91
|
-
return svg.value;
|
|
92
|
-
}
|
|
93
|
-
function reinitialize() {
|
|
94
|
-
debouncedInitialize();
|
|
95
|
-
}
|
|
96
|
-
onMounted(() => {
|
|
97
|
-
if (svg.value) {
|
|
98
|
-
debouncedInitialize();
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
__expose({
|
|
102
|
-
svg,
|
|
103
|
-
isLoading,
|
|
104
|
-
error,
|
|
105
|
-
containerRef,
|
|
106
|
-
zoomIn,
|
|
107
|
-
zoomOut,
|
|
108
|
-
reset,
|
|
109
|
-
fullscreen,
|
|
110
|
-
download,
|
|
111
|
-
getSvg,
|
|
112
|
-
reinitialize
|
|
113
|
-
});
|
|
114
|
-
return (_ctx, _cache) => {
|
|
115
|
-
return openBlock(), createElementBlock("div", {
|
|
116
|
-
ref_key: "containerRef",
|
|
117
|
-
ref: containerRef,
|
|
118
|
-
class: normalizeClass(["syntax-mermaid", { "syntax-mermaid--dark": props.isDark }])
|
|
119
|
-
}, [
|
|
120
|
-
createElementVNode("div", {
|
|
121
|
-
ref_key: "renderContainerRef",
|
|
122
|
-
ref: renderContainerRef,
|
|
123
|
-
class: "syntax-mermaid__render-container",
|
|
124
|
-
"aria-hidden": "true"
|
|
125
|
-
}, null, 512),
|
|
126
|
-
isLoading.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
127
|
-
renderSlot(_ctx.$slots, "loading", {}, () => [
|
|
128
|
-
_cache[0] || (_cache[0] = createElementVNode("span", { class: "syntax-mermaid__loading-text" }, "加载中...", -1))
|
|
129
|
-
])
|
|
130
|
-
])) : (openBlock(), createElementBlock("div", {
|
|
131
|
-
key: 1,
|
|
132
|
-
class: "syntax-mermaid__content",
|
|
133
|
-
innerHTML: svg.value
|
|
134
|
-
}, null, 8, _hoisted_2))
|
|
135
|
-
], 2);
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
});
|
|
1
|
+
import _sfc_main from "./x-markdown.es31.js";
|
|
2
|
+
/* empty css */
|
|
3
|
+
import _export_sfc from "./x-markdown.es21.js";
|
|
4
|
+
const SyntaxCodeBlock = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-ddb364e9"]]);
|
|
139
5
|
export {
|
|
140
|
-
|
|
6
|
+
SyntaxCodeBlock as default
|
|
141
7
|
};
|
|
142
8
|
//# sourceMappingURL=x-markdown.es27.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x-markdown.es27.js","sources":[
|
|
1
|
+
{"version":3,"file":"x-markdown.es27.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { defineComponent, ref, computed, nextTick, watch, onMounted, createElementBlock, openBlock, normalizeClass, createElementVNode, renderSlot } from "vue";
|
|
2
|
+
import { debounce } from "lodash-es";
|
|
3
|
+
import { useMermaid, useMermaidZoom, downloadSvgAsPng } from "./x-markdown.es9.js";
|
|
4
|
+
const _hoisted_1 = {
|
|
5
|
+
key: 0,
|
|
6
|
+
class: "syntax-mermaid__loading"
|
|
7
|
+
};
|
|
8
|
+
const _hoisted_2 = ["innerHTML"];
|
|
9
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
10
|
+
__name: "SyntaxMermaid",
|
|
11
|
+
props: {
|
|
12
|
+
content: { default: "" },
|
|
13
|
+
id: { default: "mermaid-default" },
|
|
14
|
+
isDark: { type: Boolean, default: false },
|
|
15
|
+
config: { default: () => ({}) }
|
|
16
|
+
},
|
|
17
|
+
emits: ["degraded", "ready"],
|
|
18
|
+
setup(__props, { expose: __expose, emit: __emit }) {
|
|
19
|
+
const props = __props;
|
|
20
|
+
const emit = __emit;
|
|
21
|
+
const renderContainerRef = ref(null);
|
|
22
|
+
const mermaidContent = computed(() => props.content);
|
|
23
|
+
const mermaidOptions = computed(() => ({
|
|
24
|
+
id: props.id,
|
|
25
|
+
theme: props.isDark ? "dark" : "default",
|
|
26
|
+
config: props.config,
|
|
27
|
+
container: renderContainerRef.value
|
|
28
|
+
}));
|
|
29
|
+
const mermaidResult = useMermaid(mermaidContent, mermaidOptions);
|
|
30
|
+
const svg = ref("");
|
|
31
|
+
const isLoading = computed(() => mermaidResult.isLoading.value);
|
|
32
|
+
const error = computed(() => mermaidResult.error.value);
|
|
33
|
+
const containerRef = ref(null);
|
|
34
|
+
const zoomControls = useMermaidZoom({
|
|
35
|
+
container: containerRef
|
|
36
|
+
});
|
|
37
|
+
const debouncedInitialize = debounce(initializeZoom, 500);
|
|
38
|
+
function initializeZoom() {
|
|
39
|
+
nextTick(() => {
|
|
40
|
+
if (containerRef.value) {
|
|
41
|
+
zoomControls.initialize();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
watch(
|
|
46
|
+
() => mermaidResult.data.value,
|
|
47
|
+
(newSvg, oldSvg) => {
|
|
48
|
+
console.log("[SyntaxMermaid] mermaidResult.data.value changed:", {
|
|
49
|
+
oldSvg,
|
|
50
|
+
newSvg,
|
|
51
|
+
isNewSvg: !!newSvg,
|
|
52
|
+
startsWithSvg: newSvg?.trim().startsWith("<svg"),
|
|
53
|
+
preview: newSvg?.substring(0, 50)
|
|
54
|
+
});
|
|
55
|
+
if (newSvg) {
|
|
56
|
+
svg.value = newSvg;
|
|
57
|
+
debouncedInitialize();
|
|
58
|
+
if (newSvg.trim().startsWith("<svg")) {
|
|
59
|
+
console.log("[SyntaxMermaid] Emitting ready event - Mermaid is available");
|
|
60
|
+
emit("ready");
|
|
61
|
+
} else {
|
|
62
|
+
console.log("[SyntaxMermaid] Emitting degraded event - Mermaid not available");
|
|
63
|
+
emit("degraded");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
{ immediate: true }
|
|
68
|
+
);
|
|
69
|
+
watch(svg, (newSvg) => {
|
|
70
|
+
if (newSvg) {
|
|
71
|
+
debouncedInitialize();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
function zoomIn() {
|
|
75
|
+
zoomControls?.zoomIn();
|
|
76
|
+
}
|
|
77
|
+
function zoomOut() {
|
|
78
|
+
zoomControls?.zoomOut();
|
|
79
|
+
}
|
|
80
|
+
function reset() {
|
|
81
|
+
zoomControls?.reset();
|
|
82
|
+
}
|
|
83
|
+
function fullscreen() {
|
|
84
|
+
zoomControls?.fullscreen();
|
|
85
|
+
zoomControls?.reset();
|
|
86
|
+
}
|
|
87
|
+
function download() {
|
|
88
|
+
downloadSvgAsPng(svg.value);
|
|
89
|
+
}
|
|
90
|
+
function getSvg() {
|
|
91
|
+
return svg.value;
|
|
92
|
+
}
|
|
93
|
+
function reinitialize() {
|
|
94
|
+
debouncedInitialize();
|
|
95
|
+
}
|
|
96
|
+
onMounted(() => {
|
|
97
|
+
if (svg.value) {
|
|
98
|
+
debouncedInitialize();
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
__expose({
|
|
102
|
+
svg,
|
|
103
|
+
isLoading,
|
|
104
|
+
error,
|
|
105
|
+
containerRef,
|
|
106
|
+
zoomIn,
|
|
107
|
+
zoomOut,
|
|
108
|
+
reset,
|
|
109
|
+
fullscreen,
|
|
110
|
+
download,
|
|
111
|
+
getSvg,
|
|
112
|
+
reinitialize
|
|
113
|
+
});
|
|
114
|
+
return (_ctx, _cache) => {
|
|
115
|
+
return openBlock(), createElementBlock("div", {
|
|
116
|
+
ref_key: "containerRef",
|
|
117
|
+
ref: containerRef,
|
|
118
|
+
class: normalizeClass(["syntax-mermaid", { "syntax-mermaid--dark": props.isDark }])
|
|
119
|
+
}, [
|
|
120
|
+
createElementVNode("div", {
|
|
121
|
+
ref_key: "renderContainerRef",
|
|
122
|
+
ref: renderContainerRef,
|
|
123
|
+
class: "syntax-mermaid__render-container",
|
|
124
|
+
"aria-hidden": "true"
|
|
125
|
+
}, null, 512),
|
|
126
|
+
isLoading.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
|
|
127
|
+
renderSlot(_ctx.$slots, "loading", {}, () => [
|
|
128
|
+
_cache[0] || (_cache[0] = createElementVNode("span", { class: "syntax-mermaid__loading-text" }, "加载中...", -1))
|
|
129
|
+
])
|
|
130
|
+
])) : (openBlock(), createElementBlock("div", {
|
|
131
|
+
key: 1,
|
|
132
|
+
class: "syntax-mermaid__content",
|
|
133
|
+
innerHTML: svg.value
|
|
134
|
+
}, null, 8, _hoisted_2))
|
|
135
|
+
], 2);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
export {
|
|
140
|
+
_sfc_main as default
|
|
141
|
+
};
|
|
142
|
+
//# sourceMappingURL=x-markdown.es28.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x-markdown.es28.js","sources":["../src/components/Mermaid/SyntaxMermaid.vue"],"sourcesContent":["<script setup lang=\"ts\">\r\nimport { computed, nextTick, ref, watch, onMounted } from 'vue'\r\nimport { debounce } from 'lodash-es'\r\nimport { useMermaid, useMermaidZoom, downloadSvgAsPng } from '../../hooks'\r\n\r\ninterface SyntaxMermaidProps {\r\n content: string\r\n id?: string\r\n isDark?: boolean\r\n config?: Record<string, any>\r\n}\r\n\r\nconst props = withDefaults(defineProps<SyntaxMermaidProps>(), {\r\n content: '',\r\n id: 'mermaid-default',\r\n isDark: false,\r\n config: () => ({}),\r\n})\r\n\r\nconst emit = defineEmits<{\r\n degraded: []\r\n ready: []\r\n}>()\r\n\r\nconst renderContainerRef = ref<HTMLElement | null>(null)\r\n\r\nconst mermaidContent = computed(() => props.content)\r\nconst mermaidOptions = computed(() => ({\r\n id: props.id,\r\n theme: props.isDark ? 'dark' : 'default',\r\n config: props.config,\r\n container: renderContainerRef.value,\r\n}))\r\nconst mermaidResult = useMermaid(mermaidContent, mermaidOptions)\r\n\r\nconst svg = ref('')\r\nconst isLoading = computed(() => mermaidResult.isLoading.value)\r\nconst error = computed(() => mermaidResult.error.value)\r\n\r\nconst containerRef = ref<HTMLElement | null>(null)\r\n\r\nconst zoomControls = useMermaidZoom({\r\n container: containerRef,\r\n scaleStep: 0.2,\r\n minScale: 0.1,\r\n maxScale: 5,\r\n})\r\n\r\nconst debouncedInitialize = debounce(initializeZoom, 500)\r\n\r\nfunction initializeZoom() {\r\n nextTick(() => {\r\n if (containerRef.value) {\r\n zoomControls.initialize()\r\n }\r\n })\r\n}\r\n\r\nwatch(\r\n () => mermaidResult.data.value,\r\n (newSvg, oldSvg) => {\r\n console.log('[SyntaxMermaid] mermaidResult.data.value changed:', {\r\n oldSvg,\r\n newSvg,\r\n isNewSvg: !!newSvg,\r\n startsWithSvg: newSvg?.trim().startsWith('<svg'),\r\n preview: newSvg?.substring(0, 50)\r\n })\r\n\r\n if (newSvg) {\r\n svg.value = newSvg\r\n debouncedInitialize()\r\n\r\n // 检测是否成功渲染了 SVG(以 <svg 开头)\r\n if (newSvg.trim().startsWith('<svg')) {\r\n console.log('[SyntaxMermaid] Emitting ready event - Mermaid is available')\r\n emit('ready')\r\n } else {\r\n console.log('[SyntaxMermaid] Emitting degraded event - Mermaid not available')\r\n emit('degraded')\r\n }\r\n }\r\n },\r\n { immediate: true },\r\n)\r\n\r\nwatch(svg, (newSvg) => {\r\n if (newSvg) {\r\n debouncedInitialize()\r\n }\r\n})\r\n\r\nfunction zoomIn() {\r\n zoomControls?.zoomIn()\r\n}\r\n\r\nfunction zoomOut() {\r\n zoomControls?.zoomOut()\r\n}\r\n\r\nfunction reset() {\r\n zoomControls?.reset()\r\n}\r\n\r\nfunction fullscreen() {\r\n zoomControls?.fullscreen()\r\n zoomControls?.reset()\r\n}\r\n\r\nfunction download() {\r\n downloadSvgAsPng(svg.value)\r\n}\r\n\r\nfunction getSvg() {\r\n return svg.value\r\n}\r\n\r\nfunction reinitialize() {\r\n debouncedInitialize()\r\n}\r\n\r\nonMounted(() => {\r\n if (svg.value) {\r\n debouncedInitialize()\r\n }\r\n})\r\n\r\ndefineExpose({\r\n svg,\r\n isLoading,\r\n error,\r\n containerRef,\r\n zoomIn,\r\n zoomOut,\r\n reset,\r\n fullscreen,\r\n download,\r\n getSvg,\r\n reinitialize,\r\n})\r\n</script>\r\n\r\n<template>\r\n <div ref=\"containerRef\" class=\"syntax-mermaid\" :class=\"{ 'syntax-mermaid--dark': props.isDark }\">\r\n <div ref=\"renderContainerRef\" class=\"syntax-mermaid__render-container\" aria-hidden=\"true\" />\r\n\r\n <div v-if=\"isLoading\" class=\"syntax-mermaid__loading\">\r\n <slot name=\"loading\">\r\n <span class=\"syntax-mermaid__loading-text\">加载中...</span>\r\n </slot>\r\n </div>\r\n <div v-else class=\"syntax-mermaid__content\" v-html=\"svg\" />\r\n </div>\r\n</template>\r\n\r\n<style>\r\n.syntax-mermaid {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n min-height: 200px;\r\n overflow: hidden;\r\n cursor: grab;\r\n position: relative;\r\n}\r\n\r\n.syntax-mermaid__render-container {\r\n position: absolute;\r\n max-height: 0;\r\n opacity: 0;\r\n overflow: hidden;\r\n pointer-events: none;\r\n}\r\n\r\n.syntax-mermaid:active {\r\n cursor: grabbing;\r\n}\r\n\r\n.syntax-mermaid__content {\r\n width: 100%;\r\n height: 100%;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n}\r\n\r\n.syntax-mermaid__content svg {\r\n transform-origin: center center;\r\n max-width: 100%;\r\n max-height: 100%;\r\n}\r\n\r\n.syntax-mermaid:fullscreen {\r\n max-height: 100vh;\r\n}\r\n\r\n.syntax-mermaid:fullscreen .syntax-mermaid__content {\r\n justify-content: center;\r\n}\r\n\r\n.syntax-mermaid__loading {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n width: 100%;\r\n height: 100%;\r\n min-height: 200px;\r\n}\r\n\r\n.syntax-mermaid__loading-text {\r\n color: #666;\r\n font-size: 14px;\r\n}\r\n\r\n.syntax-mermaid--dark .syntax-mermaid__loading-text {\r\n color: #999;\r\n}\r\n</style>\r\n"],"names":["_createElementBlock","_normalizeClass","_createElementVNode","_openBlock","_renderSlot"],"mappings":"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"}
|
package/dist/x-markdown.es31.js
CHANGED
|
@@ -1,5 +1,124 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { defineComponent, computed, createElementBlock, openBlock, normalizeStyle, createElementVNode, toDisplayString, normalizeClass, createTextVNode, Fragment, renderList, unref } from "vue";
|
|
2
|
+
import { useHighlight } from "./x-markdown.es7.js";
|
|
3
|
+
const _hoisted_1 = { class: "x-md-syntax-code-block" };
|
|
4
|
+
const _hoisted_2 = { class: "x-md-code-content" };
|
|
5
|
+
const _hoisted_3 = { key: 0 };
|
|
6
|
+
const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
7
|
+
...{
|
|
8
|
+
name: "SyntaxCodeBlock"
|
|
9
|
+
},
|
|
10
|
+
__name: "SyntaxCodeBlock",
|
|
11
|
+
props: {
|
|
12
|
+
code: {},
|
|
13
|
+
language: {},
|
|
14
|
+
lightTheme: { default: "vitesse-light" },
|
|
15
|
+
darkTheme: { default: "vitesse-dark" },
|
|
16
|
+
isDark: { type: Boolean, default: false },
|
|
17
|
+
colorReplacements: {},
|
|
18
|
+
codeMaxHeight: {},
|
|
19
|
+
enableAnimate: { type: Boolean, default: false }
|
|
20
|
+
},
|
|
21
|
+
setup(__props, { expose: __expose }) {
|
|
22
|
+
const props = __props;
|
|
23
|
+
const code = computed(() => props.code.trim());
|
|
24
|
+
const language = computed(() => props.language || "text");
|
|
25
|
+
const actualTheme = computed(() => props.isDark ? props.darkTheme : props.lightTheme);
|
|
26
|
+
const { lines, preStyle } = useHighlight(code, {
|
|
27
|
+
language,
|
|
28
|
+
theme: actualTheme,
|
|
29
|
+
colorReplacements: props.colorReplacements
|
|
30
|
+
});
|
|
31
|
+
const applyColorReplacement = (color, replacements) => {
|
|
32
|
+
if (!replacements) return color;
|
|
33
|
+
return replacements[color.toLowerCase()] || color;
|
|
34
|
+
};
|
|
35
|
+
const normalizeStyleKeys = (style) => {
|
|
36
|
+
const normalized = {};
|
|
37
|
+
Object.entries(style).forEach(([key, value]) => {
|
|
38
|
+
const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
39
|
+
normalized[camelKey] = value;
|
|
40
|
+
});
|
|
41
|
+
return normalized;
|
|
42
|
+
};
|
|
43
|
+
const getTokenStyle = (token) => {
|
|
44
|
+
if (!token) {
|
|
45
|
+
return {};
|
|
46
|
+
}
|
|
47
|
+
if (token.htmlStyle) {
|
|
48
|
+
const baseStyle = normalizeStyleKeys(token.htmlStyle);
|
|
49
|
+
if (!props.colorReplacements) return baseStyle;
|
|
50
|
+
const style2 = { ...baseStyle };
|
|
51
|
+
if (style2.color && typeof style2.color === "string") {
|
|
52
|
+
style2.color = applyColorReplacement(style2.color, props.colorReplacements);
|
|
53
|
+
}
|
|
54
|
+
if (style2.backgroundColor && typeof style2.backgroundColor === "string") {
|
|
55
|
+
style2.backgroundColor = applyColorReplacement(style2.backgroundColor, props.colorReplacements);
|
|
56
|
+
}
|
|
57
|
+
return style2;
|
|
58
|
+
}
|
|
59
|
+
const style = {};
|
|
60
|
+
if (token.color) {
|
|
61
|
+
style.color = props.colorReplacements ? applyColorReplacement(token.color, props.colorReplacements) : token.color;
|
|
62
|
+
}
|
|
63
|
+
if (token.fontStyle === "italic") {
|
|
64
|
+
style.fontStyle = "italic";
|
|
65
|
+
}
|
|
66
|
+
if (token.fontWeight) {
|
|
67
|
+
style.fontWeight = token.fontWeight;
|
|
68
|
+
}
|
|
69
|
+
return style;
|
|
70
|
+
};
|
|
71
|
+
const showFallback = computed(() => !lines.value?.length);
|
|
72
|
+
const codeContainerStyle = computed(() => ({
|
|
73
|
+
...preStyle.value,
|
|
74
|
+
maxHeight: props.codeMaxHeight
|
|
75
|
+
}));
|
|
76
|
+
__expose({
|
|
77
|
+
lines,
|
|
78
|
+
code,
|
|
79
|
+
language,
|
|
80
|
+
actualTheme
|
|
81
|
+
});
|
|
82
|
+
return (_ctx, _cache) => {
|
|
83
|
+
return openBlock(), createElementBlock("div", _hoisted_1, [
|
|
84
|
+
showFallback.value ? (openBlock(), createElementBlock("pre", {
|
|
85
|
+
key: 0,
|
|
86
|
+
style: normalizeStyle(codeContainerStyle.value)
|
|
87
|
+
}, [
|
|
88
|
+
createElementVNode("code", null, toDisplayString(code.value), 1)
|
|
89
|
+
], 4)) : (openBlock(), createElementBlock("pre", {
|
|
90
|
+
key: 1,
|
|
91
|
+
class: normalizeClass(["shiki", actualTheme.value]),
|
|
92
|
+
style: normalizeStyle(codeContainerStyle.value),
|
|
93
|
+
tabindex: "0"
|
|
94
|
+
}, [
|
|
95
|
+
_cache[4] || (_cache[4] = createTextVNode(" ", -1)),
|
|
96
|
+
createElementVNode("code", _hoisted_2, [
|
|
97
|
+
_cache[2] || (_cache[2] = createTextVNode("\n ", -1)),
|
|
98
|
+
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(lines), (line, i) => {
|
|
99
|
+
return openBlock(), createElementBlock("span", {
|
|
100
|
+
key: i,
|
|
101
|
+
class: "x-md-code-line"
|
|
102
|
+
}, [
|
|
103
|
+
_cache[0] || (_cache[0] = createTextVNode("\n ", -1)),
|
|
104
|
+
!line.length ? (openBlock(), createElementBlock("span", _hoisted_3, " ")) : (openBlock(true), createElementBlock(Fragment, { key: 1 }, renderList(line, (token, j) => {
|
|
105
|
+
return openBlock(), createElementBlock("span", {
|
|
106
|
+
key: j,
|
|
107
|
+
style: normalizeStyle(getTokenStyle(token)),
|
|
108
|
+
class: normalizeClass({ "x-md-animated-word": props.enableAnimate })
|
|
109
|
+
}, toDisplayString(token.content), 7);
|
|
110
|
+
}), 128)),
|
|
111
|
+
_cache[1] || (_cache[1] = createTextVNode("\n ", -1))
|
|
112
|
+
]);
|
|
113
|
+
}), 128)),
|
|
114
|
+
_cache[3] || (_cache[3] = createTextVNode("\n ", -1))
|
|
115
|
+
]),
|
|
116
|
+
_cache[5] || (_cache[5] = createTextVNode("\n ", -1))
|
|
117
|
+
], 6))
|
|
118
|
+
]);
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
});
|
|
3
122
|
export {
|
|
4
123
|
_sfc_main as default
|
|
5
124
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x-markdown.es31.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
1
|
+
{"version":3,"file":"x-markdown.es31.js","sources":["../src/components/CodeBlock/SyntaxCodeBlock.vue"],"sourcesContent":["<template>\r\n <div class=\"x-md-syntax-code-block\">\r\n <pre v-if=\"showFallback\" :style=\"codeContainerStyle\"><code>{{ code }}</code></pre>\r\n <pre v-else :class=\"['shiki', actualTheme]\" :style=\"codeContainerStyle\" tabindex=\"0\">\r\n <code class=\"x-md-code-content\">\r\n <span v-for=\"(line, i) in lines\" :key=\"i\" class=\"x-md-code-line\">\r\n <span v-if=\"!line.length\"> </span>\r\n <span \r\n v-else \r\n v-for=\"(token, j) in line\" \r\n :key=\"j\" \r\n :style=\"getTokenStyle(token)\"\r\n :class=\"{ 'x-md-animated-word': props.enableAnimate }\"\r\n >{{ token.content }}</span>\r\n </span>\r\n </code>\r\n </pre>\r\n </div>\r\n</template>\r\n\r\n<script setup lang=\"ts\">\r\nimport { computed, type CSSProperties } from 'vue'\r\nimport { useHighlight } from '../../hooks/useHighlight'\r\nimport type { SyntaxCodeBlockProps } from './types'\r\n\r\ninterface HighlightToken {\r\n content?: string\r\n color?: string\r\n fontStyle?: 'italic' | null\r\n fontWeight?: 'normal' | 'bold' | null\r\n htmlStyle?: Record<string, string>\r\n}\r\n\r\ndefineOptions({\r\n name: 'SyntaxCodeBlock',\r\n})\r\n\r\nconst props = withDefaults(defineProps<SyntaxCodeBlockProps>(), {\r\n lightTheme: 'vitesse-light',\r\n darkTheme: 'vitesse-dark',\r\n isDark: false,\r\n enableAnimate: false,\r\n})\r\n\r\nconst code = computed(() => props.code.trim())\r\n\r\nconst language = computed(() => props.language || 'text')\r\n\r\nconst actualTheme = computed(() => (props.isDark ? props.darkTheme : props.lightTheme))\r\n\r\nconst { lines, preStyle } = useHighlight(code, {\r\n language,\r\n theme: actualTheme,\r\n colorReplacements: props.colorReplacements,\r\n})\r\n\r\nconst applyColorReplacement = (color: string, replacements?: Record<string, string>) => {\r\n if (!replacements) return color\r\n return replacements[color.toLowerCase()] || color\r\n}\r\n\r\nconst normalizeStyleKeys = (style: Record<string, string | number>): CSSProperties => {\r\n const normalized: CSSProperties = {}\r\n Object.entries(style).forEach(([key, value]) => {\r\n const camelKey = key.replace(/-([a-z])/g, (_, char) => char.toUpperCase())\r\n ;(normalized as Record<string, string | number>)[camelKey] = value\r\n })\r\n return normalized\r\n}\r\n\r\nconst getTokenStyle = (token: HighlightToken | null | undefined): CSSProperties => {\r\n // 处理 null/undefined token\r\n if (!token) {\r\n return {}\r\n }\r\n\r\n // 优先使用 htmlStyle(如果存在)\r\n if (token.htmlStyle) {\r\n const baseStyle = normalizeStyleKeys(token.htmlStyle)\r\n\r\n if (!props.colorReplacements) return baseStyle\r\n\r\n const style = { ...baseStyle }\r\n\r\n if (style.color && typeof style.color === 'string') {\r\n style.color = applyColorReplacement(style.color, props.colorReplacements)\r\n }\r\n if (style.backgroundColor && typeof style.backgroundColor === 'string') {\r\n style.backgroundColor = applyColorReplacement(style.backgroundColor, props.colorReplacements)\r\n }\r\n\r\n return style\r\n }\r\n\r\n // 直接使用 token 的 color、fontStyle、fontWeight 属性\r\n const style: CSSProperties = {}\r\n\r\n if (token.color) {\r\n style.color = props.colorReplacements\r\n ? applyColorReplacement(token.color, props.colorReplacements)\r\n : token.color\r\n }\r\n\r\n if (token.fontStyle === 'italic') {\r\n style.fontStyle = 'italic'\r\n }\r\n\r\n if (token.fontWeight) {\r\n style.fontWeight = token.fontWeight\r\n }\r\n\r\n return style\r\n}\r\n\r\nconst showFallback = computed(() => !lines.value?.length)\r\n\r\nconst codeContainerStyle = computed(() => ({\r\n ...preStyle.value,\r\n maxHeight: props.codeMaxHeight,\r\n}))\r\n\r\ndefineExpose({\r\n lines,\r\n code,\r\n language,\r\n actualTheme,\r\n})\r\n</script>\r\n\r\n<style scoped>\r\n.x-md-syntax-code-block {\r\n width: 100%;\r\n}\r\n\r\n.x-md-syntax-code-block pre {\r\n margin: 0;\r\n padding: 16px;\r\n overflow: auto;\r\n background: transparent !important;\r\n}\r\n\r\n.x-md-code-content {\r\n display: flex;\r\n flex-direction: column;\r\n}\r\n\r\n.x-md-code-line {\r\n width: 100%;\r\n font-size: 14px;\r\n line-height: 1.5;\r\n display: flex;\r\n}\r\n</style>"],"names":["style","_openBlock","_createElementBlock","_createElementVNode","_Fragment","_renderList","_unref","_normalizeStyle","_normalizeClass","_toDisplayString"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"x-markdown.es33.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
package/dist/x-markdown.es7.js
CHANGED
|
@@ -2,6 +2,14 @@ import { ref, computed, isRef, toValue, watch, onUnmounted } from "vue";
|
|
|
2
2
|
let shikiModulePromise = null;
|
|
3
3
|
let shikiStreamModulePromise = null;
|
|
4
4
|
let hasShownDependencyHint = false;
|
|
5
|
+
const dynamicImport = async (moduleName) => {
|
|
6
|
+
const importFn = new Function("moduleName", `return import(moduleName)`);
|
|
7
|
+
try {
|
|
8
|
+
return await importFn(moduleName);
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
5
13
|
const showDependencyHint = () => {
|
|
6
14
|
if (hasShownDependencyHint) return;
|
|
7
15
|
hasShownDependencyHint = true;
|
|
@@ -27,10 +35,10 @@ const loadShiki = async () => {
|
|
|
27
35
|
if (!shikiModulePromise) {
|
|
28
36
|
shikiModulePromise = (async () => {
|
|
29
37
|
try {
|
|
30
|
-
const mod = await
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
38
|
+
const mod = await dynamicImport("shiki");
|
|
39
|
+
if (!mod) {
|
|
40
|
+
showDependencyHint();
|
|
41
|
+
}
|
|
34
42
|
return mod;
|
|
35
43
|
} catch (error) {
|
|
36
44
|
console.error("[x-markdown] Failed to load shiki:", error);
|
|
@@ -45,10 +53,10 @@ const loadShikiStream = async () => {
|
|
|
45
53
|
if (!shikiStreamModulePromise) {
|
|
46
54
|
shikiStreamModulePromise = (async () => {
|
|
47
55
|
try {
|
|
48
|
-
const mod = await
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
56
|
+
const mod = await dynamicImport("shiki-stream");
|
|
57
|
+
if (!mod) {
|
|
58
|
+
showDependencyHint();
|
|
59
|
+
}
|
|
52
60
|
return mod;
|
|
53
61
|
} catch (error) {
|
|
54
62
|
console.error("[x-markdown] Failed to load shiki-stream:", error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"x-markdown.es7.js","sources":["../src/hooks/useHighlight.ts"],"sourcesContent":["import { ref, watch, onUnmounted, computed, isRef, toValue, type Ref, type MaybeRef, type CSSProperties } from 'vue'\r\n\r\ninterface HighlightToken {\r\n content?: string\r\n color?: string\r\n fontStyle?: 'italic' | null\r\n fontWeight?: 'normal' | 'bold' | null\r\n htmlStyle?: Record<string, string>\r\n}\r\n\r\ninterface StreamingHighlightResult {\r\n colorReplacements?: Record<string, string>\r\n lines: HighlightToken[][]\r\n preStyle?: CSSProperties\r\n}\r\n\r\ninterface UseHighlightOptions {\r\n language: MaybeRef<string>\r\n theme?: string | Ref<string>\r\n colorReplacements?: Record<string, string>\r\n}\r\n\r\nlet shikiModulePromise: Promise<any | null> | null = null\r\nlet shikiStreamModulePromise: Promise<any | null> | null = null\r\nlet hasShownDependencyHint = false\r\n\r\nconst showDependencyHint = () => {\r\n if (hasShownDependencyHint) return\r\n hasShownDependencyHint = true\r\n\r\n console.log(\r\n '%c[x-markdown]%c 代码高亮功能已降级为纯文本模式',\r\n 'font-weight: bold; color: #0066cc;',\r\n 'color: #666;'\r\n )\r\n console.log(\r\n '%c如需语法高亮功能,请安装以下依赖:',\r\n 'color: #666; font-weight: bold;'\r\n )\r\n console.log(\r\n '%c pnpm add shiki shiki-stream',\r\n 'color: #00aa00; font-family: monospace;'\r\n )\r\n console.log(\r\n '%c安装后请重启开发服务器',\r\n 'color: #999; font-size: 12px;'\r\n )\r\n}\r\n\r\nconst loadShiki = async () => {\r\n if (!shikiModulePromise) {\r\n shikiModulePromise = (async () => {\r\n try {\r\n // 使用 /* @vite-ignore */ 告诉 Vite 跳过预构建检查\r\n // 配合 external 配置,这个 import 语句会被保留在运行时执行\r\n const mod = await import(/* @vite-ignore */ 'shiki')\r\n return mod\r\n } catch (error) {\r\n console.error('[x-markdown] Failed to load shiki:', error)\r\n showDependencyHint()\r\n return null\r\n }\r\n })()\r\n }\r\n return shikiModulePromise\r\n}\r\n\r\nconst loadShikiStream = async () => {\r\n if (!shikiStreamModulePromise) {\r\n shikiStreamModulePromise = (async () => {\r\n try {\r\n // 使用 /* @vite-ignore */ 告诉 Vite 跳过预构建检查\r\n const mod = await import(/* @vite-ignore */ 'shiki-stream')\r\n return mod\r\n } catch (error) {\r\n console.error('[x-markdown] Failed to load shiki-stream:', error)\r\n showDependencyHint()\r\n return null\r\n }\r\n })()\r\n }\r\n return shikiStreamModulePromise\r\n}\r\n\r\nconst tokensToLineTokens = (tokens: HighlightToken[]): HighlightToken[][] => {\r\n if (!tokens.length) return [[]]\r\n\r\n const lines: HighlightToken[][] = [[]]\r\n let currentLine = lines[0]\r\n\r\n const startNewLine = () => {\r\n currentLine = []\r\n lines.push(currentLine)\r\n }\r\n\r\n tokens.forEach((token) => {\r\n const content = token.content ?? ''\r\n\r\n if (content === '\\n') {\r\n startNewLine()\r\n return\r\n }\r\n\r\n if (!content.includes('\\n')) {\r\n currentLine.push(token)\r\n return\r\n }\r\n\r\n const segments = content.split('\\n')\r\n segments.forEach((segment, index) => {\r\n if (segment) {\r\n currentLine.push({\r\n ...token,\r\n content: segment,\r\n })\r\n }\r\n\r\n if (index < segments.length - 1) {\r\n startNewLine()\r\n }\r\n })\r\n })\r\n\r\n return lines.length === 0 ? [[]] : lines\r\n}\r\n\r\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\r\n if (!bg && !fg) return undefined\r\n return {\r\n backgroundColor: bg,\r\n color: fg,\r\n }\r\n}\r\n\r\nexport function useHighlight(text: Ref<string>, options: UseHighlightOptions) {\r\n const streaming = ref<StreamingHighlightResult>()\r\n const isLoading = ref(false)\r\n const error = ref<Error | null>(null)\r\n\r\n let tokenizer: any | null = null\r\n let previousText = ''\r\n let highlighter: any | null = null\r\n let currentUsedLang = ''\r\n let lastRequestedLang = ''\r\n\r\n const effectiveTheme = computed(() => {\r\n const theme = isRef(options.theme) ? options.theme.value : options.theme\r\n return theme || 'slack-dark'\r\n })\r\n\r\n const effectiveLanguage = computed(() => {\r\n return toValue(options.language) || 'text'\r\n })\r\n\r\n const lines = computed(() => streaming.value?.lines || [[]])\r\n const preStyle = computed(() => streaming.value?.preStyle)\r\n\r\n const updateTokens = async (nextText: string, forceReset = false) => {\r\n if (!tokenizer) return\r\n\r\n if (forceReset) {\r\n tokenizer.clear()\r\n previousText = ''\r\n }\r\n\r\n const canAppend = !forceReset && nextText.startsWith(previousText)\r\n let chunk = nextText\r\n\r\n if (canAppend) {\r\n chunk = nextText.slice(previousText.length)\r\n } else if (!forceReset) {\r\n tokenizer.clear()\r\n }\r\n\r\n previousText = nextText\r\n\r\n if (!chunk) {\r\n const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable]\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: mergedTokens.length ? tokensToLineTokens(mergedTokens) : [[]],\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n return\r\n }\r\n\r\n try {\r\n await tokenizer.enqueue(chunk)\r\n\r\n const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable]\r\n\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: tokensToLineTokens(mergedTokens),\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n } catch (err) {\r\n console.error('[x-markdown] Streaming highlighting failed:', err)\r\n error.value = err as Error\r\n }\r\n }\r\n\r\n const initHighlighter = async () => {\r\n isLoading.value = true\r\n error.value = null\r\n\r\n let currentLang = effectiveLanguage.value\r\n const currentTheme = effectiveTheme.value\r\n\r\n try {\r\n const mod = await loadShiki()\r\n if (!mod) {\r\n console.warn('[x-markdown] Shiki not available, falling back to plain text mode')\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: text.value }]],\r\n preStyle: undefined,\r\n }\r\n return\r\n }\r\n\r\n highlighter = await mod.getSingletonHighlighter({\r\n langs: [],\r\n themes: [currentTheme],\r\n })\r\n\r\n lastRequestedLang = currentLang\r\n\r\n try {\r\n await highlighter.loadLanguage(currentLang as any)\r\n currentUsedLang = currentLang\r\n } catch {\r\n console.warn(`[x-markdown] Failed to load language: ${currentLang}, falling back to plaintext`)\r\n currentLang = 'plaintext'\r\n currentUsedLang = 'plaintext'\r\n }\r\n\r\n const StreamMod = await loadShikiStream()\r\n if (!StreamMod) {\r\n console.warn('[x-markdown] shiki-stream not available, using non-streaming mode')\r\n const tokens = highlighter.codeToThemedTokens(text.value, currentLang, currentTheme)\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: tokensToLineTokens(tokens),\r\n preStyle: createPreStyle(\r\n highlighter.getTheme(currentTheme)?.bg,\r\n highlighter.getTheme(currentTheme)?.fg\r\n ),\r\n }\r\n return\r\n }\r\n\r\n const ShikiStreamTokenizer = StreamMod.ShikiStreamTokenizer || StreamMod.default\r\n tokenizer = new ShikiStreamTokenizer({\r\n highlighter: highlighter,\r\n lang: currentLang,\r\n theme: currentTheme,\r\n })\r\n\r\n previousText = ''\r\n\r\n const themeInfo = highlighter.getTheme(currentTheme)\r\n const preStyleValue = createPreStyle(themeInfo?.bg, themeInfo?.fg)\r\n\r\n if (text.value) {\r\n await updateTokens(text.value, true)\r\n if (streaming.value) {\r\n streaming.value.preStyle = preStyleValue\r\n }\r\n } else {\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[]],\r\n preStyle: preStyleValue,\r\n }\r\n }\r\n } catch (err) {\r\n console.error('[x-markdown] Highlighter initialization failed:', err)\r\n error.value = err as Error\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: text.value }]],\r\n preStyle: undefined,\r\n }\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n watch(\r\n () => [effectiveLanguage.value, effectiveTheme.value],\r\n async ([newLang]) => {\r\n const requestedLang = newLang as string\r\n\r\n if (\r\n highlighter &&\r\n currentUsedLang === 'plaintext' &&\r\n requestedLang !== lastRequestedLang &&\r\n requestedLang !== 'plaintext'\r\n ) {\r\n try {\r\n await highlighter.loadLanguage(requestedLang as any)\r\n initHighlighter()\r\n return\r\n } catch {\r\n lastRequestedLang = requestedLang\r\n return\r\n }\r\n }\r\n\r\n initHighlighter()\r\n },\r\n { immediate: true },\r\n )\r\n\r\n watch(text, async (newText) => {\r\n const requestedLang = effectiveLanguage.value\r\n if (\r\n highlighter &&\r\n currentUsedLang === 'plaintext' &&\r\n requestedLang !== lastRequestedLang &&\r\n requestedLang !== 'plaintext'\r\n ) {\r\n try {\r\n await highlighter.loadLanguage(requestedLang as any)\r\n await initHighlighter()\r\n return\r\n } catch {\r\n lastRequestedLang = requestedLang\r\n }\r\n }\r\n\r\n if (tokenizer) {\r\n updateTokens(newText)\r\n } else if (!highlighter) {\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: newText }]],\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n }\r\n })\r\n\r\n onUnmounted(() => {\r\n tokenizer?.clear()\r\n tokenizer = null\r\n previousText = ''\r\n })\r\n\r\n return {\r\n streaming,\r\n lines,\r\n preStyle,\r\n isLoading,\r\n error,\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"x-markdown.es7.js","sources":["../src/hooks/useHighlight.ts"],"sourcesContent":["import { ref, watch, onUnmounted, computed, isRef, toValue, type Ref, type MaybeRef, type CSSProperties } from 'vue'\r\n\r\ninterface HighlightToken {\r\n content?: string\r\n color?: string\r\n fontStyle?: 'italic' | null\r\n fontWeight?: 'normal' | 'bold' | null\r\n htmlStyle?: Record<string, string>\r\n}\r\n\r\ninterface StreamingHighlightResult {\r\n colorReplacements?: Record<string, string>\r\n lines: HighlightToken[][]\r\n preStyle?: CSSProperties\r\n}\r\n\r\ninterface UseHighlightOptions {\r\n language: MaybeRef<string>\r\n theme?: string | Ref<string>\r\n colorReplacements?: Record<string, string>\r\n}\r\n\r\nlet shikiModulePromise: Promise<any | null> | null = null\r\nlet shikiStreamModulePromise: Promise<any | null> | null = null\r\nlet hasShownDependencyHint = false\r\n\r\n/**\r\n * 动态导入辅助函数,绕过 Vite 的静态分析\r\n */\r\nconst dynamicImport = async (moduleName: string): Promise<any> => {\r\n // 使用 Function 构造器绕过静态分析\r\n // 构造一个函数,该函数返回 import(moduleName) 的结果\r\n const importFn = new Function('moduleName', `return import(moduleName)`)\r\n try {\r\n return await importFn(moduleName)\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\nconst showDependencyHint = () => {\r\n if (hasShownDependencyHint) return\r\n hasShownDependencyHint = true\r\n\r\n console.log(\r\n '%c[x-markdown]%c 代码高亮功能已降级为纯文本模式',\r\n 'font-weight: bold; color: #0066cc;',\r\n 'color: #666;'\r\n )\r\n console.log(\r\n '%c如需语法高亮功能,请安装以下依赖:',\r\n 'color: #666; font-weight: bold;'\r\n )\r\n console.log(\r\n '%c pnpm add shiki shiki-stream',\r\n 'color: #00aa00; font-family: monospace;'\r\n )\r\n console.log(\r\n '%c安装后请重启开发服务器',\r\n 'color: #999; font-size: 12px;'\r\n )\r\n}\r\n\r\nconst loadShiki = async () => {\r\n if (!shikiModulePromise) {\r\n shikiModulePromise = (async () => {\r\n try {\r\n // 使用动态导入绕过 Vite 静态分析\r\n const mod = await dynamicImport('shiki')\r\n if (!mod) {\r\n showDependencyHint()\r\n }\r\n return mod\r\n } catch (error) {\r\n console.error('[x-markdown] Failed to load shiki:', error)\r\n showDependencyHint()\r\n return null\r\n }\r\n })()\r\n }\r\n return shikiModulePromise\r\n}\r\n\r\nconst loadShikiStream = async () => {\r\n if (!shikiStreamModulePromise) {\r\n shikiStreamModulePromise = (async () => {\r\n try {\r\n // 使用动态导入绕过 Vite 静态分析\r\n const mod = await dynamicImport('shiki-stream')\r\n if (!mod) {\r\n showDependencyHint()\r\n }\r\n return mod\r\n } catch (error) {\r\n console.error('[x-markdown] Failed to load shiki-stream:', error)\r\n showDependencyHint()\r\n return null\r\n }\r\n })()\r\n }\r\n return shikiStreamModulePromise\r\n}\r\n\r\nconst tokensToLineTokens = (tokens: HighlightToken[]): HighlightToken[][] => {\r\n if (!tokens.length) return [[]]\r\n\r\n const lines: HighlightToken[][] = [[]]\r\n let currentLine = lines[0]\r\n\r\n const startNewLine = () => {\r\n currentLine = []\r\n lines.push(currentLine)\r\n }\r\n\r\n tokens.forEach((token) => {\r\n const content = token.content ?? ''\r\n\r\n if (content === '\\n') {\r\n startNewLine()\r\n return\r\n }\r\n\r\n if (!content.includes('\\n')) {\r\n currentLine.push(token)\r\n return\r\n }\r\n\r\n const segments = content.split('\\n')\r\n segments.forEach((segment, index) => {\r\n if (segment) {\r\n currentLine.push({\r\n ...token,\r\n content: segment,\r\n })\r\n }\r\n\r\n if (index < segments.length - 1) {\r\n startNewLine()\r\n }\r\n })\r\n })\r\n\r\n return lines.length === 0 ? [[]] : lines\r\n}\r\n\r\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\r\n if (!bg && !fg) return undefined\r\n return {\r\n backgroundColor: bg,\r\n color: fg,\r\n }\r\n}\r\n\r\nexport function useHighlight(text: Ref<string>, options: UseHighlightOptions) {\r\n const streaming = ref<StreamingHighlightResult>()\r\n const isLoading = ref(false)\r\n const error = ref<Error | null>(null)\r\n\r\n let tokenizer: any | null = null\r\n let previousText = ''\r\n let highlighter: any | null = null\r\n let currentUsedLang = ''\r\n let lastRequestedLang = ''\r\n\r\n const effectiveTheme = computed(() => {\r\n const theme = isRef(options.theme) ? options.theme.value : options.theme\r\n return theme || 'slack-dark'\r\n })\r\n\r\n const effectiveLanguage = computed(() => {\r\n return toValue(options.language) || 'text'\r\n })\r\n\r\n const lines = computed(() => streaming.value?.lines || [[]])\r\n const preStyle = computed(() => streaming.value?.preStyle)\r\n\r\n const updateTokens = async (nextText: string, forceReset = false) => {\r\n if (!tokenizer) return\r\n\r\n if (forceReset) {\r\n tokenizer.clear()\r\n previousText = ''\r\n }\r\n\r\n const canAppend = !forceReset && nextText.startsWith(previousText)\r\n let chunk = nextText\r\n\r\n if (canAppend) {\r\n chunk = nextText.slice(previousText.length)\r\n } else if (!forceReset) {\r\n tokenizer.clear()\r\n }\r\n\r\n previousText = nextText\r\n\r\n if (!chunk) {\r\n const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable]\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: mergedTokens.length ? tokensToLineTokens(mergedTokens) : [[]],\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n return\r\n }\r\n\r\n try {\r\n await tokenizer.enqueue(chunk)\r\n\r\n const mergedTokens = [...tokenizer.tokensStable, ...tokenizer.tokensUnstable]\r\n\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: tokensToLineTokens(mergedTokens),\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n } catch (err) {\r\n console.error('[x-markdown] Streaming highlighting failed:', err)\r\n error.value = err as Error\r\n }\r\n }\r\n\r\n const initHighlighter = async () => {\r\n isLoading.value = true\r\n error.value = null\r\n\r\n let currentLang = effectiveLanguage.value\r\n const currentTheme = effectiveTheme.value\r\n\r\n try {\r\n const mod = await loadShiki()\r\n if (!mod) {\r\n console.warn('[x-markdown] Shiki not available, falling back to plain text mode')\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: text.value }]],\r\n preStyle: undefined,\r\n }\r\n return\r\n }\r\n\r\n highlighter = await mod.getSingletonHighlighter({\r\n langs: [],\r\n themes: [currentTheme],\r\n })\r\n\r\n lastRequestedLang = currentLang\r\n\r\n try {\r\n await highlighter.loadLanguage(currentLang as any)\r\n currentUsedLang = currentLang\r\n } catch {\r\n console.warn(`[x-markdown] Failed to load language: ${currentLang}, falling back to plaintext`)\r\n currentLang = 'plaintext'\r\n currentUsedLang = 'plaintext'\r\n }\r\n\r\n const StreamMod = await loadShikiStream()\r\n if (!StreamMod) {\r\n console.warn('[x-markdown] shiki-stream not available, using non-streaming mode')\r\n const tokens = highlighter.codeToThemedTokens(text.value, currentLang, currentTheme)\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: tokensToLineTokens(tokens),\r\n preStyle: createPreStyle(\r\n highlighter.getTheme(currentTheme)?.bg,\r\n highlighter.getTheme(currentTheme)?.fg\r\n ),\r\n }\r\n return\r\n }\r\n\r\n const ShikiStreamTokenizer = StreamMod.ShikiStreamTokenizer || StreamMod.default\r\n tokenizer = new ShikiStreamTokenizer({\r\n highlighter: highlighter,\r\n lang: currentLang,\r\n theme: currentTheme,\r\n })\r\n\r\n previousText = ''\r\n\r\n const themeInfo = highlighter.getTheme(currentTheme)\r\n const preStyleValue = createPreStyle(themeInfo?.bg, themeInfo?.fg)\r\n\r\n if (text.value) {\r\n await updateTokens(text.value, true)\r\n if (streaming.value) {\r\n streaming.value.preStyle = preStyleValue\r\n }\r\n } else {\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[]],\r\n preStyle: preStyleValue,\r\n }\r\n }\r\n } catch (err) {\r\n console.error('[x-markdown] Highlighter initialization failed:', err)\r\n error.value = err as Error\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: text.value }]],\r\n preStyle: undefined,\r\n }\r\n } finally {\r\n isLoading.value = false\r\n }\r\n }\r\n\r\n watch(\r\n () => [effectiveLanguage.value, effectiveTheme.value],\r\n async ([newLang]) => {\r\n const requestedLang = newLang as string\r\n\r\n if (\r\n highlighter &&\r\n currentUsedLang === 'plaintext' &&\r\n requestedLang !== lastRequestedLang &&\r\n requestedLang !== 'plaintext'\r\n ) {\r\n try {\r\n await highlighter.loadLanguage(requestedLang as any)\r\n initHighlighter()\r\n return\r\n } catch {\r\n lastRequestedLang = requestedLang\r\n return\r\n }\r\n }\r\n\r\n initHighlighter()\r\n },\r\n { immediate: true },\r\n )\r\n\r\n watch(text, async (newText) => {\r\n const requestedLang = effectiveLanguage.value\r\n if (\r\n highlighter &&\r\n currentUsedLang === 'plaintext' &&\r\n requestedLang !== lastRequestedLang &&\r\n requestedLang !== 'plaintext'\r\n ) {\r\n try {\r\n await highlighter.loadLanguage(requestedLang as any)\r\n await initHighlighter()\r\n return\r\n } catch {\r\n lastRequestedLang = requestedLang\r\n }\r\n }\r\n\r\n if (tokenizer) {\r\n updateTokens(newText)\r\n } else if (!highlighter) {\r\n streaming.value = {\r\n colorReplacements: options.colorReplacements,\r\n lines: [[{ content: newText }]],\r\n preStyle: streaming.value?.preStyle,\r\n }\r\n }\r\n })\r\n\r\n onUnmounted(() => {\r\n tokenizer?.clear()\r\n tokenizer = null\r\n previousText = ''\r\n })\r\n\r\n return {\r\n streaming,\r\n lines,\r\n preStyle,\r\n isLoading,\r\n error,\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
package/dist/x-markdown.es9.js
CHANGED
|
@@ -65,6 +65,14 @@ let mermaidPromise = null;
|
|
|
65
65
|
let hasShownMermaidHint = false;
|
|
66
66
|
let mermaidAvailableCache = null;
|
|
67
67
|
let mermaidCheckPromise = null;
|
|
68
|
+
const dynamicImport = async (moduleName) => {
|
|
69
|
+
const importFn = new Function("moduleName", `return import(moduleName)`);
|
|
70
|
+
try {
|
|
71
|
+
return await importFn(moduleName);
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
68
76
|
async function checkMermaidAvailable() {
|
|
69
77
|
if (mermaidAvailableCache !== null) {
|
|
70
78
|
return mermaidAvailableCache;
|
|
@@ -74,10 +82,7 @@ async function checkMermaidAvailable() {
|
|
|
74
82
|
}
|
|
75
83
|
mermaidCheckPromise = (async () => {
|
|
76
84
|
try {
|
|
77
|
-
const mod = await
|
|
78
|
-
/* @vite-ignore */
|
|
79
|
-
"mermaid"
|
|
80
|
-
);
|
|
85
|
+
const mod = await dynamicImport("mermaid");
|
|
81
86
|
mermaidAvailableCache = !!mod;
|
|
82
87
|
return mermaidAvailableCache;
|
|
83
88
|
} catch (error) {
|
|
@@ -114,11 +119,8 @@ async function loadMermaid() {
|
|
|
114
119
|
if (!mermaidPromise) {
|
|
115
120
|
mermaidPromise = (async () => {
|
|
116
121
|
try {
|
|
117
|
-
const mod = await
|
|
118
|
-
|
|
119
|
-
"mermaid"
|
|
120
|
-
);
|
|
121
|
-
return mod.default;
|
|
122
|
+
const mod = await dynamicImport("mermaid");
|
|
123
|
+
return mod?.default;
|
|
122
124
|
} catch (error) {
|
|
123
125
|
console.error("[x-markdown] Failed to load mermaid:", error);
|
|
124
126
|
showMermaidHint();
|