この文書は、NESDOT の開発を人間が継続しやすくするための実務向けマニュアルです。README.md の概要説明、AGENTS.md の機械的ルール、package.json / flake.nix / src-tauri/tauri.conf.json の実装事実を、人間が追いやすい形にまとめています。
機械的な最終ルールは引き続き AGENTS.md が優先です。この文書は、その内容を人間向けの導線として整理したものと考えてください。
NESDOT は、NES / ファミコンの画面制約を意識しながらドット絵を編集するためのアプリです。
- フロントエンドは
React + TypeScript + Vite - UI は
Radix Themes + CSS Modules + static global CSS - 状態管理は
Zustand - 関数型ユーティリティとして
fp-ts - 外部データ境界の検証に
zod - デスクトップアプリ殻に
Tauri - 環境固定とビルド補助に
Nix flake
アプリは大きく 4 つの作業モードを持ちます。
sprite: スプライト編集character: キャラクター編集bg: 背景タイル編集screen: 画面配置
このモード切替は src/application/state/workbenchStore.ts の WorkMode と、src/presentation/App.tsx の画面オーケストレーションが中核です。
初めて触る人は、次の順で読むと全体像をつかみやすいです。
README.mdプロダクト概要、主要モード、開発者向け文書への入口があります。AGENTS.md開発ルールの機械的な source of truth です。package.json日常で使う script はここが正本です。flake.nixdev shell と Nix build の前提を定義しています。src/main.tsxアプリ起動時の provider 構成と static CSS 前提の entrypoint が分かります。src/presentation/App.tsxモード切替、更新ダイアログ、ネイティブメニュー連携、undo/redo の全体制御があります。src/application/state/projectStore.tsプロジェクト全体状態の入り口です。src-tauri/src/lib.rsTauri 側のメニュー、診断、更新関連の実装があります。
このリポジトリの開発コマンドはすべて Nix dev shell 内で実行します。
- 対話シェルに入る:
nix develop - 単発実行:
nix develop -c zsh -lc '<command>'
やってはいけないこと:
- ホスト shell から直接
node,pnpm,cargo,rustc,tauri,vite,tscを叩く - shell 外で失敗したコマンドを、そのまま環境差分つきで再解釈してごまかす
何かが見つからない、バージョンが違う、ネイティブ依存が足りない、という類のエラーは、まず「dev shell の外で実行していないか」を疑ってください。
- Nix
- Git
あると便利なもの:
direnv+nix-direnv- VS Code
- WSL 利用時は
Remote - WSL
nix develop -c zsh -lc 'pnpm install'direnv を使う場合:
direnv allowWSL を使う場合は、Windows 側ではなく WSL 側のファイルシステムで作業してください。
- clone 先は
/mnt/c/...ではなく~/src/...など - GUI を使うなら
WSLgを有効にする - 状態確認は
nix develop -c zsh -lc 'pnpm doctor:wsl'
WSL まわりの詳細はこの節と、必要に応じて pnpm doctor:wsl の出力で確認してください。
nix develop -c zsh -lc 'pnpm dev'- Vite の開発サーバーを起動します
- VS Code の
Launch Frontend (Chrome, 1420)もこの前提です
nix develop -c zsh -lc 'pnpm start'tauri devを通じてデスクトップアプリを起動しますsrc-tauri/tauri.conf.jsonのbeforeDevCommandでフロントエンド開発サーバーも連動します
nix develop -c zsh -lc 'pnpm build'Web 配布物だけでなく、Tauri build 前提のフロントエンドビルド確認にも使います。
nix build- デスクトップ向け:
./result/bin/nesdot - Web 向けのみ:
nix build .#web
pnpm-lock.yaml を更新したら、flake.nix の fetchPnpmDeps hash も確認してください。
nix develop -c zsh -lc 'pnpm nix:check-pnpm-deps-hash'
nix develop -c zsh -lc 'pnpm nix:sync-pnpm-deps-hash'README に貼っている製品スクリーンショットは Playwright で再生成できます。
nix develop -c zsh -lc 'pnpm e2e:install'
nix develop -c zsh -lc 'pnpm docs:readme:screenshots'生成先:
docs/assets/readme/sprite-mode.pngdocs/assets/readme/character-mode.pngdocs/assets/readme/screen-mode.png
この script は一時ディレクトリに静的 preview を build し、内蔵 HTTP server で配信してから README 用の 3 画面を撮影します。
.vscode/extensions.json では次を推奨しています。
- ESLint
- Prettier
- EditorConfig
- Nix IDE
- direnv
- Playwright
- rust-analyzer
- CodeLLDB
- Vitest Explorer
- Remote - WSL
.vscode/tasks.json には、普段使うコマンドが Nix dev shell 前提で登録されています。
代表的なもの:
Run Frontend Dev ServerRun Tauri DevFormat CheckVerifyVerify UIVerify FullVerify RustVerify CIRun Unit TestsRun Console E2ERun Full E2ERelease Desktop Dry RunDeploy Desktop ReleaseRun WSL Doctor
迷ったら、まず VS Code task を使うのが安全です。task 側で Nix dev shell 呼び出しが固定されているため、手元環境差分を踏みにくくなります。
.vscode/launch.json には 3 種類の導線があります。
Launch Frontend (Chrome, 1420)Launch Tauri DesktopLaunch Tauri Desktop + Attach Frontend
Rust にブレークポイントを張る場合は Launch Tauri Desktop を使ってください。これは task Prepare Tauri Rust Debug を前提に、src-tauri/target/debug/nesdot を LLDB で起動します。
src/domain純粋なドメインロジック。NES 制約、投影、CHR 変換、背景パレット、画面制約など。src/application/stateグローバル状態。Zustand ストアと undo 履歴。src/infrastructure/browserブラウザ / Tauri API 境界。import/export、更新確認、canvas、PWA 周辺。src/presentationReact UI。モード別の画面、共通 UI、テーマ。src-tauriTauri 側のネイティブ実装。ウィンドウ、メニュー、updater、CSP 関連診断。e2ePlaywright による E2E。tests/repoリポジトリ方針・workflow・設定・文書整合性のテスト。scriptssecurity / license / CVE / CSP / release まわりの検証スクリプト。docs設計メモ、移行計画、運用文書。
- スプライト編集:
src/presentation/components/spriteMode - キャラクター編集:
src/presentation/components/characterMode - BG 編集:
src/presentation/components/bgMode - 画面配置:
src/presentation/components/screenMode - 共通 UI:
src/presentation/components/common
迷ったときの原則:
- NES 仕様や座標変換をいじるなら
domain - 状態の持ち方をいじるなら
application/state - Tauri や browser API を触るなら
infrastructure - 見た目や操作フローをいじるなら
presentation
src/main.tsx では次を行っています。
- static CSS を読み込む
- Radix Themes の root provider を構成する
Appを mount
このため、CSP や style 配信まわりを触るときは main.tsx と src-tauri/tauri.conf.json を対で確認してください。
src/presentation/App.tsx は単なるルーターではなく、アプリ全体の制御点です。
- 作業モード切替
- グローバル undo / redo
- ネイティブメニューイベントの受け口
- Desktop / PWA 更新通知
- 共通メニューと共通ダイアログの表示
モード追加や、モード共通のショートカット追加はまずここを読みます。
src/application/state/projectStore.ts
- スプライト
- 画面
- NES 状態
- 画面プレビュー用の変換補助
を持つ、アプリ中核のストアです。
注意点:
- IndexedDB 永続化の実装は残っています
- ただし現在の store 定義では
persist(...)が無効化されており、実運用では JSON 保存 / 復元が中心です
src/application/state/workbenchStore.ts
- 現在の編集モード
- 現在のツール
- 選択中スプライト
- 各モードの UI ローカル状態
を持ちます。永続化対象ではなく、「今どんな操作をしているか」を表す状態です。
src/application/state/characterStore.ts
- キャラクターセット一覧
- 選択中キャラクター
- キャラクター JSON への変換 / 復元
を担当します。画面配置やキャラクター編集の橋渡しをするので、キャラクター機能追加時はこの store もほぼ確実に触ります。
代表的な責務:
src/domain/nesNES 制約、レンダリング、パレット、CHR、投影。src/domain/projectプロジェクト型、初期状態、保存フォーマット schema。src/domain/screen画面配置、背景パレット、制約、OAM 同期。src/domain/charactersキャラクターセットと分解ロジック。
UI で無理に座標計算や配列操作を書き始めたら、たいてい責務の置き場所がずれています。まず domain に寄せられないか検討してください。
このリポジトリでは、外から入るデータは runtime で検証する前提です。
実例:
src/infrastructure/browser/useImportImage.tsJSON import / 復元境界のzod検証src/domain/project/projectV2Schema.ts新しいプロジェクト表現の schema 契約
フォーマットを触るときの原則:
unknown/ 生 JSON のまま core ロジックに入れない- schema で parse してから state に入れる
- import/export 仕様を変えるなら schema と test を同時に更新する
src-tauri/src/lib.rs には、Web 側にはないネイティブ責務があります。
- macOS ネイティブメニューの構築
- メニューイベントを WebView へ emit
- runtime diagnostics の記録
- updater 用設定の呼び出し
ネイティブメニュー項目を追加した場合は、lib.rs だけで終わりません。App.tsx 側のイベント購読も見直してください。
このリポジトリでは、最小差分が美徳というより、「必要十分な根本修正を最小スコープで入れる」ことが求められます。
- まず既存コードを読む
- 影響範囲を絞る
- 観測可能な挙動なら先に test を足す
- 実装する
- 変更種別に応じた verify を回す
AGENTS.md から、日常的に特に重要なものだけ抜くと次のとおりです。
anyを使わないasを使わない- non-null assertion (
!) を使わない let/varを使わずconst- 引数や共有状態を mutate しない
- mutating array method を避ける
- truthy / falsy に頼らず明示比較する
- 外部データは
zodで検証する - React では view-local でないロジックを component の外へ出す
useEffectを増やす前に、導出・イベントハンドラ・state 再設計で解けないか考える- UI スタイリングは
docs/static-css-architecture.mdを基準にし、Radix Themes の semantic props、共有静的コンポーネント、CSS Modules、src/assets/global.cssを使い分ける - 新しい
styled(...)や runtime style injection 前提の見た目実装は追加しない
このリポジトリは、コードだけでなく workflow や文書の整合性も tests/repo で見ています。
つまり次の変更は、見た目以上に広く影響します。
README.mdpackage.jsonflake.nix.vscode/tasks.json- release / security / nix 周辺の scripts
「設定だけだから気軽に」という感覚で触ると、policy test に止められます。
対象:
- NES 制約
- CHR 変換
- パレット処理
- 座標計算
- 背景・画面配置ロジック
見る場所:
src/domain/**- それを使う
src/application/state/**またはsrc/presentation/**/logic/**
先にやること:
- 該当モジュールの
*.test.tsを探す - バグ修正なら再現 test を先に足す
対象:
- レイアウト
- ボタンやフォーム
- 表示状態
- モード遷移
- ユーザー操作
見る場所:
src/presentation/components/<mode>/ui/**src/presentation/components/<mode>/logic/**- 必要に応じて
src/assets/global.css
方針:
- 画面構成は shared component と CSS Modules で表す
- レイアウトや見た目が繰り返されたら shared UI へ抽出する
- ロジックを component 本体に埋め込みすぎない
対象:
- JSON save / restore
- character JSON
- PNG / SVG / CHR export
見る場所:
src/infrastructure/browser/useExportImage.tssrc/infrastructure/browser/useImportImage.tssrc/domain/project/projectV2Schema.tssrc/application/state/characterStore.ts
注意点:
- 外部データ境界なので
zodの更新が必要になりやすい - export だけ変えて import を忘れる、またはその逆をやりがち
verify:securityの対象にも関わる
対象:
- ネイティブメニュー
- updater
- CSP 診断
- バンドル設定
- Rust command / emit
見る場所:
src-tauri/src/lib.rssrc-tauri/tauri.conf.jsonsrc-tauri/Cargo.toml- 必要に応じて
scripts/verify-tauri-csp*.mjs
注意点:
- Web 側イベント購読との対応を確認する
pnpm verify:rustを必ず回す- macOS で CSP や起動経路に影響するなら
pnpm verify:tauri:cspまで見る
対象:
package.jsonpnpm-lock.yamlflake.nix- lint / tsconfig / playwright / vite 設定
追加で意識すること:
pnpm installは dev shell 内でのみ実行するpnpm-lock.yaml変更時はpnpm nix:check-pnpm-deps-hash- 必要なら
pnpm nix:sync-pnpm-deps-hash - 依存の build script を許可するなら
pnpm approve-builds - license / CVE / system library 検証の影響が出やすい
通常の TypeScript コード変更なら、まずこれを基準にします。
nix develop -c zsh -lc 'pnpm verify'中身:
pnpm format:checkpnpm lintpnpm typecheck:safetypnpm verify:securitypnpm test
| 変更種別 | 最低限の目安 |
|---|---|
| 通常コード | pnpm verify |
| UI 変更 | pnpm verify:ui |
| UI の表示や操作フロー変更 | pnpm verify:full |
| Rust / Tauri 変更 | pnpm verify:rust に加え、必要なら pnpm verify / pnpm verify:ui |
| macOS の Tauri CSP / 起動経路変更 | pnpm verify:tauri:csp |
| 依存更新 | pnpm verify:licenses、pnpm verify:cve、必要に応じて pnpm verify:ci |
| CI 相当を一括確認 | pnpm verify:ci |
nix develop -c zsh -lc 'pnpm format:check'
nix develop -c zsh -lc 'pnpm lint'
nix develop -c zsh -lc 'pnpm typecheck:safety'
nix develop -c zsh -lc 'pnpm verify:security'
nix develop -c zsh -lc 'pnpm test'
nix develop -c zsh -lc 'pnpm test:e2e:console'
nix develop -c zsh -lc 'pnpm test:e2e'
nix develop -c zsh -lc 'pnpm verify:rust'
nix develop -c zsh -lc 'pnpm verify:licenses'
nix develop -c zsh -lc 'pnpm verify:cve'verify:security は単なる依存監査ではありません。次のような境界を機械的に見ています。
- 依存サプライチェーン設定
- workflow の
--frozen-lockfile - Tauri updater 設定
- JSON import / restore 境界
保存形式や updater や CI を触ったのに verify:security を省略すると、かなり危険です。
nix develop -c zsh -lc 'pnpm verify:tauri:csp'この検証は macOS 専用です。tauri dev の devUrl 経路は使わず、pnpm tauri build --debug --no-bundle で生成した src-tauri/target/debug/nesdot を実際に起動します。これにより frontendDist と production app.security.csp は release build と同じ経路になります。
app.security.devCsp も connect-src 以外の strict directive は production csp と一致させ、debug / release 間で script/style 系 CSP がずれないようにしています。起動中はフロントエンドが console.error / window.onerror / unhandledrejection / securitypolicyviolation を一時診断ファイルへ記録し、script はその内容に加えて macOS unified log も補助的に確認します。
nix develop -c zsh -lc 'pnpm verify:licenses'この license 検証では、Node / Rust dependency の GPL / LGPL / AGPL 系 license expression と、Linux 向け Tauri system library の reviewed baseline を独立に確認します。verify:licenses は verify:security に含めず、license 確認だけを分離して回せるようにしています。
nix develop -c zsh -lc 'pnpm verify:system-libraries'この system library 確認では、flake.nix の linuxBuildInputs にある Linux 向け Tauri runtime / build 依存を対象に、lock された nixpkgs metadata から license を引いて reviewed baseline と照合します。現在の確認対象には gtk3、libsoup_3、webkitgtk_4_1 などが含まれます。
弱い copyleft を含む既知 system library は reviewed として一覧表示し、未審査 package の追加、license metadata の変更、強い copyleft の混入があれば失敗します。
nix develop -c zsh -lc 'pnpm verify:cve'この監査では pnpm audit と cargo audit を実行し、既知 advisory は scripts/cve-audit-baseline.json を baseline として管理します。baseline にない新規 advisory が出た場合だけ失敗します。
- 近接 unit test
例:
src/domain/**.test.ts - React component / logic test
例:
src/presentation/**.test.ts(x) - E2E
例:
e2e/*.spec.ts - リポジトリポリシーテスト
例:
tests/repo/*.test.ts
- 純粋ロジック: まず unit test
- UI の表示分岐: component test
- ユーザー操作や画面遷移: E2E
- 設定や workflow の契約: repo test
Playwright のブラウザが未導入なら:
nix develop -c zsh -lc 'pnpm e2e:install'E2E では階層依存セレクタではなく、role / name / label / test id を優先してください。
まず dev shell の外で実行していないか確認してください。
nix develop -c zsh -lc '<command>'ブラウザ未導入の可能性があります。
nix develop -c zsh -lc 'pnpm e2e:install'nix develop -c zsh -lc 'pnpm nix:check-pnpm-deps-hash'
nix develop -c zsh -lc 'pnpm nix:sync-pnpm-deps-hash'このリポジトリでは supply-chain policy が有効です。正当な理由があるなら内容を確認したうえで:
nix develop -c zsh -lc 'pnpm approve-builds'実行後は pnpm-workspace.yaml の差分をレビューしてください。
WSLgが有効か- リポジトリを WSL 側に置いているか
pnpm doctor:wslで問題が出ないか
を確認してください。
最終確認は Playwright web preview ではなく、macOS 上での:
nix develop -c zsh -lc 'pnpm verify:tauri:csp'です。Tauri / WebKit の実ランタイムでしか出ない問題があります。
日常開発者が全部覚える必要はありませんが、入口は知っておくべきです。
VS Code 標準フロー:
mainに checkout してgit pull --ff-only origin mainVerify CIRelease Desktop Dry RunDeploy Desktop Release
詳しい runbook は docs/release-checklist.md を参照してください。
補足:
- 自動化は
main上で version を確定し、その commit にvX.Y.Zタグを付けます Release Desktop Dry Runはmainがorigin/mainと一致している状態で実行してくださいrelease-tauri-desktopは tag がmain系列の commit を指している場合のみ継続します- GitHub Release は全 artifact upload 完了まで draft のまま保持され、最後に publish されます
- signing key などの GitHub secret 前提があります
- push 済みで失敗した場合は、履歴を書き換えて巻き戻すより follow-up commit 修正が基本です
- 純粋なルールなら
domain - 共有状態なら
application/state - browser / Tauri API 境界なら
infrastructure - 見た目とイベント結線なら
presentation
保守的に考えて、ひとつ上の verify を回してください。
例:
- UI を少し触っただけでも
pnpm verify:ui - 設定ファイルを触ったら
pnpm verify以上 - release / dependency / workflow なら
pnpm verify:ci
次の順で確認するとだいたい整理できます。
- 変更の中心は
domainかpresentationか - 外部データ境界を跨ぐか
- Tauri / Nix / CI に波及するか
- 追加すべき test は unit / component / E2E / repo test のどれか
作業内容によっては、次の文書も有用です。
README.mddocs/release-checklist.mddocs/static-css-architecture.mddocs/static-styling-status.mddocs/style-runtime-transition-notes.mddocs/bg-edit-mode-design.mddocs/nesdev-screen-rendering-context.mddocs/csp-style-src-hardening-todo.md
この文書に足りない情報が見つかったら、README に追記する前に「人が次に迷うのはどこか」を考えて、このマニュアルへ戻していくのがよいです。