特定のリンクにアイコンをつけるUserCSSを自動生成したい
共通設定の書き換えとかテンプレートのコピペとかが面倒なので、できるだけ少ない手間で設定できるようにしたい
とりあえず今回はTypeScriptで
これ、 install deno の手間と、ソースコードを読み解く手間が増えてるのでは?Summer498.icon
あとエラー対応の手間
code:error
アイコンをつける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)
Ubuntu の CLI で動かしてみたのがまずかったかな
いやでもそれ以外に deno run が動く環境知らんしな
目的は YouTube リンクにアイコンを付けることなんで、誰か後をよろしくお願いします
作ったMijinko_SD.icon
アイコンの追加は楽になった反面、共通で使っているスタイルの編集(特にセレクタ)はだるくなった
サンプル
除外するの忘れてたMijinko_SD.icon
更新履歴
2023/01/17 20:41:40 ツイートのリンクの後ろにもアイコンが付いていたのを修正
スタイル設定用のファイル
不安なら使わないほうが良さげ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: [
],
style: {
"content": "'\\f09b'",
"font-weight": 400,
"margin-right": "1px",
},
}, {
// Wikipedia
url: [
],
style: {
"content": "'\\f266'",
"font-weight": 400,
"margin-right": "3px",
},
}, {
// Amazon
url: [
],
style: {
"content": "'\\f270'",
"font-weight": 400,
"margin-right": "1px",
},
}, {
// Twitter
url: [
],
style: {} // 標準でアイコンがあるので不要
}, {
// YouTube
url: [
"https:www.youtube.com",
"https:youtube.com",
],
style: {
"content": "'\\f167'",
"font-weight": 400,
"margin-right": "1px"
}
}];
/**
* を実装するためのやつ
*/
const extraStyle: Style = {
[
".line .deco-\\. a.link:is(" +
") 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 {
}
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.reduce(
(pre, crt) => ${pre}[href${crt.selectorSyntax}"${crt.url}"],,
"",
) +
"))::after";
const style: Style = {
"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.reduce(
(pre, crt) => ${pre}[href${crt.selectorSyntax}"${crt.url}"],,
"",
) +
")::before";
/** 先頭にアイコンを付けるための共通のスタイル */
const baseStyle: Style = {
"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.reduce(
(pre, crt) => ${pre}[href${crt.selectorSyntax}"${crt.url}"],,
"",
) +
")::before";
}
return { ...baseStyle, ...individualStyle };
}
/**
* LinkInitの配列からURLを取り出す
* @param {LinkInit[]} links 取り出し元のオブジェクト
* @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,
export type {
Style,
// クリップボードにコピーするやつ
生成後のCSS
code:style.css
font-family: 'Font Awesome 5 Free', 'Font Awesome 5 Brands';
font-weight: 900;
font-size: 0.8rem;
content: '\f35d';
display: inline-block;
cursor: text;
}
display: inline-block;
width: 1em;
height: 1em;
vertical-align: -1px;
text-align: center;
background-size: contain;
background-repeat: no-repeat;
cursor: text;
}
content: '\f1c1';
font-weight: 400;
margin-right: 1px;
}
content: '\f09b';
font-weight: 400;
margin-right: 1px;
}
content: '\f266';
font-weight: 400;
margin-right: 3px;
}
content: '\f270';
font-weight: 400;
margin-right: 1px;
}
.line .deco-\. a.link:is(
) span.char-index {
display: inline-block;
width: 0;
text-indent: -9999px;
}
display: inherit;
width: inherit;
text-indent: inherit;
}
Settings.icon
UserCSS.icon