🎉

AIの出力を「咀嚼」するMarkdownエディタを作った

に公開
2026/02/02

作ったもの

Mark It Down - Chrome拡張機能のMarkdownエディタ。
AIチャット(ChatGPT、Claude、Gemini)の横で書く。
New TabとSide Panelの2モード。

なぜ作ったか

Claude Codeなどで誤って送信していらっとすることがある、Obsidianは拡張入れ過ぎて重い。Sublime textも使っていたが、IDEはコーディングで使いたいので、
マークダウンに特化したエディタが欲しかった。
高速で処理するならプレビューウィンドウが分かれているほうがよいが、
1ペインで済むものが欲しかった。
あえて自作してみようかと思った。

技術的設計判断

1. なぜChrome拡張機能か

判断: ファイルシステムではなく、Chrome拡張機能専用ストレージ(chrome.storage.local)を使用。

理由:

比較項目 ファイルシステム chrome.storage.local
起動速度 ファイル読み込み 即座にアクセス
同期 自動(クラウドに同期) 手動(意図的)
容量 無制限 ~10MB(拡張機能)
抽象度 ユーザーが.mdを意識 意識しない、保存するときだけ意識

トレードオフ:

  • ❌ 容量制限(画像埋め込み不可、リンクは貼れる)->UnlimitedStorage権限で無制限に
    (画像はbase64エンコードが増大しファイルサイズが増大する)

  • ❌ 他ツールからの直接アクセス不可、mdで受け渡しは可能
    (AIとの連携はできないし、ウェブサイトではないのでGoogle翻訳も効かない)

  • ❌ キャッシュを消しても消えないが拡張ごと消すと全て消える
    (git連携機能やmdによるExport機能実装でバックアップはとれる)

  • ✅ 起動高速(New Tabか拡張ボタンからSide Panelで)
    (Side Panelは機能制限あり、例えばCtrl+Fがブラウザ側が有効になるので使えない)

  • ✅ Side PanelでAIチャットやウェブサイトを見ながら並列作業

  • ✅ オフラインで使用可能。データ漏洩は気にしなくていい。

「大量のデータを保存する場所」ではなく「作業場」という位置づけ。
だから容量制限は許容できる。

2. なぜMilkdown(ProseMirror)か

判断: WYSIWYGエディタとして Milkdown を採用。

比較検討:

エディタ 特徴 不採用理由
CodeMirror単体 軽量、シンタックスハイライト プレビューと編集が分離
Monaco Editor VS Code互換 重い(2MB+)
TipTap 人気、活発 Markdown保存に変換必要
Milkdown ProseMirror + Markdown native ✅ 採用

Milkdownの決め手:

  • Markdownをそのまま内部表現として保持

  • ProseMirrorの堅牢なドキュメントモデル

  • プラグインアーキテクチャ(Mermaid、KaTeX追加容易)

ハマりポイント: ProseMirrorのDOM操作ルール

// ❌ アンチパターン(再描画で消える)
element.classList.add('highlight')

// ✅ 正しいパターン(Decoration API)
const decoration = Decoration.node(from, to, { class: 'highlight' })
tr.setMeta(decorationKey, DecorationSet.create(doc, [decoration]))

DOM直接操作はProseMirrorの再描画で消える。スタイル適用はDecoration APIを使う。
他にも色々、カスタムには手間取った

3. なぜ4フォルダ固定か

判断: カスタムフォルダ作成機能を初期に削除した。

Inbox    - 作業中
Template - 複製用
Archive  - 完成品(読み取り専用)
Trash    - 削除待ち

理由: 整理に時間を取られたくない。

「このノート、どのフォルダに入れよう...」

この判断コストが積み重なると、書くことから遠ざかる。

4つの固定フォルダなら「定位置に戻す」だけ。
深く考えない。

人間もAIも咀嚼しきれない知識はノイズでしかない。
必要以上にためずに処理して気持ちよく送り出すような設計に。(Export)

新しいNoteはInboxに入ってそこで作業する、
TemplateにするならTemplateフォルダに入れればTemplateから作成可能。
Archiveに入れるとレンダリングを読み取り専用にして軽く開けるように。
Archiveでスター(ピン留め)するとまとめてエクスポートできる。
Trashはその名の通りゴミ箱。

整理するならObsidianなど他のアプリでやればいい。
軽量化の為に整理の機能は排除した。

4. なぜ手動Git同期か

判断: OneDriveのような自動同期ではなく、手動コミット。

ローカル: 自動保存(履歴なし)
リモート: 手動プッシュ(履歴あり)

理由: GUIによる「意図的なバージョン管理」

自動同期は便利だが:

  • 作業途中の状態がリモートに残る

  • 「この時点に戻したい」が難しい

  • 競合解決が自動で行われる(怖い)

手動コミットなら:

  • 「片付けてからプッシュ」のリズムが生まれる

  • 意図した状態だけがリモートに残る

  • 競合は目で見て解決

実装: ツールバーに Git ▼ ドロップダウン。Sync/Push/Pull/Historyを明示的に操作。

競合の解決画面もあるし、差分確認も可能。古いバージョンに戻す機能も。

5. 情報フロー設計

ツールバーのボタン配置は情報の流れを反映:

[Notes] [Import] [Paste] | [Edit ▼] | [Move ▼] [Git ▼] | [Export] [Copy]
   ↓         ↓                ↓              ↓                  ↓
 Entry     Entry            Edit           Clear               Exit
  • Entry: 情報の入り口

  • Edit: 咀嚼・編集

  • Clear: 片付け(定位置に戻す、Git同期)

  • Exit: 卒業(他ツールへ)

UIは哲学を反映する。

技術スタック

Framework: Preact - Reactの軽量版
Editor: Milkdown (ProseMirror + CodeMirror)
State: Zustand
Styling: Tailwind CSS
Diagram: Mermaid.js
Math: KaTeX
Build: Vite + TypeScript
Test: Vitest + Playwright

まとめ

設計判断 選択 理由
ストレージ chrome.storage.local 起動速度、Side Panel対応
エディタ Milkdown Markdown native、プラグイン拡張
フォルダ 4つ固定 判断コスト削減
同期 手動Git 意図的なバージョン管理
UI配置 情報フロー順 Entry → Edit → Clear → Exit

「機能を追加する前に、既存の機能で代用できないか考える。できるなら追加しない。」

複雑さは敵。シンプルに保つ。


リンク:
Mark It Down -Chrome拡張機能のMarkdownエディタ
AIチャット(ChatGPT、Claude、Gemini)の横で書く。New TabとSide Panelの2モード。
この記事は Mark It Down で書きました。

Discussion