Instant New note — 番号を待たずに書き始める
New note(Mod+N、ネイティブメニュー、Command Palette、Note Tray toolbar の「+」)は、ネットワークを一切待たずにタブを追加・選択し、書き込めるエディタにフォーカスを渡します。番号(inbox/ の N)はあとから割り当てられ、同じタブがその場で番号に変わります。採用デザインは「A — Indicator only」です。
このページは、その契約と、変更するときに守るべき不変条件をまとめたものです。番号の割り当て自体の安全性(サーバープローブ、tombstone の回避)は Cross-device divergence と #7507 の文脈にあります。
なぜ待たせないのか
createFreshNumberedNote は、候補番号ごとにサーバーの head をプローブします(1 回あたり FRESH_NOTE_PROBE_TIMEOUT_MS = 4,000 ms)。この安全策は削らずに、待ち時間だけをユーザーの操作から外すのがこの設計です。プローブ、タイムアウト、create-only の createAt はそのまま残し、max + 1 を事前に計算して番号として扱うこともしません。
3 つの事実を分けて持つ
| 事実 | どこにあるか | 表示 |
|---|---|---|
| ローカルに保存できたか | pending note の封印済みジャーナル(journaledRevision)と、番号付きパスへの通常の書き込み(writtenRevision) | 失敗すると local-error(「!」の三角) |
| 番号が割り当てられたか | PendingNote.number | 未割り当ての間はスピナーだけのタブ |
| リモートが受け取ったか | 既存の syncMetadata.pendingItems(パスごと) | 受け取るまで番号の横に小さなマーク。完了で消える |
成功のバッジやトーストは出しません。最後の状態は「番号だけ」です。作成したパスがアップロード待ちの一覧に見当たらないだけでは「完了」とみなしません。一度その一覧に載ったあとで消えたか、作成後にサーバーの受領(lastCloudAcknowledgmentAt)があったときに限り、マークを消します。
構成
tauri-— pending note のレジストリとジャーナル。モジュールスコープで、どのペインにも属しません。ビューを閉じても note は破棄されません。app/ renderer/ services/ pending- new- notes. ts tauri-—app/ renderer/ services/ create- fresh- numbered- note. ts onCandidate/resumeCandidate/ensureFloorを追加。onCandidateを渡すと常に create-only の経路を通ります。tauri-— Note Tray の 1 リーフと registry の結線。選択、エディタの識別子、番号付き store への受け渡し(hand-off)、タブの状態を持ちます。app/ renderer/ hooks/ use- pending- note- tray. ts tauri-— 上記を使うオーバーレイ。pending note の選択中は、エディタの内容を registry から読み、編集を registry に書きます。番号付きの共有バッファには触れません。app/ renderer/ view- providers/ inbox- provider. tsx tauri-— write-page が inbox リーフの New note をプロバイダへ回すためのイベント。これで全エントリポイントが同じ経路を通ります。app/ renderer/ data/ instant- new- note. ts packages/とui- components/ src/ note- tab- status. tsx draft-bar.tsx— スピナー(Spinnerのscaled)と状態グリフ。--display-scaleに追従し、prefers-reduced-motionで回転を止めます。
流れ
beginPendingNote()は同期的にレコードを作り、一覧に載せて返します。ここまでに await はありません。cloud ワークスペースが armed でなければnullを返し、呼び出し側は従来の経路を使います。割り当ては「workspace + directory」ごとに直列化されます。作成順がそのまま番号順になります。編集は直列化しません。
割り当ての前にレコードを封印し、各候補番号は
createAtの 前 にジャーナルへ書きます。番号が付くと
subscribePendingNoteAllocationsで store に通知します(draftCountを同期的に引き上げ)。続いて最新の本文を番号付きパスへ通常の書き込みで送ります。書き込み中の編集は最大 3 回まで追いかけます。リーフがその note を表示している場合は hand-off します。
flushPendingNoteForHandOffで最新を書き、handleDraftChange(n)で store を切り替え、まだ差分があればsetContentForPaneで渡し、completePendingHandOffで registry の書き込みを止めます。エディタの識別子(負の合成値)は hand-off をまたいで同じなので、CodeMirror は作り直されず、選択・スクロール・undo・IME は保たれます。束縛のないレコードは、最新の本文が番号付きパスに書けた時点で registry 側で完了します。
再起動と再試行
ジャーナルは note ごとに 1 エントリ、アカウントとワークスペースで鍵付けし、ワークスペース鍵で暗号化して localStorage に置きます(removed-notes と同じ前例)。テストと dev:mock ではメモリ版を使います。平文の production localStorage、ローカルのワークスペースミラー、2 つ目のアップロードエンジンは作りません。
サインアウトで消去し、暗号化の再 arm(ロック、鍵の変更、ワークスペース切り替え)で揮発状態を捨てます。封印済みエントリはそのワークスペースが正しく再 arm されたときに戻ります。
ウィンドウ(ポップアウト、ブラウザのタブ)は localStorage を共有するので、動いているレコードはそれを動かしているウィンドウがリースします(id と時刻だけの平文キー、5 秒ごとに更新、150 秒で失効。ウィンドウを閉じると即座に手放し、バックグラウンドから戻ったときにも更新)。復元は、別の生きたウィンドウがリースしているレコードを飛ばし、残りをリースしたうえで少し待って取り直し、自分のリースが残ったものだけを再開します。同時に復元した 2 つのウィンドウが同じ操作を動かすことはありません。
未割り当てのレコードは同じ操作 id と同じ候補番号で再開します。再開する候補番号もサーバープローブを通します。候補番号がすでに存在していても 採用しません(別の書き手かもしれないため)。次の番号へ進み、不確かな番号には触れません。
ジャーナルは、番号付きパスに届いたリビジョン(
writtenRevision)も記録します。再起動時、最新のリビジョンが届いていればそのまま完了します(スロットにあるのはそれ以降の編集です)。届いていなければ、スロットが作成直後の空本文なら書き込み、同じ本文なら完了します。別の本文があるときは順序を判断できないので上書きせず、keptとして本文を保ちます。スロットが無いときも同じです。keptのレコードは書き込みも hand-off もせず、コピーと書き出しのあとで「Dismiss kept text」で手放せます。再試行は同じレコードと同じ操作で行い、2 つ目の作成は始めません。
既知の残り
createAtが成功してから番号が封印されるまでの間にプロセスが落ちると、空の note が 1 つ余分に残る可能性があります。採用も削除もしないので、本文が失われることはありません。別のウィンドウのリースを理由に飛ばしたレコードは、そのウィンドウが落ちたあと、次の復元(再起動か再 arm)まで再開されません。本文は封印済みのジャーナルに残っています。
ガード
番号が付くまでは、番号やパスに依存する操作(アーカイブ、移動、公開、削除、並べ替え、整理)を止め、PENDING_NOTE_ACTION_REFUSAL の説明を返します。並べ替えと整理は store 側でも assertNoPendingNotesIn で拒否します。編集、切り替え、再試行、コピー、テキストの書き出しはいつでも行えます。ワークスペース境界では pending-new-notes が editor-buffer フェーズの flush として登録され、未封印の本文を封印し、番号付きの本文を書き出します。
対象外
時系列・格子レイアウトと single-document モードは従来の経路のままです(text-editor レイアウトのみ)。
読み取り専用のフレームは従来の経路のままです。
macOS ネイティブ(WebKit)での実機確認は、リポジトリの方針どおり T3 /
pnpm examに委ねています。