diff --git a/PLAYGROUND-PLAN.md b/PLAYGROUND-PLAN.md new file mode 100644 index 00000000..4718d6d9 --- /dev/null +++ b/PLAYGROUND-PLAN.md @@ -0,0 +1,137 @@ +# Playground File Workspace Plan + +最終更新: 2026-05-14 + +## 目的 + +- `playground/public/template-assets` と同じような template collection directory を、ブラウザ内 + localStorage ではなく、ユーザーが選んだローカルディレクトリ上の実ファイル群として扱う。 +- Designer / FormViewer と、同じディレクトリを編集する AI / editor / CLI が + `/template.json` を介して協調できるようにする。 +- Playground は「編集 UI」でありつつ、保存先は mounted collection 内で選択した template の + `template.json` にする。 + +## 既存構造メモ + +- `playground/public/template-assets` は root 直下に `/template.json` が並ぶ collection。 +- 既存 gallery は `playground/src/routes/Templates.tsx` で sample templates と local projects を表示する。 +- Local project workspace の正体は `playground/src/lib/playgroundProjects.ts` の `PlaygroundProject[]`。 + `localStorage` の `playground:projects:v1` に保存される。 +- 現在の import は `Import Template JSON` のみ。1 つの `Template` を local project として保存する。 +- 現在の export は `Template JSON` download のみ。inputs、source、title、kind は export されない。 + +## Browser API 前提 + +- `showDirectoryPicker({ mode: "readwrite" })` で template collection root directory を選ぶ。 +- root 直下の child directories から `/template.json` を探し、`checkTemplate` に通るものを + mounted templates として扱う。 +- File System Access API は secure context と user gesture が必要。`localhost` と + `https://playground.pdfme.com` は前提にできる。 +- 非対応ブラウザでは従来の import/download fallback のままにする。 +- `FileSystemFileHandle.createWritable()` で selected template JSON file を上書き保存できる。 +- File / directory handle は IndexedDB に保存できる。次回アクセス時の復元候補に使う。 +- `FileSystemObserver` は experimental / non-standard。使える場合だけ使い、MVP は polling fallback を標準経路にする。 +- 現在の TypeScript DOM lib には `showDirectoryPicker` と `FileSystemObserver` の型がないため、 + playground 側に小さな ambient type を追加する。 + +参考: + +- https://developer.chrome.com/docs/capabilities/web-apis/file-system-access +- https://wicg.github.io/file-system-access/ +- https://developer.mozilla.org/en-US/docs/Web/API/FileSystemObserver/observe + +## 確定要件 + +- 対象は `playground/public/template-assets//template.json` と同じ directory collection 形式。 +- root 直下の child directories を走査し、各 `/template.json` を `checkTemplate` などで + validation して pdfme `Template` として認識できたものを mounted templates にする。 +- valid template directory が 1 つもない場合は、blank template を + `untitled-template/template.json`, `untitled-template-2/template.json` のように採番して新規作成する。 +- 保存時は `JSON.stringify(template, null, 2)` の pretty JSON で、現在開いている + `/template.json` に上書き保存する。 +- 保存時に browser 側で thumbnail を再生成し、対応する `/thumbnail.png` へ上書きする。 +- Designer に未保存変更がある状態で外部変更を検知した場合は conflict dialog を出し、ユーザーに選択させる。 +- FormViewer で template が変わった時は、同名 field の入力値を保持する。 +- workspace file は template JSON だけを同期対象にする。`inputs.json` は同期しない。 +- JSX / md2pdf の `source.tsx` / `source.md` は初期実装では対象外。 +- `base.pdf` や画像などの相対 path asset は読まない。現状どおり data URL / URL / base64 保存前提。 +- 前回開いた workspace は IndexedDB に handle を保存し、次回アクセス時に復元候補として出す。 + +## MVP 方針 + +- `Open Folder` / `Open Directory` を My Workspace 付近に追加する。 +- Directory workspace は「collection root directory handle + template entries」と定義する。 +- Template entry は `/template.json` file handle と `/thumbnail.png` file handle から作る。 +- Open 時は root 直下の child directories を scan し、`template.json` の JSON parse と `checkTemplate` + を通った directories を collection entries にする。 +- valid template directory がない場合は `getBlankTemplate()` を新規 directory に pretty JSON で作成し、その entry を開く。 +- Open 成功時は mounted collection を Templates 画面の workspace section に表示し、選択した template を + Designer または FormViewer に読み込む。 +- `metadata.json` は初期実装では考慮しない。将来、root `metadata.json` と per-template `metadata.json` を + 読み取り、title / description / tags / order に反映する余地を残す。 +- `index.json`, `manifest.json`, `manifests/.json` は mounted collection では read-only/generated-ish + metadata として扱い、初期実装では更新しない。 +- Designer の save は file workspace active 時に prompt なしで selected template JSON file へ上書き保存する。 + localStorage project 保存は fallback / 別 mode として残す。 +- FormViewer は selected template JSON file の変更を検知して `updateTemplate()` する。 + inputs は `getInputFromTemplate(template)` で補完しつつ、既存入力を schema name ベースで保持する。 +- Collection root の directory changes も polling / observer で検知し、template directory の追加・削除・rename + を Templates 画面に反映する。 + +## 同期・競合 + +- clean state で外部変更を検知した場合は自動 reload する。 +- Designer に未保存変更がある状態で外部変更を検知した場合は、`Reload from disk` / `Keep editing` / + `Save over disk` を選べる conflict dialog を出す。 +- 保存時に disk version が最後に読んだ version から進んでいた場合も同じ conflict として扱う。 +- selected template JSON file が invalid JSON / invalid Template になった場合、現在の UI は維持しつつ + error toast を出す。AI が書きかけの瞬間を polling で拾う可能性があるため、次の valid change で + 自動復帰できるようにする。 +- Form 入力中に selected template JSON file が変わった場合、同名 field の入力値は保持し、新規 field は + `getInputFromTemplate` の default を入れ、消えた field は捨てる。 + +## 実装候補 + +- `playground/src/lib/fileWorkspace.ts` + - `isFileWorkspaceSupported()` + - `openTemplateCollectionDirectory()` + - `scanTemplateCollection()` + - `readTemplateEntry(entry)` + - `writeTemplateEntry(entry, template)` + - `writeTemplateThumbnail(entry, template, inputs?)` + - `createBlankTemplateEntry(collection, title?)` + - `subscribeTemplateCollectionChanges(listener)` + - IndexedDB に active collection root handle と selected template id/path を保存・復元する helper +- `playground/src/lib/templateInputs.ts` + - template reload 時に既存 inputs を schema name ベースで reconcile する helper。 +- `playground/src/vite-env.d.ts` または `playground/src/lib/fileSystemAccess.d.ts` + - `Window.showDirectoryPicker` + - `FileSystemHandle.queryPermission/requestPermission` + - `FileSystemObserver` の最小 ambient type。 +- `playground/src/routes/Templates.tsx` + - My Workspace toolbar に `Open Folder` を追加する。 + - Open 成功後に mounted collection templates を project card と同じ gallery UI で表示する。 + - mounted collection の card は Designer / FormViewer を開くときに selected template entry を active にする。 +- `playground/src/routes/Designer.tsx` + - load request に active mounted template entry を追加する。 + - `onSaveTemplate` で file workspace active 時は selected template JSON file と thumbnail へ write。 + - 外部変更購読で `designer.current.updateTemplate(template)`。 + - dirty tracking と conflict UI。 +- `playground/src/routes/FormAndViewer.tsx` + - active mounted template entry を読み込み対象に追加する。 + - 外部変更購読で `ui.current.updateTemplate(template)` と inputs reconcile。 +- Tests + - file workspace helper は fake file handles で unit test する。 + - UI E2E は native picker を mock し、open, save overwrite, thumbnail write, external change polling, + invalid JSON recovery, conflict branch を確認する。 + +## 未確定要件 + +- 保存時の thumbnail 再生成で使う inputs。Designer は `getInputFromTemplate(template)` でよいか、 + 直近 FormViewer inputs があれば使うか。 +- Designer の `Save Project` 文言は file workspace active 時に `Save JSON` / `Save ` に変えるか。 +- `Save As` は localStorage project 複製のまま残すか、別 directory entry 作成にするか。 +- FormViewer での `Save` は inputs 保存のままか、file workspace mode では非表示にするか。 +- polling interval。AI との並走なら 1-2 秒が体感よさそう。 +- IndexedDB に保存した workspace handle の復元 UX。起動時に自動復元を試すか、`Reopen last folder` を出すか。 +- 対応ブラウザを Chromium 系に寄せるか。Safari / Firefox では fallback のままでよいか。