🖥 002 PCローカル・UI設計
画面構成・モジュール構造・データ構造・データフロー
設計書 v1.0 / 2026-04-19
1 画面構成
PCアプリはメインウィンドウ1枚+付箋ウィンドウN枚のマルチウィンドウ構成です。
- 役割
- アプリの司令塔。ユーザーには見えないことが多い(トレイ常駐)
- 機能
- 起動時に付箋ウィンドウを生成・配置
設定画面・検索オーバーレイを表示
トレイアイコンのイベントを受け取る
iPhone からの受信(ポーリング)を担当 - ファイル
app/page.tsx
- 役割
- 付箋 1 枚ごとに独立した
WebviewWindow - 機能
- テキスト編集(RichTextEditor)
ピン留め・たたむ・色変更
右クリックメニュー(iPhone 送信・アーカイブ等)
画像キャプチャ・リンク挿入 - ファイル
app/components/StickyNote.tsx
1.3 ウィンドウ生成の仕組み
付箋1枚ごとに独立したTauriウィンドウが生成される仕組みを図示します。
図 2-1 ウィンドウ生成の仕組み
2 モジュール構造
フロントエンド(TypeScript/React)とバックエンド(Rust)の2層構成です。
2.1 フロントエンド(TypeScript / React)
付箋ウィンドウを構成する主要コンポーネント・フック・ユーティリティの依存関係を示します。
図 2-2 フロントエンドモジュール構成
2.2 バックエンド(Rust)
TauriコマンドとAppStateを中心とするRust側モジュールの役割を一覧します。
表 2.2-1 バックエンドモジュール一覧
| No | ファイル | 役割 | 主な責務 |
|---|---|---|---|
| 1 | main.rs | エントリーポイント | Tauri アプリ起動・プラグイン登録・シングルインスタンス制御 |
| 2 | lib.rs | invoke ハンドラ定義 | フロントエンドから呼ばれる全コマンドを宣言。処理は各モジュールに委譲 |
| 3 | state.rs | AppState(唯一の状態) | ノート一覧・設定・VAPID鍵・デバイス情報をメモリ上で保持。Mutex で排他制御 |
| 4 | settings.rs | 設定管理 | base_path・言語・フォントサイズ等を settings.json に読み書き |
| 5 | logic.rs | 付箋ビジネスロジック | ノート作成・保存・リネーム・アーカイブ・削除・タグ管理 |
| 6 | storage.rs | ファイル I/O | Markdown ファイルの読み書き・frontmatter パース |
| 7 | tray.rs | トレイアイコン | トレイメニュー生成・全表示/全隠し・クリックイベント処理 |
| No | ファイル | 役割 | 主な責務 |
|---|---|---|---|
| 8 | webpush.rs | プッシュ通知送信 | VAPID 鍵生成・APNs/FCM への Web Push 送信 |
| 9 | gdrive.rs | Google Drive 連携 | Drive ファイルの読み書き・ポーリング(iPhone からの受信検出) |
| 10 | capture.rs | 画像キャプチャ | Snipping Tool 起動・クリップボード画像を assets/ に保存 |
| 11 | clipboard.rs | クリップボード | テキスト・画像のクリップボード読み書き |
| 12 | sound.rs | 効果音 | 保存音・通知音などの再生 |
| 13 | import.rs | ファイルインポート | 外部 Markdown ファイルを Vault に取り込む |
| 14 | logger.rs | ログ | デバッグログの出力管理 |
3 データ構造
付箋ファイル・設定ファイル・モジュール依存の3種類のデータ構造を定義します。
3.1 付箋データ(Markdown ファイル = frontmatter + 本文)
付箋1枚 = Markdown ファイル1枚。PC ローカルのファイルシステムが正。Drive は中継所に過ぎない。
表 3.1-1 付箋データフィールド一覧
| No | フィールド | 型 | 意味 |
|---|---|---|---|
| 1 | body | string | 本文テキスト(Markdown) |
| 2 | path | string | ファイルパス(実質的な主キー) |
| 3 | context | string | コンテキスト名(本文1行目から自動生成) |
| 4 | updated | string | 最終更新日時 |
| 5 | x, y | number? | 画面上の位置座標 |
| 6 | width, height | number? | ウィンドウサイズ |
| No | フィールド | 型 | 意味 |
|---|---|---|---|
| 7 | background_color | string? | 付箋の背景色 |
| 8 | always_on_top | bool? | 最前面固定(ピン留め) |
| 9 | folded | bool? | 折りたたみ状態(最小化) |
| 10 | seq | i32 | 重なり順(Z-index 相当) |
| 11 | tags | string[] | タグ一覧 |
3.2 設定データ(settings.json)
settings.json で管理するアプリ全体の設定項目5点を定義します。
表 3.2-1 設定データフィールド一覧
| No | フィールド | 型 | 意味 |
|---|---|---|---|
| 1 | base_path | string? | Vault フォルダのパス(未設定なら初回セットアップ画面) |
| 2 | language | string | 表示言語 |
| 3 | auto_start | boolean | OS 起動時に自動起動するか |
| No | フィールド | 型 | 意味 |
|---|---|---|---|
| 4 | font_size | number | 基本フォントサイズ |
| 5 | sound_enabled | boolean | 効果音 ON/OFF |
3.3 クラス図(依存関係)
主要モジュール間の依存関係をMermaidクラス図で示します。
図 2-3 クラス図(主要モジュール依存関係)
4 データフロー
アプリ起動から保存・画像・検索・iPhone連携まで6つのシーケンス図を示します。
4.1 アプリ起動
起動時にファイルシステムから付箋一覧を読み込むフローです。
図 2-4 アプリ起動シーケンス
4.2 付箋の保存・自動リネーム
本文の1行目をファイル名(context)として自動採用する。800ms のデバウンスで連続入力に追従。
図 2-5 付箋の保存・自動リネームシーケンス
4.3 画像キャプチャ
OS 内蔵の Snipping Tool を呼び出し、撮影された画像を Vault の assets/ に自動保存する。
図 2-6 画像キャプチャシーケンス
4.4 全文検索
キーワードをリアルタイムで全付箋から検索するフローです。
図 2-7 全文検索シーケンス
5 UI インタラクション
付箋ウィンドウの操作領域・モード・マウス挙動を定義します。
5.1 モード定義
表示・編集の2モードとその遷移条件を定義します。
表 5.1-1 モード定義
| No | モード | 状態 |
|---|---|---|
| 1 | 表示モード(閲覧中) | Markdown としてレンダリング済みの付箋を読んでいる状態 |
| 2 | 編集モード(執筆中) | カーソルが点滅し文字を書き込める状態。背景が半透明の白になる |
図 2-8 モード遷移図
5.2 操作領域の定義
付箋ウィンドウをエディタ本文・余白(A)・フッタ(B)の3エリアに分けて操作を割り当てます。
図 2-9 付箋ウィンドウの操作領域
表 5.2-1 操作領域の定義
| No | 領域 | 場所 | ユーザーの意図 |
|---|---|---|---|
| 1 | エディタ本文 | 文字・リンクが実際に存在する場所 | 「ここを直接操作・書き換えたい」 |
| 2 | (A) エディタ余白 | 文字の右側・左側に広がる枠線内の空白 | 「この行の続きや近くから書き込みたい」 |
| 3 | (B) フッタ領域 | 最後の行より下にある何もない空間(エディタ外) | 「一番下から追記したい」または「編集を終わらせたい」 |
5.3 インタラクション・マトリックス
エリア × 操作の組み合わせで起きる動作を一覧にまとめます。
5.3.1 🖱 ワンクリック
表 5.3-1 ワンクリック操作一覧
| No | クリックした場所 | 表示モード | 編集モード |
|---|---|---|---|
| 1 | エディタ本文 | 何もしない(ドラッグ準備) | カーソル移動(その文字へ) |
| 2 | (A) エディタ余白 | 何もしない(ドラッグ準備) | カーソル移動(最近傍の文字へ) |
| 3 | (B) フッタ領域 | 何もしない(ドラッグ準備) | 編集終了 |
| 4 | リンク | リンクを開く | カーソル移動 |
| 5 | ウィンドウ外 | フォーカス解除 | 編集終了(保存して表示モードへ) |
5.3.2 🖱🖱 ダブルクリック
表 5.3-2 ダブルクリック操作一覧
| No | クリックした場所 | 表示モード | 編集モード |
|---|---|---|---|
| 1 | エディタ本文 | 編集開始(クリックした文字から) | 単語選択 |
| 2 | (A) エディタ余白 | 編集開始(最近傍の文字から) | — |
| 3 | (B) フッタ領域 | 編集開始(文末から) | — |
5.3.3 ✋ ドラッグ(押しっぱなしで移動)
表 5.3-3 ドラッグ操作一覧
| No | ドラッグした場所 | 表示モード | 編集モード |
|---|---|---|---|
| 1 | エディタ本文 | 🪟 ウィンドウ移動 | 📝 テキスト範囲選択 |
| 2 | (A) エディタ余白 | 🪟 ウィンドウ移動 | 🪟 ウィンドウ移動 |
| 3 | (B) フッタ領域 | 🪟 ウィンドウ移動 | 🪟 ウィンドウ移動 |
6 機能一覧
システムトレイメニュー・付箋のツールバー・右クリックメニュー・設定画面の機能と、各機能の設計意図を記します。
6.1 システムトレイメニュー
タスクバーのトレイアイコンを左クリック(または右クリック)すると表示されるメニューです。show_menu_on_left_click(true) の設定により、左クリックでもメニューが開きます。
表 6.1-1 システムトレイメニュー項目
| 項目 | 機能 | 設計意図・工夫 |
|---|---|---|
| 新規メモ | 新しい付箋を作成する | プールウィンドウ方式(後述)で表示遅延ゼロを実現。トレイが発火すると fusen:create_note_from_tray をメインウィンドウに emit し、JS 側が昇格処理を担う |
| 検索 | 全文検索オーバーレイを開く | Vault 内の全 .md を Rust 側で行単位に grep。ヒットした 1 件目は自動的に対象ウィンドウにジャンプし、Enter/Shift+Enter/F3 で前後の結果をナビゲートできる |
| 全部隠す(Ctrl+Shift+H) | 全付箋を非表示にする | WebView2 の COM 層がネスト呼び出しでスタックオーバーフローを起こす問題を、JS 側が 1 窓ずつ 50ms 間隔でシリアルに処理することで回避 |
| 全部戻す | 非表示の全付箋を再表示する | 同上。Rust 側の LAST_VISIBILITY_MS(AtomicU64)で前回操作から 3 秒以内の連打をブロック。JS の連打と Rust のガードで二重に守る |
| タグで絞り込む | タグ選択で表示付箋を絞り込む | メニューを開くたびに Vault を再スキャンして最新タグを取得。☑/☐ で選択状態を可視化。タグトグルは std::thread::spawn で非同期処理し、Win32 同期メッセージによるスタック消費を防ぐ |
| 設定 | 設定画面を開く | メインウィンドウは通常 hidden。show() → unminimize() → set_focus() の順で先に表示を確保してから fusen:open_settings を emit する。順序が逆だとウィンドウが invisible のままイベントを受け取れない |
| 終了 | アプリを終了する | app.exit(0) で全 WebView ウィンドウを即時破棄。on_window_event を経由せず直接 OS プロセスを終了させる |
6.2 付箋のツールバー
付箋にカーソルを乗せると右上にホバー表示されるツールバーです。閲覧モードと編集モードで表示が切り替わります。
表 6.2-1 閲覧モードのツールバー項目
| ボタン | 機能 | 設計意図・工夫 |
|---|---|---|
| + | 新規付箋を作成する | プールウィンドウ方式で即時表示。ウェルカムノートでは animate-bounce + オレンジ色になり「ここから始めよう」と誘導する |
| △/▽ | 付箋を折りたたむ/広げる | タイトルバーの高さだけに縮小する。高さは logicalHeight × scaleFactor で物理ピクセル精度に変換し、高 DPI モニタでもズレない。折りたたみ中にユーザーが手動でウィンドウを広げた場合(10 物理 px 以上)は自動展開し、そのサイズを新たな「展開サイズ」として記憶する |
| ピン(📌) | 付箋を最前面に固定する | ピン状態によって SVG の形が変わる:ONは「縦に刺さったピン+影」、OFFは「斜め45°に倒れたピン」。固定 ON 時は「ギュッ」音(120 Hz 三角波 → 60 Hz、80ms)、固定 OFF 時は「ポン」音(400 Hz → 800 Hz サイン波、100ms)を AudioContext で生成。音声ファイルをバンドルせず、コードだけで音を作る |
表 6.2-2 編集モードのツールバー項目
| ボタン | 機能 | 設計意図・工夫 |
|---|---|---|
| ⊞ | 選択テキストをMarkdownテーブルに変換 | CSV 風に入力したテキストをテーブル記法に一発変換 |
| 🔷 | Mermaidダイアグラムのテンプレートを挿入 | フローチャート・シーケンス図のひな形を即挿入 |
| カメラ | 画面キャプチャを付箋に貼り付ける | OS 内蔵の Snipping Tool を起動し、撮影した画像を assets/ に保存して ![]() 記法で本文に挿入 |
| フローティングフォーマットバー | テキスト選択時に選択範囲の真上に浮き上がる B / H1 / ≡ / ☑ の 4 ボタン | ツールバーを探さなくていい。選んだ瞬間にフォーマット操作ができる。座標は CodeMirror の coordsAtPos() で計算し、ウィンドウ上端を超える場合は下に反転(flip)してウィンドウ内に収める |
6.3 付箋右クリックメニュー(閲覧モード)
付箋を閲覧モードで右クリックしたときのメニューです。
表 6.3-1 付箋右クリックメニュー(閲覧モード)項目
| 項目 | 機能 | 設計意図・工夫 |
|---|---|---|
| 📄 ファイル名 | 現在の付箋ファイル名を表示(選択不可) | 付箋は似たような見た目が並ぶ。「いまどの付箋を操作しているか」をメニューの冒頭で示すことで誤操作を防ぐ |
| 📂 フォルダを開く | 付箋ファイルが保存されているフォルダをエクスプローラーで開く | 「脱出口」。データは平文 .md。エクスプローラー・Obsidian・VS Code からいつでも直接触れる設計。データはユーザーが完全に所有する |
| ✨ 新規メモ(Ctrl+N) | 新しい付箋を作成する | win.outerPosition()(物理ピクセル)と scaleFactor を取得してメインプロセスに渡し、30 論理 px ずらした位置で出現。DPI 混在マルチモニタ環境でも正確にずれる |
| 📋 複製 | 現在の付箋を複製する | 同上のオフセット機構を使用。内容・フォーマット・タグを含めてそのままコピーし、隣に並べる |
| 🎨 色変更 | 付箋の背景色を変更する(青 / ピンク / 黄) | 3 色のみ。選択肢を絞ることで決断疲れを排除した設計。色は frontmatter の background_color に保存されアプリ再起動後も維持される |
| 🏷️ タグ | タグの追加・トグル・削除モードのサブメニュー | メニューを開く瞬間に fusen_get_all_tags で最新タグ一覧を Rust から取得(React state の古い値を使わない)。削除モードへの切り替えは shouldReopenMenu.current = true でメニューを閉じた直後に同じ座標で再表示する |
| ⏰ アラームを設定 | 付箋にアラームを設定する | 「5分後」〜「1週間後」のクイックボタン+日時直接指定の 2 モード。時刻は JST (+09:00) でフロントマター alarm_at に保存。時刻になると付箋が点滅し、効果音が鳴る |
| 📱 iPhoneに送る | 付箋の内容を iPhone に送信する | 送信前に fusen_check_pro_setup で Google Drive への実際の接続とプッシュ設定ファイルの存在を確認(ローカル状態ではなく Drive を実際に叩く)。未設定なら { tab: 'iphone' } を添えて設定画面の iPhone 連携タブへ直行 |
| 📦 アーカイブ | 付箋をタグフォルダ or Archive フォルダに移動する | 移動前にアプリ固有 frontmatter(background_color・geometry 等)を strip_sticky_fields で除去してから書き込む。関連画像を先にコピーし、コピー成功後に元ファイルを削除するフォールセーフな順序。複数タグ付きならサブメニューで移動先を選択 |
| 🗑️ 削除(Ctrl+D) | 付箋をゴミ箱に移動する | fusen_move_to_trash で OS のゴミ箱へ。削除音を鳴らしてからウィンドウを hide() → destroy() する順序で、ユーザーに白紙フレームを見せない |
6.4 付箋右クリックメニュー(編集モード)
編集モード中に右クリックすると切り替わるメニューです。テキスト操作に特化しています。
表 6.4-1 付箋右クリックメニュー(編集モード)項目
| 項目 | 機能 | 設計意図・工夫 |
|---|---|---|
| Undo / Redo | 直前の操作を取り消す / やり直す | PredefinedMenuItem で OS ネイティブ操作にバインド。CodeMirror の編集履歴をそのまま使う |
| 切り取り / コピー / 貼り付け / 全選択 | 標準テキスト操作 | 同上。Web クリップボード API ではなく OS ネイティブのクリップボード連携 |
| 📅 今日の日付(曜日付き) | カーソル位置に 2026-04-27(日) 形式で挿入 | メニュー項目名自体に挿入後の実際の文字列を表示する。「Insert date」ではなく「📅 2026-04-27(日)」と書くことで、押す前に何が入るか分かる。Intl.DateTimeFormat で言語設定に従い曜日を自動変換(ja: 日・月・火、en: Sun・Mon・Tue) |
| 🕒 現在時刻 | カーソル位置に HH:MM 形式で挿入 | 同上。議事録・タスクログに時刻を一瞬で打ち込める |
| 📅🕒 日付+時刻 | カーソル位置に日付と時刻を両方挿入 | 上の 2 項目を 1 アクションに合わせたショートカット |
6.5 設定画面
システムトレイ → 設定 から開く設定画面のタブと設定項目です。
表 6.5-1 設定画面タブ一覧
| タブ | 設定項目 | 設計意図・工夫 |
|---|---|---|
| 一般設定 | 言語(日本語 / English)・自動起動 ON/OFF・効果音 ON/OFF | 言語切り替えは全付箋ウィンドウの UI に即時反映。自動起動は tauri-plugin-autostart で Windows の HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run に登録。トグル操作が完了した瞬間に有効になる |
| 外観 | フォントサイズ(10〜32px、スライダーで変更) | スライダーをドラッグしながらリアルタイムにプレビューが更新される。決定した値は CSS 変数として全ウィンドウに伝播する |
| データ | Vault フォルダのパス変更・他フォルダからの Markdown インポート | インポートは .md ファイルと関連画像(fusen_img_*)を一括取り込む。Obsidian・Notion エクスポート・手書き md からの移行を想定。Vault フォルダ変更後は全付箋を再スキャンして再生成する |
| iPhone 連携 | Google Drive 連携の設定・認証 | PKCE フロー(サーバーレス OAuth)でトークンをローカルにのみ保存。Drive 接続が切れると本タブのアイコンに赤バッジが表示される。フィードバック Discord Webhook URL はバイナリに含めず Vercel 環境変数に持つ |
| このアプリについて | アプリバージョンの表示 | getVersion() が Cargo.toml からランタイムに読む。バージョン文字列はフロントエンドにハードコードされていない |
| フィードバック | 開発者へのフィードバック送信 | 送信内容は Discord Webhook 経由で開発者に届く。Webhook URL は Vercel 環境変数に持ち、バイナリには含めない |
7 エラーハンドリング・リカバリ方針
7.1 ローカルファイルとリカバリ
7.1.1 保存時のパス喪失保護(増殖バグ回避)
ユーザーが手動でファイルを移動・削除した場合や、リネーム処理が直前に行われた場合、古いパスに対する自動保存(ジオメトリ変更など)が発火すると古い名前でファイルが復活してしまう(増殖バグ)。 これを防ぐため、logic.rs の保存処理では**「上書き対象のファイルパスが物理的に存在するか(!current_path_obj.exists())」をチェック**し、存在しない場合は保存処理をブロック(Err 返却)してトレースログを出力する保護機構を備えている(実施済み)。
7.1.2 アトミック書き込みによるファイル破損防止
付箋内容の保存は、一時ファイルへの書き込み完了後に元のファイルへ rename するアトミック書き込みで行う。これにより、保存処理中の予期せぬ電源断やクラッシュによるデータ欠損・破損を完全に防ぐ(実施済み)。 ファイルシステムエラー(書き込み権限なし等)が発生した場合は、保存を中止し Tauri コマンドのエラーとして返却する。
7.1.3 PC アプリ再起動時の状態復元
起動時に Vault フォルダを全スキャン(フロントマター解析)して付箋ウィンドウを再生成するため、アプリが異常終了した場合でも次回起動時にファイルの状態から自動復元される(実施済み)。 ただし、終了直前の「メモリ上にしか存在しない未保存の内容」は失われる(保存タイミングは blur 時またはデバウンス 800ms 経過後)。
8 環境変数・シークレット
PC版(Tauri アプリ)のビルド・リリースに必要な変数・シークレットの一覧です。
8.1 ローカルビルド用(.env.local)
src-tauri/build.rs がコンパイル時に読み取り、バイナリに直接埋め込む。.env.local は .gitignore 対象のため Git には含まない。
表 8.1-1 ローカルビルド用環境変数(.env.local)
| 変数名 | 取得元 | 用途 |
|---|---|---|
GDRIVE_CLIENT_ID | Google Cloud Console → 認証情報 → OAuth 2.0 クライアント(Tauri用) → クライアント ID | PC版 Drive 認証の OAuth Client ID。gdrive.rs 内で env!() マクロで参照。 |
GDRIVE_CLIENT_SECRET | Google Cloud Console → 認証情報 → OAuth 2.0 クライアント(Tauri用) → クライアント シークレット | PC版 Drive 認証の OAuth Client Secret。gdrive.rs 内で env!() マクロで参照。 |
8.2 CI/CD用(GitHub Secrets)
GitHub Actions のリリースワークフロー(release.yml / do-release.yml)が参照するシークレット。
表 8.2-1 CI/CD用 GitHub Secrets
| シークレット名 | 用途 | 設定するワークフロー |
|---|---|---|
GDRIVE_CLIENT_ID | Tauri ビルド時の Drive Client ID(ローカルと同値) | release.yml |
GDRIVE_CLIENT_SECRET | Tauri ビルド時の Drive Client Secret(ローカルと同値) | release.yml |
TAURI_SIGNING_PRIVATE_KEY | Tauri アップデーター署名用秘密鍵 | release.yml |
TAURI_SIGNING_PRIVATE_KEY_PASSWORD | 上記秘密鍵のパスフレーズ | release.yml |
WINGET_TOKEN | winget-pkgs への PR 作成用 PAT(public_repo スコープ必要) | release.yml |
WINGET_APP_ID | do-release.yml が main ブランチへ push するための GitHub App ID | do-release.yml |
WINGET_APP_PRIVATE_KEY | 上記 GitHub App の秘密鍵 | do-release.yml |
9 改版履歴
表 9-1 改版履歴
| No | バージョン | 日付 | 変更内容 |
|---|---|---|---|
| 1 | 1.0 | 26-04-19 | 新規作成。旧 001_ARCHITECTURE_DESIGN.html のPC側内容(構造図・クラス図・シーケンス図・ER図)を統合・整理 |
| 2 | 1.1 | 26-04-24 | ウィンドウ構成図を graph LR(横向き)に変更。スクロールなしで全体が見えるよう改善。 |
| 3 | 1.2 | 26-04-26 | セクション7「環境変数・シークレット」を新規追加。ローカルビルド用とCI/CD用を分けて記載。 |
| 4 | 1.3 | 26-04-27 | セクション6「機能一覧」を新規追加(システムトレイ・ツールバー・右クリックメニュー・設定画面)。各機能に設計意図・工夫を追記。旧6→7、旧7→8、旧8→9に繰り下げ。 |