---
id: ground-types/007
title: The area map editor — paint an Area's ground from the world editor
epic: ground-types
state: done
priority: 1
blocked_by: []
estimate: L
created: 2026-08-17
updated: 2026-08-18
claimed_by: null
claimed_at: null
delivers: []
review_artifact: land-of-lor/tools/world-editor/area-editor.js
---

## What to do

The disabled *Edit the cell map · later* button in the world editor's inspector
(`tools/world-editor/editor.js`) becomes real. Built directly in the world editor's own visual
language — Kris's local tool, never deployed.

- `tools/world-editor/server.mjs`: `GET /elements.js` (serves `game/shell/elements.js`; the page
  `<script src>`s it), `GET` / `PUT /areas/<slug>/ground.json` (slug regex; 404 when absent;
  PUT validates shape, dimensions against `spellgrove.world.json`, values 1..64, then writes
  the row-string format). Still bound to 127.0.0.1, no auth.
- New `area.html`, `area-editor.js`, `area-editor.css` (reusing `editor.css` tokens). The
  inspector button opens it in a **new window** — `window.open('area.html?slug=…')` — and is
  disabled with a note until an unsaved new Area has been saved.
- The painter: a canvas at about 12 px per cell (60×60 = 720 px); eight base-type swatches
  (names and colours from the table); a variant picker per type including **"random mix"**
  (Earth-upper by default with the other seven sprinkled in); brush sizes 1 / 3 / 5; rectangle;
  flood fill; eyedropper; undo / redo (client-side). Overlays for portal cells, the entry cell,
  the (1,1) spawn and the home landing cell — painting an impassable type onto any of them is
  refused inline. A "loose ends" strip like the world editor's: impassable under a door, a door
  that can't be reached. If the Area was resized since the file was written, crop / pad with
  hexagram 2 on load and say so.
- Save = `PUT`, then the same three steps as the world map: read the diff, `npm run load-world`,
  commit and push. Document in `tools/world-editor/README.md` and a "The ground" subsection of
  `game/README.md`'s world-map section. Optional: the world editor's own `cellPicker` shows the
  ground colours behind its ruled grid.

The scaffold (server routes, page, palette) can be built before task 005 lands; the end-to-end
check needs 005's loader.

## Definition of done

Paint the Village river by hand → save → `git diff game/content/areas/village-of-lor.ground.json`
reads as row changes → `load-world` accepts → visible in the client. Committed and pushed.

## Where the work lands

`land-of-lor/tools/world-editor/`, `game/README.md`.

## What was done (2026-08-17, laptop session)

- `tools/world-editor/area.html` + `area-editor.js` + `area-editor.css`; `server.mjs`
  serves `/elements.js` and `GET`/`PUT /areas/<slug>/ground.json` (PUT checks shape, dims
  against the world file, 1..64; writes the row-per-line format). The inspector's
  placeholder became **Edit the ground →** (disabled until the world is saved).
- Painter: 8 type swatches, flavour picker + Random mix, brush 1/3/5, rect, fill (same
  type), pick, undo/redo, keys; portal / entry / spawn overlays with refusal; loose-ends
  strip (impassable under a door, door unreachable — the loader's rules); crop/pad on a
  resize with a note. Driven end to end in headless Chrome (paint, refusal, loose end, undo,
  save → a 4-line diff → reverted). Docs in `tools/world-editor/README.md` and
  `game/README.md`.
- Not done: the optional ground colours behind the world editor's `cellPicker`.