特定のリンクにアイコンをつけるUserCSSを自動生成したい
共通設定の書き換えとかテンプレートのコピペとかが面倒なので、できるだけ少ない手間で設定できるようにしたい
テンプレート:特定のリンクにアイコンをつけるUserCSS#620132d37838e300003015ac
CSSの生成はTypeScriptとGoogleスプレッドシートでやったことがあるMijinko_SD.icon
とりあえず今回はTypeScriptで
これ、 install deno の手間と、ソースコードを読み解く手間が増えてるのでは?Summer498.icon
あとエラー対応の手間
code:error
$ deno run --allow-run --reload https://scrapbox.io/api/code/villagepump/特定のリンクに
アイコンをつけるUserCSSを自動生成したい/script.ts
error: Uncaught NotFound: No such file or directory (os error 2)
const process = await Deno.run({
^
at opRun (ext:runtime/40_process.js:46:14)
at Object.run (ext:runtime/40_process.js:132:15)
at write (https://deno.land/x/copy_paste@v1.1.3/mod.ts:29:30)
at writeText (https://deno.land/x/copy_paste@v1.1.3/mod.ts:48:16)
at https://scrapbox.io/api/code/villagepump/%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%81%AB%E3%82%A2%E3%82%A4%E3%82%B3%E3%83%B3%E3%82%92%E3%81%A4%E3%81%91%E3%82%8BUserCSS%E3%82%92%E8%87%AA%E5%8B%95%E7%94%9F%E6%88%90%E3%81%97%E3%81%9F%E3%81%84/script.ts:104:7
Ubuntu の CLI で動かしてみたのがまずかったかな
いやでもそれ以外に deno run が動く環境知らんしな
目的は YouTube リンクにアイコンを付けることなんで、誰か後をよろしくお願いします
作ったMijinko_SD.icon
ついでに外部リンクを区別するUserCSSも生成するようにした
アイコンの追加は楽になった反面、共通で使っているスタイルの編集(特にセレクタ)はだるくなった
サンプル
普通の外部リンク
https://google.com
scrapbox.ioへのリンク
除外するの忘れてたMijinko_SD.icon
展開ヘルプ
progfay/scrapbox-parser
増井俊之
https://ja.wikipedia.org/wiki/ご注文はうさぎですか?
更新履歴
2023/01/17 20:35:24 ✅️リンクを出典アイコンにするUserCSSでリンクをクリックできない問題があるので、アイコンをマウスホバーしてもカーソルが👆にならないようにした
2023/01/17 20:41:40 ツイートのリンクの後ろにもアイコンが付いていたのを修正
スタイル設定用のファイル
$ deno run --allow-run --reload https://scrapbox.io/api/code/villagepump/特定のリンクにアイコンをつけるUserCSSを自動生成したい/script.ts
クリップボードにCSSを保存する仕様の為(正確にはiuioiua/deno_copy_pasteが要求してくる為)--allow-runが必要になる
不安なら使わないほうが良さげMijinko_SD.icon
externalLinksに設定を書き込むと反映される
code:script.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
import { Style, toCSSText, writeText } from "./deps.ts";
import {
generateExternalLinkStyle,
generateLinkIconStyle,
LinkInit,
} from "./stylesheetGenerator.ts";
/**
* CSSの生成に使用するオブジェクト
*/
const externalLinks: LinkInit[] = [{
// PDF
url: {
url: ".pdf",
selectorSyntax: "$=",
},
style: {
"content": "'\\f1c1'",
"font-weight": 400, /* use Regular Style */
"margin-right": "1px",
},
}, {
// GitHub
url: [
"https://github.com",
"https://raw.githubusercontent.com",
"https://gist.github.com",
],
style: {
"content": "'\\f09b'",
"font-weight": 400,
"margin-right": "1px",
},
}, {
// Wikipedia
url: [
"https://ja.wikipedia.org",
"https://en.wikipedia.org",
],
style: {
"content": "'\\f266'",
"font-weight": 400,
"margin-right": "3px",
},
}, {
// Amazon
url: [
"https://www.amazon.co.jp",
"https://amazon.co.jp",
],
style: {
"content": "'\\f270'",
"font-weight": 400,
"margin-right": "1px",
},
}, {
// Twitter
url: [
"https://twitter.com"
],
style: {} // 標準でアイコンがあるので不要
}, {
// YouTube
url: [
"https://www.youtube.com",
"https://youtube.com",
],
style: {
"content": "'\\f167'",
"font-weight": 400,
"margin-right": "1px"
}
}, {
// Arch
url: [
"https://archlinux.org",
"https://www.archlinux.org",
"https://aur.archlinux.org",
"https://wiki.archlinux.org",
"https://wiki.archlinux.jp",
],
style: {
"content": "''",
"font-weight": 400,
"margin-right": "1px",
"background-image": 'url("https://i.gyazo.com/7031deebce32df5fd696879df98c34e0.png")',
}
}];
/**
* 特定のリンクにアイコンをつけるUserCSS#26a3f9(https://scrapbox.io/villagepump/特定のリンクにアイコンをつけるUserCSS#61ffa1ce1280f0000026a3f9)
* を実装するためのやつ
*/
const extraStyle: Style = {
[
".line .deco-\\. a.link:is(" +
'[href^="https://ja.wikipedia.org"],' +
'[href^="https://en.wikipedia.org"],' +
") span.char-index"
]: {
"display": "inline-block",
"width": 0,
"text-indent": "-9999px",
"&:nth-of-type(30) ~ span.char-index": {
"display": "inherit",
"width": "inherit",
"text-indent": "inherit",
},
},
};
const elStyle: Style = generateExternalLinkStyle(externalLinks);
const liStyle: Style = generateLinkIconStyle(externalLinks);
await writeText(toCSSText({ ...elStyle, ...liStyle, ...extraStyle }));
.cssから変換した時のメモ
property: value;を"property": "value",に変換するやつ
正規表現:^(\s*)(.+):\s*(.*)\s*;.*$
置換文字:$1"$2": "$3",
この他に、contentプロパティの値の\をエスケープする必要がある
\ -> \\
CSS生成用のファイル
code:stylesheetGenerator.ts
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
import { Style } from "./deps.ts";
interface ChildStyle {
K: string: string | number | ChildStyle;
}
export type AttributeSelectorOperator = "^=" | "=" | "$=" | "*=" | "~=" | "|=";
export interface URLInit {
url: string;
/**
* 属性セレクタに使用する演算子
* 指定しない場合は^=が用いられる
*/
selectorSyntax: AttributeSelectorOperator;
}
export interface LinkInit {
url: string | URLInit | (string | URLInit)[];
style: ChildStyle;
}
/**
* 外部リンクを区別するUserCSSを生成する関数
*/
export function generateExternalLinkStyle(links: LinkInit[]): Style {
const urls = getURLFromLinkInit(links);
const selector =
`.line span:not(.modal-image):not(.pointing-device-map) > a.link:not(.icon):not(:is(${
urls.map((crt) => [href${crt.selectorSyntax}"${crt.url}"]).join(",")
}))::after`;
const style: Style = {
selector: {
"font-family": "'Font Awesome 5 Free', 'Font Awesome 5 Brands'",
"font-weight": "900",
"font-size": "0.8rem",
"content": "'\\f35d'",
"display": "inline-block",
"cursor": "text",
},
};
return style;
}
/**
* 特定のリンクにアイコンをつけるUserCSSを生成する関数
*/
export function generateLinkIconStyle(links: LinkInit[]): Style {
const allURLs = getURLFromLinkInit(links);
const baseSelector = `.line span:not(.deco-\\.) > span > a.link:is(${
allURLs.map((crt) => [href${crt.selectorSyntax}"${crt.url}"]).join(",")
})::before`;
/** 先頭にアイコンを付けるための共通のスタイル */
const baseStyle: Style = {
baseSelector: {
"display": "inline-block",
"width": "1em",
"height": "1em",
"vertical-align": "-1px",
"text-align": "center",
"background-size": "contain",
"background-repeat": "no-repeat",
"cursor": "text",
},
};
const individualStyle: Style = {};
for (const link of links) {
const urls = getURLFromLinkInit(link);
const selector = `:is(.line, .line .deco) a.link:is(${
urls.map((crt) => [href${crt.selectorSyntax}"${crt.url}"]).join(",")
})::before`;
individualStyleselector = link.style;
}
return { ...baseStyle, ...individualStyle };
}
/**
* LinkInitの配列からURLを取り出す
* @param {LinkInit[]} links 取り出し元のオブジェクト
* @param {AttributeSelectorOperator} defaultOperator="^=" selectorSyntaxが指定されていなかった際に使用するデフォルト値
* @return {URLInit[]}
*/
function getURLFromLinkInit(
links: LinkInit[],
defaultOperator: AttributeSelectorOperator = "^=",
): URLInit[] {
const resultURLs: URLInit[] = [];
for (const link of links) {
if (Array.isArray(link.url)) {
for (const url of link.url) {
if (typeof url === "string") {
resultURLs.push({ url: url, selectorSyntax: defaultOperator });
} else {
resultURLs.push(url);
}
}
} else {
const url = link.url;
if (typeof url === "string") {
resultURLs.push({ url: url, selectorSyntax: defaultOperator });
} else {
resultURLs.push(url);
}
}
}
return resultURLs;
}
依存関係をまとめたやつ
code:deps.ts
// CSSテキストを生成するのに使う
export {
toCSSText,
} from "https://scrapbox.io/api/code/Mijinko/SCSSっぽい連想配列からCSSを生成するTypeScript関数/style.ts";
export type {
Style,
} from "https://scrapbox.io/api/code/Mijinko/SCSSっぽい連想配列からCSSを生成するTypeScript関数/style.ts";
// クリップボードにコピーするやつ
export { writeText } from "https://deno.land/x/copy_paste@v1.1.3/mod.ts";
生成後のCSS
$ https://scrapbox.io/api/code/villagepump/特定のリンクにアイコンをつけるUserCSSを自動生成したい/style.css
code:style.css
.line span:not(.modal-image):not(.pointing-device-map) > a.link:not(.icon):not(:is(href$=".pdf",[href^="https://github.com"],[href^="https://raw.githubusercontent.com"],[href^="https://gist.github.com"],[href^="https://ja.wikipedia.org"],[href^="https://en.wikipedia.org"],[href^="https://www.amazon.co.jp"],[href^="https://amazon.co.jp"],[href^="https://twitter.com"],[href^="https://www.youtube.com"],[href^="https://youtube.com"],[href^="https://archlinux.org"],[href^="https://www.archlinux.org"],[href^="https://aur.archlinux.org"],[href^="https://wiki.archlinux.org"],[href^="https://wiki.archlinux.jp"]))::after{font-family:'Font Awesome 5 Free', 'Font Awesome 5 Brands';font-weight:900;font-size:0.8rem;content:'\f35d';display:inline-block;cursor:text;}.line span:not(.deco-\.) > span > a.link:is(href$=".pdf",[href^="https://github.com"],[href^="https://raw.githubusercontent.com"],[href^="https://gist.github.com"],[href^="https://ja.wikipedia.org"],[href^="https://en.wikipedia.org"],[href^="https://www.amazon.co.jp"],[href^="https://amazon.co.jp"],[href^="https://twitter.com"],[href^="https://www.youtube.com"],[href^="https://youtube.com"],[href^="https://archlinux.org"],[href^="https://www.archlinux.org"],[href^="https://aur.archlinux.org"],[href^="https://wiki.archlinux.org"],[href^="https://wiki.archlinux.jp"])::before{display:inline-block;width:1em;height:1em;vertical-align:-1px;text-align:center;background-size:contain;background-repeat:no-repeat;cursor:text;}:is(.line, .line .deco) a.link:is(href$=".pdf")::before{content:'\f1c1';font-weight:400;margin-right:1px;}:is(.line, .line .deco) a.link:is([href^="https://github.com"],[href^="https://raw.githubusercontent.com"],[href^="https://gist.github.com"])::before{content:'\f09b';font-weight:400;margin-right:1px;}:is(.line, .line .deco) a.link:is([href^="https://ja.wikipedia.org"],[href^="https://en.wikipedia.org"])::before{content:'\f266';font-weight:400;margin-right:3px;}:is(.line, .line .deco) a.link:is([href^="https://www.amazon.co.jp"],[href^="https://amazon.co.jp"])::before{content:'\f270';font-weight:400;margin-right:1px;}:is(.line, .line .deco) a.link:is([href^="https://www.youtube.com"],[href^="https://youtube.com"])::before{content:'\f167';font-weight:400;margin-right:1px;}:is(.line, .line .deco) a.link:is([href^="https://archlinux.org"],[href^="https://www.archlinux.org"],[href^="https://aur.archlinux.org"],[href^="https://wiki.archlinux.org"],[href^="https://wiki.archlinux.jp"])::before{content:'';font-weight:400;margin-right:1px;background-image:url("https://i.gyazo.com/7031deebce32df5fd696879df98c34e0.png");}.line .deco-\. a.link:is([href^="https://ja.wikipedia.org"],[href^="https://en.wikipedia.org"],) span.char-index{display:inline-block;width:0;text-indent:-9999px;}.line .deco-\. a.link:is([href^="https://ja.wikipedia.org"],[href^="https://en.wikipedia.org"],) span.char-index:nth-of-type(30) ~ span.char-index{display:inherit;width:inherit;text-indent:inherit;}
Settings.icon
UserCSS.icon