@jgengine/editor 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,10 +12,30 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
12
12
 
13
13
  - `collectAuthoredTriggers` (function) · `import { collectAuthoredTriggers } from "@jgengine/shell/gameKit"`
14
14
 
15
+ ## chase-view-cycle — cycle a vehicle camera between chase, hood and cockpit views at runtime
16
+
17
+ - `nextChaseView` (function) · `import { nextChaseView } from "@jgengine/core/runtime/cameraDirector"`
18
+
19
+ ## client-side — input prediction with replay reconciliation
20
+
21
+ - `createPredictionBuffer` (function) · `import { createPredictionBuffer } from "@jgengine/core/runtime/prediction"`
22
+
15
23
  ## collider-autofit — hitboxes and physical bodies fit each kind's rendered model bounds automatically — no hand-tuned sizes
16
24
 
17
25
  - `GameContextModels` (interface) · `import { GameContextModels } from "@jgengine/core/runtime/gameContext"`
18
26
 
27
+ ## command-number — validate finite, bounded numbers and integers at command boundaries
28
+
29
+ - `readInputNumber` (function) · `import { readInputNumber } from "@jgengine/core/runtime/commandInput"`
30
+
31
+ ## command-parse — parse untrusted command input once so every command stage sees typed, bounded values
32
+
33
+ - `defineCommand` (function) · `import { defineCommand } from "@jgengine/core/runtime/commandRunner"`
34
+
35
+ ## command-quantity — validate bounded whole-item counts at command boundaries
36
+
37
+ - `readQuantity` (function) · `import { readQuantity } from "@jgengine/core/runtime/commandInput"`
38
+
19
39
  ## default-walk-codes — stock WASD + jump key codes for the shell walk controller
20
40
 
21
41
  - `DEFAULT_WALK_CODES` (const) · `import { DEFAULT_WALK_CODES } from "@jgengine/shell/gameKit"`
@@ -32,6 +52,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
32
52
 
33
53
  - `EditorCatalogDefinition` (interface) · `import { EditorCatalogDefinition } from "@jgengine/shell/gameKit"`
34
54
 
55
+ ## fire-input — bind a key or mouse button to a named action that runs the same-named command with the aim; repeatMs auto-fires while held
56
+
57
+ - `ActionCodesMap` (type) · `import { ActionCodesMap } from "@jgengine/core/authoring"`
58
+
35
59
  ## game-system — declare a composable capability with its own schedule and lifecycle
36
60
 
37
61
  - `SystemDefinition` (interface) · `import { SystemDefinition } from "@jgengine/shell/gameKit"`
@@ -84,6 +108,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
84
108
 
85
109
  - `StatBar` (function) · `import { StatBar } from "@jgengine/shell/gameKit"`
86
110
 
111
+ ## input-recorder — tick-indexed input log for replay, rollback, and regression tests
112
+
113
+ - `createInputRecorder` (function) · `import { createInputRecorder } from "@jgengine/core/runtime/inputRecorder"`
114
+
115
+ ## local-players — Couch co-op seats: devices hot-join into slots, each with its own user id and input snapshot.
116
+
117
+ - `createLocalPlayers` (function) · `import { createLocalPlayers } from "@jgengine/core/runtime/localPlayers"`
118
+
87
119
  ## loot-table — validate a loot table definition for use with the registry
88
120
 
89
121
  - `lootTable` (function) · `import { lootTable } from "@jgengine/core/authoring"`
@@ -116,6 +148,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
116
148
 
117
149
  - `toRuntimeObjectRow` (function) · `import { toRuntimeObjectRow } from "@jgengine/core/runtime/objectRows"`
118
150
 
151
+ ## pose-interpolation — render entity poses between fixed simulation steps without stutter
152
+
153
+ - `createPoseHistory` (function) · `import { createPoseHistory } from "@jgengine/core/runtime/poseInterpolation"`
154
+
119
155
  ## render-fallback-diagnostics — Enable/disable the dev-only placeholder-fallback probe.
120
156
 
121
157
  - `armFallbackSeams` (function) · `import { armFallbackSeams } from "@jgengine/core/devtools/fallbackSeams"`
@@ -126,6 +162,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
126
162
  - `resetTextureErrors` (function) · `import { resetTextureErrors } from "@jgengine/core/devtools/textureErrors"`
127
163
  - `textureErrorsSnapshot` (function) · `import { textureErrorsSnapshot } from "@jgengine/core/devtools/textureErrors"`
128
164
 
165
+ ## runtime — snapshot interpolation
166
+
167
+ - `createSnapshotBuffer` (function) · `import { createSnapshotBuffer } from "@jgengine/core/runtime/snapshotBuffer"`
168
+
129
169
  ## runtime-save — save/load the whole game world through a pluggable backend, autosave or save points
130
170
 
131
171
  - `createRuntimeSave` (function) · `import { createRuntimeSave } from "@jgengine/core/runtime/runtimeSave"`
@@ -142,6 +182,14 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
142
182
 
143
183
  - `seededRng` (function) · `import { seededRng } from "@jgengine/shell/gameKit"`
144
184
 
185
+ ## sim-loop — fixed-step or variable simulation stepping with tick count, catch-up cap, and interpolation alpha
186
+
187
+ - `createSimLoop` (function) · `import { createSimLoop } from "@jgengine/core/runtime/simLoop"`
188
+
189
+ ## sim-snapshot — capture and restore whole-simulation state for replay, rollback, and regression tests
190
+
191
+ - `createSimSnapshotRegistry` (function) · `import { createSimSnapshotRegistry } from "@jgengine/core/runtime/simSnapshot"`
192
+
145
193
  ## typed-keyed-store — a cast-free typed handle onto a per-owner keyed family of reactive game-store slots, replayable and host-authoritative
146
194
 
147
195
  - `defineKeyedStore` (function) · `import { defineKeyedStore } from "@jgengine/shell/gameKit"`
@@ -150,6 +198,10 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
150
198
 
151
199
  - `defineStore` (function) · `import { defineStore } from "@jgengine/shell/gameKit"`
152
200
 
201
+ ## warn-once — emit a dev-only warning a single time per call site
202
+
203
+ - `warnOnce` (function) · `import { warnOnce } from "@jgengine/core/devtools/warnOnce"`
204
+
153
205
  ## world-chunk-key — map world positions to persisted chunk keys at a declared cell size
154
206
 
155
207
  - `chunkKeyOf` (function) · `import { chunkKeyOf } from "@jgengine/core/runtime/worldChunks"`
@@ -165,3 +217,7 @@ Reach for these before hand-rolling. Each row is *the thing you need* → *the p
165
217
  ## world-environment — compose editor/preset-derived outdoor dressing — terrain, weather, water, structures — into a legacy environment feature
166
218
 
167
219
  - `environment` (function) · `import { environment } from "@jgengine/shell/gameKit"`
220
+
221
+ ## world-item-pickup — pick up dropped items: ctx.scene.worldItem nearestInRadius → pickup into the player's inventory
222
+
223
+ - `SceneWorldItemContext` (interface) · `import { SceneWorldItemContext } from "@jgengine/core/runtime/gameContext"`
@@ -45,6 +45,9 @@ spatial place. When the game *is* a place, declare only substrate + laws with `w
45
45
  `@jgengine/core/world/place` (e.g. `{ id, ground: { mode: "flat", size: { x: Infinity, z: Infinity } }, physics: { gravity: -24 } }`,
46
46
  or `mode: "board"` for 2D). Never seed, sky, or vegetation in `world` — the engine renders its
47
47
  default sky until you author one in the editor, and foliage is editor scatter over the ground.
48
+ A created 3D game instead ships hills as a terrain sculpt in `editor.scene.json` and a `world.ts`
49
+ that consumes it with `environment({ sculpt })` via `environmentContentFromDocument`; the file
50
+ holds only the ground palette. Reshape the hills in the editor, not in code.
48
51
 
49
52
 
50
53
  ```ts
@@ -17,7 +17,7 @@ Every editor-visible object has an explicit provenance — `authored`, `generate
17
17
 
18
18
  ## Headless fast path
19
19
 
20
- Inspect, mutate, and persist a game's scene in single commands — no GUI session and no reference reading needed for basic authoring:
20
+ Inspect, mutate, and persist a game's scene in single commands — no GUI session and no reference reading needed for basic authoring (monorepo):
21
21
 
22
22
  ```sh
23
23
  bun packages/editor/src/mcp/cli.ts --game <id> --tools # verb inventory with descriptions
@@ -28,6 +28,8 @@ bun packages/editor/src/mcp/cli.ts --game <id> \
28
28
 
29
29
  Repeat `--rpc` to batch several mutations on one session; `--save` then writes the document back to `Games/<id>/src/editor.scene.json` in the same format as the GUI's Ctrl+S, and a failed RPC aborts the batch before anything is saved.
30
30
 
31
+ That CLI exists only in the monorepo (`npx jgengine editor-mcp` says so). In a created project the same verbs run on the live page through the agent bridge: `bun run drive -- --rpc '{"method":"editor_summon"}' --rpc '<verb json>' --rpc '{"method":"save_scene"}'`. `editor_summon` resolves once the editor has mounted, so the verbs after it in the same batch reach it.
32
+
31
33
  ## Canonical workflow
32
34
 
33
35
  1. Inspect the current scene and available editor capabilities.
@@ -61,9 +61,11 @@
61
61
  - `EntityCatalogDocumentLike` (interface): interface EntityCatalogDocumentLike — Minimal document shape {@link entityEntryFromCatalog} reads — any `EditorDocument` satisfies it.
62
62
  - `EnvironmentContent` (interface): interface EnvironmentContent — The coordinate/placement content of a world, derived from its scene document.
63
63
  - `EnvironmentContentOptions` (interface): interface EnvironmentContentOptions extends ClearanceOptions — World-authoring convergence (#1018, epic #1006 Phase 4): derive the **coordinate/placement** content of an `environment()` world from the scene document, so a game with an authored document needs no coordinate arrays in `world.ts` — the remaining `world.ts` surface is engine tuning (noise, materials, sky preset, physics). Terrain footprint follows the authored objects, ground clearings come from authored spawns/POIs (`clearanceZonesFrom`), and the sculpt snapshot is the document's own terrain overlay. Pure and serializable; consumers pass the result straight to `environment({ terrain: terrain({ bounds }), clearings, sculpt })`.
64
+ - `EnvironmentSource` (type): type EnvironmentSource = | { kind: "gradient"; sky: string; ground: string; sun?: string; intensity?: number } /** Equirectangular HDR or EXR environment map loaded from `url`. */ | { kind: "hdri"; url: string; rotation?: number; intensity?: number } /** Six faces of a cube environment map, in three… — A declarative source for image-based scene lighting.
64
65
  - `HudPanelTypeDef` (interface): interface HudPanelTypeDef — Declared panel type: growable axes, size limits, optional ParamSchema for the editor.
65
66
  - `HudResizeAxes` (type): type HudResizeAxes = "none" | "x" | "y" | "both" — Which axes a panel type may grow when resized in canvas mode. Resize is semantic — content reflows (longer track, more rows) — never a CSS scale of the whole panel.
66
67
  - `MaterializedInstance` (interface): interface MaterializedInstance — The deterministic materializer for scene {@link EditorDirective}s — the bake side of the bake/patch seam in #985. A directive is one authored line ("scatter rocks across the north ridge"); this expands it into many placements with **stable per-instance ids** (`<directiveId># <index>`) so a sparse overlay can hand-tweak or delete individual instances by id without every re-materialize orphaning those edits. Fully deterministic: same directive + seed + region ⇒ the same field, every time (safe for verify/CI). Built on the existing `scatter` primitive.
68
+ - `PointLightingConfig` (interface): interface PointLightingConfig — A bounded dynamic point light in world space.
67
69
  - `RuntimeEntityState` (interface): interface RuntimeEntityState — One live entity row the runtime may stream to the editor (play-mode inspector feed).
68
70
  - `RuntimeInspectorGetResult` (interface): interface RuntimeInspectorGetResult — One resolved runtime field returned by `runtime_get`.
69
71
  - `RuntimeInspectorSetPlan` (interface): interface RuntimeInspectorSetPlan — Desired mutation from `runtime_set` before the host applies it.
@@ -130,6 +132,7 @@
130
132
  - `importCsvGrid` (function): function importCsvGrid(text: string, options: CsvGridImportOptions): EditorGridLayer — Parses a CSV grid — one row per line, one value id per comma-separated field — into a grid layer. Blank fields become the empty value. Unlike ASCII this carries multi-character value ids directly, so it suits grids whose cell values are names rather than single glyphs.
131
133
  - `inGridBounds` (function): function inGridBounds(layer: EditorGridLayer, col: number, row: number): boolean — True when `col,row` falls inside the layer's declared bounds.
132
134
  - `installDocumentLiveSync` (function): function installDocumentLiveSync(sync: DocumentLiveSync): () => void — Publishes a live-sync bus globally so AuthoredScene / games can subscribe without prop drilling.
135
+ - `lightingFromDocument` (function): function lightingFromDocument(doc: EditorDocument): LightingConfig | undefined — Converts authored `light` markers into the runtime lighting contract.
133
136
  - `loadWorldDocument` (function): function loadWorldDocument(manifest: WorldManifest, resolve: WorldShardResolver, query?: WorldQuery): EditorDocument — Loads the live neighborhood document for a query: reads each selected shard through `resolve`, applies its overlay (if any) on top, then merges them into one {@link EditorDocument} downstream consumers (`placeAuthoredObjects`, `collectAuthoredTriggers`, scatter, …) read unchanged. The monolith becomes an internal detail of the loader; a one-shard world yields exactly that shard.
134
137
  - `materializeDirective` (function): function materializeDirective(doc: EditorDocument, directive: EditorDirective): MaterializedInstance[] — Materializes one directive against a document, resolving its footprint first. Returns an empty list when the region does not resolve.
135
138
  - `materializeDirectives` (function): function materializeDirectives(doc: EditorDocument, overlays?: readonly EditorDirectiveOverlay[]): MaterializedInstance[] — Materializes every directive on a document into one flat instance list, applying each directive's overlay (matched by `over` === directive id) as it goes. The bulk-content counterpart to `resolveAuthoredObjects` / `resolveScatter`: games and headless tests read the same list.
@@ -197,6 +200,7 @@
197
200
  - `EnvironmentContentOptions` (interface): interface EnvironmentContentOptions extends ClearanceOptions — World-authoring convergence (#1018, epic #1006 Phase 4): derive the **coordinate/placement** content of an `environment()` world from the scene document, so a game with an authored document needs no coordinate arrays in `world.ts` — the remaining `world.ts` surface is engine tuning (noise, materials, sky preset, physics). Terrain footprint follows the authored objects, ground clearings come from authored spawns/POIs (`clearanceZonesFrom`), and the sculpt snapshot is the document's own terrain overlay. Pure and serializable; consumers pass the result straight to `environment({ terrain: terrain({ bounds }), clearings, sculpt })`.
198
201
  - `environmentContentFromDocument` (function): function environmentContentFromDocument(doc: EditorDocument, options: EnvironmentContentOptions = {}): EnvironmentContent — Derives the coordinate/placement content of an `environment()` world from its scene document: terrain footprint (via {@link terrainBoundsFromDocument}), ground clearings under authored spawns/POIs (via `clearanceZonesFrom`), and the document's sculpt snapshot. This is the seam that lets a game author its world footprint and flatten regions in the editor instead of hardcoding them — the `world.ts` that consumes it carries only engine tuning.
199
202
  - `lakebedFromWaterVolumes` (function): function lakebedFromWaterVolumes(doc: EditorDocument): TerraformSnapshot | undefined — Derives a terrain-depression sculpt snapshot from the document's authored `water` volumes: each water box carves a lake bed that ramps from the shoreline down to below the box's bottom face, so an editor-authored pond/lake sits *in* the ground instead of floating as a sheet on top of it (and ground-sampled vegetation sinks below the surface rather than growing through it). Returns `undefined` when the document has no water volumes.
203
+ - `lightingFromDocument` (function): function lightingFromDocument(doc: EditorDocument): LightingConfig | undefined — Converts authored `light` markers into the runtime lighting contract.
200
204
  - `skyFromDocument` (function): function skyFromDocument(doc: EditorDocument): SkyEnvironmentConfig | undefined — Maps the scene document's serializable environment bag onto the runtime sky config consumed by `sky()` / `environment({ sky })`. Returns `undefined` when the document has no environment field so callers can fall back to a world.ts default without inventing values.
201
205
  - `terrainBoundsFromDocument` (function): function terrainBoundsFromDocument(doc: EditorDocument, options: EnvironmentContentOptions = {}): WorldBounds — The origin-centered terrain footprint that covers every authored object in a document, padded and floored to `minBounds`. An empty document yields exactly `minBounds`. Because `environment()` terrain is centered on the origin, the footprint is sized symmetrically to the farthest object on each axis, so no authored object falls off the ground.
202
206
 
@@ -291,6 +295,7 @@
291
295
 
292
296
  ## @jgengine/core/editor/types
293
297
 
298
+ - `EditorBake` (interface): interface EditorBake — A named baked artifact produced by an editor or build tool.
294
299
  - `EditorCatalogData` (interface): interface EditorCatalogData — Persisted values for one gameplay data catalog (weapons, waves, economy, …). Schemas are not stored here — they come from the game's `editorCatalogs` export and drive SchemaInspector.
295
300
  - `EditorCatalogDefinition` (interface): interface EditorCatalogDefinition — Game-exported catalog definition: a `ParamSchema` plus default entries. Schemas stay in code; entry values merge into `document.catalogs` and are what the editor/RPC edits and saves.
296
301
  - `EditorCatalogEntry` (interface): interface EditorCatalogEntry — One row in a gameplay data catalog — id + optional label + a meta bag matching the catalog's `ParamSchema`. Values persist on the scene document; the schema lives in the game export.
@@ -322,6 +327,7 @@
322
327
  - `EditorVolumeShape` (type): type EditorVolumeShape = "sphere" | "cylinder" | "box" — Collision shape a volume is rendered and tested as.
323
328
  - `HudPanelTypeDef` (interface): interface HudPanelTypeDef — Declared panel type: growable axes, size limits, optional ParamSchema for the editor.
324
329
  - `HudResizeAxes` (type): type HudResizeAxes = "none" | "x" | "y" | "both" — Which axes a panel type may grow when resized in canvas mode. Resize is semantic — content reflows (longer track, more rows) — never a CSS scale of the whole panel.
330
+ - `PointLightingConfig` (interface): interface PointLightingConfig — A bounded dynamic point light in world space.
325
331
  - `WELL_KNOWN_MARKER_KINDS` (const): const WELL_KNOWN_MARKER_KINDS: readonly ["player_spawn", "mob", "boss", "vendor", "chest", "travel", "npc", "poi", "prop", "goal", "branch"] — Standard marker kinds recognized with default colors and behavior.
326
332
  - `WELL_KNOWN_PATH_KINDS` (const): const WELL_KNOWN_PATH_KINDS: readonly ["road", "corridor", "branch", "route"] — Standard path kinds recognized with default colors and behavior.
327
333
  - `WELL_KNOWN_VOLUME_KINDS` (const): const WELL_KNOWN_VOLUME_KINDS: readonly ["zone", "flatten", "cluster", "aggro", "leash", "discover", "capture", "prompt", "poi", "respawn_skip"] — Standard volume kinds recognized with default colors and behavior.
@@ -620,6 +626,11 @@
620
626
 
621
627
  - `minimapHandlers` (const): const minimapHandlers: Pick<HandlerTable, "bake_minimap"> — Bake the authored terrain into a stored minimap PNG (#1036). Composes the live viewport's base ground field with the document's sculpt snapshot, rasterizes it top-down via the pure core bake, and stores `{ background, bounds }` on `document.minimap` as an undoable edit — runtime then feeds those straight into the `Minimap`/`WorldMap` props with no re-raster. Needs the mounted viewport's composed sampler, so it is a live-editor-only action, not a headless CLI verb.
622
628
 
629
+ ## @jgengine/editor/handlers/navmesh
630
+
631
+ - `navMeshHandlers` (const): const navMeshHandlers: Pick<HandlerTable, "bakeNavMesh"> — Bake caller-provided indexed geometry into a named document navigation mesh.
632
+ - `warmNavBake` (function): function warmNavBake(): void — Start loading the recast module so the first bake request does not have to wait.
633
+
623
634
  ## @jgengine/editor/handlers/runtime
624
635
 
625
636
  - `runtimeHandlers` (const): const runtimeHandlers: Pick< HandlerTable, | "push_document_patch" | "pull_document_patches" | "document_revision" | "push_runtime_delta" | "pull_runtime_deltas" | "runtime_snapshot" | "runtime_summary" | "runtime_get" | "runtime_set" | "runtime_pause" | "runtime_resume" | "runtime_step" | "set_runt… — Live-sync document patches, the ephemeral runtime reverse channel, and play-mode poke verbs.
@@ -698,6 +709,7 @@
698
709
 
699
710
  ## @jgengine/editor/modelAnimationAuthoring
700
711
 
712
+ - `AnimGraphSource` (type): type AnimGraphSource = "authored" | "locomotion" | "auto" — Where the graph a placement plays comes from.
701
713
  - `AnimationMode` (type): type AnimationMode = "default" | "auto" | "none" | "custom" — Which authoring mode a stored setting represents. `default` = no override key.
702
714
  - `AnimationSetting` (type): type AnimationSetting = AuthoredAnimationConfig | "auto" | "none" — The value stored at `meta.animation`.
703
715
  - `AuthoredAnimationConfig` (interface): interface AuthoredAnimationConfig — Structurally compatible with `ModelAnimationConfig`, but every field optional for authoring.
@@ -709,12 +721,16 @@
709
721
  - `OneShotEvent` (type): type OneShotEvent = (typeof ONE_SHOT_EVENTS)[number] — One-shot animation event a placement can bind a clip to (attack/hit/death/...).
710
722
  - `animationMetaPatch` (function): function animationMetaPatch(setting: AnimationSetting | undefined): { animation: AnimationSetting | undefined } — The meta patch that persists a setting (or clears the override when undefined). Shallow-merges onto `marker.meta` via `setMarker`; an undefined value drops out of the saved JSON document.
711
723
  - `animationMode` (function): function animationMode(setting: AnimationSetting | undefined): AnimationMode — The authoring mode a stored setting maps to.
724
+ - `clearAnimGraph` (function): function clearAnimGraph(setting: AnimationSetting | undefined): AnimationSetting | undefined — Removes a stored graph so `states`/`oneShots` (or clip roles) drive the placement again.
712
725
  - `defaultCustomConfig` (function): function defaultCustomConfig(clips: readonly string[]): AuthoredAnimationConfig — A concrete custom config derived from a rigged asset's clip roles — the `custom` mode seed.
726
+ - `effectiveAnimGraph` (function): function effectiveAnimGraph(setting: AnimationSetting | undefined, clips: readonly string[]): { graph: AnimGraph; source: AnimGraphSource } | null — The graph a placement plays at run time: its stored `graph`, else the locomotion graph of its custom `states`/`oneShots`, else the one clip roles derive for `"auto"` and no-override placements. `null` for `"none"`, a single-clip config, or a rig whose clips have no idle.
713
727
  - `readAnimationSetting` (function): function readAnimationSetting(meta: Record<string, unknown> | undefined): AnimationSetting | undefined — Reads and shape-validates the animation override from a marker's meta; undefined when absent/invalid.
714
728
  - `setAnimationMode` (function): function setAnimationMode(setting: AnimationSetting | undefined, mode: AnimationMode, clips: readonly string[] = []): AnimationSetting | undefined — Switches authoring mode. `custom` seeds from the asset's clip-role defaults (so the user starts from a working config, then overrides) when there is no existing object config; the other modes are the bare string/undefined values.
715
729
  - `setLocomotionClip` (function): function setLocomotionClip(setting: AnimationSetting | undefined, role: LocomotionRole, clipName: string | null): AuthoredAnimationConfig — Sets or clears (null) a locomotion state clip; forces the setting into `custom`.
716
730
  - `setLocomotionNumber` (function): function setLocomotionNumber(setting: AnimationSetting | undefined, key: LocomotionNumber, value: number | null): AuthoredAnimationConfig — Sets or clears (null) a numeric locomotion tuning.
717
731
  - `setOneShotClip` (function): function setOneShotClip(setting: AnimationSetting | undefined, event: string, clipName: string | null): AuthoredAnimationConfig — Binds or clears (null) a one-shot event to a clip.
732
+ - `setTransitionDuration` (function): function setTransitionDuration(setting: AnimationSetting | undefined, graph: AnimGraph, layerId: string, index: number, duration: number): AuthoredAnimationConfig — Sets one transition's crossfade seconds in `graph` and stores the result, so editing a derived graph turns it into an authored one.
733
+ - `storeAnimGraph` (function): function storeAnimGraph(setting: AnimationSetting | undefined, graph: AnimGraph): AuthoredAnimationConfig — Stores `graph` on the placement, keeping its other fields; the stored graph wins at play time.
718
734
 
719
735
  ## @jgengine/editor/networkSnapshot
720
736
 
@@ -810,6 +826,18 @@
810
826
  - `WORKSPACES` (const): const WORKSPACES: readonly RailEntry[] — Rail order and support map. Unsupported modes are visible but disabled — never fake panels.
811
827
  - `WorkspaceRail` (function): function WorkspaceRail({ active, onSelect, }: { active: EditorWorkspace; onSelect: (workspace: EditorWorkspace) => void; }): React.JSX.Element — Narrow left workspace rail. Supported modes activate their home panels; planned modes are disabled with an explanatory tooltip so nothing pretends to work.
812
828
 
829
+ ## @jgengine/editor/shell/animGraphPreview
830
+
831
+ - `GraphParamControl` (interface): interface GraphParamControl — A parameter the preview needs a control for: blend inputs and condition operands.
832
+ - `GraphPreviewFrame` (interface): interface GraphPreviewFrame — What the rig shows at the scrub time.
833
+ - `GraphPreviewInput` (interface): interface GraphPreviewInput — Inputs for {@link simulateGraphPreview}.
834
+ - `MAX_PREVIEW_SECONDS` (const): const MAX_PREVIEW_SECONDS: 20 — Longest preview timeline the scrubber offers, in seconds.
835
+ - `graphClipNames` (function): function graphClipNames(graph: AnimGraph): string[] — Every clip name a graph plays.
836
+ - `graphParamControls` (function): function graphParamControls(graph: AnimGraph): GraphParamControl[] — Parameters a graph reads, with a range covering its blend points and condition values.
837
+ - `graphTriggers` (function): function graphTriggers(graph: AnimGraph): string[] — Every trigger name a graph's transitions listen for, in first-seen order.
838
+ - `recordTrigger` (function): function recordTrigger(triggers: readonly GraphPreviewTrigger[], name: string, at: number): GraphPreviewTrigger[] — Adds a trigger press at `at`, keeping the log in time order.
839
+ - `simulateGraphPreview` (function): function simulateGraphPreview(input: GraphPreviewInput): GraphPreviewFrame — Replays `graph` from its entry states to `time` and reports the pose there.
840
+
813
841
  ## @jgengine/editor/shell/cameraTelemetry
814
842
 
815
843
  - `CameraTelemetry` (interface): interface CameraTelemetry — Per-frame camera orientation channel between the in-canvas probe and the DOM orientation widget. Deliberately a mutable record, not React state: the probe writes every frame and the widget reads on its own rAF loop, applying styles directly — zero shell rerenders.
@@ -73,7 +73,7 @@ bun packages/editor/src/mcp/cli.ts --game <id> \
73
73
  ```
74
74
 
75
75
  - **Required:** `id`, `kind`, and `x`/`z` (`y` optional, defaults to `0`). A game reads its own custom kinds off the document (`editorLayers.markers.filter(m => m.kind === "stash")`), so no engine change is needed to introduce one.
76
- - **Optional:** `color`, `label`, `rotationY`, `meta` — `meta` is validated against the kind's registered schema when one exists (custom kinds skip validation), same as `set_marker`.
76
+ - **Optional:** `color`, `label`, `rotationY`, `catalogId`, `meta` — `catalogId` is the entity kind a `mob`/`boss` marker spawns (without it the marker spawns nothing); `set_marker` patches it too. `meta` is validated against the kind's registered schema when one exists (custom kinds skip validation), same as `set_marker`.
77
77
  - **Id collisions re-id** exactly as `add_path`; the response's `result` carries the id the marker landed under. Use `place_asset` instead when the marker should carry a mesh (it stamps `catalogId`/`meta.assetId`); `add_marker` is for logical, mesh-free markers.
78
78
 
79
79
  ### Click-to-place custom kinds in the GUI
@@ -93,6 +93,17 @@ export const STASH_KIND = definePlaceableMarkerKind({
93
93
  - **Load path:** register from a module the editor loads. Importing your `editorKinds.ts` (side effect) from `editorLayers.ts` covers it — `loadGameLayers` imports `editorLayers.ts` for every editor session, and the game runtime imports it too, so one registration serves both.
94
94
  - `fields` also gives `add_marker`/`set_marker`/`set_meta` a schema to validate that kind's `meta` against; omit `fields` for a bare placeable marker.
95
95
 
96
+ ## Placement animation
97
+
98
+ A placed rigged asset's animation override lives at `marker.meta.animation` and reaches the game through `markerAnimation` → `ModelConfig.animation`. It is `"auto"`, `"none"` or a config with `states`, `oneShots` and an optional stored `graph` (an `AnimGraph`, validated with `parseAnimGraph` on read).
99
+
100
+ - **Inspector → Animation** picks the mode and binds idle/walk/run and one-shot clips.
101
+ - **Animation workspace → Graph** (rail button, or the bottom dock's Animation tab) shows the graph the selected placement plays: layers, states and transitions, with the source (stored, built from states, or derived from clip roles).
102
+ - The scrub slider replays the graph runtime from its entry states. Parameter sliders come from the graph's blend points and conditions.
103
+ - Trigger buttons record a press at the playhead, so scrubbing back and forth replays it. The rig at the camera focus is posed from the runtime's output through the shell's `createGraphPose`.
104
+ - Editing a crossfade, or **Store graph**, writes the graph to `meta.animation.graph` as one undoable edit. **Use derived graph** drops it.
105
+ - Scripted: `set_meta` with `{ "animation": { "graph": { ... } } }` on the marker; see the `jgengine-world` recipe `character-animation.md` for the graph shape.
106
+
96
107
  ## Pure API (`@jgengine/editor`)
97
108
 
98
109
  ```ts
@@ -213,8 +224,7 @@ undoable `setMinimapBake` command. Runtime feeds the stored `background`/`bounds
213
224
  mounted). It is **not** a headless CLI/MCP verb: with no viewport the sampler is null and
214
225
  `bake_minimap` returns `{ ok:false, error:"bake_minimap needs the live editor viewport" }`. To bake
215
226
  a committed scene offline, run a deterministic node script that rebuilds the base field with
216
- `groundFieldFor(world)` and calls `bakeMinimapFromDocument` (see
217
- `scripts/bake-claudecraft-minimap.ts`).
227
+ `groundFieldFor(world)` and calls `bakeMinimapFromDocument`.
218
228
  - GUI: the Terrain panel's **Bake minimap** button calls `bake_minimap` and surfaces any `ok:false`
219
229
  error inline.
220
230
  - RPC/CLI verb: `bake_minimap` (optional `padding`, `resolution`, `waterLevel`). The bake path uses
@@ -11,7 +11,7 @@ Own the spatial realization of game design: player metrics, topology, routes, la
11
11
 
12
12
  All authorable world content belongs in `editor.scene.json`, changed through the editor GUI or RPC/CLI. Never hand-edit the document or keep geometry, paths, spawns, zones, objectives, terrain, foliage, encounters, or coordinate arrays in code. File an editor `[FEATURE]` issue before any fallback.
13
13
 
14
- Read [references/application-playbook.md](references/application-playbook.md) only when the task explicitly calls for spatial design depth — a level/world design brief, an audit or redesign, or "make this fun/better" work; it is not a mandatory step of creating a game. Read [references/live-editor-loop.md](references/live-editor-loop.md) whenever live captures, an editor-agent handoff, or iterative scene improvement is involved. Read [references/field-guide.md](references/field-guide.md) for metric families, topology, pacing, genre lenses, accessibility, and research.
14
+ For a greenfield build, read [references/application-playbook.md](references/application-playbook.md) §1 and produce its metric sheet, topology graph, beat sheet, and encounter cards before art; §3–5 hold the templates. Audits may read only the playbook's audit sections. Read [references/live-editor-loop.md](references/live-editor-loop.md) when a separate editor agent or live editor session applies the changes, or when repairing an existing scene from captures. Read [references/field-guide.md](references/field-guide.md) for metric families, topology, pacing, genre lenses, accessibility, and research.
15
15
 
16
16
  ## Choose the mode
17
17