A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 143 lines 5.0 kB view raw
1import {onTransaction, transaction} from "../wysiwyg/transaction"; 2import {preventScroll} from "../scroll/preventScroll"; 3import {Constants} from "../../constants"; 4import {hideElements} from "../ui/hideElements"; 5import {scrollCenter} from "../../util/highlightById"; 6import {matchHotKey} from "../util/hotKey"; 7import {ipcRenderer} from "electron"; 8 9interface IOperations { 10 doOperations: IOperation[], 11 undoOperations: IOperation[] 12} 13 14export class Undo { 15 private hasUndo = false; 16 public redoStack: IOperations[]; 17 public undoStack: IOperations[]; 18 19 constructor() { 20 this.redoStack = []; 21 this.undoStack = []; 22 } 23 24 public undo(protyle: IProtyle) { 25 if (protyle.disabled) { 26 return; 27 } 28 if (this.undoStack.length === 0) { 29 return; 30 } 31 const state = this.undoStack.pop(); 32 this.render(protyle, state, false); 33 this.hasUndo = true; 34 this.redoStack.push(state); 35 if (protyle.breadcrumb) { 36 const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]'); 37 if (undoElement) { 38 if (this.undoStack.length === 0) { 39 undoElement.setAttribute("disabled", "true"); 40 } 41 protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]').removeAttribute("disabled"); 42 } 43 } 44 } 45 46 public redo(protyle: IProtyle) { 47 if (protyle.disabled) { 48 return; 49 } 50 if (this.redoStack.length === 0) { 51 return; 52 } 53 const state = this.redoStack.pop(); 54 this.render(protyle, state, true); 55 this.undoStack.push(state); 56 if (protyle.breadcrumb) { 57 const redoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]'); 58 if (redoElement) { 59 protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]').removeAttribute("disabled"); 60 if (this.redoStack.length === 0) { 61 redoElement.setAttribute("disabled", "true"); 62 } 63 } 64 } 65 } 66 67 private render(protyle: IProtyle, state: IOperations, redo: boolean) { 68 hideElements(["hint", "gutter"], protyle); 69 protyle.wysiwyg.lastHTMLs = {}; 70 if (!redo) { 71 state.undoOperations.forEach(item => { 72 onTransaction(protyle, item, true); 73 }); 74 transaction(protyle, state.undoOperations); 75 } else { 76 state.doOperations.forEach(item => { 77 onTransaction(protyle, item, true); 78 }); 79 transaction(protyle, state.doOperations); 80 } 81 document.querySelector(".av__panel")?.remove(); 82 preventScroll(protyle); 83 scrollCenter(protyle); 84 } 85 86 public replace(doOperations: IOperation[], protyle: IProtyle) { 87 // undo 引发 replace 导致 stack 错误 https://github.com/siyuan-note/siyuan/issues/9178 88 if (this.hasUndo && this.redoStack.length > 0) { 89 this.undoStack.push(this.redoStack.pop()); 90 this.redoStack = []; 91 this.hasUndo = false; 92 if (protyle.breadcrumb) { 93 const redoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="redo"]'); 94 if (redoElement) { 95 redoElement.setAttribute("disabled", "true"); 96 } 97 } 98 } 99 if (this.undoStack.length > 0) { 100 this.undoStack[this.undoStack.length - 1].doOperations = doOperations; 101 } 102 } 103 104 public add(doOperations: IOperation[], undoOperations: IOperation[], protyle: IProtyle) { 105 this.undoStack.push({undoOperations, doOperations}); 106 if (this.undoStack.length > Constants.SIZE_UNDO) { 107 this.undoStack.shift(); 108 } 109 if (this.hasUndo) { 110 this.redoStack = []; 111 this.hasUndo = false; 112 } 113 if (protyle.breadcrumb) { 114 const undoElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="undo"]'); 115 if (undoElement) { 116 undoElement.removeAttribute("disabled"); 117 } 118 } 119 } 120 121 public clear() { 122 this.undoStack = []; 123 this.redoStack = []; 124 } 125} 126 127export const electronUndo = (event: KeyboardEvent) => { 128 /// #if !BROWSER 129 if (matchHotKey(window.siyuan.config.keymap.editor.general.undo.custom, event)) { 130 ipcRenderer.send(Constants.SIYUAN_CMD, "undo"); 131 event.preventDefault(); 132 event.stopPropagation(); 133 return true; 134 } 135 if (matchHotKey(window.siyuan.config.keymap.editor.general.redo.custom, event)) { 136 ipcRenderer.send(Constants.SIYUAN_CMD, "redo"); 137 event.preventDefault(); 138 event.stopPropagation(); 139 return true; 140 } 141 /// #endif 142 return false; 143};