# feat: リロードしても右ペインで開いていたファイルに戻る (#958)

## 問題

ペインでファイルを開いた状態でリロードすると、開いていたファイルと展開していたツリーが失われ、
ツリーのトップに戻る。ペインが開いているかと幅は localStorage に残るのに、中身だけ消える。

## 実装上の本題: uid はリロードを跨がない

`paneStateByUid` のキーは**セルの uid**。これはリロード後に同じ値にならないので、この Map を
そのまま localStorage に移しても引き当てられない。

一方 uid をやめるわけにもいかない。**同じリポジトリのセルが 2 つある**のは普通で、
セッション中は「セルごとに別の木を覚えている」のが正しい（それがこの Map の存在理由）。

### 決定: 2 層にする（uid はそのまま、cwd 層を足す）

| 層 | キー | 置き場所 | 役割 |
|---|---|---|---|
| 既存（変更なし） | セル uid | メモリ `Map` | セッション中。ズームをセル間で歩いても各セルの木を覚える |
| **追加** | **cwd** | **localStorage** | リロードを跨ぐ。メモリ層に無いときだけ読む |

読む順は **メモリ → localStorage → null**。

### 訂正: 「メモリ層が常に勝つからセッション中は無変更」は誤りだった

最初この 2 層だけで実装したところ、**既存テストが落ちた** —
`TerminalGrid.spec` の「re-roots and re-binds between two cells sharing a directory」。

メモリ層が「勝つ」のは**エントリがあるとき**だけ。同じリポジトリの 2 セル目は
セッション中でもメモリ層が空なので、素通しで cwd 層に落ちて **1 セル目のファイルを継承**して
しまう。既存の挙動（2 セル目は自分の空の木から始まる）を壊していた。

**cwd 層は 1 セッションにつき 1 回だけ配る**（`claimedCwds` の Set）。保存されているのは
「リロード前に画面にあった 1 枚のペイン」であって、そのディレクトリの既定値ではない。
リロード後に最初に尋ねたセル（＝ズームが復元されたセル、つまり元々開いていたセル）が受け取り、
以降はメモリ層のみ。これで既存の挙動は本当に無変更になる。

## もう一つの落とし穴: リロード時には誰も snapshot していない

`rememberPaneState()` が呼ばれるのは「ペインを閉じたとき」と「再ルートしたとき」だけ。
**ページを閉じる/リロードする経路では呼ばれない**ので、これを足さないと「最後に閉じた時点の
状態」しか残らず、目的を果たさない。`pagehide` で snapshot する。

（`FilesPane` 側は未保存バッファのために `onPageHide` を既に持っているが、あれは**バッファの
書き出し**であって木の状態ではない。別物。）

## 決めたこと

| 論点 | 決定 |
|---|---|
| キー | cwd（上記 2 層） |
| 復元する範囲 | 開いていたファイル **と** 展開していたツリー。`FilesPaneState` が既に両方持ち、`restore()` が両方戻す |
| 覚える件数 | **最新 20 ディレクトリ**の LRU。無制限だと localStorage が太る |
| 1 件あたりの展開パス数 | **200 まで**。1 件が肥大して quota を割ると*全部*保存できなくなる |
| 消えたファイル | 何もしない。`restore()` が「見つからないものは黙って飛ばす」設計 |
| サーバ再起動 | リロードと同じ（localStorage はブラウザ側なので区別しない） |

## 実装

1. `src/components/filesPaneStore.ts`（新規・純粋）— localStorage には触らない。
   パース（壊れた JSON / 想定外の形に耐える）、LRU 挿入、取り出し、上限適用。
2. `TerminalGrid.vue` — 既存の `stored()` / `remember()` 経由で読み書き。
   `rememberPaneState` で cwd 層にも書き、`paneState` を引くときにフォールバックする。
   `pagehide` で snapshot。
