Projectを横断してリンクを置換するUserScript
https://gyazo.com/4543a17e3436d7dcebc50e99eb39bf16
↑Gyazo GIFがなぜかガクガクしてしまったので、別途GIFを作った
https://gyazo.com/ab4f4583e92efa03b516a64f2c17a08f.mp4
使い方
code:script.js
import { replace, getLink } from "./mod.js";
scrapbox.PopupMenu.addButton({
title: (text) => getLink(text) ? "update link" : "",
onClick: (text) => replace(
text,
),
});
以下の修正が必要
実装したいこと
現状だと、置換後のリンクと同名のタイトルがあるとエラーになる
置換対象のprojectを選択できるようにする?
UIが複雑になりそう
現在のprojectも対象にしたい
各自でreplace()の引数に入れてください
2024-11-13
2024-07-05
微妙に正規表現が違うせいで、popup menu上は検知されているのに押しても置換windowが表示されないことがしょっちゅうあってまあまあストレスだった
重い腰を上げて、ようやくなおした
dependencies
code:mod.ts
import {
disconnect,
connect,
patch,
replaceLinks,
useStatusBar,
} from "../scrapbox-userscript-std/mod.ts";
import type { ErrorLike } from "../scrapbox-jp%2Ftypes/rest.ts";
import { getLinks } from "../複数のリンクをまとめて置換するUserScript/mod.ts";
import { isErr, unwrapErr, unwrapOk } from "npm:option-t@49/plain_result";
export const replace = async (text: string, projects: string[]) => {
const link = getLinks(text)0; if (!link) return;
const newLink = window.prompt(
`Replace "${link}" to this in ${
projects.map((project) => "/${project}").join(", ")
}`,
link,
)
?.replace?.(/[\\\n]/g, " ") ?? ""; if (newLink === "") return;
const { render, dispose } = useStatusBar();
const socket = unwrapOk(await connect())!;
try {
render(
{ type: "spinner" },
{
type: "text",
text: Replacing links in ${projects.length} projects...,
},
);
let count = 0;
const replacedNums = await Promise.all(projects.map(async (project) => {
const result = await Promise.all([ // 本当はhasBackLinksOrIcons === trueのときのみ置換したい
replaceLinks(project, link, newLink),
patch(project, link, (lines, { persistent }) => {
if (!persistent) return;
return [
newLink,
...lines.map((line) => line.text).slice(1),
];
}, { socket }),
]);
if (isErr(result)) {
render(
{ type: "exclamation-triangle" },
{
type: "text",
text: ${unwrapErr(result).name} ${unwrapErr(result).message},
},
);
throw toError(unwrapErr(result));
}
count++;
render(
{ type: "spinner" },
{
type: "text",
text: Replacing links in ${projects.length - count} projects...,
},
);
return unwrapOk(result);
}));
const replaced = replacedNums.reduce((acc, cur) => acc + cur, 0);
render(
{ type: "check-circle" },
{ type: "text", text: Successfully replaced ${replaced} links. },
);
} finally {
await disconnect(socket);
setTimeout(dispose, 1000);
}
}
const toError = (e: ErrorLike) => {
const error = new Error();
error.name = e.name;
error.message = e.message;
return error;
}