scrapbox-url-customizer
cosenseに張り付けたURLを変換するUserScript
scrapbox-url-customizer
作った意味なかったかな……と少し思ったが、返信先tweetや引用tweetを取り込めるようになったから上位互換だった
くそ!やられた!(演技)基素.icon
やったぜtakker.icon
基素.iconさんに先越されてちょっとくやしかった
powered by GPT-4.iconだから…基素.icon
GPT使いこなせないせいでcodingが遅い……takker.icon
人間にしては早いと思いますgpt-4.icon
https://gyazo.com/20f7f72c2fc8d13f7f06835e841db2c9.mp4
やったこと
やったー基素.icon
余計なURL parametersを除去したり、短縮URLを展開したりするのにも使える
いいね!nishio.icon
code:設計の解説のつもり.ts
const promise = convert(
// 変換したい文字列もしくはURL object
text,
// 変換処理
// 上から順に適用される
// 非同期処理も可能
(url: URL) => { /* ... */ },
(url: URL) =>
url.pathname.endsWith(".pdf") ?
// 文字列を返すと、その文字列で対象のURLが置換され、以降の変換処理は実行されない
[pdf ${url}] :
// URLで返すと、次の変換処理にそれが渡される
url,
// ...
)
// すべての変換が同期で終わるとstringに、一つでも非同期で変換処理が走ったらPromise<string>になる
const convertedText = promise instanceof Promise ? await promise : promise;
書き直すか……
引用tweetと返信先tweetのみを一緒に展開している
その他、t.coの展開などの処理をサンプルとして置いてある
リンク切れしていた基素.icon
これもJSRへ移行したせいですtakker.icon
mainから、最後にhttp importを使っていたversionである0.4.3に固定しておきました
編集用コマンド
0.2.1 => 0.2.1
https://img.shields.io/github/v/release/takker99/scrapbox-url-customizer?display_name=tag&sort=semver#.svg
使い方
簡単な方法
ここを押して生成されたコードを、自分のページのscript.jsに貼り付ける このバージョンは0.3.3基素.icon
最新0.4.5にしたら失敗した。0.4.4も通らず。0.4.3は通った
0.4.4からjsrに移行したからですtakker.icon URLをなおしました
これは以下のコードをbuildしたものです
https://code2svg.vercel.app/svg/https://raw.githubusercontent.com/takker99/scrapbox-url-customizer/0.2.1/examples/popup.ts#.svg https://github.com/takker99/scrapbox-url-customizer/blob/0.2.1/examples/popup.ts
同期変換、つまりfetchせずに変換が終了したときは、即座に文字列置換が走るようになっている
https://gyazo.com/44c798aa41ee218cbcbb40927a0ed31a
カスタマイズ方法
このUserScriptが提供するのは、URLの変換処理のみです
実際にScrapbox上で編集するコードは各自で書く必要があります
上記のコードを参考にしてカスタマイズしてください
カスタマイズ例
twitterの画像をgyazoにuploadする
連続してtwitter APIを叩いて、連ツイを取得する
リンク先のページの本文全てをscrapboxに取り込む
別の全然違うUserScript内で、URLを変換する処理として使う
基素.icon
使ってみたいけどScrapboxで利用するための初期設定がまだわかっていない
簡単に使う場合は、ここからPopupMenuとして実装したサンプルを取得できますtakker.icon code:mod.ts
そんな感じですtakker.icon
githubのdepsに依存しているからそのままだと読めない気がする
URL変換処理以外のすべてを自前で実装しなければならないので、設定はけっこう大変かも……takker.icon
ユーザーが自由にカスタマイズできるようにしたかった
なるほど基素.icon
まだ触れていない基素.icon
wogikaze.icon
https://gyazo.com/f05fd3060a6eade5c06c8b9a3b493852
エラー出るけど何かミスっているのだろうか..
結局エラー出てるのか
ビンゴ
code:ok(js)
fetch_(https://cdn.syndication.twimg.com/tweet-result?id=${tweetId}&token=x);
でも全文じゃなくなってるな、なぜだ
Blue課金勢の長文は全文を取得できませんtakker.icon
それ以外も展開されない場合はおかしいな
もしかしたら埋め込みも全文でないようになってるのかも
帰ったら調べます
いやこれscrapbox側のjavascriptやないかい、console.errorでこっち呼べるんだ
この辺適当に放置してた気がする……takker.icon
いまどういうdirectory structureになってるんだっけ
middlewareを自作して注入でなんとかならないかなtakker.icon
format()に与える函数を替えればいけそう
defaultFormatterがexportされてないのがめんどくさい
exportしようっと
ありがとうございますwogikaze.icon
wogikaze.iconさんのwork4ai向けのformatを組み込んだコードです
たぶん動く
bundle errorが出たのでいくつか書き換えしましたwogikaze.icon
code:popup2.ts
import {
convert,
convertGyazoURL,
convertScrapboxURL,
expandShortURL,
formatTweet,
formatURL,
formatWikipedia,
redirectGoogleSearch,
redirectWikiwand,
shortenAmazonURL,
} from "jsr:@takker/scrapbox-url-customizer@^0.4.6";
import { insertText } from "jsr:@cosense/std@0.29/browser/dom";
import { scrapbox } from "jsr:/@cosense/types@0.10/userscript";
import { format } from "./format.ts";
// 毎回functionsを作るのは無駄なので、globalに保持しておく
const middlewares = [
redirectGoogleSearch,
expandShortURL,
redirectGoogleSearch,
redirectWikiwand,
shortenAmazonURL,
convertScrapboxURL(),
convertGyazoURL,
formatTweet(),
formatWikipedia,
formatURL(format),
] as const;
scrapbox.PopupMenu.addButton({
title: (text) => /https?:\/\/\S+/.test(text) ? "URL" : "",
onClick: (text) => {
const promise = convert(text, ...middlewares);
if (typeof promise === "string") {
// 文字列に違いがあるときのみ更新
return text === promise ? undefined : promise;
}
// 選択範囲に変換後の文字列を上書きする
// 変換中に選択範囲が変わると、ずれた位置に挿入されるので注意
promise.then((converted) => {
if (text === converted) return;
return insertText(converted);
});
return undefined;
},
});
code:format.ts
import { defaultFormat } from "jsr:@takker/scrapbox-url-customizer@^0.4.6/middlewares";
import { scrapbox } from "jsr:/@cosense/types@0.10/userscript";
import { isString } from "jsr:/@core/unknownutil@^4.0.0/is/string";
export const format = (material: Document | string, url: URL): string => {
if (!MyProjects.includes(scrapbox.Project.name)) return defaultFormat(material, url);
if (url.host === "github.com") {
const lastTwoSegments = url.href.split("/").slice(-2);
return [. ${url}][${lastTwoSegments[0]}]/[${lastTwoSegments[1]}];
} else if (url.host === "huggingface.co" && !url.href.includes("huggingface.co/papers") && !url.href.includes("huggingface.co/blog")) {
const lastTwoSegments = url.href.split("/").slice(-2);
return [. ${url}][${lastTwoSegments[0]}]/[${lastTwoSegments[1]}];
}
const title = (isString(material) ? material : material.title)
.replace(/\s/g, " ") // スペースと改行を全て半角スペースにする
.replaceAll("[", "[")
.replaceAll("]", "]");
const hash = url.hash ? ${decodeURIComponent(url.hash.slice(1))} | : "";
if (url.host === "arxiv.org") {
return title ? [. ${hash}${url}][${title.replace(/\[[\d\.]+\]\s/, "")}] : ${url};
} else if (url.host.includes("github.io")) {
return title ? [. ${hash}${url}][${title}] : ${url};
} else if (url.href.includes("huggingface.co/papers")) {
return title ? [.${hash}${url}][${title.replace(/Paper page -\s/, "")}] : ${url};
}
return title ? [. ${hash}${url}]${title.split("|")[0]} : ${url};
};
UserScript.icon