@jgengine/editor 0.11.0 → 0.13.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 (171) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/README.md +12 -0
  3. package/dist/AssetBrowser.d.ts +33 -1
  4. package/dist/AssetBrowser.js +64 -2
  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 +15 -1
  15. package/dist/EditorApp.js +147 -35
  16. package/dist/EditorChrome.d.ts +27 -6
  17. package/dist/EditorChrome.js +314 -130
  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 +33 -0
  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/TerrainSculpt.js +98 -59
  41. package/dist/agent/AgentPanel.d.ts +5 -2
  42. package/dist/agent/AgentPanel.js +11 -6
  43. package/dist/agent/toolBridge.js +5 -0
  44. package/dist/assetImport.d.ts +36 -0
  45. package/dist/assetImport.js +66 -0
  46. package/dist/authoredComponentMeta.d.ts +25 -0
  47. package/dist/authoredComponentMeta.js +65 -0
  48. package/dist/gizmoPivot.d.ts +4 -0
  49. package/dist/gizmoPivot.js +34 -0
  50. package/dist/handlers/catalogs.d.ts +3 -0
  51. package/dist/handlers/catalogs.js +137 -0
  52. package/dist/handlers/collections.d.ts +3 -0
  53. package/dist/handlers/collections.js +57 -0
  54. package/dist/handlers/context.d.ts +36 -0
  55. package/dist/handlers/context.js +1 -0
  56. package/dist/handlers/document.d.ts +3 -0
  57. package/dist/handlers/document.js +350 -0
  58. package/dist/handlers/grids.d.ts +3 -0
  59. package/dist/handlers/grids.js +142 -0
  60. package/dist/handlers/hierarchy.d.ts +3 -0
  61. package/dist/handlers/hierarchy.js +55 -0
  62. package/dist/handlers/index.d.ts +8 -0
  63. package/dist/handlers/index.js +25 -0
  64. package/dist/handlers/minimap.d.ts +9 -0
  65. package/dist/handlers/minimap.js +32 -0
  66. package/dist/handlers/runtime.d.ts +3 -0
  67. package/dist/handlers/runtime.js +191 -0
  68. package/dist/handlers/streets.d.ts +3 -0
  69. package/dist/handlers/streets.js +159 -0
  70. package/dist/handlers/terrain.d.ts +3 -0
  71. package/dist/handlers/terrain.js +204 -0
  72. package/dist/index.d.ts +3 -2
  73. package/dist/index.js +3 -2
  74. package/dist/listDocumentTriggers.d.ts +42 -0
  75. package/dist/listDocumentTriggers.js +49 -0
  76. package/dist/materialAssignments.d.ts +63 -0
  77. package/dist/materialAssignments.js +84 -0
  78. package/dist/mcp/fieldSpec.d.ts +32 -0
  79. package/dist/mcp/fieldSpec.js +174 -0
  80. package/dist/mcp/rpcRequest.d.ts +8 -5
  81. package/dist/mcp/rpcRequest.js +72 -93
  82. package/dist/mcp/tools.js +105 -912
  83. package/dist/networkSnapshot.d.ts +82 -0
  84. package/dist/networkSnapshot.js +73 -0
  85. package/dist/parentCandidates.d.ts +12 -0
  86. package/dist/parentCandidates.js +25 -0
  87. package/dist/session.d.ts +91 -4
  88. package/dist/session.js +36 -1017
  89. package/dist/shell/AnimationPanel.d.ts +17 -0
  90. package/dist/shell/AnimationPanel.js +134 -0
  91. package/dist/shell/AssetThumbnail.d.ts +12 -0
  92. package/dist/shell/AssetThumbnail.js +23 -0
  93. package/dist/shell/BottomDock.d.ts +25 -0
  94. package/dist/shell/BottomDock.js +40 -0
  95. package/dist/shell/CommandPalette.d.ts +12 -0
  96. package/dist/shell/CommandPalette.js +56 -0
  97. package/dist/shell/ConsolePanel.d.ts +8 -0
  98. package/dist/shell/ConsolePanel.js +45 -0
  99. package/dist/shell/ContentBrowser.d.ts +21 -0
  100. package/dist/shell/ContentBrowser.js +130 -0
  101. package/dist/shell/HierarchyPanel.d.ts +26 -0
  102. package/dist/shell/HierarchyPanel.js +387 -0
  103. package/dist/shell/NetworkWorkspacePanel.d.ts +11 -0
  104. package/dist/shell/NetworkWorkspacePanel.js +49 -0
  105. package/dist/shell/PlayModeBar.d.ts +12 -0
  106. package/dist/shell/PlayModeBar.js +27 -0
  107. package/dist/shell/ProfilerPanel.d.ts +10 -0
  108. package/dist/shell/ProfilerPanel.js +41 -0
  109. package/dist/shell/SceneToolbar.d.ts +43 -0
  110. package/dist/shell/SceneToolbar.js +122 -0
  111. package/dist/shell/StatusBar.d.ts +18 -0
  112. package/dist/shell/StatusBar.js +41 -0
  113. package/dist/shell/TopAppBar.d.ts +35 -0
  114. package/dist/shell/TopAppBar.js +64 -0
  115. package/dist/shell/ViewportOverlays.d.ts +23 -0
  116. package/dist/shell/ViewportOverlays.js +136 -0
  117. package/dist/shell/WorkspaceRail.d.ts +20 -0
  118. package/dist/shell/WorkspaceRail.js +31 -0
  119. package/dist/shell/cameraTelemetry.d.ts +27 -0
  120. package/dist/shell/cameraTelemetry.js +24 -0
  121. package/dist/shell/commandRegistry.d.ts +58 -0
  122. package/dist/shell/commandRegistry.js +133 -0
  123. package/dist/shell/consoleSink.d.ts +11 -0
  124. package/dist/shell/consoleSink.js +17 -0
  125. package/dist/shell/consoleStore.d.ts +27 -0
  126. package/dist/shell/consoleStore.js +43 -0
  127. package/dist/shell/fields.d.ts +39 -0
  128. package/dist/shell/fields.js +104 -0
  129. package/dist/shell/formatSavedRelative.d.ts +2 -0
  130. package/dist/shell/formatSavedRelative.js +14 -0
  131. package/dist/shell/glbThumbnailCache.d.ts +45 -0
  132. package/dist/shell/glbThumbnailCache.js +241 -0
  133. package/dist/shell/hierarchyA11y.d.ts +21 -0
  134. package/dist/shell/hierarchyA11y.js +35 -0
  135. package/dist/shell/icons.d.ts +13 -0
  136. package/dist/shell/icons.js +85 -0
  137. package/dist/shell/layoutStore.d.ts +68 -0
  138. package/dist/shell/layoutStore.js +156 -0
  139. package/dist/shell/pathFlythrough.d.ts +37 -0
  140. package/dist/shell/pathFlythrough.js +54 -0
  141. package/dist/shell/perfHistory.d.ts +70 -0
  142. package/dist/shell/perfHistory.js +130 -0
  143. package/dist/shell/renameObject.d.ts +3 -0
  144. package/dist/shell/renameObject.js +25 -0
  145. package/dist/shell/theme.d.ts +36 -0
  146. package/dist/shell/theme.js +37 -0
  147. package/dist/shell/ui.d.ts +84 -0
  148. package/dist/shell/ui.js +90 -0
  149. package/dist/shell/useGlbThumbnail.d.ts +8 -0
  150. package/dist/shell/useGlbThumbnail.js +19 -0
  151. package/dist/uiStore.d.ts +27 -0
  152. package/dist/uiStore.js +10 -0
  153. package/dist/viewportContextMenu.d.ts +4 -2
  154. package/dist/viewportContextMenu.js +5 -1
  155. package/package.json +4 -4
  156. package/skills/jgengine/SKILL.md +8 -4
  157. package/skills/jgengine/api.md +182 -81
  158. package/skills/jgengine/capabilities.md +104 -2
  159. package/skills/jgengine/recipes/minimal-game.md +88 -0
  160. package/skills/jgengine-editor/SKILL.md +18 -3
  161. package/skills/jgengine-editor/api.md +403 -20
  162. package/skills/jgengine-editor/capabilities.md +46 -0
  163. package/skills/jgengine-editor/reference.md +87 -0
  164. package/skills/level-design/SKILL.md +54 -0
  165. package/skills/level-design/agents/openai.yaml +4 -0
  166. package/skills/level-design/references/application-playbook.md +192 -0
  167. package/skills/level-design/references/field-guide.md +337 -0
  168. package/skills/level-design/references/live-editor-loop.md +179 -0
  169. package/dist/OutlinerPanel.d.ts +0 -14
  170. package/dist/OutlinerPanel.js +0 -84
  171. package/skills/jgengine/reference-cartridge.md +0 -72
@@ -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
+ }
package/dist/session.d.ts CHANGED
@@ -1,7 +1,11 @@
1
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, 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";
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,25 @@ export type EditorBridgeRequest = {
164
195
  } | {
165
196
  method: "convert_scatter";
166
197
  pathId: string;
198
+ } | {
199
+ method: "generate_streets";
200
+ volumeId?: string;
201
+ center?: {
202
+ x: number;
203
+ y: number;
204
+ z: number;
205
+ };
206
+ halfX?: number;
207
+ halfZ?: number;
208
+ seed?: string;
209
+ mode?: "net" | "circuit";
210
+ kind?: string;
211
+ params?: Record<string, unknown>;
212
+ } | {
213
+ method: "bake_minimap";
214
+ padding?: number;
215
+ resolution?: number;
216
+ waterLevel?: number;
167
217
  } | {
168
218
  method: "add_foliage";
169
219
  points: {
@@ -236,6 +286,11 @@ export type EditorBridgeRequest = {
236
286
  } | {
237
287
  method: "select_collection";
238
288
  id: string;
289
+ } | {
290
+ method: "set_object_flags";
291
+ ids: string[];
292
+ locked?: boolean;
293
+ hidden?: boolean;
239
294
  } | {
240
295
  method: "batch_set_properties";
241
296
  ids: string[];
@@ -402,6 +457,11 @@ export interface EditorAssetInfo {
402
457
  }
403
458
  /** Rolling frame-rate sample published by the in-canvas PerfProbe. */
404
459
  export interface EditorPerfSample {
460
+ /**
461
+ * JS heap used in megabytes when the browser exposes `performance.memory` (Chromium).
462
+ * Omitted entirely when unavailable — never fabricated.
463
+ */
464
+ memoryMb?: number;
405
465
  fps: number;
406
466
  frameMs: number;
407
467
  drawCalls: number;
@@ -419,6 +479,25 @@ export interface EditorPerfSample {
419
479
  rebuildMs?: number;
420
480
  /** raycastMs + rebuildMs — total authoring overhead separated from the frame/sim budget. */
421
481
  authoringMs?: number;
482
+ /**
483
+ * Average sim-driver time (ms) from core `devtools.frame` when FrameDriver has recorded frames.
484
+ * Same source as `debug_snapshot` / F2+D Perf panel — omitted when no frame samples exist
485
+ * (e.g. pure edit mode with no runtime tick). Never fabricated.
486
+ */
487
+ simMs?: number;
488
+ /**
489
+ * Average outside-sim time (ms) = frame − sim (render / React / GPU / GC / vsync miss).
490
+ * Same availability rule as `simMs`. Matches `FrameStats.avgOutsideMs`.
491
+ */
492
+ outsideMs?: number;
493
+ /**
494
+ * Top named sim phases (avg ms) from the same frame window when `measure("name", …)` marks exist.
495
+ * Omitted when the frame tracker has no named phases.
496
+ */
497
+ phases?: readonly {
498
+ name: string;
499
+ avgMs: number;
500
+ }[];
422
501
  }
423
502
  /** The live editor's global control surface — session, visibility, camera focus, assets, mode, RPC. */
424
503
  export interface EditorHostApi {
@@ -449,12 +528,20 @@ export interface EditorHostApi {
449
528
  getCatalogDefinitions(): readonly EditorCatalogDefinition[];
450
529
  getPerf(): EditorPerfSample | null;
451
530
  setPerf(sample: EditorPerfSample): void;
531
+ /**
532
+ * The live composed ground field (base procedural terrain + document sculpt) from the mounted
533
+ * viewport, or null when no viewport is mounted. `bake_minimap` reads it to rasterize authored
534
+ * terrain; it exists only while the editor world is mounted, so headless CLIs get a null here.
535
+ */
536
+ getTerrainSampler(): TerrainField | null;
537
+ setTerrainSampler(field: TerrainField | null): void;
452
538
  getMode(): EditorRunMode;
453
539
  setMode(mode: EditorRunMode): void;
454
540
  subscribeMode(listener: (mode: EditorRunMode) => void): () => void;
455
541
  /** Play-mode pause/step gate consumed by the runtime publisher. */
456
542
  getPlayControl(): RuntimePlayControl;
457
- setPlayControl(next: RuntimePlayControl): void;
543
+ /** Normalizes and stores the play control, notifies listeners, and returns the applied value. */
544
+ setPlayControl(next: RuntimePlayControl): RuntimePlayControl;
458
545
  subscribePlayControl(listener: (play: RuntimePlayControl) => void): () => void;
459
546
  handle(request: EditorBridgeRequest): EditorBridgeResponse;
460
547
  }