Cross-platform (macOS/Windows/Linux) Electron map editor for a 2.5D old-school raycasting FPS engine.
Nomos Studio is designed to run fully offline and keep Electron security boundaries tight (renderer UI has no Node access; privileged operations go through a narrow preload API).
Core workflows:
- Open, edit, and save JSON map files.
- Validate maps by invoking your configured game executable.
- Fast iteration loop: Run → Save & Run (F5) (save → validate → run).
Editor UI:
- Dockable editor layout (Map Editor + Inspector + Entities).
- Wireframe or textured rendering.
- Floor/ceiling surface selection in textured mode.
- Grid toggle + opacity controls.
- View overlays: portal highlighting, toggle-wall highlighting, door visibility.
- Tools: select, move (drag entities/lights), door placement, room creation (rectangle/square/triangle).
- Asset Browser with double-click routing:
Levels/*.jsonopens in the editor.- everything else opens via the OS default handler.
Inspector editing:
- Object Properties for common map objects (walls/sectors/doors/entities/lights/particles).
- Map Properties (name, sky, music/soundfont, player start with “pick” mode).
- SKY ceilings supported: setting a sector ceiling texture to
SKYrenders using the map-levelskytexture.
Undo/redo:
- Main-process-owned, bounded undo/redo with transactional edits.
- Node.js (recommend Node 20+)
- npm
npm cinpm run devnpm run typecheck
npm run lint
npm testNotes:
- Jest is configured to run with coverage by default.
- If you prefer serial tests:
npm test -- --runInBand.
Nomos Studio needs two settings before map open/validation works:
- Assets directory (a folder containing your game/editor assets)
- Game executable (used for validation and “Save & Run”)
Open Settings via:
- macOS: Nomos Studio → Preferences… (
CommandOrControl+,) - Windows/Linux: Settings → Settings… (
CommandOrControl+,)
Once configured:
- Use Open Existing to open a map file.
- Use File → Open Map… from the menu.
- Use Run → Save & Run (F5) to validate + launch quickly.
Nomos Studio delegates map validation to your configured game executable.
Validation contract:
- Command:
<gameExecutablePath> --validate-map <absoluteMapPath> - Exit code
0= valid - Any other exit code = invalid (the validator report is shown)
Save & Run contract:
- Save current map → validate → run
- Run command args are the map filename only (not full path):
<gameExecutablePath> <mapFileName.json>
Nomos Studio indexes files under your configured assets directory and uses POSIX-style relative paths (with /) in the asset index regardless of OS.
Commonly used paths:
- Maps:
Levels/*.json - Textures:
Images/Textures/*(some UI surfaces also tolerateAssets/Images/Textures/*) - Sky textures:
Images/Sky/* - Entities:
- Manifest at
Entities/entities_manifest.json(orAssets/Entities/entities_manifest.json) - Entity def entries referenced by the manifest are resolved under
Entities/(orAssets/Entities/)
- Manifest at
Example minimal layout:
<YourAssetsDir>/
Levels/
demo.json
Images/
Textures/
WALL_1.png
FLOOR_1.png
CEIL_1.png
Sky/
red.png
Entities/
entities_manifest.json
imp.json
imp.png
Texture naming note:
- Map fields like
wall.tex,sector.floor_tex, andsector.ceil_texare treated as texture filenames (e.g.WALL_1.png), not full paths.
This repo uses Electron Forge.
- Package (local packaged app):
npm run build
- Make distributables (platform-specific installers/zips):
npm run make
Forge output is written under out/.
Nomos Studio uses explicit boundaries:
src/main/— Electron main process (services, store, window creation, menus)src/preload/— preload bridge (typedwindow.nomosAPI)src/renderer/— React renderer UI (DockView shell + editor UI)src/shared/— shared domain + IPC contract (no Electron/React imports)
State model:
- Main process owns the authoritative
AppStore. - Renderer keeps a small Zustand snapshot store and refreshes when main signals
nomos:state:changed.
Security model:
- Renderer runs with
contextIsolation: true,sandbox: true, andnodeIntegration: false. - Privileged operations (filesystem, dialogs, process execution) are only reachable via the preload/IPC surface.
The authoritative subsystem docs live under docs/. Start here:
docs/README.md
Notable docs:
- Maps:
docs/maps-system.md - Transactional edits/undo/redo:
docs/map-edit-command-system.md - Assets:
docs/assets-system.md - Settings:
docs/settings-system.md - IPC and preload boundary:
docs/ipc-system.md,docs/preload-system.md - Renderer UI:
docs/renderer-ui-system.md
- Non-negotiable invariants:
.ushabti/laws.md - Style guide:
.ushabti/style.md
If you change a subsystem’s behavior or public API, update its doc under docs/ in the same change.
Open Settings (CommandOrControl+,) and set Assets directory.
- Ensure Game executable is configured.
- The validator must support:
--validate-map <absoluteMapPath>. - Read the validator report shown by the editor (stdout preferred, otherwise stderr).
- Ensure the assets directory contains
Images/Textures/. - Ensure map texture fields are filenames that exist in that folder.
- Textured rendering loads images via
blob:URLs; the app CSP is configured to allowimg-src ... blob:.
- Set the sector ceiling texture to
SKY(case-insensitive). - Set map-level
skyto a filename present underImages/Sky/. - If
skyis missing or unloadable, Nomos Studio skips the fill (no crash).
- Ensure
Entities/entities_manifest.json(orAssets/Entities/entities_manifest.json) exists. - Ensure the manifest entries point to valid entity def JSON files under the same prefix.