プロジェクト概要
ものしりメモ(app.monoshiri.jp)は、「デザインを気にせず書くだけで、きれいにまとまる」をコンセプトにした Markdown メモアプリです。Mac / iPad は App Store から、ブラウザ版は Google サインインだけで、いずれも無料で使えます。
最大の特徴はプレビュー画面を持たないことです。一般的な Markdown エディタは「編集ペイン」と「プレビューペイン」を左右に並べますが、ものしりメモはソーステキストそのものをその場で装飾する(インプレース・ライブスタイリング)方式を採ります。見出しは打った瞬間に大きく色づき、表や Mermaid 図はカーソルが外れると図として描画され、クリックすればまた生の Markdown に戻って編集できます。書く行為と見た目の確認が分断されません。
保存先は Google Drive のみで、ローカルフォルダモードを持ちません。起動時に Drive と接続し、以降は自動保存だけ。「どこに保存したか分からないファイル」が増えず、Mac・iPad・ブラウザのどこからでも同じメモの続きが書けます。企画・UI設計・実装・LP・ストア申請・運営まで、すべてを個人で担当しています。
プロダクトの特徴
書いたそばから、整う
- 見出し・強調・コード: 記法を打った瞬間に、その記号を残したまま装飾が乗る
- 表: パイプ記法の表を、カーソルが離れると整形済みテーブルとして描画
- Mermaid 図:
```mermaidブロックをその場でフローチャートとして描画 - チェックリスト:
- [ ]がクリック可能なチェックボックスに変わり、押すと本文が書き換わる - リンクプレビュー: 貼った URL の OGP を取得し、段落の下にカード表示
- スラッシュコマンド: 行頭で
/を打つと Notion 風のコマンドパレットが開く
保存を意識しなくていい
- 編集のたびにデバウンス保存 + フォーカスが外れたタイミングでも保存。手動保存の操作は存在しない
- ファイル本体はユーザー自身の Google Drive に保存され、こちらのサーバーには置かない
- 画像や添付ファイルは Drive 上の
images//files/に自動整理してリンクを挿入
3つの見た目、2つの端末、12の言語
- テーマは ライト / セピア / ダークの3種類、文字サイズは 小 / 中 / 大
- Mac のキーボード執筆と iPad のタッチ操作の双方を前提に設計(ハードウェアキーボード接続にも対応)
- ランディングページは日本語・英語・中国語(繁/簡)・韓国語・フランス語・ドイツ語・イタリア語・スペイン語・ポルトガル語・ヒンディー語・インドネシア語の12言語対応
技術スタック
デスクトップ / モバイルアプリ
- Tauri v2(Rust): Electron より遥かに軽量なネイティブシェル。macOS / iPadOS 版を同一コードから生成
- CodeMirror 6: エディタの中核。装飾はすべて自前の Decoration 実装
- Vite + TypeScript(Vanilla): フレームワークを使わず、エディタ体験に必要な部分だけを持つ軽量構成
- Mermaid: ダイアグラムのインライン描画
ブラウザ版 / ランディングページ
- Cloudflare Pages + Pages Functions: ブラウザ版アプリと LP を同一ドメインで配信
- Astro + Tailwind CSS v4: LP を静的生成。12言語分のルートをビルド時に出力
- サーバーサイド OAuth: リフレッシュトークンは httpOnly Cookie に格納し、JavaScript から触れない設計
ストレージ / 認証
- Google Drive API: ファイル一覧・読み書き・フォルダ作成をすべて Drive の file ID ベースで操作
- OAuth 2.0 + PKCE(ループバックリダイレクト): デスクトップ版はローカルポートで認可コードを受け取り、リフレッシュトークンを端末内にのみ保存
- スコープは
drive.fileのみ: アプリが作成した/ユーザーが明示的に開いたファイルにしかアクセスしない
アーキテクチャ:1つのコードベースで3つの配布形態
デスクトップ版・iPad 版・ブラウザ版は、同じフロントエンドのソースを共有しています。これを可能にしているのが「Rust 呼び出しを1ファイルに閉じ込める」設計です。
- 境界を1ファイルに集約: Rust コマンドの呼び出し(
invoke)はすべてapi.tsの薄いラッパー経由とし、UI コンポーネントからは直接呼ばない - ビルド時にすげ替える: Web ビルドでは Vite のエイリアスで Tauri API をシムに差し替え、同じ呼び出しが Drive REST API と Cloudflare Pages Functions に流れる
- 結果: 機能追加は基本的に1箇所の実装で3プラットフォームに反映される。ネイティブ固有の処理(OAuth のループバック受信など)だけが Rust 側に残る
設計上の工夫
1. ブロック装飾は StateField、行内装飾は ViewPlugin
CodeMirror 6 では、行を丸ごと置き換えるブロック装飾を ViewPlugin から返すと実行時例外になります(Block decorations may not be specified via plugins)。そのため表・Mermaid・リンクプレビューは StateField、見出し背景やチェックボックスなどの行内装飾は ViewPlugin、と制約に沿って明確に分離しています。
2. 非同期な副作用を純粋な StateField から追い出す
リンクプレビューは URL のメタデータ取得という非同期処理を伴い、純粋関数である StateField 内では実行できません。取得は専用の ViewPlugin が担い、完了時に StateEffect を dispatch して StateField 側が再構築する、という副作用と状態の分離で解決しています。
3. 装飾の優先度を明示して「色の勝敗」を制御する
シンタックスハイライトのスパンが内側で color を指定するため、CSS の !important では自前の色が勝てません。装飾プラグインの優先度を最上位に設定し、色指定スパンがハイライトの内側に入れ子になるよう順序を制御しています。
4. IME と undo を壊さないコマンドパレット
スラッシュコマンドの入力中の文字列を、別途の入力欄ではなくドキュメント本文そのものに置いています。これにより日本語入力(IME)の変換も undo 履歴もエディタ標準のまま動作し、コマンド確定時に入力した /… の範囲ごと置換します。
5. 「非機微スコープ」を選ぶという設計判断
Google Drive 全体を読める drive スコープではなく drive.file を選んだことで、ユーザーのプライバシーを構造的に守りつつ、Google の OAuth 検証や制限付きスコープのセキュリティ審査(CASA)を回避し、個人開発でも一般公開できる配布経路を確保しました。機能要件とコンプライアンス要件を同時に満たす選択です。
担当領域
- プロダクト企画・コンセプト設計・UI/UX デザイン
- エディタ実装(CodeMirror 6 の装飾・キーマップ・コマンドパレット)
- ネイティブ層実装(Rust / Tauri コマンド・Google Drive クライアント・OAuth)
- ブラウザ版実装(Cloudflare Pages Functions によるサーバーサイド認証)
- ランディングページ制作(Astro / Tailwind)と12言語のコピーライティング
- App Store 申請(Mac / iPad の署名・ビルド番号運用・審査対応)
- プライバシーポリシー起草・リリースノート運用
技術的な学び
- CodeMirror 6 の装飾モデル(StateField / ViewPlugin / Decoration の使い分けと優先度)
- Tauri v2 での macOS / iPadOS 同時開発と、App Store 配布に必要な署名・エクスポート設定の勘所
- 同一フロントエンドをネイティブと Web の両方で動かすための「境界の切り方」
- OAuth 2.0 + PKCE のループバック実装と、リフレッシュトークンの安全な保管(端末内 / httpOnly Cookie)
- OAuth スコープの選択がプロダクトの配布可能性そのものを左右するという学び
- Astro での多言語静的サイト構築(hreflang・ロケール別ルーティング)