⬜DOM経由でページを差分更新する機能を入れる
code:patch.ts
import { diff } from "../takker99%2Fonp/mod.ts";
import { Scrapbox } from "../scrapbox-jp%2Ftypes/userscript.ts";
import { Range, takeSelection, insertText } from "../scrapbox-userscript-std/dom.ts";
declare const scrapbox: Scrapbox;
/** DOM操作版patch
*
* websocket版とは違って、undoで操作を戻すことができる
*
* @param updater 書き換え前の行から書き換え後の行を作って返す関数。書き換えないときはundefinedを返す
*/
export const patchViaDOM = async (
updater: (before: string[]) => string[] | undefined,
): Promise<void> => {
if (scrapbox.Layout !== "page") return;
const prev = scrapbox.Page.lines.map((line) => line.text);
const next = updater(prev);
if (!next) return;
const selection = takeSelection();
for (const range, lines of findReplacePositions(prev, next)) { selection.setRange(range);
await insertText(${lines.join("\n")}\n);
}
};
function* findReplacePositions(before: string[], after: string[]) {
const { buildSES } = diff(before, after);
let position: [Range, string[]] | undefined;
let lineNo = 0;
for (const change of (buildSES())) {
switch (change.type) {
case "common":
if (position) {
yield position;
position = undefined;
}
lineNo++;
break;
case "added":
if (!position) {
position = [{
start: { line: lineNo, char: 0 },
end: { line: lineNo, char: 0 },
}, []];
}
position1.push(change.value); break;
case "deleted":
if (!position) {
position = [{
start: { line: lineNo, char: 0 },
end: { line: lineNo, char: 0 },
}, []];
}
lineNo++;
break;
}
}
if (position) yield position;
}
code:test.ts
import { patchViaDOM } from "./patch.ts";
globalThis.patchViaDOM = patchViaDOM;