Vditor-网页富文本编辑器
[gitee源代码](https://gitee.com/mirrors/Vditor)
[官方使用说明](https://ld246.com/article/1549638745630)
去[jsdelivr](https://www.jsdelivr.com/package/npm/vditor?path=dist&utm_source=ld246.com&tab=files)下载,位置在右上角,有个下载图标
# 源码编译安装
```
git clone https://gitee.com/mirrors/Vditor.git
cd Vditor
export PUPPETEER_SKIP_DOWNLOAD=true
npm install
```
这时可能会出现很多peer dependencies 冲突的错误。
1. 删除 `node_modules`和 `package-lock.json`
2. 修改package.json中相关包的版本,我这次修改了:
```
"ts-jest": "^29.4.6",
"eslint-plugin-jest": "^29.2.1",
"jest": "^29.7.0",
```
之后再次运行 `npm install`
```
npm run start
```
这时可能会出现/Vditor/src/ts/undo/index.ts有错误,可以把代码和错误给元宝,它会修改代码。修改过的代码如下:
```
import * as DiffMatchPatch from "diff-match-patch";
import {disableToolbar, enableToolbar, hidePanel} from "../toolbar/setToolbar";
import {isFirefox, isSafari} from "../util/compatibility";
import {scrollCenter} from "../util/editorCommonEvent";
import {execAfterRender} from "../util/fixBrowserBehavior";
import {highlightToolbar} from "../util/highlightToolbar";
import {processCodeRender} from "../util/processCode";
import {setRangeByWbr, setSelectionFocus} from "../util/selection";
import {renderToc} from "../util/toc";
// 定义 DiffMatchPatch 的类型
interface PatchObj {
diffs: Array<[number, string]>;
start1: number;
start2: number;
length1: number;
length2: number;
}
interface IUndo {
hasUndo: boolean;
lastText: string;
redoStack: PatchObj[][];
undoStack: PatchObj[][];
}
class Undo {
private stackSize = 50;
// 修复错误1: 使用正确的类型声明
private dmp: any; // 使用 any 类型,因为 diff-match-patch 的类型定义不完整
private wysiwyg: IUndo;
private ir: IUndo;
private sv: IUndo;
constructor() {
this.resetStack();
// 修改这里:直接使用 default 导出
this.dmp = new (DiffMatchPatch as any).diff_match_patch();
}
public clearStack(vditor: IVditor) {
this.resetStack();
this.resetIcon(vditor);
}
public resetIcon(vditor: IVditor) {
if (!vditor.toolbar) {
return;
}
if (this[vditor.currentMode].undoStack.length > 1) {
enableToolbar(vditor.toolbar.elements, ["undo"]);
} else {
disableToolbar(vditor.toolbar.elements, ["undo"]);
}
if (this[vditor.currentMode].redoStack.length !== 0) {
enableToolbar(vditor.toolbar.elements, ["redo"]);
} else {
disableToolbar(vditor.toolbar.elements, ["redo"]);
}
}
public undo(vditor: IVditor) {
if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
return;
}
if (this[vditor.currentMode].undoStack.length < 2) {
return;
}
const state = this[vditor.currentMode].undoStack.pop();
if (!state) {
return;
}
this[vditor.currentMode].redoStack.push(state);
this.renderDiff(state, vditor);
this[vditor.currentMode].hasUndo = true;
// undo 操作后,需要关闭 hint
hidePanel(vditor, ["hint"]);
}
public redo(vditor: IVditor) {
if (vditor[vditor.currentMode].element.getAttribute("contenteditable") === "false") {
return;
}
const state = this[vditor.currentMode].redoStack.pop();
if (!state) {
return;
}
this[vditor.currentMode].undoStack.push(state);
this.renderDiff(state, vditor, true);
}
public recordFirstPosition(vditor: IVditor, event: KeyboardEvent) {
if (getSelection().rangeCount === 0) {
return;
}
if (this[vditor.currentMode].undoStack.length !== 1 || this[vditor.currentMode].undoStack[0].length === 0 ||
this[vditor.currentMode].redoStack.length > 0) {
return;
}
if (isFirefox() && event.key === "Backspace") {
// Firefox 第一次删除无效
return;
}
if (isSafari()) {
// Safari keydown 在 input 之后,不需要重复记录历史
return;
}
const text = this.addCaret(vditor);
if (text.replace("<wbr>", "").replace(" vditor-ir__node--expand", "")
!== this[vditor.currentMode].undoStack[0][0].diffs[0][1].replace("<wbr>", "")) {
// 当还不没有存入 undo 栈时,按下 ctrl 后会覆盖 lastText
return;
}
this[vditor.currentMode].undoStack[0][0].diffs[0][1] = text;
this[vditor.currentMode].lastText = text;
// 不能添加 setSelectionFocus(cloneRange); 否则 windows chrome 首次输入会烂
}
public addToUndoStack(vditor: IVditor) {
// afterRenderEvent.ts 已经 debounce
const text = this.addCaret(vditor, true);
const diff = this.dmp.diff_main(text, this[vditor.currentMode].lastText, true);
const patchList = this.dmp.patch_make(text, this[vditor.currentMode].lastText, diff);
if (patchList.length === 0 && this[vditor.currentMode].undoStack.length > 0) {
return;
}
this[vditor.currentMode].lastText = text;
this[vditor.currentMode].undoStack.push(patchList);
if (this[vditor.currentMode].undoStack.length > this.stackSize) {
this[vditor.currentMode].undoStack.shift();
}
if (this[vditor.currentMode].hasUndo) {
this[vditor.currentMode].redoStack = [];
this[vditor.currentMode].hasUndo = false;
disableToolbar(vditor.toolbar.elements, ["redo"]);
}
if (this[vditor.currentMode].undoStack.length > 1) {
enableToolbar(vditor.toolbar.elements, ["undo"]);
}
}
private renderDiff(state: PatchObj[], vditor: IVditor, isRedo: boolean = false) {
let text;
if (isRedo) {
const redoPatchList = this.dmp.patch_deepCopy(state).reverse();
// 修复错误2和3: 为回调参数添加类型注解
redoPatchList.forEach((patch: any) => {
patch.diffs.forEach((diff: any) => {
diff[0] = -diff[0];
});
});
text = this.dmp.patch_apply(redoPatchList, this[vditor.currentMode].lastText)[0];
} else {
text = this.dmp.patch_apply(state, this[vditor.currentMode].lastText)[0];
}
this[vditor.currentMode].lastText = text;
vditor[vditor.currentMode].element.innerHTML = text;
if (vditor.currentMode !== "sv") {
vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview`)
.forEach((blockElement: HTMLElement) => {
if (blockElement.parentElement.querySelector(".language-echarts")) {
if (vditor.currentMode === "ir") {
blockElement.parentElement.outerHTML = vditor.lute.SpinVditorIRDOM(blockElement.parentElement.outerHTML);
} else {
blockElement.parentElement.outerHTML = vditor.lute.SpinVditorDOM(blockElement.parentElement.outerHTML);
}
}
});
vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
.forEach((blockElement: HTMLElement) => {
processCodeRender(blockElement, vditor);
});
}
if (!vditor[vditor.currentMode].element.querySelector("wbr")) {
// Safari 第一次输入没有光标,需手动定位到结尾
const range = getSelection().getRangeAt(0);
range.setEndBefore(vditor[vditor.currentMode].element);
range.collapse(false);
} else {
setRangeByWbr(
vditor[vditor.currentMode].element, vditor[vditor.currentMode].element.ownerDocument.createRange());
scrollCenter(vditor);
}
renderToc(vditor);
execAfterRender(vditor, {
enableAddUndoStack: false,
enableHint: false,
enableInput: true,
});
highlightToolbar(vditor);
vditor[vditor.currentMode].element.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='2']`)
.forEach((item: HTMLElement) => {
processCodeRender(item, vditor);
});
if (this[vditor.currentMode].undoStack.length > 1) {
enableToolbar(vditor.toolbar.elements, ["undo"]);
} else {
disableToolbar(vditor.toolbar.elements, ["undo"]);
}
if (this[vditor.currentMode].redoStack.length !== 0) {
enableToolbar(vditor.toolbar.elements, ["redo"]);
} else {
disableToolbar(vditor.toolbar.elements, ["redo"]);
}
}
private resetStack() {
this.ir = {
hasUndo: false,
lastText: "",
redoStack: [],
undoStack: [],
};
this.sv = {
hasUndo: false,
lastText: "",
redoStack: [],
undoStack: [],
};
this.wysiwyg = {
hasUndo: false,
lastText: "",
redoStack: [],
undoStack: [],
};
}
private addCaret(vditor: IVditor, setFocus = false) {
let cloneRange: Range;
if (getSelection().rangeCount !== 0 && !vditor[vditor.currentMode].element.querySelector("wbr")) {
const range = getSelection().getRangeAt(0);
if (vditor[vditor.currentMode].element.contains(range.startContainer)) {
cloneRange = range.cloneRange();
const wbrElement = document.createElement("span");
wbrElement.className = "vditor-wbr";
range.insertNode(wbrElement);
}
}
// 移除数学公式、echart 渲染 https://github.com/Vanessa219/vditor/issues/1738
const cloneElement = vditor[vditor.currentMode].element.cloneNode(true) as HTMLElement;
cloneElement.querySelectorAll(`.vditor-${vditor.currentMode}__preview[data-render='1']`)
.forEach((item: HTMLElement) => {
if (!item.firstElementChild) {
return;
}
if (item.firstElementChild.classList.contains("language-echarts") ||
item.firstElementChild.classList.contains("language-plantuml") ||
item.firstElementChild.classList.contains("language-mindmap")) {
item.firstElementChild.removeAttribute("_echarts_instance_");
item.firstElementChild.removeAttribute("data-processed");
item.firstElementChild.innerHTML = item.previousElementSibling.firstElementChild.innerHTML;
item.setAttribute("data-render", "2");
} else if (item.firstElementChild.classList.contains("language-math")) {
item.setAttribute("data-render", "2");
item.firstElementChild.textContent = item.firstElementChild.getAttribute("data-math");
item.firstElementChild.removeAttribute("data-math");
}
});
const text = cloneElement.innerHTML;
vditor[vditor.currentMode].element.querySelectorAll(".vditor-wbr").forEach((item) => {
item.remove();
// 使用 item.outerHTML = "" 会产生 https://github.com/Vanessa219/vditor/pull/686;
});
if (setFocus && cloneRange) {
setSelectionFocus(cloneRange);
}
return text.replace('<span class="vditor-wbr"></span>', "<wbr>");
}
}
export {Undo};
```
之后,就是 `npm run build`,目录下的dist就是全部的文件。
- 共 0 条回复
- 需要登录 后方可回复, 如果你还没有账号请点击这里注册。
wiseAI
✨ 梦初醒 茅塞开
- 不经他人苦,莫劝他人善。
- 能量足,心态稳,温和坚定可以忍。
- 辛苦决定不了收入,真正决定收入的只有一个,就是不可替代性。
- 要么忙于生存,要么赶紧去死!
- 内心强大到混蛋,比什么都好!
- 规范流程比制定制度更重要!
-
立志需要高远,但不能急功近利;
行动需要迅速,却不可贪图速成。 - 不要强求人品,要设计高效的机制。
-
你弱的时候,身边都是鸡零狗碎;
你强的时候,身边都是风和日丽。 - 机制比人品更可靠,契约比感情更可靠。
- 合作不意味着没有冲突,却是控制冲突的最好方法。
- 误解是人生常态,理解本是稀缺的例外。
- 成功和不成功之间,只差一次坚持!
- 祁连卧北雪,大漠壮雄关。
- 利益顺序,过程公开,机会均等,付出回报。
