Files
isokit/README.md
T
grabowski 2810477f1b
Deploy web Studio / deploy (push) Successful in 1m4s
Data centres: multi-storey shell, racks and pods, cooling, fire safety, power
datacentre.rhai (palette groups Data centre, Cooling, Fire safety, Power):

- dc_building: multi-storey shell (storeys of storey_h), walls in pieces,
  slabs, roof with parapet, louvres, front door and loading dock; levels
  cut it open storey by storey
- raised_floor; rack_row and dc_pod (cabinets by U, width, depth,
  equipment, fill, doors, LEDs; in-row coolers; cold / hot aisle
  containment with vent tiles, roof, end doors, ceiling duct, airflow);
  dc_cage for colocation
- crah, chiller (air-cooled, dry, adiabatic), cooling_tower, pipe_run
- smoke_detectors (grid + coverage), fire_pipe (VESDA, sprinkler, gas),
  gas_bank (Novec 1230, FM-200, Inergen sized for its halls), fire_device
  (panels, call points, abort switches, beacons, extinguishers)
- power_cab (UPS, battery, PDU, switchgear, RPP), busway, cable_tray,
  transformer
- tooltips sum IT load, kW/m2, cooling redundancy per storey, UPS load,
  battery runtime, plant cooling and agent quantity (warning when short)

Engine and stdlib:

- part_hidden(o) in house.rhai: parts on closed storeys inside a
  dc_building are not drawn (they only made sort cycles under the slab
  above); house parts, furniture and people use it. Existing examples
  render byte-identical.
- overhead services sort in their own "ceiling" layer with tight boxes
- sort records which sprites each broken cycle involved; isokit check
  names them (Rendered.cycle_at)

examples/datacentre (three storeys and a roof plant), Data centre demo in
the web Studio, tests, SCRIPTING/README/ROADMAP, MCP instructions.
2026-10-27 17:08:39 +07:00

339 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# isokit
Isometric scene engine for B4L projects: site plans, plants and buildings drawn
as SimCity-style views that you can pan, zoom, rotate, cut open by storey and
click for details. One Rust binary does everything:
- **Studio**: a GUI editor in the browser for people who don't code. You add
objects from a palette, drag them into place, and edit their properties in forms.
- **Web Studio**: the same Studio running entirely in the browser, with the
engine compiled to WebAssembly. Nothing to install: projects live in the
browser, or in a folder on disk (Chrome / Edge) that agents edit alongside.
- **Agent window**: chat with a language model right in the Studio (by default
B4L's inference server); it edits the scene with the MCP tools while you watch.
- **MCP server**: AI agents (Hermes and others) edit the same scene with the
same operations; their changes show up live in an open Studio.
- **Build**: a self-contained HTML page with every view prerendered, or
standalone SVGs for documents.
- **Build your own house**: walls, windows, doors, floors, roofs and stairs,
plus furniture (sofa, TV, bed, kitchen, table and chairs, desk, bath, …).
Draw rooms by dragging; windows and doors snap onto walls; the GF / 1F levels
cut the house open like a doll's house.
- **Engine and scripting**: everything you can place is a *prefab* written in
[Rhai](https://rhai.rs). Projects add their own prefabs, scripts and colours
without touching the engine.
The engine is a Rust port of the hand-written JS views in `cnx_vinyl`
(fleet calculator), `cnx_scuba` (centre view) and `mooban` (estate view),
and it keeps their look: same projection, depth sort, face shading, palette and
viewer behaviour.
## Quick start
```bash
cargo build --release # target/release/isokit.exe (one file, ~7 MB)
isokit new C:/dev/myproject/site-view # starter project
isokit studio C:/dev/myproject/site-view # opens the editor in the browser
isokit build C:/dev/myproject/site-view # dist/index.html, no server needed
```
Try the demos: `isokit studio examples/demo` (a works site),
`isokit studio examples/house` (a furnished house built from parts),
`isokit studio examples/factory` (presses, conveyors and robot arms),
`isokit studio examples/streets` (sidewalks, curves, junctions, a roundabout) and
`isokit studio examples/datacentre` (a three-storey data centre).
### Studio (for everyone)
| Do | How |
|---|---|
| Add | pick an object on the left, click on the map (Shift-click places several) |
| Draw | walls, fences, hedges, roads: drag along the map, or click corner after corner for a run around a plot (double-click / Enter ends it, the first corner closes it); ends snap onto the walls and fences they meet; a road run gets a curve in each corner. Pieces keep to x or y, or go diagonal at 45° once the pointer is more than 22.5° off; hold Ctrl for any angle. Floors, areas, buildings, roofs, rugs: drag a rectangle. Routes: click point after point |
| Select | click it on the map or in the object list; click a wall or fence again to select every piece joined to it |
| Several | Ctrl+click (map or list) adds or takes out one, Shift+click in the list a range, Shift+drag on the map a box, Ctrl+A all; then drag one to move them all, arrows, R, Ctrl+D, Delete and the property form work on all of them |
| Group | select several, then ▤ Group or Ctrl+G; Ctrl+Shift+G ungroups. In the object list a group folds open and shut, a click on it selects all of it (drag one on the map to move them all), its eye and lock hide or lock all of it; drag rows into or out of a group |
| Order | the object list is the drawing order of ground pieces (areas, beds, ponds, roads): higher lies on top. Drag rows, or ▲ ▼ / Ctrl+] Ctrl+[ (with Shift: to the front / back) |
| Points | a selected route or outlined area shows its points: drag one to move it, drag a + (middle of a side) to add one, double-click one to remove it; points snap to the grid and onto wall, fence and road ends. An Area's Edit corners turns its rectangle into points |
| Move | drag it, or arrow keys by the snap step (0.1 / 0.5 / 1 / 5 m) |
| Turn | R / Shift+R or ⟲ ⟳ (quarter turns), or type any angle in Rotation |
| Edit | the right panel: size, storeys, colours, solar, tooltip text, label, levels |
| Copy / delete | Ctrl+D / Delete; Ctrl+Z / Ctrl+Y undo and redo |
| Look | drag the ground to pan, Ctrl+wheel or pinch to zoom, ↺ ↻ rotate, Site / GF / 1F levels |
### Building a house
The **House** palette has the parts, **Furniture** the interior:
1. **Room**: drag a rectangle; you get a floor and a house wall on each edge
(walls already standing along an edge are not doubled). Or draw single
**House walls** by dragging along their line, and **Floors** as rectangles.
2. **Window** / **Door**: click on a wall. They snap onto its line, the wall
leaves the opening, and they move, turn, copy and delete with the wall. A
door opens to the side you clicked on; R turns it to the other hinge, then
to the other side. Styles: panel, glass, double, sliding, plain opening.
3. **Furniture**: chair, sofa / armchair, table with chairs, desk with monitor
and chair, TV, bed, cupboard, bookshelf, kitchen run (sink, hob, wall
cabinets, fridge), bathtub, shower, toilet, washbasin, plant, rug, floor
lamp. Fronts face local -y, so the back goes against a wall; R turns them.
4. **Roof** (hip, gable, flat, optional solar) as a rectangle over the walls,
and **Stairs**.
**Garden**: the Pond (natural, round or square; stone or grass edge) has fish
swimming round under lily pads, ripples and reeds; trees, hedges and rows of
trees are in Nature.
**Animals and nature**: Deer (a herd of a stag, does and fawns, or one
kind), Wild boar, Fox, Elephant (bull, cow, calf), Rabbits, a flock of Birds
circling and Butterflies; Beehives on stands with bees flying round, Bushes
(round, flowering, berries), Flowers (a bed in rows or a wildflower meadow),
Rocks and a Log or stump. Animals are low-poly solids that read from every
side; a herd scatters about the object's spot, each animal its own sprite.
Grazing animals lower and raise their heads in turn, elephants curl their
trunks, rabbits hop, birds flap. `examples/country` (Country demo in the web
Studio) puts them in a countryside corner with a lane, fence and hedgerow at
an angle.
**Routes**: a Route moves cars, buses, motorbikes, cyclists, people, deer or
elephants along waypoints. Pick Route and click point after point on the map
(points snap onto road and wall ends; double-click or Enter ends it, the
first point again makes a loop). A loop is driven round, an open route there
and back; each way keeps to the left of the line (or the right), corners are
rounded, and the movers pass in front of and behind trees and houses as they
go. The streets demo has traffic round the block, a bus, motorbikes, cyclists
and people strolling.
**Data centres** (palette groups Data centre, Cooling, Fire safety, Power):
the DC building is the shell of a multi-storey data centre (storeys of the
project's `storey_h`, 5 m in the demo); GF, 1F, 2F cut it open, and parts on
closed storeys are not drawn at all. On each storey a Raised floor (0.6 m
tiles over the supply plenum) carries Rack rows and Rack pods: cabinets of
any width, depth and height in U (servers, storage, GPU, network or empty,
mesh, glass or solid doors, status LEDs), in-row coolers between groups of
racks, and pods with cold aisle containment (vent tiles, glass roof, sliding
end doors, cold air rising) or hot aisle containment (backs to the aisle,
optional duct to the ceiling); Cages for colocation. Cooling: CRAH / CRAC
units, chillers and dry or adiabatic coolers with fan rows, cooling towers
with a plume, flow and return pipe runs. Fire safety: detector grids with
their coverage, VESDA sampling pipes with the detector unit, pre-action
sprinkler and gas discharge pipes, clean-agent cylinder banks (Novec 1230,
FM-200, Inergen), alarm and release panels, call points, abort switches,
beacons, extinguishers. Power: UPS, battery, PDU, switchgear and RPP
line-ups, busways with tap-off boxes, cable trays and fibre ducts,
transformers. The tooltips do the sums: IT load per pod, floor and building,
kW/m², cooling against load per storey (N+1 and so on), UPS loading,
battery runtime, the agent a gas bank needs for its halls (a warning label
when it is short). `examples/datacentre` (Data centre demo in the web Studio)
has a power room, a hall with cold aisle pods, a GPU hall with hot aisle pods
and cages, and chillers on the roof.
**Fences and walls** around the plot: the Fence has six designs (mesh, wooden
boards, steel bars, mesh on a stone base, cobble wall, brick wall with
pillars). Fences and site walls whose ends meet join like house walls: corners
close, a T stops at the other's face, a straight run shares one post, and a
fence ending at a wall stops at its face.
Level **GF** shows the house cut open: walls in front of a room are cut low
(walls tell inside from outside by the floors next to them), the roof becomes an
outline on the ground. For more storeys, view **1F** (2F, …): new walls, floors
and furniture go onto that storey (3 m apart, or the project parameter
`storey_h`), and the storeys below show closed.
Every change is saved to `scene.json` at once. When the file changes on disk
(an agent, git pull, another editor), the Studio reloads it, and that change can
be undone like any other.
### Web Studio (no install)
```bash
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli --version 0.2.129 # must match Cargo.lock
python tools/build_web.py # -> dist/web/ (static files, ~2.8 MB, 0.75 MB gzipped)
```
`dist/web/` runs from any static web server (http or https, not `file://`).
A live build is at https://isokit.buildfor.life/: every push to `master` that
touches the engine or the front end rebuilds and deploys it
(`.gitea/workflows/deploy-web.yml`; server setup in `deploy/Caddyfile`).
The first visit opens a copy of the demo. Everything the desktop Studio does
works the same, because the page runs the same engine and answers the same
requests. In addition:
| | |
|---|---|
| **Projects ▾** | list, open, delete; new project; demo copy; house demo; export as `.zip`; import a `.zip` or a project folder (drop it on the strip, or pick it) |
| **Open folder live** | Chrome / Edge: edits are written straight to the folder's files, and changes made there (an agent running `isokit mcp <folder>`, an editor, `git pull`) show up within 1.5 s and can be undone |
| **{ } Code** | edit `scene.rhai`, `lib/*.rhai` prefabs, `theme.toml`, `isokit.toml`; Ctrl+S applies, script errors show with file and line |
| **Build page** | downloads the self-contained HTML page (a folder project gets `dist/index.html` like `isokit build`) |
| **Preview** | the built page in a new tab |
Projects in the browser are stored in IndexedDB (per browser and site; export
them to keep a copy). Two tabs on the same project follow each other. The 15
MCP tools also run inside the page: `await isokit.mcp({jsonrpc: "2.0", id: 1,
method: "tools/list"})`, and browsers with WebMCP (`navigator.modelContext`)
get them as page tools.
### Agent window
**✦ Agent** in the Studio's top bar opens a chat with a language model that
edits the scene for you: "plant a row of trees along the road", "make the
workshop one storey taller", "what is on this site?". It works with the 15 MCP
tools below plus two page tools (point at an object, turn the view), streams
its answer and its reasoning, and shows each tool call (click one for the
arguments and the result, click an object id to select it). Every edit lands
on the map at once, and Undo takes it back. Stop ends a turn; the window moves
by its title bar and resizes at its top-left corner.
Any OpenAI-compatible endpoint with tool calling works (vLLM, llama.cpp,
Ollama, LM Studio, hosted APIs). The default is B4L's inference server,
`https://inference.p0.contact/v1`; ⚙ sets endpoint, key, model and whether
the model thinks first.
- `isokit studio` passes the calls through its own server (`/api/agent/*`), so
the key never reaches the page and the endpoint needs no CORS. Settings:
`ISOKIT_LLM_URL`, `ISOKIT_LLM_KEY`, `ISOKIT_LLM_MODEL`, or the window's ⚙,
which saves them in `%APPDATA%\isokit\agent.json` (`~/.config/isokit` elsewhere).
- The web Studio calls the endpoint from the page; the key stays in that
browser. The endpoint must allow the page (CORS, preflight included), or the
site relays it: https://isokit.buildfor.life/ is built with
`--agent-url /llm/v1`, a same-site path that Caddy forwards to B4L's
inference server (`deploy/Caddyfile`). The relay adds no key: each browser
enters its own once.
### Agents (MCP)
While the Studio runs, it also serves MCP over HTTP at `http://127.0.0.1:7878/mcp`.
Without the Studio, run `isokit mcp <project>` over stdio. In Hermes
(`config.yaml`):
```yaml
mcp_servers:
isokit:
command: C:\dev\isokit\target\release\isokit.exe
args: [mcp, C:\dev\myproject\site-view]
```
Tools: `project_info`, `list_prefabs`, `list_objects`, `get_object`,
`add_object`, `update_object`, `remove_object`, `duplicate_object`,
`apply_ops` (batch, one undo step), `set_params`, `render_view`,
`check_project`, `build_html`, `undo`, `redo`. Bad input comes back with a
message that says what is allowed, e.g. the list of types or a prop's range.
## A project
```text
isokit.toml title, levels, parameters, theme files, prefab libraries
scene.json the objects (edited by the Studio and agents)
scene.rhai optional script, drawn before the objects
lib/*.rhai optional project prefabs (listed under prelude)
theme.toml optional colours and materials
```
An object in `scene.json`:
```json
{ "id": "hall", "type": "building", "name": "Workshop", "x": -12, "y": 6, "rot": 90,
"props": { "w": 24, "d": 12, "floors": 2, "pv": true },
"tag": "Workshop", "info": "Two storeys, solar on the roof", "levels": [] }
```
Coordinates are metres: x points east, y north, z up. `x, y` is the centre of
the footprint, and `rot` turns the object in 90° steps. Levels: `site` (or any
other id) shows buildings closed, while `gf`, `1f`, `2f` … cut them open at that
storey.
Production lines: a Producer (category Industry) makes goods at a set rate
and puts them out on its +x side; Conveyors drawn from there carry them on,
round corners and up inclines, each at its own speed, so the gap between goods
follows rate and speed. A Robot arm placed past a belt's end picks the goods
off and puts them on the next belt or a pallet, in step with them. Tooltips
show the numbers. `examples/factory` (Factory demo in the web Studio) has a
few lines.
Streets: a Road can have sidewalks on both sides or one (12 cm high paving
with kerbs). A Road curve carries a street round a bend, a road ending on
another road's centre line makes a junction (the sidewalk opens there), and a
Roundabout has up to four arms with flared kerbs, splitter islands and
give-way lines for traffic on the left or right. Roads meet by their centre
lines, so put the ends on each other. `examples/streets` (Streets demo in the
web Studio) is a small town quarter.
Diagonals: roads, walls, fences, hedges and house walls run at any angle.
Where they meet at an angle the corners are mitred, side streets come in at a
slant (the sidewalk opens along them), fence corners get a post or pillar, and
a road run turning 45° gets a 45° curve. Windows and doors sit on diagonal
house walls like on any other.
Animations (fans, smoke, fish, traffic) step on about ten times a second by
default ("light"): running CSS animations repaint the whole drawing every frame,
which keeps a slow laptop busy. The ▷ button in the view toolbar switches
between light, smooth and off, and the browser remembers the choice. In
`isokit.toml`, `[build] animation = "smooth"` sets the default for built pages.
When the system asks for less motion (Windows: Settings → Accessibility →
Visual effects → Animation effects off), views start still; ▷ turns them on.
## Commands
| Command | |
|---|---|
| `isokit new <dir>` | starter project |
| `isokit studio [dir] [--port N] [--no-open]` | GUI editor + `/mcp` + `/view` live preview + the agent window's link to the model |
| `isokit mcp [dir]` | MCP over stdio |
| `isokit build [dir] [-o file]` | self-contained HTML (all levels × 4 rotations) |
| `isokit render [dir] -o x.svg [--level gf] [--rot 2] [--light]` | one view as SVG |
| `isokit check [dir]` | lint: unknown types, props out of range, script warnings, NaN, node budget, unstyled classes |
| `isokit prefabs [dir]` | object types and their properties |
## Architecture
```text
crates/isokit-core projection (4 rotations), primitives, sprites, depth sort, SVG (no deps but serde)
crates/isokit-theme TOML palettes / materials / rules -> CSS; built-in default theme
crates/isokit-script Rhai runtime: drawing API, prefab library (stdlib/*.rhai), scene document driver
crates/isokit-project project folder, document ops, export, check, Workspace (undo, reload), MCP tools
crates/isokit-cli the `isokit` binary: Studio HTTP server, MCP stdio, build/render/check
crates/isokit-wasm the engine for the browser (wasm-bindgen): a project in memory behind the Studio API
viewer/ isokit-view.js/.css: pan, zoom, levels, rotation, tooltips (any page can embed it)
studio/ the Studio GUI (served by `isokit studio`, and by the web Studio); agent.js = agent window
web/ web Studio shell: WASM backend for studio.js, projects, folders, zip, code editor
examples/demo demo project (works site)
examples/house house built from parts (walls, windows, doors, floors, furniture, roof)
examples/factory production yard: producers, conveyors, robot arms
examples/streets town quarter: sidewalks, curves, junctions, a roundabout
deploy/Caddyfile the web Studio's vhost on the buildfor.life web server (s1)
```
The layers are independent. A Rust program can draw with `isokit-core` alone.
`isokit-script` reads sources from memory, with no disk access, and a
`Workspace` reads and writes through a `Store` (a folder on disk, or memory),
so the same code runs natively and in the browser. The Studio, the web Studio
and MCP all apply the same `ops::Op` JSON operations through a `Workspace`, and
both Studio front ends speak the JSON API in `isokit_project::api`.
Writing prefabs and scripts: [docs/SCRIPTING.md](docs/SCRIPTING.md).
Plans: [docs/ROADMAP.md](docs/ROADMAP.md).
## Development
```bash
cargo test # engine, runtime, ops, demo, MCP
cargo clippy --all-targets -- -D warnings
ISOKIT_LLM_URL=http://127.0.0.1:7932/v1 ISOKIT_LLM_KEY=test-key ISOKIT_CONFIG_DIR=<scratch> \
isokit studio <copy of examples/demo> --port 7890 --no-open & # agent window on tools/mock_llm.py
uv run --with playwright python tools/check_studio.py <shot-dir> http://127.0.0.1:7890 # GUI + MCP + agent window end to end (Edge)
cargo clippy -p isokit-wasm --target wasm32-unknown-unknown -- -D warnings
python tools/build_web.py && uv run --with playwright python tools/check_web.py <shot-dir> # web Studio end to end
uv run --with playwright python tools/bench_web.py --rate 6 --headed # timings on a CPU slowed 6x (low-power stand-in)
```
The agent window checks run on `tools/mock_llm.py`, a scripted stand-in for an
OpenAI-compatible server that the check scripts start on port 7932.
`python tools/build_web.py --skip-cargo` re-copies the front end (studio/,
viewer/, web/) without rebuilding the WASM. Code that runs in the browser must
not use `std::time` (it traps on wasm32-unknown-unknown): use `web_time`.
Set `ISOKIT_ASSETS=C:/dev/isokit` to serve `viewer/` and `studio/` from disk, so
you can edit the GUI without rebuilding. `tools/css2theme.py` converted the
original views' CSS into `crates/isokit-theme/themes/default.toml`.