# Editor Project Selector

Launching `hikari-editor` without `--project` boots a full-screen project selector: the engine brand mark, an **Open Project…** action, and **Exit**.

```
hikari-editor                      # boots the project selector
hikari-editor --project=<path>     # opens the project directly (unchanged)
```

## Flow

1. **No project argument.** The native frontends (`src/hikari/src/native_frontends/macOS/main.mm`, `src/hikari/src/native_frontends/Windows/main.cpp`) pass an empty project path through the frontend ABI instead of rejecting the launch.
2. **Selector boot.** `src/hikari/src/application/editor_application.zig` maps the empty path to a null `Project`. It skips the game-module compile worker and boots `EditorApplication` with `boot_select_project = true` and null project roots (all project inputs were already optional). The window uses a fixed `Editor` 1280×800 config since there is no project descriptor to read one from. Session `scene_path` is still anchored at staged **`<exe_dir>/data/untitled.json`** so `content_root` is product `data/` — renderer device-init loads `_engine/shaders` from that tree even before a game project is open. A bare relative `untitled.json` would derive `content_root="."` and crash on shader load.
3. **Selection.** *Open Project…* opens the platform's native folder dialog — `NSOpenPanel` on macOS, `IFileOpenDialog` with `FOS_PICKFOLDERS` on Windows (`src/hikari/src/platform/dialogs.zig`). The dialog is modal; cancelling returns to the selector.
4. **Validation.** The chosen folder is opened with `Project.open` (`src/hikari/src/editor/project.zig`), the same code path a normal launch uses: `hikari.project.json` must exist and be `com.hikari.project` version `5` with a required durable UUID `id` (identity only — session policy lives in `configs/*.json`). `engine_sdk` may be a relative path, `"bundled"`, or omitted (`<exe_dir>/sdk` if present from a packaged / `--vendor:engine=bundle` editor, else walk ancestors for `hikari/sdk` or `hikari/src/hikari/sdk`). Game projects do not store an engine path. Failures surface as an inline message on the selector:

   | Failure | Message |
   | --- | --- |
   | descriptor missing | That folder has no hikari.project.json |
   | wrong kind / version / malformed JSON | hikari.project.json is not a valid project descriptor |
   | engine SDK missing | Engine SDK not found (`<exe>/sdk`, ancestor `hikari/sdk` or `hikari/src/hikari/sdk`, or set `engine_sdk`) |
   | anything else | Could not open the project — see the console log |

5. **Editor version check.** The editor compares `current.version.number` from its global identity (`src/hikari/src/version.zig`) to `last_editor_version` in `hikari.project.json`. Missing field = first open (no warning). A mismatch shows an **Editor Version Changed** alert (Open Anyway / Cancel). Cancel returns to the selector; Continue records the new version and proceeds.
6. **Open.** On success the editor writes `last_editor_version`, spawns a fresh `hikari-editor --project=<path>` process, and closes itself. A session is composed around one project for its whole lifetime (asset roots, caches, the game-module build), so a process hand-off is the entire "open" implementation — the new process goes through the exact launch path a direct `--project` start uses, including the project's own window config. A direct `--project=` launch runs the same version check after boot (Cancel closes back to the selector).

### Zig toolchain discovery

Game-module compile needs a Zig 0.17 toolchain. Before spawning `zig build game-module`, the editor runs a **discovery phase** (`resolveZigExecutable` in `src/hikari/src/editor/project/zig_toolchain.zig`) and picks the first usable candidate:

| Order | Source | Notes |
| --- | --- | --- |
| 1 | `--zig=<path>` | Explicit full path override |
| 2 | `<editor_exe_dir>/tools/zig/zig` (`.exe` on Windows) | Bundled SDK only if Kaji staged it via `--vendor:zig=…bundle…` |
| 3 | `PATH` | Scan for `zig` / `zig.exe` |
| 4 | Platform fallbacks | macOS: `/opt/homebrew/bin`, `/usr/local/bin`; Linux: `/usr/local/bin`, `/usr/bin`, Homebrew; Windows: `C:\Zig`, `C:\Program Files\Zig` |

If none succeed, compile fails with guidance to rebuild with `--vendor:zig=fetch+bundle`, install Zig 0.17, or set `--zig=<path>`.

The winner is logged as `Zig: <path> (<source>)` in the compile splash output, and as `std.log.info` (`Zig discovery: <source> → <path>`) on the editor console / stderr / `--log=` file. Sources: `--zig=<path>`, `tools/zig (bundled)`, `PATH`, `platform fallback`.

Packaged `.app` / Start Menu launches often have a stripped `PATH` (and no `HOME`). Shipping a self-contained editor means opting into `--vendor:zig=bundle` (usually `fetch+bundle`) at package time so step 2 hits. Without that flag, the editor still builds and packages — it just relies on discovery steps 1/3/4 at runtime. Zig caches are pinned under `<project>/.engine/cache/game/zig/{local,global}` so GUI launches do not hit `AppDataDirUnavailable`.

The game-module build also forwards the **host product profile** so `GameModule` validation matches the editor binary: `-Doptimize=<builtin.mode>` (Debug from `--config=debug`, ReleaseFast from `--config=release`) and the same `-Dprofiler-timing` / `-Dprofiler-timing-markers` / `-Dprofiler-timing-pix` / `-Dprofiler-timing-xcode` values baked into `hikari_build_options` when Kaji built the editor. Those flags appear in the compile splash output as `Profile: …`. Game modules compile against the thin `hikari_game` SDK only (no shared `Entity`/`World` layout). Editor recompile always adds **`-Deditor-contrib=true`** so projects may export `registerEditor` / `hi.editor`; standalone `kaji game` products leave that flag off (default false) so editor extension code is not compiled in. Details: [Game editor SDK](/docs/systems/ui-and-editor.md#game-editor-sdk-hieditor).

### Where to read logs

| Surface | What it shows |
|---|---|
| Compile splash | The newest line under the header; **Show Output** expands the transcript (Zig path and source, SDK, profile, then the Zig compile lines). A failed build opens it automatically and offers **Reveal Log** for the full `.engine/logs/game-module-build.log` and **Retry** |
| Editor console (bottom dock) | Engine `std.log` after the splash closes |
| stderr | Same engine log stream — visible when launching from a terminal |
| `--log=<path>` | Redirect engine NDJSON log file (default with project: `.engine/logs/editor-latest.json`; no file on selector-only boot) |

Finder-launched `.app` bundles have no terminal; use the splash output (or **Reveal Log**) or relaunch with `--log=…`.

## Closing a project

When a project is open, **File → Close Project** relaunches the editor with no `--project` argument, landing back in the selector — the same hand-off as opening, in reverse. If the scene document has unsaved changes, the standard unsaved-changes dialog (Save / Discard / Cancel) runs first. The action is a no-op when the editor booted without a project.

## Code map

| Piece | Location |
| --- | --- |
| Selector screen (retained UI) | `src/hikari/src/editor/project_selector.zig` |
| Boot flag + action handling | `src/hikari/src/editor/editor_app/app.zig` (`boot_select_project`); open/relaunch in `editor_app/project_lifecycle.zig` |
| Optional-project frontend | `src/hikari/src/application/editor_application.zig` |
| Native folder dialog facade | `src/hikari/src/platform/dialogs.zig` |
| macOS dialog | `src/hikari/src/native/macOS/src/services/Dialogs.m` (`engineDarwinPickFolder`) |
| Windows dialog | `src/hikari/src/native/Windows/src/services/Dialogs.cpp` (`engineWindowsPickFolder`) |

The selector follows the compile-splash pattern: a full-screen retained-UI overlay owned by `EditorApplication`, updated at the top of `tick`, which early-returns while open so no editor chrome logic runs behind it. Linux has no native frontend yet; `dialogs.pickFolder` reports no selection there. Folder pick lives in the platform library (`engineDarwinPickFolder` / `engineWindowsPickFolder`).
