マークダウンからCosenseへの変換ツール
最新ファイル
code:convert.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Markdown to Scrapbox Converter - Dark Mode (ver 1.0 / updated 2025-01-26)</title>
<style>
/* バージョン管理: ver 1.0 / updated 2025-01-26
ページ全体のスタイル */
body {
font-family: Arial, sans-serif;
display: flex;
gap: 20px;
padding: 20px;
}
/* コンテナのスタイル(左右均等に配置) */
.container {
width: 50%;
display: flex;
flex-direction: column;
}
/* 入力エリアのスタイル */
textarea {
width: 100%;
height: 400px;
font-size: 16px;
padding: 10px;
box-sizing: border-box;
}
/* ボタンのスタイル */
.btn {
padding: 10px;
color: white;
border: none;
cursor: pointer;
font-size: 16px;
margin-bottom: 10px;
}
/* ボタンホバー時のスタイル */
.btn:hover {
}
/* 出力エリアのスタイル */
.output {
width: 100%;
height: 400px;
padding: 10px;
overflow-y: auto;
white-space: pre-wrap;
box-sizing: border-box;
}
/* コピー完了メッセージのスタイル */
.copy-message {
font-weight: bold;
margin-top: 5px;
display: none;
}
/* バージョン表示用のフッター */
.footer {
position: absolute;
bottom: 10px;
left: 10px;
font-size: 14px;
}
</style>
</head>
<body>
<!-- Markdown入力欄 -->
<div class="container">
<button class="btn" onclick="clearMarkdown()">削除</button>
<textarea id="markdown-input" placeholder="ここにMarkdownを貼り付けなさい"></textarea>
</div>
<!-- Scrapbox形式への変換結果表示エリア -->
<div class="container">
<button class="btn" onclick="copyToClipboard()">コピー</button>
<span id="copy-message" class="copy-message">コピーしてあげたわよ!</span>
<div class="output" id="scrapbox-output"></div>
</div>
<!-- バージョン表示 -->
<div class="footer">
ver 1.0 / updated 2025-01-26
</div>
<script>
/**
* バージョン管理: ver 1.0 / updated 2025-01-26
* マークダウンの1行をScrapbox形式に変換する
* (表以外の通常行を変換。表行は別途table処理で置換する)
*/
function convertMarkdownLine(line) {
// 見出しの変換
if (/^#### /.test(line)) {
return "" + line.slice(5).replace(/\*\*(.+?)\*\*/g, "$1").trim() + "";
}
if (/^### /.test(line)) {
return "" + line.slice(4).replace(/\*\*(.+?)\*\*/g, "$1").trim() + "";
}
if (/^## /.test(line)) {
return "" + line.slice(3).replace(/\*\*(.+?)\*\*/g, "$1").trim() + "";
}
if (/^# /.test(line)) {
return "" + line.slice(2).replace(/\*\*(.+?)\*\*/g, "$1").trim() + "";
}
// 箇条書きの変換:- を半角スペースに変更し、行頭のスペースを2つに制限
if (/^\s*- /.test(line)) {
line = line.replace(/^\s*- /, " ");
}
// 水平線の変換
if (/^---$/.test(line)) {
return "/icons/hr.icon";
}
// 太字や斜体、リンクなどの変換
let replaced = line;
replaced = replaced.replace(/\*\*(.+?)\*\*/g, "$1"); // **太字**
replaced = replaced.replace(/(^|^*)\*(?!\*)(.+?)(?!\*)\*(?!\*)/g, "$1$2"); // *斜体* replaced = replaced.replace(/([^]+)/g, "$1`"); // インラインコード
replaced = replaced.replace(/\[(^\]+)\]\((^)+)\)/g, "$1 $2"); // リンク // 行内数式 \( ... \) → $ ...
replaced = replaced.replace(/\\\((.+?)\\\)/g, "$ $1");
if (replaced.trim() === "") return ""; // 空行対応: スペース1つ返す
return replaced;
}
/**
* テーブル行をScrapbox形式に変換
* @param {string} line - "| TH | TH |" みたいな行
* -> " TH\tTH"
*/
function convertTableLine(line) {
let content = line.trim().replace(/^\|\s*(.*?)\s*\|$/, "$1"); let cells = content.split("|").map(c => c.trim());
if (cells.every(c => /^-+(:?|-*)$/.test(c))) {
return null;
}
cells = cells.map(c => c.replace(/\*\*(.+?)\*\*/g, "$1"));
return " " + cells.join("\t");
}
/**
* マークダウンをScrapbox形式に変換する
*/
function markdownToScrapbox(md) {
const lines = md.split("\n");
let inCodeBlock = false;
let inBlockMath = false;
let blockMathLines = [];
let inTable = false;
let tableLines = [];
let resultLines = [];
for (let i = 0; i < lines.length; i++) {
if (line.trim().startsWith("`") && !inCodeBlock) {
const langMatch = line.trim().match(/^`(\w*)/);
const lang = langMatch && langMatch1 ? langMatch1 : "plaintext"; resultLines.push("code:" + lang);
inCodeBlock = true;
continue;
} else if (line.trim().startsWith("`") && inCodeBlock) {
inCodeBlock = false;
continue;
}
if (line.trim() === "\\[") {
inBlockMath = true;
blockMathLines = [];
continue;
}
if (line.trim() === "\\]") {
inBlockMath = false;
const mathContent = blockMathLines.join(" ");
resultLines.push("$ " + mathContent + "");
blockMathLines = [];
continue;
}
if (inBlockMath) {
blockMathLines.push(line.trim());
continue;
}
if (/^\s*\|.*\|\s*$/.test(line)) {
if (!inTable) {
inTable = true;
tableLines = [];
}
tableLines.push(line);
} else {
if (inTable) {
resultLines.push("table: table");
for (let tline of tableLines) {
let converted = convertTableLine(tline);
if (converted !== null) {
resultLines.push(converted);
}
}
tableLines = [];
inTable = false;
}
if (inCodeBlock) {
resultLines.push(" " + line.replace(/^(\s{2,})/, " "));
} else {
let converted = convertMarkdownLine(line);
resultLines.push(converted);
}
}
}
if (inTable && tableLines.length > 0) {
resultLines.push("table: table");
for (let tline of tableLines) {
let converted = convertTableLine(tline);
if (converted !== null) {
resultLines.push(converted);
}
}
}
return resultLines.join("\n");
}
document.getElementById("markdown-input").addEventListener("input", function () {
const mdText = this.value;
const convertedText = markdownToScrapbox(mdText);
document.getElementById("scrapbox-output").innerText = convertedText;
});
/**
* 入力欄をクリアする
*/
function clearMarkdown() {
document.getElementById("markdown-input").value = "";
document.getElementById("scrapbox-output").innerText = "";
}
/**
* 変換結果をクリップボードにコピーする
*/
function copyToClipboard() {
const outputText = document.getElementById("scrapbox-output").innerText;
const textarea = document.createElement("textarea");
textarea.value = outputText;
document.body.appendChild(textarea);
textarea.select();
document.execCommand("copy");
document.body.removeChild(textarea);
const message = document.getElementById("copy-message");
message.style.display = "block";
setTimeout(() => {
message.style.display = "none";
}, 2000);
}
</script>
</body>
</html>