@yltrcc/vditor 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +2 -2
- package/dist/index.js +366 -70
- package/dist/index.min.js +1 -1
- package/dist/method.js +56 -56
- package/dist/method.min.js +1 -1
- package/dist/ts/util/imageResize.d.ts +1 -0
- package/dist/ts/wysiwyg/renderDomByMd.d.ts +11 -0
- package/package.json +1 -1
- package/src/assets/less/_wysiwyg.less +314 -314
- package/src/index.ts +3 -0
- package/src/ts/markdown/getMarkdown.ts +107 -60
- package/src/ts/markdown/setLute.ts +26 -26
- package/src/ts/util/fixBrowserBehavior.ts +1570 -1570
- package/src/ts/util/imageResize.ts +166 -0
- package/src/ts/wysiwyg/highlightToolbarWYSIWYG.ts +1163 -1163
- package/src/ts/wysiwyg/index.ts +31 -0
- package/src/ts/wysiwyg/input.ts +13 -9
- package/src/ts/wysiwyg/renderDomByMd.ts +109 -1
package/src/ts/wysiwyg/index.ts
CHANGED
|
@@ -251,8 +251,39 @@ class WYSIWYG {
|
|
|
251
251
|
}
|
|
252
252
|
});
|
|
253
253
|
|
|
254
|
+
// 处理带尺寸的图片 - 将 <img> 标签替换为自定义 Markdown 语法
|
|
255
|
+
const images = tempElement.querySelectorAll('img[data-resizable="true"]');
|
|
256
|
+
images.forEach((imgElement) => {
|
|
257
|
+
const img = imgElement as HTMLImageElement;
|
|
258
|
+
const src = img.getAttribute("src") || "";
|
|
259
|
+
const alt = img.getAttribute("alt") || "";
|
|
260
|
+
|
|
261
|
+
let width = img.getAttribute("width") || "";
|
|
262
|
+
let height = img.getAttribute("height") || "";
|
|
263
|
+
|
|
264
|
+
if (!width && img.style.width) {
|
|
265
|
+
width = img.style.width.replace("px", "").replace("%", "");
|
|
266
|
+
}
|
|
267
|
+
if (!height && img.style.height) {
|
|
268
|
+
height = img.style.height.replace("px", "").replace("%", "");
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (width && height) {
|
|
272
|
+
const md = ``;
|
|
273
|
+
imgElement.replaceWith(document.createTextNode(md));
|
|
274
|
+
} else if (width) {
|
|
275
|
+
const md = ``;
|
|
276
|
+
imgElement.replaceWith(document.createTextNode(md));
|
|
277
|
+
} else if (height) {
|
|
278
|
+
const md = ``;
|
|
279
|
+
imgElement.replaceWith(document.createTextNode(md));
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
254
283
|
event.clipboardData.setData("text/plain", vditor.lute.VditorDOM2Md(tempElement.innerHTML).trim());
|
|
255
284
|
event.clipboardData.setData("text/html", "");
|
|
285
|
+
console.log('Copy completed, data set to clipboard');
|
|
286
|
+
|
|
256
287
|
}
|
|
257
288
|
|
|
258
289
|
private bindEvent(vditor: IVditor) {
|
package/src/ts/wysiwyg/input.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {setRangeByWbr} from "../util/selection";
|
|
|
9
9
|
import {renderToc} from "../util/toc";
|
|
10
10
|
import {afterRenderEvent} from "./afterRenderEvent";
|
|
11
11
|
import {previoueIsEmptyA} from "./inlineTag";
|
|
12
|
+
import {processImageSizeInDOM, preprocessImageSize} from "./renderDomByMd";
|
|
12
13
|
|
|
13
14
|
export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
|
|
14
15
|
// 检查是否在代码块编辑模式中
|
|
@@ -165,16 +166,19 @@ export const input = (vditor: IVditor, range: Range, event?: InputEvent) => {
|
|
|
165
166
|
blockElement.innerHTML = html;
|
|
166
167
|
} else {
|
|
167
168
|
blockElement.outerHTML = html;
|
|
169
|
+
}
|
|
168
170
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
// 处理图片尺寸
|
|
172
|
+
processImageSizeInDOM(vditor.wysiwyg.element);
|
|
173
|
+
|
|
174
|
+
if (footnoteElement) {
|
|
175
|
+
// 更新正文中的 tip
|
|
176
|
+
const footnoteItemElement = hasTopClosestByTag(vditor.wysiwyg.element.querySelector("wbr"), "LI");
|
|
177
|
+
if (footnoteItemElement) {
|
|
178
|
+
const footnoteRefElement = vditor.wysiwyg.element.querySelector(`sup[data-type="footnotes-ref"][data-footnotes-label="${footnoteItemElement.getAttribute("data-marker")}"]`);
|
|
179
|
+
if (footnoteRefElement) {
|
|
180
|
+
footnoteRefElement.setAttribute("aria-label",
|
|
181
|
+
footnoteItemElement.textContent.trim().substr(0, 24));
|
|
178
182
|
}
|
|
179
183
|
}
|
|
180
184
|
}
|
|
@@ -1,13 +1,121 @@
|
|
|
1
1
|
import {processCodeRender} from "../util/processCode";
|
|
2
2
|
import {afterRenderEvent} from "./afterRenderEvent";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 后处理 DOM,处理图片尺寸
|
|
6
|
+
* 1. 将文本节点中的  转换为带尺寸的 <img>
|
|
7
|
+
* 2. 给没有 width/height 的 <img> 添加默认尺寸 300x200
|
|
8
|
+
*/
|
|
9
|
+
export const processImageSizeInDOM = (element: HTMLElement) => {
|
|
10
|
+
console.log('processImageSizeInDOM called');
|
|
11
|
+
|
|
12
|
+
// 第一步:处理文本节点中的带尺寸图片语法 
|
|
13
|
+
const walker = document.createTreeWalker(
|
|
14
|
+
element,
|
|
15
|
+
NodeFilter.SHOW_TEXT,
|
|
16
|
+
null
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const textNodes: Text[] = [];
|
|
20
|
+
let node: Node | null;
|
|
21
|
+
while (node = walker.nextNode()) {
|
|
22
|
+
textNodes.push(node as Text);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
textNodes.forEach((textNode) => {
|
|
26
|
+
const text = textNode.textContent || '';
|
|
27
|
+
|
|
28
|
+
// 匹配带尺寸的图片: 
|
|
29
|
+
const imageWithSizeRegex = /!\[([^\]]*)\]\(([^)]+?)\s*=(\d+)x(\d+)\)/g;
|
|
30
|
+
|
|
31
|
+
if (imageWithSizeRegex.test(text)) {
|
|
32
|
+
console.log('Found image with size in text:', text);
|
|
33
|
+
|
|
34
|
+
const parent = textNode.parentNode;
|
|
35
|
+
if (parent) {
|
|
36
|
+
const tempDiv = document.createElement('div');
|
|
37
|
+
|
|
38
|
+
const processedText = text.replace(imageWithSizeRegex, (match, alt, url, width, height) => {
|
|
39
|
+
console.log('Match found:', { match, alt, url, width, height });
|
|
40
|
+
let imgHtml = `<img src="${url.trim()}" alt="${alt}" data-resizable="true"`;
|
|
41
|
+
if (width) imgHtml += ` width="${width}"`;
|
|
42
|
+
if (height) imgHtml += ` height="${height}"`;
|
|
43
|
+
imgHtml += " />";
|
|
44
|
+
console.log('Generated imgHtml:', imgHtml);
|
|
45
|
+
return imgHtml;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
tempDiv.innerHTML = processedText;
|
|
49
|
+
|
|
50
|
+
// 替换文本节点
|
|
51
|
+
while (tempDiv.firstChild) {
|
|
52
|
+
parent.insertBefore(tempDiv.firstChild, textNode);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
parent.removeChild(textNode);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 第二步:给没有 width/height 的 <img> 添加默认尺寸
|
|
61
|
+
const images = element.querySelectorAll('img:not([data-resizable="true"])');
|
|
62
|
+
images.forEach((img) => {
|
|
63
|
+
const imgElement = img as HTMLImageElement;
|
|
64
|
+
const width = imgElement.getAttribute('width');
|
|
65
|
+
const height = imgElement.getAttribute('height');
|
|
66
|
+
const styleWidth = imgElement.style.width;
|
|
67
|
+
const styleHeight = imgElement.style.height;
|
|
68
|
+
|
|
69
|
+
// 如果没有设置尺寸,添加默认尺寸
|
|
70
|
+
if (!width && !height && !styleWidth && !styleHeight) {
|
|
71
|
+
console.log('Adding default size to img:', imgElement.src);
|
|
72
|
+
imgElement.setAttribute('width', '300');
|
|
73
|
+
imgElement.setAttribute('height', '200');
|
|
74
|
+
imgElement.setAttribute('data-resizable', 'true');
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* 预处理 Markdown,为没有尺寸的图片添加默认尺寸
|
|
81
|
+
* 将  转换为 
|
|
82
|
+
*/
|
|
83
|
+
export const preprocessImageSize = (md: string): string => {
|
|
84
|
+
// 匹配普通图片语法 ,但排除已经有 =widthxheight 的
|
|
85
|
+
// 使用负向前瞻来排除已包含 = 的图片
|
|
86
|
+
const imageRegex = /!\[([^\]]*)\]\(([^)=\]]+)\)(?![\s\w]*=)/g;
|
|
87
|
+
|
|
88
|
+
return md.replace(imageRegex, (match, alt, url) => {
|
|
89
|
+
// 检查 URL 是否已经包含尺寸参数
|
|
90
|
+
if (url.includes('=')) {
|
|
91
|
+
return match;
|
|
92
|
+
}
|
|
93
|
+
console.log('Preprocess: Adding default size to image:', match);
|
|
94
|
+
return `} =300x200)`;
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
|
|
4
98
|
export const renderDomByMd = (vditor: IVditor, md: string, options = {
|
|
5
99
|
enableAddUndoStack: true,
|
|
6
100
|
enableHint: false,
|
|
7
101
|
enableInput: true,
|
|
8
102
|
}) => {
|
|
9
103
|
const editorElement = vditor.wysiwyg.element;
|
|
10
|
-
|
|
104
|
+
|
|
105
|
+
console.log('renderDomByMd input:', md);
|
|
106
|
+
|
|
107
|
+
// 预处理:为普通图片添加默认尺寸
|
|
108
|
+
const processedMd = preprocessImageSize(md);
|
|
109
|
+
console.log('After preprocess:', processedMd);
|
|
110
|
+
|
|
111
|
+
editorElement.innerHTML = vditor.lute.Md2VditorDOM(processedMd);
|
|
112
|
+
|
|
113
|
+
console.log('After Lute render:', editorElement.innerHTML);
|
|
114
|
+
|
|
115
|
+
// 后处理图片尺寸(处理带 =widthxheight 的)
|
|
116
|
+
processImageSizeInDOM(editorElement);
|
|
117
|
+
|
|
118
|
+
console.log('After processImageSizeInDOM:', editorElement.innerHTML);
|
|
11
119
|
|
|
12
120
|
editorElement.querySelectorAll(".vditor-wysiwyg__preview[data-render='2']").forEach((item: HTMLElement) => {
|
|
13
121
|
processCodeRender(item, vditor);
|