@jgengine/editor 0.10.0 → 0.12.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.
Files changed (179) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +12 -0
  3. package/dist/AssetBrowser.d.ts +26 -1
  4. package/dist/AssetBrowser.js +54 -1
  5. package/dist/AssetDropZone.d.ts +9 -0
  6. package/dist/AssetDropZone.js +65 -0
  7. package/dist/CatalogsPanel.d.ts +4 -2
  8. package/dist/CatalogsPanel.js +162 -9
  9. package/dist/CityPreview.d.ts +11 -0
  10. package/dist/CityPreview.js +45 -0
  11. package/dist/CollectionsPanel.js +5 -5
  12. package/dist/DebugDraw.d.ts +3 -1
  13. package/dist/DebugDraw.js +37 -19
  14. package/dist/EditorApp.d.ts +9 -1
  15. package/dist/EditorApp.js +208 -37
  16. package/dist/EditorChrome.d.ts +25 -6
  17. package/dist/EditorChrome.js +310 -118
  18. package/dist/InspectorPanel.d.ts +17 -7
  19. package/dist/InspectorPanel.js +173 -27
  20. package/dist/LightingPanel.d.ts +11 -0
  21. package/dist/LightingPanel.js +99 -0
  22. package/dist/MaterialsWorkspacePanel.d.ts +12 -0
  23. package/dist/MaterialsWorkspacePanel.js +86 -0
  24. package/dist/ParentPickerMenu.d.ts +13 -0
  25. package/dist/ParentPickerMenu.js +33 -0
  26. package/dist/PerfProbe.js +69 -2
  27. package/dist/PrefabsPanel.js +5 -5
  28. package/dist/RuntimePlayBridge.d.ts +5 -3
  29. package/dist/RuntimePlayBridge.js +6 -13
  30. package/dist/ScatterPreview.d.ts +3 -1
  31. package/dist/ScatterPreview.js +10 -7
  32. package/dist/SchemaInspector.js +21 -8
  33. package/dist/ScriptingPanel.d.ts +15 -0
  34. package/dist/ScriptingPanel.js +46 -0
  35. package/dist/SelectionGizmo.js +114 -30
  36. package/dist/StandaloneEditor.d.ts +16 -9
  37. package/dist/StandaloneEditor.js +19 -20
  38. package/dist/TerrainPanel.d.ts +3 -2
  39. package/dist/TerrainPanel.js +16 -5
  40. package/dist/TerrainReadout.d.ts +17 -0
  41. package/dist/TerrainReadout.js +163 -0
  42. package/dist/TerrainReadoutHud.d.ts +9 -0
  43. package/dist/TerrainReadoutHud.js +20 -0
  44. package/dist/TerrainSculpt.js +98 -59
  45. package/dist/agent/AgentPanel.d.ts +5 -2
  46. package/dist/agent/AgentPanel.js +11 -6
  47. package/dist/agent/toolBridge.js +5 -0
  48. package/dist/assetImport.d.ts +36 -0
  49. package/dist/assetImport.js +66 -0
  50. package/dist/authoredComponentMeta.d.ts +25 -0
  51. package/dist/authoredComponentMeta.js +65 -0
  52. package/dist/gizmoPivot.d.ts +4 -0
  53. package/dist/gizmoPivot.js +34 -0
  54. package/dist/handlers/catalogs.d.ts +3 -0
  55. package/dist/handlers/catalogs.js +137 -0
  56. package/dist/handlers/collections.d.ts +3 -0
  57. package/dist/handlers/collections.js +57 -0
  58. package/dist/handlers/context.d.ts +36 -0
  59. package/dist/handlers/context.js +1 -0
  60. package/dist/handlers/document.d.ts +3 -0
  61. package/dist/handlers/document.js +350 -0
  62. package/dist/handlers/grids.d.ts +3 -0
  63. package/dist/handlers/grids.js +142 -0
  64. package/dist/handlers/hierarchy.d.ts +3 -0
  65. package/dist/handlers/hierarchy.js +55 -0
  66. package/dist/handlers/index.d.ts +8 -0
  67. package/dist/handlers/index.js +23 -0
  68. package/dist/handlers/minimap.d.ts +9 -0
  69. package/dist/handlers/minimap.js +32 -0
  70. package/dist/handlers/runtime.d.ts +3 -0
  71. package/dist/handlers/runtime.js +191 -0
  72. package/dist/handlers/terrain.d.ts +3 -0
  73. package/dist/handlers/terrain.js +204 -0
  74. package/dist/index.d.ts +3 -2
  75. package/dist/index.js +3 -2
  76. package/dist/listDocumentTriggers.d.ts +42 -0
  77. package/dist/listDocumentTriggers.js +49 -0
  78. package/dist/materialAssignments.d.ts +63 -0
  79. package/dist/materialAssignments.js +84 -0
  80. package/dist/mcp/fieldSpec.d.ts +32 -0
  81. package/dist/mcp/fieldSpec.js +173 -0
  82. package/dist/mcp/rpcRequest.d.ts +8 -5
  83. package/dist/mcp/rpcRequest.js +72 -83
  84. package/dist/mcp/tools.js +104 -763
  85. package/dist/networkSnapshot.d.ts +82 -0
  86. package/dist/networkSnapshot.js +73 -0
  87. package/dist/parentCandidates.d.ts +12 -0
  88. package/dist/parentCandidates.js +25 -0
  89. package/dist/perfPill.d.ts +21 -0
  90. package/dist/perfPill.js +20 -0
  91. package/dist/session.d.ts +167 -5
  92. package/dist/session.js +36 -879
  93. package/dist/shell/AnimationPanel.d.ts +17 -0
  94. package/dist/shell/AnimationPanel.js +134 -0
  95. package/dist/shell/AssetThumbnail.d.ts +12 -0
  96. package/dist/shell/AssetThumbnail.js +23 -0
  97. package/dist/shell/BottomDock.d.ts +25 -0
  98. package/dist/shell/BottomDock.js +40 -0
  99. package/dist/shell/CommandPalette.d.ts +12 -0
  100. package/dist/shell/CommandPalette.js +56 -0
  101. package/dist/shell/ConsolePanel.d.ts +8 -0
  102. package/dist/shell/ConsolePanel.js +45 -0
  103. package/dist/shell/ContentBrowser.d.ts +21 -0
  104. package/dist/shell/ContentBrowser.js +130 -0
  105. package/dist/shell/HierarchyPanel.d.ts +26 -0
  106. package/dist/shell/HierarchyPanel.js +387 -0
  107. package/dist/shell/NetworkWorkspacePanel.d.ts +11 -0
  108. package/dist/shell/NetworkWorkspacePanel.js +49 -0
  109. package/dist/shell/PlayModeBar.d.ts +12 -0
  110. package/dist/shell/PlayModeBar.js +27 -0
  111. package/dist/shell/ProfilerPanel.d.ts +10 -0
  112. package/dist/shell/ProfilerPanel.js +41 -0
  113. package/dist/shell/SceneToolbar.d.ts +43 -0
  114. package/dist/shell/SceneToolbar.js +122 -0
  115. package/dist/shell/StatusBar.d.ts +18 -0
  116. package/dist/shell/StatusBar.js +41 -0
  117. package/dist/shell/TopAppBar.d.ts +33 -0
  118. package/dist/shell/TopAppBar.js +64 -0
  119. package/dist/shell/ViewportOverlays.d.ts +23 -0
  120. package/dist/shell/ViewportOverlays.js +136 -0
  121. package/dist/shell/WorkspaceRail.d.ts +20 -0
  122. package/dist/shell/WorkspaceRail.js +31 -0
  123. package/dist/shell/cameraTelemetry.d.ts +27 -0
  124. package/dist/shell/cameraTelemetry.js +24 -0
  125. package/dist/shell/commandRegistry.d.ts +58 -0
  126. package/dist/shell/commandRegistry.js +133 -0
  127. package/dist/shell/consoleSink.d.ts +11 -0
  128. package/dist/shell/consoleSink.js +17 -0
  129. package/dist/shell/consoleStore.d.ts +27 -0
  130. package/dist/shell/consoleStore.js +43 -0
  131. package/dist/shell/fields.d.ts +39 -0
  132. package/dist/shell/fields.js +104 -0
  133. package/dist/shell/formatSavedRelative.d.ts +2 -0
  134. package/dist/shell/formatSavedRelative.js +14 -0
  135. package/dist/shell/glbThumbnailCache.d.ts +45 -0
  136. package/dist/shell/glbThumbnailCache.js +241 -0
  137. package/dist/shell/hierarchyA11y.d.ts +21 -0
  138. package/dist/shell/hierarchyA11y.js +35 -0
  139. package/dist/shell/icons.d.ts +13 -0
  140. package/dist/shell/icons.js +85 -0
  141. package/dist/shell/layoutStore.d.ts +68 -0
  142. package/dist/shell/layoutStore.js +156 -0
  143. package/dist/shell/pathFlythrough.d.ts +37 -0
  144. package/dist/shell/pathFlythrough.js +54 -0
  145. package/dist/shell/perfHistory.d.ts +70 -0
  146. package/dist/shell/perfHistory.js +130 -0
  147. package/dist/shell/renameObject.d.ts +3 -0
  148. package/dist/shell/renameObject.js +25 -0
  149. package/dist/shell/theme.d.ts +36 -0
  150. package/dist/shell/theme.js +37 -0
  151. package/dist/shell/ui.d.ts +84 -0
  152. package/dist/shell/ui.js +90 -0
  153. package/dist/shell/useGlbThumbnail.d.ts +8 -0
  154. package/dist/shell/useGlbThumbnail.js +19 -0
  155. package/dist/terrainReadoutStore.d.ts +19 -0
  156. package/dist/terrainReadoutStore.js +36 -0
  157. package/dist/uiStore.d.ts +33 -0
  158. package/dist/uiStore.js +13 -0
  159. package/dist/viewportContextMenu.d.ts +4 -2
  160. package/dist/viewportContextMenu.js +5 -1
  161. package/package.json +4 -4
  162. package/skills/jgengine/SKILL.md +35 -804
  163. package/skills/jgengine/api.md +190 -79
  164. package/skills/jgengine/capabilities.md +112 -2
  165. package/skills/jgengine/recipes/minimal-game.md +88 -0
  166. package/skills/jgengine-editor/SKILL.md +29 -362
  167. package/skills/jgengine-editor/api.md +519 -18
  168. package/skills/jgengine-editor/capabilities.md +91 -0
  169. package/skills/jgengine-editor/reference.md +113 -0
  170. package/skills/level-design/SKILL.md +54 -0
  171. package/skills/level-design/agents/openai.yaml +4 -0
  172. package/skills/level-design/references/application-playbook.md +192 -0
  173. package/skills/level-design/references/field-guide.md +337 -0
  174. package/skills/level-design/references/live-editor-loop.md +179 -0
  175. package/dist/OutlinerPanel.d.ts +0 -14
  176. package/dist/OutlinerPanel.js +0 -84
  177. package/skills/jgengine/reference-cartridge.md +0 -72
  178. package/skills/jgengine/reference-orphans.md +0 -9
  179. package/skills/jgengine/reference-systems.md +0 -60
@@ -0,0 +1,82 @@
1
+ import { type MultiplayerAdapterConfig, type MultiplayerAuthority } from "@jgengine/core/runtime/adapter";
2
+ import type { PresencePoseRow } from "@jgengine/core/runtime/transport";
3
+ /** Transport kind declared by the game, or `"unknown"` when the config is unreadable. */
4
+ export type EditorNetworkAdapterKind = MultiplayerAdapterConfig["kind"] | "unknown";
5
+ /**
6
+ * One online actor from a host-supplied presence feed. Fields mirror
7
+ * {@link PresencePoseRow} so hosts can forward backend rows without inventing shape.
8
+ */
9
+ export interface EditorNetworkPresenceActor {
10
+ userId: string;
11
+ position: {
12
+ x: number;
13
+ y: number;
14
+ z: number;
15
+ };
16
+ rotationY: number;
17
+ rotationPitch?: number;
18
+ lastSeenAt?: number;
19
+ }
20
+ /**
21
+ * Optional live session identity when a multiplayer backend is attached.
22
+ * Omit entirely when the host has no session — the panel reports offline honestly.
23
+ */
24
+ export interface EditorNetworkSession {
25
+ userId: string;
26
+ /** Joined room/server id when known; `null` means joined but id not reported. */
27
+ serverId?: string | null;
28
+ feedActions?: readonly string[];
29
+ }
30
+ /**
31
+ * Host-facing network inspection payload for the editor Network workspace.
32
+ *
33
+ * - Adapter fields come from the game definition (always real config).
34
+ * - `session` is present only when the host attached a live multiplayer backend.
35
+ * - `online` is `undefined` when the host has not supplied a presence feed; `[]` when
36
+ * the feed is connected but empty. Never fabricate actors.
37
+ */
38
+ export interface EditorNetworkSnapshot {
39
+ gameId: string;
40
+ adapterKind: EditorNetworkAdapterKind;
41
+ /** `null` for offline / single-player games. */
42
+ authority: MultiplayerAuthority | null;
43
+ /** Optional topology string from the adapter when declared. */
44
+ topology?: string;
45
+ /** Optional endpoint hint from the adapter (ws/socketio url, lan port, p2p room). */
46
+ endpoint?: string;
47
+ session?: EditorNetworkSession;
48
+ /**
49
+ * Online presence roster from the host.
50
+ * - `undefined` — no feed supplied (honest empty / waiting).
51
+ * - array — real rows only (may be empty).
52
+ */
53
+ online?: readonly EditorNetworkPresenceActor[];
54
+ /** Wall-clock ms of the last presence update when the host tracks it. */
55
+ updatedAt?: number;
56
+ }
57
+ /** Optional live presence/session injection from the editor host. */
58
+ export interface EditorNetworkPresenceInput {
59
+ userId?: string;
60
+ serverId?: string | null;
61
+ feedActions?: readonly string[];
62
+ online?: readonly PresencePoseRow[] | readonly EditorNetworkPresenceActor[];
63
+ updatedAt?: number;
64
+ }
65
+ /**
66
+ * Builds a Network workspace snapshot from the game's multiplayer config plus optional live
67
+ * presence rows. Adapter fields always reflect declared config; presence is only included when
68
+ * the host passes it.
69
+ *
70
+ * @internal
71
+ */
72
+ export declare function buildEditorNetworkSnapshot(args: {
73
+ gameId: string;
74
+ multiplayer: unknown;
75
+ presence?: EditorNetworkPresenceInput;
76
+ }): EditorNetworkSnapshot;
77
+ /**
78
+ * True when the snapshot represents a multiplayer-capable game (not offline/unknown-null).
79
+ *
80
+ * @internal
81
+ */
82
+ export declare function isNetworkMultiplayerConfigured(snapshot: EditorNetworkSnapshot): boolean;
@@ -0,0 +1,73 @@
1
+ import { adapterOf, resolveAuthority, } from "@jgengine/core/runtime/adapter";
2
+ function endpointOf(adapter) {
3
+ if (adapter === null)
4
+ return undefined;
5
+ if (adapter.kind === "ws" || adapter.kind === "socketio")
6
+ return adapter.url;
7
+ if (adapter.kind === "lan") {
8
+ const port = adapter.port ?? 8080;
9
+ const path = adapter.path ?? "/ws";
10
+ return `port ${port}${path}`;
11
+ }
12
+ if (adapter.kind === "p2p")
13
+ return adapter.room;
14
+ return undefined;
15
+ }
16
+ function topologyOf(adapter) {
17
+ if (adapter === null || adapter.kind === "offline")
18
+ return undefined;
19
+ return "topology" in adapter ? adapter.topology : undefined;
20
+ }
21
+ /**
22
+ * Builds a Network workspace snapshot from the game's multiplayer config plus optional live
23
+ * presence rows. Adapter fields always reflect declared config; presence is only included when
24
+ * the host passes it.
25
+ *
26
+ * @internal
27
+ */
28
+ export function buildEditorNetworkSnapshot(args) {
29
+ const adapter = adapterOf(args.multiplayer);
30
+ const adapterKind = adapter?.kind ?? "unknown";
31
+ const authority = resolveAuthority(args.multiplayer);
32
+ const snapshot = {
33
+ gameId: args.gameId,
34
+ adapterKind,
35
+ authority,
36
+ };
37
+ const topology = topologyOf(adapter);
38
+ if (topology !== undefined)
39
+ snapshot.topology = topology;
40
+ const endpoint = endpointOf(adapter);
41
+ if (endpoint !== undefined)
42
+ snapshot.endpoint = endpoint;
43
+ const presence = args.presence;
44
+ if (presence !== undefined) {
45
+ if (typeof presence.userId === "string" && presence.userId.length > 0) {
46
+ snapshot.session = {
47
+ userId: presence.userId,
48
+ ...(presence.serverId !== undefined ? { serverId: presence.serverId } : {}),
49
+ ...(presence.feedActions !== undefined ? { feedActions: presence.feedActions } : {}),
50
+ };
51
+ }
52
+ if (presence.online !== undefined) {
53
+ snapshot.online = presence.online.map((row) => ({
54
+ userId: row.userId,
55
+ position: { x: row.position.x, y: row.position.y, z: row.position.z },
56
+ rotationY: row.rotationY,
57
+ ...(row.rotationPitch !== undefined ? { rotationPitch: row.rotationPitch } : {}),
58
+ ...("lastSeenAt" in row && row.lastSeenAt !== undefined ? { lastSeenAt: row.lastSeenAt } : {}),
59
+ }));
60
+ }
61
+ if (presence.updatedAt !== undefined)
62
+ snapshot.updatedAt = presence.updatedAt;
63
+ }
64
+ return snapshot;
65
+ }
66
+ /**
67
+ * True when the snapshot represents a multiplayer-capable game (not offline/unknown-null).
68
+ *
69
+ * @internal
70
+ */
71
+ export function isNetworkMultiplayerConfigured(snapshot) {
72
+ return snapshot.adapterKind !== "offline" && snapshot.adapterKind !== "unknown" && snapshot.authority !== null;
73
+ }
@@ -0,0 +1,12 @@
1
+ import { type EditorDocument } from "@jgengine/core/editor/index";
2
+ /** One option in the Parent-to… picker / inspector parent field. @internal */
3
+ export interface ParentCandidate {
4
+ id: string;
5
+ label: string;
6
+ }
7
+ /**
8
+ * Placeable objects that may become parents of every id in `childIds` without creating a cycle:
9
+ * excludes the children themselves and all of their descendants.
10
+ * @internal
11
+ */
12
+ export declare function listParentCandidates(document: EditorDocument, childIds: readonly string[]): ParentCandidate[];
@@ -0,0 +1,25 @@
1
+ import { collectDescendants, } from "@jgengine/core/editor/index";
2
+ /**
3
+ * Placeable objects that may become parents of every id in `childIds` without creating a cycle:
4
+ * excludes the children themselves and all of their descendants.
5
+ * @internal
6
+ */
7
+ export function listParentCandidates(document, childIds) {
8
+ if (childIds.length === 0)
9
+ return [];
10
+ const banned = collectDescendants(document, childIds);
11
+ for (const id of childIds)
12
+ banned.add(id);
13
+ const labelOf = (node) => node.label ?? node.id;
14
+ const candidates = [
15
+ ...document.markers.map((m) => ({ id: m.id, label: labelOf(m) })),
16
+ ...document.volumes.map((v) => ({ id: v.id, label: labelOf(v) })),
17
+ ...document.paths.map((p) => ({ id: p.id, label: labelOf(p) })),
18
+ ...document.annotations.map((n) => ({
19
+ id: n.id,
20
+ label: n.text.slice(0, 30) || n.id,
21
+ })),
22
+ ].filter((entry) => !banned.has(entry.id));
23
+ candidates.sort((a, b) => a.label.localeCompare(b.label) || a.id.localeCompare(b.id));
24
+ return candidates;
25
+ }
@@ -0,0 +1,21 @@
1
+ /** Toolbar perf-pill health, separating an idle render-on-demand loop from a genuinely slow frame. */
2
+ export type EditorPerfTone = "idle" | "healthy" | "busy";
3
+ /**
4
+ * FPS at or below which an *actively rendering* editor frame is treated as struggling.
5
+ * Only applied when the loop is active — an idle / browser-throttled loop never trips it.
6
+ */
7
+ export declare const EDITOR_PERF_LOW_FPS = 30;
8
+ /**
9
+ * Classify a perf sample for the toolbar pill. The editor renders `frameloop="always"`, so a
10
+ * healthy visible loop sits at ~60fps; a low wall-clock fps only appears when the browser throttles
11
+ * rAF (backgrounded tab / headless screenshot) while nothing is happening. `PerfProbe` measures
12
+ * that idleness (no camera move, no raycast/rebuild, no scene change) and reports it as `active:false`,
13
+ * which classifies here as `"idle"` instead of the red danger cue. The danger cue (`"busy"`) is
14
+ * reserved for sustained low fps while the loop is genuinely active (orbit / drag / sculpt) — the
15
+ * regression actually worth surfacing.
16
+ * @internal — drives the editor toolbar perf pill; not game-facing.
17
+ */
18
+ export declare function classifyEditorPerf(sample: {
19
+ fps: number;
20
+ active: boolean;
21
+ }): EditorPerfTone;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * FPS at or below which an *actively rendering* editor frame is treated as struggling.
3
+ * Only applied when the loop is active — an idle / browser-throttled loop never trips it.
4
+ */
5
+ export const EDITOR_PERF_LOW_FPS = 30;
6
+ /**
7
+ * Classify a perf sample for the toolbar pill. The editor renders `frameloop="always"`, so a
8
+ * healthy visible loop sits at ~60fps; a low wall-clock fps only appears when the browser throttles
9
+ * rAF (backgrounded tab / headless screenshot) while nothing is happening. `PerfProbe` measures
10
+ * that idleness (no camera move, no raycast/rebuild, no scene change) and reports it as `active:false`,
11
+ * which classifies here as `"idle"` instead of the red danger cue. The danger cue (`"busy"`) is
12
+ * reserved for sustained low fps while the loop is genuinely active (orbit / drag / sculpt) — the
13
+ * regression actually worth surfacing.
14
+ * @internal — drives the editor toolbar perf pill; not game-facing.
15
+ */
16
+ export function classifyEditorPerf(sample) {
17
+ if (!sample.active)
18
+ return "idle";
19
+ return sample.fps < EDITOR_PERF_LOW_FPS ? "busy" : "healthy";
20
+ }
package/dist/session.d.ts CHANGED
@@ -1,7 +1,11 @@
1
- import { type DocumentLiveSync, type DocumentPatch, type EditorCatalogDefinition, type EditorCommand, type EditorDocument, type EditorKindVisibility, type EditorLayersInput, type EditorSession, type EditorSessionState, type RuntimeEntityState, type RuntimePlayControl } from "@jgengine/core/editor/index";
2
- import { type TerraformMode, type TerrainMaterialLayer } from "@jgengine/core/world/terraform";
3
- /** How the editor hosts the game: frozen placement view, roamable world, or the real game. */
4
- export type EditorRunMode = "edit" | "walk" | "play";
1
+ import { type DocumentLiveSync, type DocumentPatch, type EditorCatalogDefinition, type EditorCommand, type EditorDocument, type EditorGridPaletteEntry, type EditorKindVisibility, type EditorLayersInput, type EditorSession, type EditorSessionState, type RuntimeEntityState, type RuntimePlayControl } from "@jgengine/core/editor/index";
2
+ import type { TerraformMode, TerrainMaterialLayer } from "@jgengine/core/world/terraform";
3
+ import type { ParamSchema } from "@jgengine/core/scene/sceneKinds";
4
+ import type { TerrainField } from "@jgengine/core/world/terrain";
5
+ /** How the editor hosts the game: frozen placement view, roamable world, the real game, or HUD-layout authoring. */
6
+ export type EditorRunMode = "edit" | "walk" | "play" | "hud";
7
+ /** The accepted run modes, in canonical order — the runtime guard behind {@link EditorRunMode}. @internal */
8
+ export declare const EDITOR_RUN_MODES: readonly EditorRunMode[];
5
9
  /** RPC request shapes the editor host understands, used by the MCP bridge and UI. */
6
10
  export type EditorBridgeRequest = {
7
11
  method: "editor_status";
@@ -24,6 +28,29 @@ export type EditorBridgeRequest = {
24
28
  entryId: string;
25
29
  patch: Record<string, unknown>;
26
30
  label?: string;
31
+ } | {
32
+ method: "add_catalog_entry";
33
+ catalogId: string;
34
+ entryId: string;
35
+ meta?: Record<string, unknown>;
36
+ label?: string;
37
+ } | {
38
+ method: "remove_catalog_entry";
39
+ catalogId: string;
40
+ entryId: string;
41
+ } | {
42
+ method: "add_catalog";
43
+ catalogId: string;
44
+ label?: string;
45
+ schema?: ParamSchema;
46
+ } | {
47
+ method: "remove_catalog";
48
+ catalogId: string;
49
+ } | {
50
+ method: "set_catalog_schema";
51
+ catalogId: string;
52
+ schema: ParamSchema;
53
+ label?: string;
27
54
  } | {
28
55
  method: "list_selection";
29
56
  } | {
@@ -72,6 +99,10 @@ export type EditorBridgeRequest = {
72
99
  method: "set_meta";
73
100
  id: string;
74
101
  patch: Record<string, unknown>;
102
+ } | {
103
+ method: "apply_preset";
104
+ id: string;
105
+ preset: string;
75
106
  } | {
76
107
  method: "select";
77
108
  ids: string[];
@@ -164,6 +195,11 @@ export type EditorBridgeRequest = {
164
195
  } | {
165
196
  method: "convert_scatter";
166
197
  pathId: string;
198
+ } | {
199
+ method: "bake_minimap";
200
+ padding?: number;
201
+ resolution?: number;
202
+ waterLevel?: number;
167
203
  } | {
168
204
  method: "add_foliage";
169
205
  points: {
@@ -236,6 +272,11 @@ export type EditorBridgeRequest = {
236
272
  } | {
237
273
  method: "select_collection";
238
274
  id: string;
275
+ } | {
276
+ method: "set_object_flags";
277
+ ids: string[];
278
+ locked?: boolean;
279
+ hidden?: boolean;
239
280
  } | {
240
281
  method: "batch_set_properties";
241
282
  ids: string[];
@@ -246,6 +287,89 @@ export type EditorBridgeRequest = {
246
287
  method: "assign_material";
247
288
  ids: string[];
248
289
  materialId: string;
290
+ } | {
291
+ method: "list_grids";
292
+ } | {
293
+ method: "get_grid_cell";
294
+ id: string;
295
+ col: number;
296
+ row: number;
297
+ } | {
298
+ method: "add_grid_layer";
299
+ id: string;
300
+ kind: string;
301
+ cols: number;
302
+ rows: number;
303
+ label?: string;
304
+ cellSize?: number;
305
+ origin?: {
306
+ x: number;
307
+ y: number;
308
+ z: number;
309
+ };
310
+ axes?: "xz" | "xy";
311
+ empty?: string;
312
+ palette?: EditorGridPaletteEntry[];
313
+ } | {
314
+ method: "remove_grid_layer";
315
+ id: string;
316
+ } | {
317
+ method: "set_grid_layer";
318
+ id: string;
319
+ label?: string;
320
+ kind?: string;
321
+ visible?: boolean;
322
+ empty?: string;
323
+ cellSize?: number;
324
+ origin?: {
325
+ x: number;
326
+ y: number;
327
+ z: number;
328
+ };
329
+ axes?: "xz" | "xy";
330
+ palette?: EditorGridPaletteEntry[];
331
+ } | {
332
+ method: "paint_grid_cells";
333
+ id: string;
334
+ cells: {
335
+ col: number;
336
+ row: number;
337
+ value: string;
338
+ }[];
339
+ } | {
340
+ method: "fill_grid_rect";
341
+ id: string;
342
+ col0: number;
343
+ row0: number;
344
+ col1: number;
345
+ row1: number;
346
+ value: string;
347
+ } | {
348
+ method: "flood_fill_grid";
349
+ id: string;
350
+ col: number;
351
+ row: number;
352
+ value: string;
353
+ } | {
354
+ method: "resize_grid_layer";
355
+ id: string;
356
+ cols: number;
357
+ rows: number;
358
+ } | {
359
+ method: "import_grid";
360
+ id: string;
361
+ kind: string;
362
+ format: "ascii" | "csv";
363
+ text: string;
364
+ empty?: string;
365
+ cellSize?: number;
366
+ origin?: {
367
+ x: number;
368
+ y: number;
369
+ z: number;
370
+ };
371
+ glyphMap?: Record<string, string>;
372
+ palette?: EditorGridPaletteEntry[];
249
373
  } | {
250
374
  method: "push_document_patch";
251
375
  patch: DocumentPatch;
@@ -319,17 +443,47 @@ export interface EditorAssetInfo {
319
443
  }
320
444
  /** Rolling frame-rate sample published by the in-canvas PerfProbe. */
321
445
  export interface EditorPerfSample {
446
+ /**
447
+ * JS heap used in megabytes when the browser exposes `performance.memory` (Chromium).
448
+ * Omitted entirely when unavailable — never fabricated.
449
+ */
450
+ memoryMb?: number;
322
451
  fps: number;
323
452
  frameMs: number;
324
453
  drawCalls: number;
325
454
  triangles: number;
326
455
  sampledAt: number;
456
+ /**
457
+ * Whether the sampled window saw camera or edit activity. `false` means the render-on-demand /
458
+ * browser-throttled loop is idle, so a low `fps` is expected pacing rather than lag — the pill
459
+ * shows a neutral "idle" instead of the red danger cue.
460
+ */
461
+ active: boolean;
327
462
  /** Avg viewport raycast (pick) time this window — editor-authoring cost, not sim cost. */
328
463
  raycastMs?: number;
329
464
  /** Avg preview-mesh rebuild (displace) time this window — editor-authoring cost, not sim cost. */
330
465
  rebuildMs?: number;
331
466
  /** raycastMs + rebuildMs — total authoring overhead separated from the frame/sim budget. */
332
467
  authoringMs?: number;
468
+ /**
469
+ * Average sim-driver time (ms) from core `devtools.frame` when FrameDriver has recorded frames.
470
+ * Same source as `debug_snapshot` / F2+D Perf panel — omitted when no frame samples exist
471
+ * (e.g. pure edit mode with no runtime tick). Never fabricated.
472
+ */
473
+ simMs?: number;
474
+ /**
475
+ * Average outside-sim time (ms) = frame − sim (render / React / GPU / GC / vsync miss).
476
+ * Same availability rule as `simMs`. Matches `FrameStats.avgOutsideMs`.
477
+ */
478
+ outsideMs?: number;
479
+ /**
480
+ * Top named sim phases (avg ms) from the same frame window when `measure("name", …)` marks exist.
481
+ * Omitted when the frame tracker has no named phases.
482
+ */
483
+ phases?: readonly {
484
+ name: string;
485
+ avgMs: number;
486
+ }[];
333
487
  }
334
488
  /** The live editor's global control surface — session, visibility, camera focus, assets, mode, RPC. */
335
489
  export interface EditorHostApi {
@@ -360,12 +514,20 @@ export interface EditorHostApi {
360
514
  getCatalogDefinitions(): readonly EditorCatalogDefinition[];
361
515
  getPerf(): EditorPerfSample | null;
362
516
  setPerf(sample: EditorPerfSample): void;
517
+ /**
518
+ * The live composed ground field (base procedural terrain + document sculpt) from the mounted
519
+ * viewport, or null when no viewport is mounted. `bake_minimap` reads it to rasterize authored
520
+ * terrain; it exists only while the editor world is mounted, so headless CLIs get a null here.
521
+ */
522
+ getTerrainSampler(): TerrainField | null;
523
+ setTerrainSampler(field: TerrainField | null): void;
363
524
  getMode(): EditorRunMode;
364
525
  setMode(mode: EditorRunMode): void;
365
526
  subscribeMode(listener: (mode: EditorRunMode) => void): () => void;
366
527
  /** Play-mode pause/step gate consumed by the runtime publisher. */
367
528
  getPlayControl(): RuntimePlayControl;
368
- setPlayControl(next: RuntimePlayControl): void;
529
+ /** Normalizes and stores the play control, notifies listeners, and returns the applied value. */
530
+ setPlayControl(next: RuntimePlayControl): RuntimePlayControl;
369
531
  subscribePlayControl(listener: (play: RuntimePlayControl) => void): () => void;
370
532
  handle(request: EditorBridgeRequest): EditorBridgeResponse;
371
533
  }