Components & libraries in the Wadi DSL
Part of the Wadi documentation. Prerequisite: the authoring guide. This chapter covers reuse.
Reuse in the Wadi DSL (.wdl) comes in two layers:
- Components: a named, parametric mini-house (a stair, a bathroom, a
verandah, a bench…) you define once and stamp onto any floor with
use. - Libraries: a
.wdlfile of components (and furnitureassets) that other filesimportand reuse. A library is a.wdlfile. There is no separate format.
Both are first-class in the language. This guide shows how to author them and how to save / load / resolve libraries in the WDL editor (web app + desktop app).
1. Components
Define a component
A component is authored in its own local coordinates (origin (0, 0)) so it
can be dropped anywhere. It may declare params (knobs, with optional defaults),
local var/points, and any floor objects.
component Bench {
param length = 60 label "Bench length" // a knob (default 60)
param seat = 18
beam name "Seat" at (0, 0) size (length, seat) height 6
}
An optional goal "…" documents intent and is the discovery key for module search
(see Libraries and MCP):
component Stairwell goal "climb to the next floor" {
param rise = 116
staircase name "Stair" at (0, 0) step (7, 11, 44) direction south climb up total_height rise
}
Use (stamp) a component
use places a component on a floor at (x, y), optionally naming the instance and
overriding params. Param overrides use = (not :):
floor 1 "Ground" {
use Bench at (40, 40) // all defaults
use Bench as "LongBench" at (40, 120) with { length = 120 }
}
at (x, y)offsets the component's local origin onto the floor.rotation <deg>(optional) turns the whole stamped assembly about its origin: yaw°, 0=south, 90=east. Right angles (0/90/180/270) are exact for any component; a non-right angle is allowed only for a furniture-only component (a free angle on a component containing a room/pillar/beam/slab/ staircase is a compile error; arbitrary structural rotation is a future need).with { p = v, … }overrides params; un-overridden params fall back to their declared defaults.- A component expands byte-identical to writing its objects inline. It has no runtime cost and nothing special downstream.
Rules & tips
- Local coords. Author everything relative to
(0, 0);use … at (x, y)does the placement. - Components nest freely. A component (in-file or in an imported library)
may
useanother component and placeitemfurniture. A library component canusea sibling in the same library,usea component from a library it itselfimports, anditem ns."id"from its own imports. Imports resolve transitively (a library may import libraries), each relocated under its namespace, with a clear error on an import cycle. - Reserved param names. A
paramname can't be a grammar keyword (width,depth,height,size, …). Useacross/deep/tall, etc. - Furniture too. A component can place
itemfurniture (see the furniture section of the DSL reference).
Promote a component to a primitive (expose as)
A component can be promoted to a runtime typed primitive, so it reads and behaves
like a built-in object type (pack.type) instead of a use instance:
component Bench goal "a place to sit" expose as garden.bench [layer "id"] [label "…"] {
param length = 60
beam name "Seat" at (0, 0) size (length, 18) height 6
}
expose as <pack>.<type> names the promoted primitive (a dotted pack.type id);
optional layer "id" and label "…" set its default layer and menu label. Once
exposed, pack.type is a first-class object type throughout the model, its params
becoming that type's fields, so authors place it directly rather than through use.
2. Libraries (reusable .wdl modules)
A library is a .wdl file whose top level holds component / asset (and
import) declarations, with no house block needed:
// konkan-parts.wdl - a reusable library (a "module")
component Otla goal "a raised front platform" {
param across = 240
param deep = 40
plinth name "Otla" at (0, 0) size (across, deep) height 15
}
asset "daybed" src "https://…/daybed.glb" dims (1.8, 0.4, 0.9) name "Daybed" category "Living"
Another file pulls it in with import, then stamps components with use ns.Comp
and places assets with item ns."id":
house Home {
import "konkan-parts" as kp
floor 1 "Ground" slab_thickness 0 {
room Hall at (20, 20) size (200, 200) { wall north east south west }
use kp.Otla at (20, 4) with { across = 200 }
item kp."daybed" at (60, 60)
}
}
Two bundled packs
Shipped with the editor and MCP, importable by name anywhere with no loading needed:
std-furniture: 120 furniture assets →item f."bed_double"afterimport "std-furniture" as f.konkan/base: goal-tagged Konkan parts (Stairwell, Verandah, Otla, Bathroom, Kitchen, TulsiVrindavan, Parapet) →use kb.Verandah …afterimport "konkan/base" as kb.
examples/konkan_cottage.wdl assembles a whole house from both.
Want a live preview while authoring a library?
A house-less module has no floors, so the editor preview shows a "no floors"
notice. That's expected; it isn't a renderable house. To see your components as
you build them, give the library a demo house that uses them. Importers
pull only the library's component / asset exports and ignore the demo house.
3. Saving & reusing libraries in the WDL editor
The editor keeps a cache of loaded libraries that import resolves from. It is
identical on the web app and the desktop app. Open the 📚 Library toolbar
menu:
| Action | What it does |
|---|---|
| 💾 Save current as library… | Names the current file and puts it in the cache. |
| 📂 Load library file… | Loads one or more .wdl files into the cache (multi-select). |
| (the list of cached libraries) | Click a name to insert its import "…" as ns line · ✎ opens it in the editor · × removes it from the cache. |
Each entry shows an origin badge: saved (from Save current as library) or
file (loaded from a .wdl).
Resolution order
import "name" resolves in this order:
- Your cache (saved + loaded libraries)
- Bundled packs (
std-furniture,konkan/base)
Desktop: libraries as real files
In the desktop app, a library can be a file on disk, with no explicit load:
- Any
.wdlbeside your open file, or in amodules/subfolder, is auto-loaded into the cache when you open the house, importable by its basename (kitchens.wdl→import "kitchens"). - These are real files you can commit, share, and version, and the coding agent + MCP can read them.
If a library isn't loaded
Open a house that imports something not in the cache and the editor names exactly what's missing:
⚠ missing libraries "kitchens", "bathrooms". 📚 Library → Load library file…
Load the named .wdl(s), several at once, and it resolves.
4. MCP (for coding agents)
Over the Wadi MCP server:
wadi_modules [query]: list importable modules (filter by a component goal keyword, e.g. "stairs", "sit-out").wadi_module "<name>" [query]: show a module's components (name + goal + params) and assets (id + dimensions).
5. Quick reference
// ── define a component ───────────────────────────────────────────────
component Name goal "…"? { // goal optional (discovery key)
param p = default label "…"? // default & label optional
…objects in LOCAL coords (origin 0,0)…
}
// ── stamp it (same file) ─────────────────────────────────────────────
use Name as "id"? at (x, y) [rotation <deg>] [with { p = v, … }] // overrides use `=`
// rotation: 0/90/180/270 for structural, any angle for furniture-only
// ── a library: a .wdl of component/asset decls, no `house` needed ─────
// then in another file:
import "name" as ns // ns.Comp · ns."assetId"
use ns.Comp as "id"? at (x, y) [with { … }]
item ns."assetId" at (x, y)
See also: the authoring guide (the end-to-end tutorial) and the
dense syntax reference at
wadi-skill/architect/reference/dsl.md.