Deploy web Studio / deploy (push) Successful in 1m4s
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.
339 lines
20 KiB
Markdown
339 lines
20 KiB
Markdown
# 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`.
|