generated at
選択範囲をMarkdown記法に変換してclip boardにcopyするPopupMenu



script.js
import { ScrapboxParser } from "/api/code/takker/scrapbox-parser.min.js/parser.js"; scrapbox.PopupMenu.addButton({ title: "+md", onClick: (text) => { try { const blocks = ScrapboxParser.parse(text, { hasTitle: false }); const topIndentLevel = Math.min(...blocks.map((block) => block.indent)); navigator.clipboard.writeText( blocks.map((block) => convertSb2Md(block, topIndentLevel)).join("\n") ); } catch (e) { alert(`Failed to copy:\n${JSON.stringify(e)}`); } }, });


script.js
// @ts-check /** * @typedef {import("../scrapbox-parser/mod.ts").Block} Block * @typedef {import("../scrapbox-parser/mod.ts").Table} Table * @typedef {import("../scrapbox-parser/mod.ts").Line} Line * @typedef {import("../scrapbox-parser/mod.ts").Node} NodeType * @typedef {import("../scrapbox-jp%2Ftypes/userscript.ts").Scrapbox} Scrapbox */ /** Scrapbox記法をMarkdown記法に変える * * @param {Block} block * @param {number} topIndentLevel * @return {string} */ function convertSb2Md(block, topIndentLevel) { switch (block.type) { case 'title': return ''; // タイトルは選択範囲に入らないので無視 case 'codeBlock': return [ block.fileName, `\n\`\`\`${getFileType(block.fileName)}`, block.content, '```\n', ].join('\n'); case 'table': return convertTable(block); case 'line': return convertLine(block, topIndentLevel); } } /** Table記法の変換 * * @param {Table} table * @return {string} */ function convertTable(table) { const maxCol = Math.max(...table.cells.map(row => row.length)); return [ table.fileName, ...table.cells.flatMap((row, i) => [ `| ${row .map(column => column.map(node => convertNode(node)).join('')) .join(' | ')} |`, ...(i === 0 ? [`|${' -- |'.repeat(maxCol)}`] : []), ]), ].join('\n'); } const INDENT = ' '; // インデントに使う文字


script.js
/** 行の変換 * * @param {Line} line * @param {number} topIndentLevel * @return {string} */ function convertLine(line, topIndentLevel) { const content = line.nodes .map(node => convertNode(node, { section: line.indent === topIndentLevel })) .join('') .trim(); if (content === '') return ''; // 空行はそのまま返す // リストを作る if (line.indent === topIndentLevel) return content; // トップレベルの行はインデントにしない let result = INDENT.repeat(line.indent - topIndentLevel - 1); if (!/^\d+\. /.test(content)) result += '- '; // 番号なしの行は`-`を入れる return result + content; }


script.js
/** Nodeを変換する * * @param {NodeType} node * @param {{section?:boolean}} [init] * @return {string} */ function convertNode(node, init) { const { section = false } = init ?? {}; switch (node.type) { case 'quote': return `> ${node.nodes.map(node => convertNode(node)).join('')}`; case 'helpfeel': return `\`? ${node.text}\``; case 'image': case 'strongImage': return `![image](${node.src})`; case 'icon': case 'strongIcon': // 仕切り線だけ変換する return ['/icons/hr', '/scrapboxlab/hr'].includes(node.path) ? '---' : ''; case 'strong': return `**${node.nodes.map(node => convertNode(node)).join('')}**`; case 'formula': return `$${node.formula}$`; case 'decoration': { let result = node.nodes.map(node => convertNode(node)).join(''); if (node.decos.includes('/')) result = `*${result}*`; // 見出しの変換 // お好みで変えて下さい if (section) { if (node.decos.includes('*-3')) result = `# ${result}\n`; if (node.decos.includes('*-2')) result = `## ${result}\n`; if (node.decos.includes('*-1')) result = `### ${result}\n`; } else { if (node.decos.some(deco => /\*-/.test(deco[0]))) { result = `**${result}**`; } } if (node.decos.includes('~')) result = `~~${result}~~`; return result; } case 'code': return `\`${node.text}\``; case 'commandLine': return `\`${node.symbol} ${node.text}\``; case 'link': switch (node.pathType) { case 'root': return `[${node.href}](https://scrapbox.io${encodeURI(node.href)})`; case 'relative': //@ts-ignore declare宣言が使えないため、`scrapbox`に型定義をつけられない return `[${node.href}](https://scrapbox.io/${ scrapbox.Project.name }/${encodeURI(node.href)})`; default: return node.content === '' ? ` ${node.href} ` : `[${node.content}](${encodeURI(node.href)})`; } case 'googleMap': return `[${node.place}](${node.url})`; case 'hashTag': //@ts-ignore declare宣言が使えないため、`scrapbox`に型定義をつけられない return `[#${node.href}](https://scrapbox.io/${scrapbox.Project.name}/${node.href})`; case 'numberList': return `${node.number}. ${node.nodes .map(node => convertNode(node)) .join('')}`; case 'blank': case 'plain': return node.text; } }