# NoteDeck Roadmap マーケティング戦略・ポジショニング・競合分析・収益化については [STRATEGY.md](STRATEGY.md) を参照。 設計原則は [DESIGN.md](DESIGN.md#設計原則) を参照。 --- ## 対応プラットフォーム | プラットフォーム | リリースステージ | 備考 | |---|---|---| | macOS | **v1.0.0 リリース対象** | 公式 DMG 配布 | | Linux | **v1.0.0 リリース対象** | AppImage / AUR | | Windows | **v1.0.0 リリース対象** | NSIS / winget | | Android | **オプション β(v1.0.0 対象外)** | Play Store 未認証。UnifiedPush 等のモバイル固有機能・バグは v1.0.0 のブロッカーにしない | v1.0.0 のリリース条件はデスクトップ 3 OS のみで判断する。 Android 固有の bug / feature(バックグラウンド通知・Haptics・モバイル UX)は v1.1.0+ で順次対応する。 --- ## 構造的に対応しない領域 低頻度・管理系の操作は Web UI の責務とし、実装しない。 **ただし、ユーザーが迷わず Web UI へ到達できるリンク導線を整備する。** | 領域 | 理由 | リンク導線 | |---|---|---| | 管理画面 | サーバー管理者は SSH + Web UI で運用しており、NoteDeck の責務外 | ✅ アカウントメニュー → Admin | | プロフィール編集 | 年に数回の操作。Web UI リンクで十分 | ✅ プロフィールバナー → 編集ボタン | | チャンネル管理 | 閲覧は対応。作成・編集は Web UI で | ✅ カラムヘッダー → 外部リンク | | ミュート・ブロック | 設定の下位項目。一覧管理は Web UI で | ✅ アカウントメニュー → Settings | | フォロー管理画面 | 一覧表示・整理は Web UI で。フォロー操作のみ対応 | ✅ プロフィール → Following / Followers | | 設定画面の網羅 | Misskey 本家の膨大な設定項目は Apple 式の思想と矛盾 | ✅ アカウントメニュー → Settings | | ノート詳細 | アプリ内で会話チェーン・タブ表示済み。Web UI リンクは補助導線 | ✅ ノートメニュー → Web UIで開く | | 他人のプロフィール | アプリ内で閲覧・ノート一覧・フォロー操作済み。Web UI リンクは補助導線 | ✅ プロフィールバナー → 外部リンクボタン | | Misskey Games | 物理演算ゲーム・リアルタイム対戦の移植は過大。Web UI で遊べば十分 | ✅ アカウントメニュー → Misskey Games | **判断基準**: 月 1 回以下の操作 / Web UI リンクで代替可能 / 情報フロー(収集→分類→知識化)を中断しない > **Note**: リスト・アンテナ・クリップの CRUD は情報分類パイプラインの中核であるため、 > 「構造的に対応しない領域」から外し L2「情報整理 CRUD」で対応する。 --- ## レイヤーモデル NoteDeck の機能は 5 つのレイヤーで構成される。各レイヤーは前の層の上に乗り、 下が成熟していないと上の価値を発揮できない。 ``` Layer 0: 収集 ───────────────────────────────── マルチサーバー │ ストリーミング │ 全カラム種別 ↓ Layer 1: 蓄積 ───────────────────────────────── SQLite FTS5 │ 永久キャッシュ │ notecli 分離 ↓ Layer 2: 反応 ◀ 現在のフロンティア ──────────── デッキ UI │ Vim モード │ 投稿 │ マルチウィンドウ ↓ Layer 3: 検索・整理 ─────────────────────────── 全文検索 │ 横断検索 │ 外部PKM連携 │ エクスポート ═══════════════ v1.0.0 ════════════════════════ ↑ v1.0.0: Layer 0〜2 完成 + Layer 3 検索 ↓ v1.0.0 以降: PKM 連携 + AI 統合 + i18n + Layer 4(Nd:* API 拡充・プラグイン開発支援) ↓ Layer 4: 拡張 ───────────────────────────────── HTTP API │ CLI │ AI 統合 │ Nd:* API ``` --- ## Layer 0: 収集 — 情報を大量に集める **思想: 複数サーバーの情報を一箇所に引き込む。NoteDeck の価値の根源。** ### マルチサーバー接続 - [x] **複数サーバー同時接続** — SQLite 管理(UNIQUE(host, user_id) 制約)、サーバー単位のアカウント分類 - [x] **アカウント切替** — アカウントメニューから即座に切替 - [x] **サーバー情報キャッシュ** — TTL 24 時間、ソフトウェア検出、バージョン追跡、機能検出 - [x] **接続状態インジケーター** — アカウントごとのストリーミング接続状態 (接続中 / 再接続中 / 切断)をアイコンで表示 ### ストリーミング・データ取得 - [x] **WebSocket ストリーミング** — requestAnimationFrame バッファリング、forceUpdate 定期実行 - [x] **カスタム絵文字** — ホスト単位キャッシュ、`@.` リモート絵文字、並列フェッチ重複排除 - [x] **OGP リンクプレビュー** — IntersectionObserver 遅延読み込み、Rust バックエンドでメタデータ抽出&キャッシュ - [x] **3 層画像キャッシュ** — メモリ→ディスク→ネットワーク(Rust image_cache.rs) - [x] **画像プロキシ** — `http://127.0.0.1:19820/proxy/image` 経由でキャッシュ付き配信 ### カラム種別 - [x] タイムライン(HTL / LTL / STL / GTL / カスタム) - [x] 通知カラム(リアルタイム、タイプ別アイコン・ラベル、ユーザーホバープレビュー) - [x] 検索カラム(ローカル FTS5 + サーバー検索ハイブリッド、重複排除マージ) - [x] リストカラム - [x] アンテナカラム - [x] ダイレクト投稿カラム(visibility: specified) - [x] チャットカラム(Misskey v2025.2.0+ chat API、リアルタイム WS 対応) - [x] クリップカラム - [x] ユーザーノート一覧カラム - [x] メンションカラム - [x] チャンネルカラム - [x] ロールタイムラインカラム(公開ロールのタイムラインを購読。コマンドパレットからロール選択、WebSocket リアルタイム受信) - [x] お気に入りカラム - [x] ウィジェットカラム(AiScript Console / App 実行環境) - [x] スクラッチパッドカラム(旧 AiScript エディタ。独立カラム、上下分割レイアウト、Web Worker LSP、UI インスペクター) - [x] ページカラム(Misskey Pages の閲覧・いいね。MFM + AiScript 実行、Plugin:register_page_view_interruptor 対応) - [x] お知らせカラム(サーバーからの告知を閲覧) - [x] ドライブカラム(ファイル一覧の閲覧、フォルダナビゲーション、ファイルD&D アップロード) - [x] ギャラリーカラム(ギャラリー投稿の閲覧) - [x] 実績カラム(実績一覧の表示) - [x] フォローリクエストカラム(フォローリクエストの管理) - [x] Explore カラム(notes/featured の閲覧) - [x] API コンソールカラム(Misskey API をインタラクティブに実行・レスポンス確認) - [x] API ドキュメントカラム(OpenAPI 仕様を Scalar で表示、ダーク/ライト自動切替、ワイドカラム対応) - [x] 照会カラム(URL or @user@host でノート・ユーザーを照会。同一サーバー直接取得 + ap/show リモート解決) - [x] サーバー情報カラム(サーバーの概要・管理者・ルール・統計・リンクを Misskey 本家 /about 準拠で表示) - [x] Misskeyについてカラム(Misskey ソフトウェアの紹介・クレジットを /about-misskey 準拠で表示。meta API からバージョン取得、プロジェクトメンバー・スポンサー・I❤Misskey 投稿ボタン。改変版フォーク検出対応) - [x] カスタム絵文字カラム(サーバーのカスタム絵文字一覧。カテゴリ別グルーピング・検索フィルタ・クリックでショートコードコピー。既存の絵文字キャッシュを流用) - [x] 広告カラム(サーバー広告の一覧表示。全広告種別を表示、ミュート対応。既存の useAds.ts + meta API の ads フィールドを流用) - [x] Workspace Explorer カラム(VSCode Explorer 相当。settings.json ノード、コンテキストメニュー、Rename 対応) - [x] Stream Inspector カラム(WebSocket ストリーミングイベントのリアルタイム監視。アカウントアバター/サーバーバッジ表示、detail ペインのドラッグリサイズ、イベント重複排除) - [x] 連合カラム(federation/instances の一覧表示。ソフトウェア・ユーザー数・最終通信日時等。ソート・フィルタ付き。サーバー詳細ウィンドウ + Well-known リンク対応) - [x] チャートカラム(charts/* API によるサーバー統計グラフ。ユーザー活動・ノート数・連合等。chart.js + matrix/gradient/zoom) - [x] クエリ管理カラム(カラムクエリの定義・サイドロード・MisStore 導入を一元化。→ Layer 3「カラムクエリ」) ### サーバー運営者との共生 - [x] **サーバー広告表示** — `/api/meta` から広告データを取得し TL に自然に差し込み(デフォルト ON)。 サーバー設定の `notesPerOneAd` に従った表示間隔、ratio による重み付きランダム選択、dayOfWeek ビットマスクフィルタ対応。 画像右上の i ボタンから「Ads by {host}」表示・表示頻度低下(ミュート)が可能。アカウント単位で 10 分キャッシュ - [x] **広告表示ポリシーの明示** — README で「サーバー広告を尊重する」方針を明記。 サーバー管理者に安心感を与え、NoteDeck を推薦してもらうための信頼構築 ### フォーク adapter | フォーク | 状態 | 備考 | |---|---|---| | Misskey 本家 | 基本対応済み | Chat API (v2025.2.0+) 含む | | yamisskey 系 | 必要に応じて対応 | Misskey API 準拠のため差異は小さい | | misskey-tempura 系 | 必要に応じて対応 | Misskey API 準拠のため差異は小さい | --- ## Layer 1: 蓄積 — 集めた情報を失わない **思想: サーバーが消えても、あなたのタイムラインはここにある。** ### データ永続化 - [x] **SQLite FTS5** — trigram トークナイザーによる日本語対応全文検索基盤。 ストリーミング受信・API 取得時にノートを自動キャッシュ - [x] **ノートキャッシュ永久保存** — `cleanup_cache()` を no-op 化し無期限保存。 サーバーからノートが消えてもローカルに残る - [x] **キャッシュファースト表示** — 全カラムでローカルキャッシュを先読みし即座に表示。 サーバーからは差分のみ取得(sinceId)。オフライン時もキャッシュ済み TL を表示継続 - [x] **OS キーチェーン連携** — Keychain / DPAPI / Secret Service で暗号化保管。 SQLite フォールバック、メモリキャッシュ + zeroize ### notecli(ストレージエンジン分離) notecli は NoteDeck の Rust バックエンドから Tauri 非依存のコードを分離した独立リポジトリ ([github.com/notedeck-dev/notecli](https://github.com/notedeck-dev/notecli))。 NoteDeck は `Cargo.toml` で git 依存として参照。 - [x] **API クライアント** — Misskey HTTP API ラッパー(api.rs) - [x] **SQLite データベース** — ノートキャッシュ・FTS5 検索・アカウント管理(db.rs) - [x] **データモデル** — Misskey エンティティの型定義(models.rs) - [x] **エラー型** — 統一エラーハンドリング(error.rs) - [x] **イベントバス** — tokio broadcast ベースの pub/sub(event_bus.rs) - [x] **streaming の notecli 移行** — `AppHandle.emit()` を `FrontendEmitter` trait に抽象化。 notedeck 側は薄い TauriEmitter アダプタのみ - [x] **ノート実体と所属の分離** — ノートそのものと「どのタイムラインに現れたか」を別々に持つ。 同じノートが複数のカラムに現れても所属が後勝ちで上書きされなくなり、 カラムの所属で絞った検索が取りこぼさなくなった(カラムクエリのキャッシュ検索の前提) ### 未完了 - [x] **ワンファイルバックアップ** — `notecli.db` を丸ごとコピーで完全バックアップ。 サーバーが消滅しても自分の投稿・お気に入りは残る。 GUI 設定(デッキレイアウト・テーマ・キーバインド等)もJSONバンドルで一括バックアップ可能。 設定メニュー →「DBバックアップ」「設定バックアップ」でファイル保存ダイアログ経由のエクスポート。 `profiles/` / `themes/` / `plugins/` / `snippets/` / `memos/` 配下のファイルは全て対象。 localStorage-only scalar 設定は `settings.json` に統合済み。`ai.json` / `keybinds.json5` / `performance.json` も dual-write で `settings.json` に統合済み ([DESIGN.md](DESIGN.md) の「settings.json」節参照) - [x] **完全オフライン動作** — キャッシュ済みノートの閲覧・検索はネットワーク不要。 飛行機の中でも過去の TL を読み返せる。 アプリレベルのオフラインモード(ナビバー / コマンドパレット / システムトレイから切替)で 全通信を停止し、キャッシュのみで動作。書き込み操作はブロック、再開時に差分フェッチで復帰 - [ ] **サーバー移転支援** (v1.0.0 以降)— 旧サーバーのキャッシュを保持したまま新サーバーに移行。 「あのノートなんだっけ」がローカル検索で見つかる - [x] **ログアウト時のデータ保持選択** — ログアウト時に「データを残す/すべて削除」を選択可能にする。 「データを残す」ではトークンのみ削除し、カラムとキャッシュは読み取り専用で残る。 オフラインファースト表示(A-8)と同じ仕組みでキャッシュ済みノートを閲覧可能。 プライバシー保護は OS のロック画面で担保する --- ## Layer 2: 反応 — 人間が情報に対してアクションする **思想: 見て、反応して、発信する。マウスに手を伸ばした時点で負け。** ### デッキ UI - [x] **カラム管理** — 追加・削除・並べ替え(ドラッグ&ドロップ)、レイアウト localStorage 永続化 - [x] **カラムリサイズ** — ドラッグで 280px〜600px に自由変更、幅を localStorage に永続化 - [x] **カラムスタック(上下分割)** — カラムをグループ化して上下分割表示。ドラッグ&ドロップで スタック内の並べ替え・スタック間のカラム移動・空きエリアへのドロップで独立カラム化が可能 - [x] **ナビバー構造** — 折りたたみ/展開、ドラッグリサイズ、細くするとアイコンのみ表示に自動切り替え。 選んだ幅は `settings.json5` に永続化し、ウィンドウが狭いあいだの自動折りたたみでは上書きしない (幅と閾値は `src/composables/useNavbarResize.ts` が正本) - [x] **フローティングウィンドウ** — ノート詳細・ユーザープロフィール・ログインをウィンドウで表示。 ドラッグ移動、最小化/最大化、Z-index 自動管理、同一対象の重複排除、サーバーテーマ反映 - [x] **インスペクタウィンドウ** — ノート/通知/ユーザーの Raw JSON 表示(`RawJsonView` 共通コンポーネント、`useSensitiveMask` で機密マスキング)。 settings.json の Raw JSON エディタウィンドウも同系統 - [x] **カスタムタイトルバー** — 最小化/最大化/閉じる、サイドバー切替、モバイルサイズ切替、PiP トグル - [x] **カラムフィルター** — Renote 除外・リプライ除外・ファイル付きのみ・Bot 除外(timelineFilter) - [x] **カラムサウンド** — カラム単位のミュート/アンミュート、通知サウンド再生 - [x] **デッキプロファイル** — 名前付きプリセット(「仕事用」「趣味用」等)でカラム構成を保存・切替。 `Alt+1`〜`9` で即切替、コマンドパレットからも実行可能 ### 投稿・リアクション - [x] **投稿** — テキスト・CW・公開範囲・ファイル添付・ドライブファイル添付・投票・編集・ローカルオンリー・Ctrl+Enter 送信 - [x] **下書き管理** — localStorage、最大10件、アカウント別、保存・復元・削除UI - [x] **リアクション / リプライ / リノート / 引用** - [x] **リアクション楽観的更新** — UI 即時反映+失敗時ロールバック - [x] **フォロー / アンフォロー** - [x] **投票表示** — 投票結果の表示、投票操作 - [x] **ドラッグ&ドロップ投稿** — デスクトップからファイルをドロップ → アップロード。 ドライブカラムへのファイル D&D にも対応 - [x] **ノートコピー(テキスト・リンク)** — ノートメニュー →「内容をコピー」「リンクをコピー」。 Misskey 本家と同じ 2 ボタン構成 - [x] **アカウント別予約投稿** — サーバー側の予約投稿 API に委譲。 NoteDeck 側にキューを持たず、対応サーバーのみ feature detection で有効化(#341)。 `useScheduleDialog` で予約投稿ダイアログを切り出し - [x] **下書き一覧** — 投稿フォームのフッター「下書き一覧」ボタンからピッカー表示。 クリックで復元、個別/全削除。独立ウィンドウ化は不要と判断(#342) - [ ] **アップロード前のローカルトランスコード** (#927) — v1.0.0 以降。 サーバーの upload 上限を超えるファイルを、アップロード前にローカルで縮小する。 Web クライアントは WASM でのトランスコードになり実用速度が出ないためネイティブ固有の領域。 既定は「上限を超える場合のみ」とし、常時圧縮は明示設定。バンドルサイズへの影響を見て判断する ### レンダリング・表示 - [x] **MFM 完全レンダリング** — bold/italic/strike/code/mention/hashtag/URL/link/カスタム絵文字/ Unicode 絵文字/コードブロック + アニメーション関数(spin/shake/bounce/jelly/tada/jump/twitch/ rainbow/sparkle/blur/fontSize/color/bgColor/border 等) - [x] **Twemoji** — Unicode 絵文字を自動変換、リアクション絵文字 URL 生成 - [x] **コードハイライト** — Shiki(Dark Plus テーマ)、17 言語対応 (bash/c/cpp/css/go/html/java/js/json/kotlin/markdown/python/ruby/rust/sql/ts/yaml) - [x] **メディアグリッド** — 画像/動画/音声の表示(最大 4 ファイル)、NSFW 切替、ライトボックス、遅延読み込み - [x] **ユーザーホバープレビュー** — アバターホバー 400ms でユーザー情報ポップアップ表示 - [x] **リアクション者一覧** — リアクションホバーでリアクションしたユーザー一覧をポップアップ表示 - [x] **インスタンスティッカー** — リモートユーザーのサーバー favicon + テーマカラー表示 ### キーボード操作 - [x] **基本ノートナビゲーション** — `j` / `k` でノート移動、`Enter` で詳細展開(useNoteFocus) - [x] **ノートアクション** — `r` リプライ、`e`/`+` リアクション、`q` リノート/引用、`b` ブックマーク、`v` CW トグル - [x] **カラム間移動** — `H` / `L` でカラム切替、`Ctrl+1`〜`9` でカラム直接選択。 キーバインドカスタマイズで変更可能 - [x] **クイックリアクションキー** — `1`〜`9` でお気に入りリアクション(pinnedReactions)を即送信。 ノートにフォーカスした状態で数字キー一発。キーバインドカスタマイズで変更可能 - [x] **コマンドパレット** — Ctrl+K で起動、fuzzy 検索、ショートカットキー連携 - [x] **キーバインドカスタマイズ** — `useKeybindsStore` でデフォルト+ユーザー上書きを管理。 VSCode の `keybindings.json` と同じ思想。設定 UI からリマップ可能 - [x] **投稿コマンド** — コマンドパレットから直接投稿。`post ここに本文` で即送信 - [x] **Quick Note** — `Ctrl+Alt+N` でコマンドパレットを `post` モードで起動(アプリ非アクティブ時も動作)。 テキスト入力 → Enter で即送信、パレットは自動で閉じる - [x] **Boss Key** — `Ctrl+Shift+B` で即座にウィンドウを非表示。 上司が来た時に一瞬で消す(タスクバーからも消える) - [x] **ダイアログ・フォーカストラップ** — 確認ダイアログ・カラム追加・リアクションピッカー等で `Esc` 閉じ、`Tab` がダイアログ内でループ、初期フォーカス自動設定(useFocusTrap) - [x] **ポップアップメニュー・キーボード操作** — ノート操作メニュー・設定メニュー・プロフィールメニュー・ アカウントメニュー等で `↑`/`↓` アイテム移動、`Enter` 選択、`Home`/`End`、`Esc` 閉じ(useMenuKeyboard) - [x] **ライトボックス・キーボード操作** — `Esc` 閉じ、`←`/`→` で前後の画像に移動 - [x] **リアクションピッカー・キーボード選択** — 検索入力 → `↓` で結果に移動 → `←`/`→` で絵文字間ナビ → `Enter` で選択 - [x] **カラム並び替えコマンド** — コマンドパレットから「カラムを左/右に移動」で実行可能 ### テーマ - [x] **Misskey 互換テーマコンパイラ** — Misskey テーマ定義を CSS 変数にコンパイル - [x] **アカウント別テーマ** — サーバーのデフォルトテーマ(meta.themeDark / meta.themeLight)を自動取得、 アカウント単位でカラム・ウィンドウに適用 - [x] **ダーク/ライト自動選択** — システム設定に連動 - [x] **テーマ手動切替** — ナビバーの太陽/月トグルでダーク/ライト切替 - [x] **テーマ JSON5 インストール** — Misskey テーマコードを貼り付けてインストール - [x] **テーマエディタカラム** — カラム上でテーマカラーをリアルタイム編集、プレビュー反映 - [x] **カスタムテーマ管理** — 設定メニュー・コマンドパレットからカスタムテーマの編集・削除・適用が可能 - [x] **カスタム CSS カラム** — ユーザー独自の CSS をエディタで記述・即時適用、プリセット機能付き - [x] **アカウント別カスタムテーマ** — アカウントごとに per-account テーマカラムを開ける。 カラムは `crossAccount: true` で「全アカウント」/「特定アカウント」を選択可能。 ストア (MisStore) からのインストール、ローカル作成、ビルトインテーマを統一表示し、 各テーマの `installedFor` で per-account 紐付けを管理(#339) ### デスクトップ統合 - [x] **システムトレイ** — 常駐、トレイアイコン - [x] **デスクトップ通知** — リアルタイム通知(ノート・リアクション・フォロー等)、 アプリフォーカス中は非表示、通知アクション(クリックでノート/ユーザーを開く) - [x] **通知サウンド** — AudioContext デコード+キャッシュ、カラム単位ミュート、フェードイン再生 - [x] **自動アップデート** — Tauri updater で起動時チェック、ダウンロード+インストール+自動再起動 - [x] **自動起動** — tauri-plugin-autostart でOS起動時に自動起動 - [x] **仮想スクロール** — @tanstack/vue-virtual で全カラムを仮想化、DOM 30-50 件に抑制、CSS @keyframes で新着アニメーション維持 ### 未完了: OS 統合 — v1.0.0 以降 > 常駐アプリが OS の側に踏み込む領域。ブラウザからは原理的に到達できないため、 > Web クライアントとの差が構造的に開く。設定項目を増やさず OS の作法へ寄せる方向で扱う。 - [ ] **通知アクションボタン** (#924) — 通知に並ぶボタンから、アプリを開かずに返信・リアクション・ フォローバック・会話ミュートを完了する。現在は通知クリックでの遷移 (#754) までで、 アクションボタンは未対応。ブラウザ通知では action からページを開かずに処理できないため ネイティブ固有の領域にあたる。認可の扱いは権限モデル (#712) の中で決める - [ ] **OS の共有シートへ送出** (#926) — ノート・添付ファイル・ドライブファイル・プロフィールを OS の共有シートへ渡す。#863(他アプリから NoteDeck を呼ぶ)の逆向きで、 現状は「受け取れるが送り出せない」非対称になっている。送出先は OS に委ねる - [ ] **OS の集中モードに従う** (#928) — OS がおやすみモード / 集中モードのとき、 アプリが自前で再生している通知音を鳴らさない。#747 で通知制御を OS へ委譲する判断をしたが、 自前再生の音は OS 側で止まらないため委譲先に穴が空いている。追加する設定項目はゼロを目指す - [ ] **ファイルのドラッグアウト** (#929) — ノートの添付画像・動画・ドライブファイル・ギャラリーの画像を 掴んで他アプリやデスクトップへ持ち出す。現在は受け取り(OS からのドロップ)のみで、 出す方向が空いている。#926 のデスクトップ側にあたる。ドラッグ開始時に実体ファイルを どう用意するかが論点 - [ ] **トレイからのミニ操作** (#930) — 現在のトレイはウィンドウの表示切替のみで、情報も操作も持たない。 未読件数のバッジ、最新通知の数件表示、クイック投稿、オフライン切替をトレイに載せ、 ウィンドウを開かずに済ませる。閲覧はウィンドウ / カラムに任せ、トレイに TL は出さない - [ ] **電源状態への自動適応** (#931) — バッテリー駆動・残量低下・省電力モードを OS から読み、 画像の先読み・アニメーション絵文字・ストリーミング再接続間隔を自動で落とす。 手動のパフォーマンス設定はあるが、状態を検知して自動調整する仕組みが無い。 電源状態を取る Web API は WebKit で使えないため判定は Rust 側で行う。 追加する設定は「自動調整する」の 1 つ(既定 ON)にとどめる - [ ] **従量制回線の検知と通信量の抑制** (#935) — テザリング等を検知して画像・動画の自動読み込みを止める。 電源状態と違いクロスプラットフォームの手段が無く、OS ごとに別の API を叩くため #931 から分離した。 ストリーミングは維持する(切ると通知が届かなくなる) ### マルチウィンドウ - [x] **ミラー式マルチウィンドウ** — タイトルバーから同等のウィンドウを複数作成。 全ウィンドウが同じカラム構成を共有し、Tauri イベントで状態を同期 - [x] **デッキのウィンドウ分離** — カラムヘッダーメニューから「別ウィンドウで開く」で カラムをサブウィンドウにポップアウト。サブウィンドウからは「メインウィンドウに戻す」で回収。 モニター 1 に TL 系カラム、モニター 2 に通知 + 検索 + AI カラム等の配置が可能 - [x] **ウィンドウ間カラム移動** — ドラッグ&ドロップでカラムをウィンドウ間で移動。 カーソルがウィンドウ外に出るとドロップオーバーレイが表示され、クリックで移動を確定。 Tauri IPC イベント(deck:drag-start / deck:drag-end)で同期 - [x] **ウィンドウレイアウト永続化** — プロファイル = 全体スナップショット方式。 ウィンドウの数・位置・サイズ・カラム割当をプロファイルに含めて保存。 `Alt+1`〜`9` で切替時にウィンドウレイアウトごと復元(余剰ウィンドウは閉じ、不足分は開く) - [x] **モニター記憶** — ウィンドウがどのモニターに配置されていたかを記憶し、次回起動時に復元。 Tauri `currentMonitor()` でモニター名を保存、`availableMonitors()` で復元時に検証 - [x] **モニター切断時のフォールバック** — 保存されたモニターが切断された場合、 プライマリモニターの中央にウィンドウを配置。画面外にはみ出す場合は座標をクランプ - [x] **ウィンドウスナップ統合** — OS のスナップ機能(Win+矢印等)に対応。 `decorations: false`(カスタムタイトルバー)でも Windows の端スナップが正常動作 ### PiP ウィンドウ デッキのカラムを常前面フローティングウィンドウとして切り離す機能。 - [x] **ウィンドウ管理** — 375×700px(リサイズ可能)、`alwaysOnTop`、複数同時起動(動的ラベル `pip-*`) - [x] **任意カラム表示** — 全カラムタイプをフル機能で表示。カラムヘッダーと統一されたデザイン - [x] **起動導線** — コマンドパレット / タイトルバー PiP ボタン / カラムメニューの 3 経路 - [x] **メインウィンドウ連携** — アカウント切替オーバーレイ、ノートクリックでメインウィンドウをフォーカス ### ナビバー機能 **カラムで再現できる機能はナビバーに置かない。** カラムを超えた操作のみをナビバーに配置する。 - [x] **投稿ボタン** — 新規ノート作成(展開時テキスト付き / 折りたたみ時 FAB 化) - [x] **アカウント管理** — アバター一覧、切替、モード設定、ログアウト、アカウント追加 - [x] **横断検索** — 全アカウントのキャッシュ+サーバー検索を統合。 単一アカウントの検索カラムでは不可能な、全アカウント横断の検索体験 - [x] **横断通知** — 全アカウントの通知を統合表示するウィンドウ - [x] **テーマ切替** — ダーク / ライト手動切替トグル(太陽/月アイコン) - [x] **設定** — ボトムバーの設定ボタンからアプリ設定メニューにアクセス (カスタムCSS・壁紙・テーマインストール等) ### Web UI リンク導線 - [x] **アカウントメニュー → Admin** — 管理者のみ表示、外部ブラウザで管理画面を開く - [x] **アカウントメニュー → Settings** — サーバーの設定画面を外部ブラウザで開く - [x] **プロフィールバナー → 編集ボタン** — 自分のプロフィール編集を Web UI で開く - [x] **プロフィールバナー → 外部リンクボタン** — 他人のプロフィールを Web UI で開く - [x] **プロフィール → Following / Followers** — フォロー管理画面を Web UI で開く - [x] **カラムヘッダー → 外部リンク** — リスト・アンテナ・チャンネルの管理画面を Web UI で開く - [x] **ノートメニュー → Web UIで開く** — ノート詳細を外部ブラウザで表示 > **採用しない: アプリ内の別ウィンドウで Web UI を開く** (#933) > > 外部ブラウザを起動すると文脈が切れるため、アプリ内のウィンドウで開く案を検討したが採用しない。 > Misskey Web UI はトークンを localStorage に保存しており、アプリ本体とは origin が異なるため > 認証状態を引き継げず、必ず未ログインから始まる。NoteDeck が保有するトークンを注入する案も、 > MiAuth で要求している権限に admin 系が含まれないため管理画面が機能せず、 > keychain に隔離しているトークンを WebView へ平文で永続化することになる。 > 既にログイン済みの可能性が高い外部ブラウザへ渡すほうが優位。 > ウィンドウを作ること自体は可能で、実装できないのではなく実装しても目的を達成できない。 > **採用しない: 汎用ブラウザカラム** > > 内蔵ブラウザで開いた画面にはミュート・可視性述語 (#831) が一切効かず、 > 「NoteDeck では見えないはずのものが NoteDeck の中で見える」領域を作ることになる。 > 取得と描画を外部に委ねる以上、技術の進歩では解けない。 > 外部リンクは従来どおり OS のブラウザへ渡す。 ### Misskey Web UI 互換(未完了) Misskey 本家 Web UI と食い違うポイント。アプリ独自のデッキ UI 等は除外。 「構造的に対応しない領域」の方針(設定画面は Web UI)は維持しつつ、 日常操作で本家ユーザーが違和感を覚える箇所を埋める。 #### 情報整理 CRUD 情報フロー(収集→分類→知識化)のパイプラインで「分類」を担う機能群。 閲覧カラムは実装済みだが、箱の作成・編集ができないと分類フローが Web UI 往復で中断する。 - [x] **クリップ CRUD** — クリップの新規作成・名前編集・削除。 ノートメニュー →「クリップに追加」の選択リスト末尾に「+新規クリップ」を配置し、 分類フローを中断させない。クリップカラムヘッダーからも管理可能 - [x] **リスト CRUD** — リストの新規作成・名前編集・削除・メンバー管理。 カラム追加ダイアログ・リストカラムヘッダーから操作可能。 ユーザーメニューからの「リストに追加」は実装済み - [x] **アンテナ CRUD** — アンテナの新規作成・条件編集・削除。 カラム追加ダイアログ・アンテナカラムヘッダーから操作可能。 条件設定 UI は本家準拠(キーワード・除外・ユーザー・返信含む等) #### ノート操作 - [x] **ミュート / ブロック操作** — ユーザーメニュー・ノートメニューからワンクリックで実行。 一覧管理は従来通り Web UI リンク導線のまま - [x] **通報(レポート)** — ノートメニュー・ユーザーメニューから通報ダイアログを表示。 SNS の基本的な自衛手段として必須 - [x] **クリップに追加** — ノートメニューからクリップ選択・追加。 クリップカラムでの閲覧は実装済み、追加操作が未対応 - [x] **リストに追加** — ユーザーメニューからリストへの追加・削除。 リストカラムでの閲覧は実装済み、メンバー操作が未対応 - [x] **削除して編集** — ノートメニューに「削除して編集」を追加。 本家ユーザーが慣れている操作フロー - [x] **リアクション詳細モーダル** — リアクションクリックでリアクション別ユーザー一覧を表示。 モーダル + ホバーポップアップの両方に対応 #### 通知 - [x] **通知フィルター** — 通知カラムでタイプ別フィルタ(リアクション / フォロー / メンション等)。 通知が多いユーザーにとって必須 - [x] **通知グループ化** — 同一ノートへのリアクションをまとめ表示。 reaction:grouped / renote:grouped 対応、サーバー非対応時はフォールバック #### ユーザープロフィール - [x] **フォロー / フォロワー一覧** — プロフィール内でフォロー・フォロワーをスクロール閲覧。 現在は Web UI リンクのみ。簡易一覧はアプリ内で見られると利便性向上 - [x] **フィールド表示** — プロフィールのカスタムフィールド(自由記述項目)の表示 - [x] **ロール / バッジ表示** — サーバーロール・バッジの表示(Bot/Cat バッジ含む) **プロフィールタブ:** - [x] **ノートタブ** — ユーザーの投稿一覧(overview/notes に統合) - [x] **アクティビティタブ** — 投稿頻度ヒートマップ + Notes stacked bar / Following bar / PV bar chart(#217) - [x] **ファイルタブ** — ユーザーがアップロードしたファイル一覧(image/video グリッド、無限スクロール対応) - [x] **実績タブ** — ユーザーの実績(アチーブメント)一覧 - [x] **リアクションタブ** — ユーザーがしたリアクションの一覧(publicReactions フラグ連動) - [x] **クリップタブ** — ユーザーのクリップ一覧(他人のプロフィールではお気に入りのみ表示、詳細ウィンドウ起動対応) - [x] **リストタブ** — ユーザーが作成したリスト一覧(他人のプロフィールでは公開リストのみ、詳細ウィンドウ起動対応) - [x] **ページタブ** — ユーザーが作成した Pages 一覧 - [x] **Play タブ** — ユーザーが作成した Misskey Play 一覧 - [x] **ギャラリータブ** — ユーザーのギャラリー投稿一覧 - [x] **Raw タブ** — ユーザー情報の生データ(JSON)表示(機密マスキング対応) #### タイムライン・表示 - [x] **スレッド表示** — 会話チェーンをインデント / ツリーで表示。 最大 5 階層ネスト、超過時は「スレッドを続ける」で詳細遷移 - [x] **ハッシュタグトレンド** — AiScript ウィジット (`trending-hashtags`) として実装済み。 デッキ右ペイン / プロフィールサイドバー等に配置可能 --- ## Layer 3: 検索・整理 — 蓄積データを引き出し構造化する **思想: Layer 1 で蓄積されたデータを「キャッシュ」から「知識」に昇格させる。** ### 検索 - [x] **ローカル全文検索** — SQLite FTS5 trigram トークナイザーで日本語対応。 検索カラムで入力中に即座にローカル結果を表示、Enter でサーバー検索結果とマージ - [x] **横断検索** — 複数サーバー × 全カラムのノートを統合検索。 全アカウントのローカルキャッシュ+サーバー検索を並列実行し、結果をマージ表示。 サーバー A のリスト + サーバー B のアンテナ + サーバー C の HTL をまたいだ検索は NoteDeck だけの体験 - [x] **正規表現検索** — 検索カラム・横断検索で正規表現パターンによる検索が可能。 `.*` トグルで正規表現モードに切替、ローカル/サーバー検索結果をクライアント側で正規表現ポストフィルタ。 構造化フィルタビルダー(いずれかを含む/すべてを含む/除外する)で直感的に条件構築 - [x] **クロスサーバー検索** — 複数サーバーの API 検索結果を統合表示。 ローカル検索 + API 検索のハイブリッド(横断検索ウィンドウで実装済み) - [x] **TL 履歴タイムマシン** — 日付指定で過去の TL を再現。 時計ボタンから日付選択、キャッシュ済みノートを閲覧、Live ボタンで復帰 アプリの外から蓄積を引く導線は L4「外部ツール統合」のランチャ拡張 (#511) で扱う。 > **採用しない: OS 検索インデックスへの登録** — Spotlight / Windows Search 等への登録も検討したが、 > macOS は署名済み bundle が前提、Windows は COM の protocol handler / IFilter 実装、 > Linux はデスクトップ環境ごとに別実装、Android は Google のサービス依存で F-Droid 配布 (#881) と衝突する。 > 4 プラットフォームで別々の重い実装を書くことになり、同じ目的をランチャ拡張で達成できるため > コストが釣り合わない。macOS のみ、コード署名 (#866 / #882) 完了後に単独で再検討する余地がある - [ ] **ローカル埋め込みによるセマンティック検索** (#932) — v1.0.0 以降。 全文検索は語の一致で引くため、内容は覚えていても単語を思い出せないものに到達できない。 ノートをローカルで埋め込みベクトル化し、意味の近さで引けるようにする。 ローカル AI (#880) と実行基盤を共有すれば外部にデータを送らずに成立する。 全文検索の置き換えではなく結果をマージする。対象範囲は #511 と揃える ### カラムクエリ — 実装済み (#783) **思想: カラムの視界をユーザーが定義する。独自 DSL を増やさず、Misskey 文化の AiScript をそのまま表層構文にする。** Krile の KQL 型「カラムごとのクエリフィルタ」を、AiScript 1.2.1 の式で書く。 解析できた式は QIR(型付きクエリ IR)に落として全取り込み経路で同期評価し、 サブセット外の純粋な式は Worker 隔離の逐次適用に明示降格する。表示制御 3 層モデル (#831) の層 2。 - [x] **QIR 基盤** — Rust が型の source of truth。JS / Rust / Interpreter の 3 評価器を共有 golden vector で一致検証 - [x] **⚡ 高速適用** — キャッシュ復元 / REST / ページング / streaming / refresh の全経路で同期評価 - [x] **🐢 逐次適用への降格** — サブセット外は専用 Worker で評価。暴走フィルタは強制停止 + 犯人だけサスペンド - [x] **ローカルキャッシュ検索** — FTS5 で粗く絞ってから Rust QIR 評価。条件に合うノートが見つかるまでページングで遡れる - [x] **名前付きクエリ** — sidecar 形式で保存、カラムからは id 参照(複数参照は And 合成)。参照消失は fail-closed - [x] **MisStore 配布の導入** — 配布はソースのみでローカル再コンパイル必須、自動適用なし - [ ] **配布クエリの更新フロー** — 更新時にソース差分を明示承認させる(#783 Phase 3.5 の残り) - [ ] **コンテキスト述語** — `ctx.isFollowing` 等。following キャッシュの鮮度設計が本体(#783 Phase 4) - [ ] **フォーク独自 TL フィルターの受け皿** (#775) — フォーク固有の TL 絞り込みを専用実装ではなくカラムクエリで表現する ### クロスサーバースレッドウィーバー — 実装済み (#323) **思想: 連合で分断された会話の全体像を、NoteDeck だけが見せられる。** Misskey の連合では、あるノートへのリプライ・引用リノートが全サーバーに届く保証はない。 サーバー A で 3 件のリプライが見えていても、サーバー B のローカルで盛り上がった 2 件は A には届いていない。単一サーバーのクライアントでは、この断片化に気づくことすらできない。 NoteDeck は複数サーバーに同時接続し、全ノートをローカルに永久キャッシュしている。 ActivityPub の正規 URI(`note.uri`)で同一ノートを特定し、全サーバーの断片を 1 つのスレッドビューに統合する。横断検索を「スレッド」に適用するアーキテクチャ。 #323 で「照会カラム」の cross-account 拡張として実装。新カラムタイプは追加せず、 既存照会カラムを全アカウント並列の `ap/show` 実行 + uri ベースマージに拡張した。 - [x] **`uri` インデックス** — notecli `notes_cache` に uri カラム追加(V2 マイグレーション + 部分インデックス) - [x] **スレッドマージエンジン** — `src/engine/threadMerge.ts`。リアクション合算、`renoteCount` / `repliesCount` の max 取得 - [x] **統合スレッドビュー** — 照会カラムでスレッドツリー表示(ancestors + children)、プログレッシブ表示 - [x] **能動的照会** — 全アカウントで `ap/show` 並列実行 → uri マージ - [x] **ナビバー / コマンドパレット導線** — 横断照会カラムをデフォルト配置、`CROSS_ACCOUNT_TYPES` で追加可能 ### 未完了(外部 PKM 連携)— v1.0.0 以降 > Layer 0〜2 の完成・安定を優先し、PKM 連携は v1.0.0 以降に着手する。 > 基盤が安定していない段階で上位レイヤーの仕様を固めても、手戻りが発生する。 NoteDeck は Misskey クライアントであり、PKM ツールではない。 ノートの整理・知識化は Obsidian / Logseq 等の専門ツールに委ね、NoteDeck はデータの橋渡しに徹する。 - [x] **JSON エクスポート** — `export_settings_json` / `import_settings_json` で GUI 設定の一括バックアップ・復元を実装済み。DB バックアップと合わせて全データ対応 - [x] **メモカラム (ローカル Markdown vault)** — メモは `settings/memos/{YYYYMMDDHHmmss}.md` に Zettelkasten 形式 + YAML frontmatter で保存。そのディレクトリを Obsidian / Logseq の vault として直接開ける。NoteDeck 側にはメモカラム + エディタウィンドウ (ビジュアル / コード タブ) を提供し、外部エディタで開くボタンで同じファイルを Obsidian などに引き渡せる - [ ] **ノート → PKM 送信** — ノートメニュー →「PKM に送る」で Misskey のノートを メモ vault に保存。現状はメモが local-only なので、ノートを取り込む導線を追加する段階 --- ## Layer 4: 拡張 — 外部ツール・AI がデータにアクセスする **思想: NoteDeck を閉じた箱にせず、外部ツール・AI・プラグインがデータにアクセスするプラットフォームにする。** このレイヤーの各項目は個別の連携機能ではなく、AI が自分の環境を作り替えてその結果を次の判断に使う **自己拡張ループ** を閉じるための部品である。到達点とループが閉じたと言える条件は [STRATEGY.md](STRATEGY.md) の「AI 統合」を参照。どこまで開放するかの上限は [#838](https://github.com/notedeck-dev/notedeck/issues/838) が正本。 ### HTTP API(localhost:19820) - [x] **HTTP サーバー基盤** — localhost で起動 - [x] **Bearer トークン認証** — 起動時にランダムトークンを生成しファイルに書き出し。 Jupyter Notebook 方式で同一マシンの認可済みプロセスのみアクセス可能 - [x] **コマンド実行 API** — 登録済みコマンドを HTTP 経由で実行。 コマンドパレットで人間ができることは全て API からもできる - [x] **状態取得 API** — カラム一覧、アクティブカラム、ノート取得等の読み取り操作 - [x] **イベントストリーム** — SSE で状態変化を購読。 AI が「新着通知があったら要約して」等のリアクティブ処理を実現 - [x] **API ドキュメント公開** — OpenAPI 形式で API 仕様を公開(`/api/openapi.json`)。 Scalar API Reference をアプリ内カラムとして表示。ダーク/ライトテーマ自動切替、ワイドカラム対応 **発展的な可能性: HTTP API をフロントエンドから叩く** notecli デーモンが localhost:19820 で全操作を API 公開しているため、 Tauri invoke の代わりに HTTP API を叩くアダプタ層を書けば、理論上はブラウザから NoteDeck の機能にアクセスできる。 「ブラウザ版 NoteDeck」をそのまま作る価値はないが、この構造は以下のような拡張に活かせる可能性がある: - **Web ダッシュボード** — 蓄積データの統計・可視化を別画面で表示(アクティビティヒートマップ、フォロー関係グラフ等) - **リモートアクセス** — 同一 LAN 内の別端末からノート検索・投稿(モバイルから PC の DB を横断検索) - **外部ツール連携の UI** — PKM エクスポートのプレビュー画面、AI チャットの Web UI 等、 デスクトップアプリに組み込むほどではないが GUI が欲しいユースケース 現時点では構想段階。HTTP API の成熟度と実際の需要を見て判断する。 ### AiScript プラグイン - [x] **AiScript ランタイム基盤** — @syuilo/aiscript v1.2.1 を WebView 内で実行。 ウィジェットカラム(Console / App)およびスクラッチパッドカラム(旧 AiScript エディタ)で記述・実行可能。 CodeMirror 6 エディタ統合(VS Code Dark+ テーマ、行番号、横スクロール、Web Worker LSP による補完・診断) - [x] **Misskey Play 互換 API** — `Mk:dialog`, `Mk:confirm`, `Mk:toast`, `Mk:api`, `Mk:save`, `Mk:load`, `Mk:remove`, `Ui:render`, `Ui:get`, `Ui:root`, `Ui:C:*`(text/mfm/button/buttons/textInput/textarea/numberInput/ switch/select/container/folder/postFormButton/postForm/spacer)。 `Mk:dialog`/`Mk:confirm`/`Mk:toast` は専用 Vue コンポーネント(AiScriptDialog/AiScriptToast)で表示 - [x] **Misskey Plugin API 完全互換** — Misskey 本家の全 Plugin API を実装: `Plugin:register_note_action`, `Plugin:register_user_action`, `Plugin:register_post_form_action`, `Plugin:register_note_view_interruptor`, `Plugin:register_note_post_interruptor`, `Plugin:register_page_view_interruptor`, `Plugin:open_url`, `Plugin:config`。 コロン区切りエイリアス(`Plugin:register:*`)にも後方互換対応。 レガシー AiScript(v0.13.2 / v0.19.0)のプラグインにも対応 - [x] **プラグイン管理 UI** — インストール・有効/無効切替・アンインストール。 Misskey 本家と同じくテキストベースでコピペインストール、AiScript エディタによるシンタックスハイライト - [x] **アカウント別プラグイン管理** — プラグインカラムを per-account / 全アカウントで 開ける。`installedFor` で per-account 紐付けを管理し、handler dispatch (`note_action` / `note_view_interruptor` / `note_post_interruptor` / `page_view_interruptor` 等) は実行時に該当 account のみフィルタ。 ストア (MisStore) からのインストールとローカル作成を「インストール済み」タブの ローカル/ストア セクションで分離表示(#340) - [x] **アカウント別ウィジット管理** — ウィジットカラムを per-account / 全アカウントで 開ける。「インストール済み」「ストア」の 2 タブ構成で、ライブラリ (= storeId 有無で ストア由来/ローカル) と「カラムへの配置」を二層モデルに分離。 ヘッダー+ボタンで新規ローカルウィジットを作成し、widget-edit window (コード/ビジュアル 2 タブ) で編集。ライブラリピッカーから既存ウィジットを配置・編集・削除。 配置済みは grip ハンドルでドラッグ並び替え可能(#387) - [x] **NoteDeck 拡張 API(Nd:*)** — Misskey 本家にない NoteDeck 固有の拡張ポイント。 名前空間は `Nd:*` に統一し、Misskey 本家の `Mk:*` / `Plugin:*` との衝突を回避。 AiScript カラム・Play カラム・ウィジェット・プラグインの全実行ポイントに統合済み。 個別 API を網羅的に並べる代わりに、`Nd:call` で capability registry に 接続することで `notes.*` / `column.*` / `account.*` / `memos.*` / `theme.*` / `skills.*` / `widgets.*` / `plugins.*` / `ai.*` / `meta.*` / `clipboard.*` / `drive.*` / `http.fetch` / `ui.notify` / `misstore.search` / `logs.recent` 等の builtin capability を AiScript からも即時利用可能にしている (AI tool calling / HTTP API / CLI / コマンドパレット / AiScript の 5 経路統一)。 skill / widget / plugin / theme の write 系は権限・確認ダイアログ・個別ガードの 3 層で守り、編集履歴 + revert (`*.history` / `*.revert`) を全カテゴリで完備。 - `NOTEDECK` — bool 定数。`exists("NOTEDECK")` でフィーチャーゲート可能 - `Nd:version` — NoteDeck のバージョン文字列 - `Nd:register_command` — コマンドパレットにカスタムコマンドを登録。 第 5 引数 `options` (`signature` / `permissions` / `aiTool` / `requiresConfirmation`) を渡すと capability registry にもミラー登録され、 AI tool calling / HTTP API / CLI からも呼べる - `Nd:call(id, params?)` — capability registry の任意 capability を呼び出す。 permissions / requiresConfirmation は `dispatchCapability` が enforce - `Nd:capabilities()` — registry にある capability の宣言情報 (id / label / description / params / returns / permissions / requiresConfirmation) を列挙。 プラグインが使える capability を自己発見できる - `Nd:on(name, handler) -> unsubscribe` — NoteDeck 内部イベントの購読。 `account:switch` / `column:added` / `column:removed` / `streaming:status` に加えて `note:new` / `notification:new` (queryDelta 経由で全アカウント 横断、payload は id のみで note 本体は別途 `Nd:call('notes.show', { id })` で取得させる ⇒ 機密マスキングは permissions に委ねる)。Tauri event は raw 公開せず、ファサードで payload subset を整形して情報漏洩リスクから plugin を切り離す - `Nd:http(url, options?)` — 外部 HTTP/HTTPS API (CORS なし)。Rust 側で SSRF 防御 (loopback / private / link-local / multicast / NoteDeck 自身の `localhost:19820` / `.local` / `.internal` / `.localhost` を deny)、 timeout 1〜120s、response 10MB 上限。実体は `http.fetch` capability で、 `Nd:call('http.fetch', ...)` でも呼べる。`requiresConfirmation: true` で 呼び出し都度に URL を確認させる - カラム操作は `Nd:call('column.list')` / `Nd:call('column.add', ...)` / `Nd:call('column.remove', ...)` 経由 (旧 `Nd:columns` / `Nd:addColumn` / `Nd:removeColumn` は β段階のため後方互換は残さず capability に統合) ### notecli CLI(外部インターフェース) - [x] **CLI モード** — notecli 単体での CLI 実行(投稿・検索・TL 取得等)。 clap ベースのサブコマンド: `accounts`, `post`, `tl`, `search`, `notifications`, `note`, `delete`。 `--json` フラグで AI エージェント向けマシンリーダブル出力 - [x] **デーモンモード** — バックグラウンドでストリーミング接続を維持し、HTTP API + SSE で配信。 `EventBusEmitter` により WebSocket → EventBus → SSE のパイプラインが完結 ### 未完了: 外部ツール統合 — v1.0.0 以降 > 19820 HTTP API + capability registry を基盤に、外部アプリ・物理デバイス・OS ランチャーから > NoteDeck を駆動するための連携層。Nd:* API がプラグインを *NoteDeck の内側* に取り込むのに対し、 > こちらは NoteDeck を *外側に開く* 方向の拡張。 すべて既存の capability registry を共通ディスパッチ層として共有する設計。本体側の追加実装は 最小限で、各連携は独立リポジトリ / サブコマンドとして配布する。 - [ ] **Stream Deck プラグイン** (#510) — Elgato Stream Deck から物理ボタンで投稿・リアクション・ カラム切替・プロファイル切替・Boss Key などを実行。ボタン LCD に未読数・接続状態を SSE で反映 - [ ] **ランチャ拡張** (#511) — ランチャから検索・投稿・コマンドパレット呼び出し。 capability registry を起動時取得して動的にコマンド一覧を生成(プラグイン登録 capability も自動公開)。 Raycast (macOS) を先行実装し、Alfred / Ulauncher / Flow Launcher へ同じ API の上に順次追加する。 **アプリを開く前にローカル蓄積へ到達する導線** (旧 #925) をここに統合済み。ランチャから引ける対象は 自分の投稿・クリップ・お気に入り・メモに限る。OS 検索インデックスへの登録を採用しない理由は L3「検索」に記録 - [ ] **`notedeck://` カスタム URI スキーム OS 登録** (#512) — ROADMAP「部分採用で完成していない機能」を 正式実装。`tauri-plugin-deep-link` で macOS / Windows / Linux に登録し、外部アプリ・ブラウザ ブックマークレット・OS Shortcuts から `notedeck://compose` / `notedeck://search` などを起動可能に - [ ] **MCP サーバー公開** (#513) — Claude Desktop / Cursor / Cline 等の MCP クライアントから NoteDeck capability を呼び出せるようにする。`notecli mcp-server` サブコマンド + capability registry → MCP tool schema 自動変換。credential proxy 実行モデルを継承し AI に Misskey トークンを渡さない - [ ] **OS グローバルホットキー(任意 capability bind)** (#514) — 現状 Quick Note / Boss Key の 2 つだけハードコードされているグローバルホットキーを、任意の capability に bind 可能に拡張。 `settings.json` の `globalShortcuts` セクションで管理。Stream Deck / Raycast 未導入のユーザー でも外部ホットキー連携を享受可能 - [ ] **汎用 Secret Vault** (#564) — AiScript / AI / プラグインから任意の外部サービストークンを OS キーチェーン経由で利用可能に。Misskey トークン / AI API キーで運用している credential proxy 実行モデル (Rust 側で注入、JS / AI には raw secret を渡さない) を任意の外部 API へ拡張。 **仕様は 5 round の adversarial review で確定済み (v5.1)**。Phase 1 のスコープ: - **接続モデル**: `connections.json5` (metadata、atomic write + file lock) + OS キーチェーン (`vault/v1//`)。authType は Bearer / Header / Query / Basic の判別共用体、 slot 引数で OAuth (v2) への拡張余地を確保 - **`vault.fetch` capability**: Rust 側で secret 注入。HTTP/1.1 only + redirect 各 hop で URL/IP/allowedHosts 再検証 + DNS pinning で SSRF/rebinding 防御。response は literal redaction (per-fetch nonce placeholder) + drop_headers - **AI 統合**: `aiVisible` toggle (default OFF) で開示制御、error は `denied`/`rate_limited`/ `external` の 3 値正規化 (enumeration 防止)、AI/user/HEARTBEAT 独立 rate バケット、 timing 量子化、confirmation scope (`once` / `session-connection`) - **型安全**: secret は `secrecy::SecretString` で workspace 一貫、`Debug` derive 禁止 lint、 dispatcher caller は transport 由来固定 - HTTP API (19820) からは vault を完全排除、CLI は Tauri IPC + peer credential 検査 - 内蔵接続テンプレ (AI プロバイダー + MisStore 配布物が要求する外部サービス) + URL ペースト推論。テンプレ一覧は `src/data/connectionTemplates.ts` が正本。 ここに無いサービスは手動登録できる - OAuth 2 フロー / Webhook (inbound) / MCP 連携 / dynamic capability は v2 以降。 詳細仕様は [DEVELOPMENT.md](DEVELOPMENT.md) の "Secret Vault" 節 ### 未完了: AI 統合 — v1.0.0 以降 > Layer 0〜2 の完成・安定を優先し、AI 統合は v1.0.0 以降に着手する。 > 基盤が安定していない段階で上位レイヤーの仕様を固めても、手戻りが発生する。 NoteDeck に AI を統合する。ただし AI を「持つ」のではなく「受け入れる」プラットフォームとして。 **なぜ新プロトコル(MCP 等)を追加しないか:** notecli CLI + notedeck HTTP API で AI が必要とする操作は全てカバー済み。 MCP は需要が出た時点で CLI / API の薄いラッパーとして追加すれば十分。 **Step 1: 要約・チャット** - [x] **AI チャットカラム** (#103) — カラム追加ダイアログから追加可能、他カラムと同じくリサイズ・削除可能。 **Anthropic Messages 互換 / OpenAI Chat Completions 互換**の 2 プロトコル対応 (内蔵テンプレは `src/data/connectionTemplates.ts` が正本)、 SSE ストリーミング応答、セッション永続化 (`notedeck/sessions/.json5`、Unix では 0600。 カラムから独立、master-detail UI で一覧/切替/CRUD)、 Markdown レンダリング (自前実装 + DOMPurify)、コードブロック個別コピー、 ChatGPT/Claude 風 suggested prompts chip (空状態)、ユーザーメッセージ copy button、 スキルストア (`composedSystemPrompt()`) による system prompt 統合。 `AiSessionKind = 'chat' | 'command' | 'task' | 'heartbeat'` で kind 別ドロワー表示。 API キーは Secret Vault ([#564](https://github.com/notedeck-dev/notedeck/issues/564)) の 接続として OS キーチェーンに格納 (フロント・AI はキー本体に触れない) **Step 2: AI 設定 — permissions / dataSources** (#408) - [x] **AI 権限・データソース設定** — Claude Code の `settings.json` 相当。 `settings.json` の `ai.permissions` / `ai.dataSources` に Read-only / Safe / Full / Custom プリセットを追加し、Custom 選択時のみ機能別 toggle が展開。 Misskey トークン等の credentials は denylist で system prompt から確実に除去。 dispatch 直前に `reloadAiConfig()` で **permission 変更は再起動なしに即反映** (singleton `useAiConfig`)。書き込み系 capability は **確認ダイアログ** (引数 JSON を code block + Shiki ハイライト表示) で enforce 済み - [x] **コンテキスト注入の集約** — `useAiSystemContext.buildAiContextBlock` に 全 dataSource の合流点を一本化。`` XML ブロックとして system prompt 末尾に追加。 Phase 2 の対象/公開度フィルタ追加が 1 箇所で済むようにする - [ ] **per-account / per-column scope** — 「サブ垢では AI 無効」「DM 系カラムは AI 監視対象外」を アカウント・カラム単位で設定可能に (Phase 1 は global のみ、後続フェーズで拡張) **Step 3: Tool Calling — Capability Registry as Single Source of Truth** - [x] **`Command` interface の `Capability` 進化** — `signature` (型シグネチャ) と `permissions` (必要な権限) と `requiresConfirmation` を追加。 コマンドパレット / HTTP API / CLI / AiScript / AI Tool calling の 5 経路が **全て同じ registry を共有**する。AiScript からは `Nd:call(id, params)` で builtin capability (notes / column / account / memos / theme / skills / widgets / plugins / ai / meta / clipboard / drive / http / time / user / notifications / tasks / ui / misstore / logs) を呼び、`Nd:register_command(..., options)` で新規 capability を登録すると即 5 経路に公開される。 (`src/capabilities/types.ts` 型定義 + `dispatchCapability` 経由) - [x] **AI tool schema の自動変換** — `capability.signature` から Anthropic `tools[]` / OpenAI `functions` block を自動生成。新コマンド追加が 自動で AI ツール候補になる (UI 発見性と AI 拡張性が同じ仕組みで進化) (`src/capabilities/toolSchema.ts` + テスト) - [x] **credential proxy 実行モデル** — AI に Misskey トークン等を渡さない。 AI は capability 呼び出しを要求するだけで、実 API は NoteDeck が credentials 付きで代理実行する (`dispatchCapability` で credentials 制御) - [ ] **細粒度 allow/deny** — Phase 1 のプリセット/Custom の上位として、 Claude Code 同様の `permissions.allow[] / deny[]` リスト形式を導入。 ワイルドカード (`notes.*`)、パラメータ条件 (`notes.post(visibility:public)`) 対応 **Step 4: リアクティブ AI** (#411) - [x] **HEARTBEAT 機能** — [OpenClaw の HEARTBEAT](https://docs.openclaw.ai/gateway/heartbeat) 相当。 AI agent が定期的に自律起動し、ユーザー定義のチェックリストを確認 → 重要な発見があれば 会話としてカラム表示・デスクトップ通知 (#114)、何もなければ `HEARTBEAT_OK` で静かに終了。 「リアクティブな AI チャット」から「常駐型アシスタント」へ (`useHeartbeatDaemon` + `src-tauri/commands/heartbeat.rs` + `nd:ai-heartbeat-tick`) - [x] **Cheap Check First** — AI を呼ぶ前にローカルで低コスト判定 (未読数・新着件数等)。 閾値以下なら即 `HEARTBEAT_OK` で終了 (=トークン消費爆発の防止) (commit `ee7e5c66` "HEARTBEAT Cheap Check First + daily 安全装置") - [x] **HEARTBEAT 専用 deny リスト** — 通常時は許可している `notes.write` 等を HEARTBEAT 中だけ deny にできる。AI が定期実行で暴走してスパム投稿するリスクを封じる (`ai.json5` の `heartbeat.permissions` + `resolvePermissions`) - [x] **プリセット中心 UI + skills/ raw 編集の二段** — 「未読メンション要約」 「重要投稿ピックアップ」「日次サマリ」等のプリセットで設定し、 上級者は `skills/heartbeat.md` で自由編集 (skill-edit window + `SkillEditContent` + frontmatter `mode: heartbeat`) **Step 4.5: AI Persona & Persistent Memory** (#491 / #494) - [x] **AI Persona** — `skill.isPersona` フラグ + `Identity` 構造 + `` block (system prompt 末尾に注入)。session が persona skill を持つと AI は 「あなたは として振る舞え」「`memos.create` 呼び出し時は `authorId=''`」と指示される。persona indicator UI (settings selector / session snapshot / chat header) + theme accent 色のアバター - [x] **永続記憶 (memos)** — 専用 MEMORY/SOUL 機構は作らず、既存プリミティブで再現: メモ + `dataSources.memos` + `memos.write` permission + `notedeck-memo` always skill の 4 要素 (PR #489)。`memos.{create,update,delete,list,search,backlinks}` capability、 `memo:` markdown link の link expand + `referencedBy` 添付、 author 埋め込みブロック (#493) で persona との紐付けが完成 - [x] **`notedeck-memo` (永続記憶の運用手順) / `self-profile` (`skills.replaceSection` で AI が自分のプロフィールを継続更新する自己編集デモ)** — 当初は同梱して初回起動時に seed していたが、修正版を届けられないため MisStore 配布へ移した (#746) **Step 4.6: 自己拡張する IDE** (Hackable layer) - [x] **skill / theme / widget / plugin 編集 capability** — AI が IDE 自身を 編集できる: `skills.{append,replaceSection,toggle}` / `theme.{create,update,apply}` / `widgets.{create,update,delete,setAutoRun}` / `plugins.{create,update,setActive,delete}`。 全カテゴリで編集履歴 + revert (`*.history` / `*.revert`) 完備。 write 系は `aiTool:false` ガードで AI から見えず、明示的に有効化が必要。 `theme.create` は `installedFor` で per-account 紐付け、`theme.apply` は manualMode 自動切替 - [x] **プラグインから本体 AI を呼ぶ `ai.chat` capability** + AI Actions プラグイン - [x] **AI 自己内省** — `meta.permissions` / `meta.activeSkills` / `meta.persona` / `meta.config` / `ai.sessions.{list,read,search}` / `logs.recent` で AI が自分の権限・skill・過去セッション・ログを参照できる - [x] **MisStore 検索 capability** (`misstore.search`) で AI がストアから プラグイン/テーマを発見できる **AI × Layer 3 連携:** - [ ] **自然言語検索** (#106) — 「先月の技術的な議論」等の曖昧なクエリを AI が FTS5 クエリに変換。 `search.fts5(query)` capability に AI が自然言語からパラメータを生成する形 **AI × AiScript:** - [x] **自然言語 → AiScript** (#107) — 「○○するプラグイン作って」で AI がコード生成・インストールまで完結 - [x] **プラグイン生成 API** (#108) — AI がプラグインコードを生成し、HTTP API 経由でインストール。 `Nd:register_command` で動的に新 capability が登録され、**次の会話ターンで AI tool として自動公開**される (= AI が自分のツールセットを成長させる) **Step 5: ローカル AI モード** — privacy-first - [ ] **Ollama / llama.cpp 統合** — ローカル LLM 経路を選んだとき、外部送信が無いため データソース制限を自動で緩める。HEARTBEAT (Step 4) と相性抜群 (定期送信のコスト・プライバシー懸念が消える) - [ ] **F-Droid 版との統合** — オフラインモード親和、外部 AI 不使用での完全動作 ### Nd:* API の最小化 — capability registry 統合で大半を吸収 > 当初は `Nd:db.query` / `Nd:db.stats` / `Nd:http` / `Nd:on` / `Nd:storage` / > `Nd:ui.notify` / `Nd:accounts` の 7 API を個別実装する計画だったが、 > `Nd:call` で capability registry に接続する方式に変更。Nd:* の表面積は > 7 シンボル (`NOTEDECK` / `Nd:version` / `Nd:register_command` / `Nd:call` / > `Nd:capabilities` / `Nd:on` / `Nd:http`) に絞り、できることは capability > 増殖で広げる。 - [x] **`Nd:http`** (#111) — 外部 HTTP/HTTPS API (CORS なし)。Rust 側で SSRF 防御込み。実体は `http.fetch` capability で AI / コマンドパレット / HTTP API / CLI / AiScript の全経路から呼べる - [x] **`Nd:on`** (#112) — Phase 1 で `account:switch` / `column:added` / `column:removed` / `streaming:status` の 4 種、Phase 2 で `note:new` / `notification:new` を追加。後者は queryDelta を queryId → {flavor, accountId} にマップして fan-out。payload には id のみを載せ note 本体は載せない (本体取得は `Nd:call('notes.show', { id })` 経由で permissions が enforce) - [x] **`ui.notify`** capability (#114) — `Nd:call('ui.notify', { title, body })` で OS のデスクトップ通知。Nd:* に別 API は生やさず capability に統合 - 個別 API 不要: `Nd:db.query` (#109) → `Nd:call('notes.search', ...)` / `Nd:db.stats` (#110) → 必要時に `notes.stats` capability 新設 / `Nd:accounts` (#115) → `Nd:call('account.list')` / `Nd:storage` (#113) → 既存 `Mk:save` / `Mk:load` (plugin 単位の名前空間 分離は `storagePrefix: plugin:${installId}` で済んでいる) ### 未完了: プラグイン開発支援 — v1.0.0 以降 > プラグイン作者を増やすための開発体験の整備。 > 配布は MisStore が担っているので、残るのは「書き始めやすさ」の整備。 > Nd:* API 拡充と合わせて v1.0.0 以降に着手する。 - [ ] **テンプレートギャラリー** — 「TL フィルター」「外部 API 連携」「カスタムカラム」 「通知カスタマイズ」等のユースケース別テンプレートから開発を開始できる - [ ] **プラグイン開発ドキュメント** — Nd:* API リファレンス、チュートリアル、サンプル集 - [ ] **ホットリロード** — AiScript エディタカラムで編集 → 即座にプラグインに反映。 保存するたびにアプリ再起動不要 ### 未完了: 多言語対応(i18n)— v1.0.0 以降 > UI の安定化を優先し、i18n は v1.0.0 以降に着手する。 > 翻訳キーの挿入は UI が固まってから一括で行う方が効率的。 - [ ] **多言語対応** — UI テキストの i18n 化。vue-i18n 等の導入、翻訳キーの抽出・管理 --- ## 実装優先度まとめ | 優先度 | やること | Layer | 理由 | |--------|----------|-------|------| | ~~**今すぐ**~~ | ~~情報整理 CRUD(クリップ・リスト・アンテナ)~~ | ~~L2~~ | ✅ 実装済み | | ~~**今すぐ**~~ | ~~Markdown エクスポート + Obsidian 連携~~ | ~~L3~~ | v1.0.0 以降に延期(PKM 連携) | | ~~**次**~~ | ~~Nd:* API 拡充、プラグインテンプレート~~ | ~~L4~~ | v1.0.0 以降に延期 | | ~~**基盤強化**~~ | ~~オフライン動作 + バックアップ~~ | ~~L1~~ | ✅ 実装済み | | ~~**AI 基盤**~~ | ~~HEARTBEAT / AI permissions / Tool schema 自動変換 / credential proxy~~ | ~~L4~~ | ✅ 実装済み (#411 / #408 Phase 1) | | ~~**プラグイン基盤**~~ | ~~Nd:* API 再設計 (capability registry 統合)~~ | ~~L4~~ | ✅ 実装済み (Nd:call / Nd:capabilities / Nd:on / Nd:http) | | ~~**v1.0.0 機能条件**~~ | ~~L0〜L2 残タスク + L3 検索安定化~~ | ~~L0-L3~~ | ✅ 達成済み (ハッシュタグトレンド = AiScript ウィジット / クロスサーバースレッドウィーバー = #323 / 添付一括 DL・言語タグは不要化) | | **v1.0.0** | デスクトップ版 (macOS / Linux / Windows) の残バグ修正 + リリース CI 安定化 | — | Android 版は **オプション β** として v1.0.0 のリリース条件外 | | **v1.0.0 以降** | PKM 連携 / AI 統合の残 (per-account scope / 細粒度 allow-deny / 自然言語検索 / 自然言語→AiScript / Ollama) / プラグイン開発支援 / i18n / サーバー移転支援 / Android 通知・UX 改善 / 添付一括 DL (#92) | L3-L4 | 基盤安定後に着手 | | **v1.0.0 以降** | OS 統合 (通知アクション #924 / 共有シート #926 / 集中モード #928 / ドラッグアウト #929 / トレイ操作 #930 / 電源適応 #931 / 従量制回線 #935 / 送信前トランスコード #927) | L2 | ブラウザから到達できない領域。既存のトレイ・OS 通知・投稿の各基盤の上に載る | | **v1.0.0 以降** | 蓄積データの活用 (セマンティック検索 #932 / ランチャからの検索は #511 に統合) | L3-L4 | 永久キャッシュの使い道が検索しかない状態への回答 (#923) | --- ## 技術的残課題 アーキテクチャ上の改善点。詳細な分析は [ARCHITECTURE.md — 責務境界の課題と改善方針](ARCHITECTURE.md#責務境界の課題と改善方針) を参照。 ### 責務境界の強化 [重要度: 中] 実益のある改善のみ採用。詳細な分析は [ARCHITECTURE.md — 責務境界の課題と改善方針](ARCHITECTURE.md#責務境界の課題と改善方針) を参照。 **実施済み:** - [x] useNoteColumn.ts の merge パターン重複を `mergeOrEnqueue` ヘルパーに統合 - [x] adapter を Misskey 系フォーク単位(yamisskey, misskey-tempura 等)の capability ベースに再定義 **実施済み(追加):** - [x] invoke の型付き契約 — specta/tauri-specta で Rust → TypeScript 型自動生成。 `src/bindings.ts` に型定義を出力、`commands` オブジェクト経由で型付き呼び出しを実現 **やらない(コストに見合わない):** - 起動処理の bootstrap orchestrator(起動コードの変更頻度が低い) - deckProfile.ts の CQRS 分離(線形フローを分割するメリットなし) - plugin sandbox(第三者プラグイン不在の段階では YAGNI) - domain service 層の新設(adapter 層が既にその役割) ### テスト未カバー領域 notecli に DB テスト 18件、notedeck に 239件のユニットテストを追加済み。 **未カバー**: `normalize()` モデル変換、HTTP API 統合テスト、`StreamingManager` 接続管理 ### 部分採用で完成していない機能 #### カスタム URI スキーム(`notedeck://`) `deck.ts` で URI を生成しタイトルバーに表示するのみ。OS レベルのディープリンク登録はなし。非公式クライアントとして Misskey URL を横取りするのは不自然なため、外部ツール連携用途に限定して検討。 #### Android バックグラウンド通知 [重要度: 中] `NotificationWorker.kt` で15分間隔のポーリングを実装済み。WebSocket ベースのフォアグラウンドサービスによるリアルタイム通知は未実装(最大15分の遅延)。 #### AiScript プラグインシステム [重要度: 低] Web Worker で LSP 搭載のコードエディタ、プラグイン実行エンジン、UI レンダラー、MisStore からの配布・インストールまで実装済み。残るのはプラグイン作者向けの開発体験(テンプレート・ドキュメント・ホットリロード)。 --- ## 将来の検討材料 優先度は低いが、技術的に興味深い方向性。 | 項目 | メモ | |---|---| | ソーシャルアナリティクス | L1 蓄積データ + L4 AI で投稿傾向分析・TL 活性度グラフ。需要次第 | | Digital Wellbeing | Misskey 依存を防ぐ使用時間の可視化・利用制限・休憩リマインダー。ソーシャルアナリティクスの延長として、自分のセッション時間・投稿頻度・リアクション頻度を本人向けに可視化し、閾値超過で「そろそろインターネットやめろ」通知。L1 蓄積データ(自分の投稿・リアクションのタイムスタンプ)と Boss Key / オフラインモード基盤を再利用可能(#359) | | ffmpeg 連携 | 動画トランスコード。サーバーのサイズ制限に合わせて圧縮 | | アプリロック(モバイル限定) | Android / iOS の指紋認証・PIN 連携。デスクトップは OS ロック画面で代替可能なため対象外 | | 多言語対応(i18n) | 海外ユーザー需要が明確になってから |