generated at
インライン問題方式を実験してみる

題材
> (double standard) 仲間内と部外者、国内向けと外国向けなどのように、対象によって異なった価値判断の基準を使い分けること。
Anki英語:{{c1::double standard}}\n日本語:{{c2::二重規範}}
Anki対象によって{{c1::異なった価値判断の基準}}を使い分けることを{{c2::ダブルスタンダード::カタカナ英語}}と呼ぶ
>
> ダブルスタンダードとは - コトバンク
Anki{{c1::二重規範::全部漢字}}は略して{{c2::ダブスタ}}とも呼ぶ
> 略してダブスタ
> 二重規範とも
> ふたつ以上の場合は、マルチスタンダード/多重規範というらしい suto3
Anki2つ以上の異なった価値判断の基準を使う場合は、{{c1::二重規範}}ではなく{{c1::多重規範}}とも呼ぶ
> 通常、人を非難する場合に使われる
Anki二重規範は主に人を{{c1::非難}}する場合に使われる
本当はAnkiの問題とページ本文とを区別しないほうが楽なのかもしれないが、問題として加工する必要があるし、難しそうtakker
とりあえず↑のように別行に問題を記述する方針で実装してみる

書式
行頭にAnkiを付けている行が変換対象
Anki 」でもいいかもしれないtakker
改行するときは \n と書く
複数行にも対応できるようにしてみる
単一行
Anki %9E}%=)=5I 対象によって{{c1::異なった価値判断の基準}}を使い分けることを{{c2::ダブルスタンダード::カタカナ英語}}と呼ぶ
インデント配下の文章は無視する
%9E}%=)=5I は問題のGUID
1秒に1個問題を新規作成する場合、衝突確率が1%になるまで約31年以上かかる
これだけ衝突確率が低ければ十分だろう
複数行
Anki Za?7qh#5-a
{{c1::温度ひび割れ}}の対策
{{c2::温度変化}}を弱める
{{c3::水和熱}}を抑える
コンクリートを{{c4::冷却}}する
{{c5::引張}}応力を抑える
ひび割れを{{c6::分散}}させる
配下の行全てを一つの問題とみなす

問題作成支援ツール
穴埋め記号を作る
行頭にアイコンとIDを入れる
popup.js
import { customAlphabet } from "./nanoid.js"; const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz-!"#$%&\'^()=~|{@[+*};:]<>?_,./'; const nanoid = customAlphabet(alphabet, 10); scrapbox.PopupMenu.addButton({ title: (text) => /^\s+/.test(text) ? "☆" : "", onClick: (text) => /^\s+/.test(text) ? text.replace(/^(\s+)/, `$1[Anki.icon]\`${nanoid()}\``) : text, });
nanoid.js
/* esm.sh - esbuild bundle(nanoid@4.0.0/non-secure) es2022 production */ var i="useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict",h=(t,e=21)=>(l=e)=>{let r="",n=l;for(;n--;)r+=t[Math.random()*t.length|0];return r},o=(t=21)=>{let e="",l=t;for(;l--;)e+=i[Math.random()*64|0];return e};export{h as customAlphabet,o as nanoid};

実装
一つのscrapboxのページデータを入力して、Ankiの問題を出力する
parse.ts
import { parseScrapbox } from "./deps.ts"; export const parse = (text: string): string[] => { const blocks = parseScrapbox(text, { hasTitle: true }); return blocks.map((block) => { case "table" }) text.split("\n").flatMap((line) => { const question = line.match(/^\s*\[anki.icon\](.*)\s*$/i)?.[1]; return question ? [question] : []; });
projectからページを全部取得し、Ankiの問題を生成する
owner権限のある場合はexport apiで一度に取得し、それ以外の場合はapi/pages/:project/:titleで1ページずつクロールする
loadAllPages.ts
import { exportPages, readLinks, getPage } from "./deps.ts"; export const loadAllPages = async (project: string) => Promise<Result<Page[], NotFoundError | NotLoggedInError>> => { const result = await exportPages(project); if (result.ok) return { ok: true, value: result.value.pages }; if (result.value.name !== NotPrivilegeError) { return { ok: false, value: result.value }; } // 一ページずつ取得する // 実装はOmnibox版HelpLineを参照して書く for await (const link of readLinks()) { } };

deps.ts
export * from "https://github.com/takker99/scrapbox-userscript-std/blob/main/rest/mod.ts"; export { parse as parseScrapbox } from "https://esm.sh/@progfay/scrapbox-parser@8.1.0";