@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,337 @@
1
+ # Level design field guide
2
+
3
+ Use this reference for spatial planning, authoring, critique, and playtest diagnosis. It synthesizes research and production talks into adaptable tools. There are no universal corridor widths, jump gaps, cover intervals, or pacing curves: derive them from the actual game, camera, controller, opponents, audience, and performance budget.
4
+
5
+ ## Contents
6
+
7
+ 1. Spatial design contract
8
+ 2. Metrics and test spaces
9
+ 3. Topology and spatial grammar
10
+ 4. Beats, pacing, and learning
11
+ 5. Wayfinding and readability
12
+ 6. Encounters and challenge spaces
13
+ 7. Nonlinearity, exploration, and world structure
14
+ 8. Genre lenses
15
+ 9. Multiplayer spaces
16
+ 10. Accessibility and comfort
17
+ 11. JGengine authoring passes
18
+ 12. Playtesting and evidence
19
+ 13. Diagnostic checklist
20
+ 14. Research basis
21
+
22
+ ## 1. Spatial design contract
23
+
24
+ Translate the game-design contract into a one-level hypothesis:
25
+
26
+ | Concern | Question |
27
+ | --- | --- |
28
+ | purpose | What skill, choice, story beat, resource change, or social behavior exists because of this level? |
29
+ | entry | What does the player know, possess, expect, and see on arrival? |
30
+ | arc | How should orientation, pressure, discovery, mastery, and recovery change over time? |
31
+ | exit | What state and understanding should the player carry forward? |
32
+ | topology | What is critical, optional, gated, reversible, looping, or one-way? |
33
+ | constraints | What camera, movement, AI, combat, streaming, multiplayer, and accessibility facts bound the space? |
34
+ | proof | What will a fresh player do if the level works, and what observations would disprove it? |
35
+
36
+ Every space spends production and player attention. Give traversal, vistas, safe rooms, side paths, and empty space a purpose; atmosphere and anticipation are valid purposes when their duration supports the arc.
37
+
38
+ ## 2. Metrics and test spaces
39
+
40
+ ### Measure the real game
41
+
42
+ Author a compact metric gym in the editor. Measure at normal and extreme settings:
43
+
44
+ - walk, sprint, crouch, vehicle, climb, swim, and air speeds
45
+ - acceleration, deceleration, turn radius, strafe behavior, and input latency
46
+ - avatar radius/height, step height, slope limit, clearance, and camera offset/FOV
47
+ - jump height, time aloft, horizontal reach, forgiveness, ledge grab, and fall recovery
48
+ - interaction distance, target acquisition, use duration, and readable feedback range
49
+ - weapon/effect ranges, spread, travel time, damage falloff, reload, and cover exposure
50
+ - AI sensing, pursuit, attack, group size, navigation footprint, and disengage/reset behavior
51
+ - objective readability distance, audio cue range, and UI assistance behavior
52
+ - multiplayer latency assumptions, team size, spawn delay, revive range, and regroup time
53
+ - frame, draw, navigation, simulation, and streaming budgets at the densest intended moment
54
+
55
+ Record units and evidence. Derive spatial values from combinations: traversal distance from speed and desired travel time; arena depth from weapon bands and mobility; landing area from jump variance and camera visibility.
56
+
57
+ ### Metric families
58
+
59
+ Track ranges rather than magic constants:
60
+
61
+ - minimum comfortable, standard, and mastery traversal widths
62
+ - safe, pressured, and impossible reaction distances
63
+ - intimate, tactical, navigational, and landmark sightline bands
64
+ - time from spawn/checkpoint to meaningful action
65
+ - time and distance from failure to retry
66
+ - branch divergence and rejoin time
67
+ - cover/exposure rhythm and flank travel time
68
+ - choke throughput and escape capacity for expected actors
69
+ - clue-to-commitment distance at each decision point
70
+
71
+ ## 3. Topology and spatial grammar
72
+
73
+ ### Draw a graph first
74
+
75
+ Represent the level as nodes and directed edges before detailed geometry. Annotate:
76
+
77
+ - start, goals, exits, checkpoints, safe spaces, peaks, and rewards
78
+ - critical and optional paths
79
+ - locks, keys, ability gates, resource gates, soft gates, and knowledge gates
80
+ - one-way drops, shortcuts, loops, returns, teleports, and failure resets
81
+ - decision points and information available before commitment
82
+ - enemy, hazard, objective, resource, and social pressure zones
83
+ - visibility, audibility, and foreshadowing across non-adjacent nodes
84
+
85
+ Check reachability for every valid state. Check the return route after objectives, not only the approach. A branch should change risk, information, cost, verb use, story, or experience; geometry alone does not create meaningful choice.
86
+
87
+ ### Compose space deliberately
88
+
89
+ Use contrast to make places legible and memorable:
90
+
91
+ - compression -> threshold -> release
92
+ - exposure -> refuge -> re-entry
93
+ - low -> high, narrow -> broad, dark -> bright, quiet -> loud
94
+ - concealed approach -> reveal -> understood destination
95
+ - path -> edge -> district -> node -> landmark relationships
96
+
97
+ Preserve silhouettes and negative space. Repetition teaches a visual/spatial grammar; variation creates interest only while the grammar remains recognizable.
98
+
99
+ ### Respect camera and viewpoint
100
+
101
+ Design what the player can perceive, not only the plan view. Validate corners, slopes, doors, stairs, jumps, cover, targets, and landmarks from the actual camera at gameplay speed. Third-person cameras need obstruction and shoulder-space checks; first-person spaces need motion/scale checks; top-down spaces need silhouette separation and input target clarity.
102
+
103
+ ## 4. Beats, pacing, and learning
104
+
105
+ ### Write a beat sheet
106
+
107
+ Useful beat types include arrival, orientation, foreshadowing, approach, choice, commitment, engagement, escalation, reversal, climax, aftermath, reward, reflection, and recovery. Record duration or distance plus separate intensity estimates for:
108
+
109
+ - threat/resource pressure
110
+ - motor precision and timing
111
+ - cognitive load and novelty
112
+ - navigation uncertainty
113
+ - emotional/narrative weight
114
+ - social coordination
115
+
116
+ Graph the axes. Avoid treating all intensity as one number: a quiet navigation puzzle can be cognitively intense while physically calm.
117
+
118
+ Create waves and contrast. Sustained peak pressure becomes a new baseline and prevents reflection. Fixed authored beats can coexist with adaptive population or pacing systems, but protect landmark encounters, guaranteed rests, and minimum recovery windows.
119
+
120
+ ### Sequence learning
121
+
122
+ For each required mechanic:
123
+
124
+ 1. signal the opportunity before punishment
125
+ 2. isolate the mechanic in a low-cost situation
126
+ 3. let the player act and see a clear consequence
127
+ 4. test independent use
128
+ 5. combine it with one mastered demand
129
+ 6. vary context or invert an assumption
130
+ 7. provide a mastery application and later recall
131
+
132
+ This is a diagnostic progression, not a mandatory number of rooms. Skip, compress, or branch based on target players and observed mastery. Never confuse obscurity with difficulty.
133
+
134
+ ## 5. Wayfinding and readability
135
+
136
+ ### Build an information hierarchy
137
+
138
+ At a glance, players should distinguish:
139
+
140
+ 1. immediate hazards and safe footing
141
+ 2. interactables, opponents, allies, and objectives
142
+ 3. current route choices and traversal boundaries
143
+ 4. distant goals, districts, and optional opportunities
144
+ 5. decorative context
145
+
146
+ Use multiple aligned channels:
147
+
148
+ - framing, leading edges, occlusion/reveal, and dominant silhouettes
149
+ - light/value contrast and restrained color coding
150
+ - motion, particles, animation, and population flow
151
+ - audio direction, rhythm, and environmental sound
152
+ - material, shape, scale, signage, and repeated affordance language
153
+ - vistas, landmarks, breadcrumbs, path wear, and environmental change
154
+
155
+ Do not make critical meaning depend on color or audio alone. Decorative contrast must not compete with gameplay signals.
156
+
157
+ ### Design decision points
158
+
159
+ Before commitment, show enough of each choice to form an intention: destination, risk, cost, verb requirement, or likely reward. After commitment, confirm the route and preserve a mental link to origin. Make return paths readable from the reverse direction; a one-way composition often fails backward.
160
+
161
+ Layer navigation assistance: spatial cues first, optional map/compass/objective aids second, explicit route guidance when requested or required for access. If every tester follows a marker while ignoring the world, test without it to expose the spatial problem.
162
+
163
+ ## 6. Encounters and challenge spaces
164
+
165
+ ### Write an encounter card
166
+
167
+ - purpose in the level arc
168
+ - player entry position, knowledge, resources, and likely condition
169
+ - enemies/hazards/objectives and their readable introduction
170
+ - geometry supporting each core verb and counterplay
171
+ - initial choice, escalation trigger, reinforcement rules, and peak
172
+ - resources, cover/refuge, exits, retreat, and alternate approaches
173
+ - success/failure state, reset, reward, and downstream consequence
174
+ - expected duration, intensity axes, and performance budget
175
+
176
+ ### Spatial combat checks
177
+
178
+ - sightlines match weapon and perception ranges
179
+ - cover has readable protection, exposure, flanking, and escape relationships
180
+ - vertical advantage has counters and does not hide essential information
181
+ - enemy entrances are foreshadowed and do not feel like arbitrary materialization
182
+ - spawn locations avoid immediate unavoidable damage and repeated trapping
183
+ - the arena supports movement instead of collapsing every fight into one safe corner
184
+ - retreat and recovery do not accidentally reset or exploit the whole encounter
185
+ - resource placement changes decisions rather than merely refilling after them
186
+
187
+ For stealth, model visibility, audibility, patrol information, hiding capacity, bypasses, alarm propagation, search, and recovery. For puzzles, model known facts, manipulable state, hypothesis space, feedback, undo/reset, and whether failure adds information.
188
+
189
+ ## 7. Nonlinearity, exploration, and world structure
190
+
191
+ Use hubs, loops, braided paths, spokes, layered spaces, or open fields according to the verbs and orientation burden. More branches do not automatically create agency.
192
+
193
+ Distinct routes can trade:
194
+
195
+ - speed versus safety
196
+ - information versus resources
197
+ - execution difficulty versus planning complexity
198
+ - stealth versus confrontation
199
+ - immediate reward versus future access
200
+ - authored story versus systemic discovery
201
+ - solo efficiency versus group support
202
+
203
+ Reward exploration with knowledge, tactical position, shortcuts, expressive content, systemic advantage, story, resources with real use, or a changed understanding of the world. Uniform collectibles turn discovery into coverage work.
204
+
205
+ Control density. Leave room for anticipation, long views, traversal mastery, and ecological/narrative coherence. Revisit value rises when routes unlock, world state changes, new verbs reinterpret old obstacles, or return travel is meaningfully compressed.
206
+
207
+ ## 8. Genre lenses
208
+
209
+ ### Platforming and traversal
210
+
211
+ Prioritize landing visibility, input commitment, correction windows, camera stability, readable collision, consequence, restart speed, and progression from isolated movement to chained decisions. Optional mastery routes can carry tighter metrics without blocking the critical path.
212
+
213
+ ### Racing and vehicles
214
+
215
+ Design sight distance from speed and braking, corners from handling and camera, passing from route width and line tradeoffs, recovery from crash cost, and landmarks from rapid recognition. Validate reverse/off-course behavior and vehicle congestion.
216
+
217
+ ### Puzzle
218
+
219
+ Separate discovering the rule from executing it. Present sufficient information, minimize irrelevant state, keep causality visible, support undo/reset, and test whether hints preserve the player's final inference. Combine mechanics only after their individual language is learned.
220
+
221
+ ### Survival and horror
222
+
223
+ Use incomplete information deliberately but keep rules trustworthy. Space controls vulnerability, refuge, resource forecasting, pursuit, and route commitment. Alternate anticipation, exposure, consequence, and relief; random pressure still needs fairness constraints.
224
+
225
+ ### Strategy, tactics, and defense
226
+
227
+ Make territory, ranges, lanes, objectives, buildable areas, influence, and path consequences legible from the command view. Check dominant positions, stalled states, path manipulation, economy access, and whether early advantage becomes irreversible.
228
+
229
+ ### Exploration and narrative
230
+
231
+ Let navigation itself reveal relationships, history, and choice. Align story beats with movement and attention rather than competing exposition. Optional spaces should deepen context or play, not contain the explanation required to understand the critical path.
232
+
233
+ ## 9. Multiplayer spaces
234
+
235
+ Symmetry guarantees sameness, not balance; asymmetry can be fair when route time, information, objectives, roles, and counterplay support the intended mode. Measure from every spawn and team state.
236
+
237
+ Validate:
238
+
239
+ - time to first choice, first contact, objective, power position, and regroup
240
+ - spawn visibility, nearby threats, exits, ally access, and repeat-kill risk
241
+ - lane and flank value, rotation time, choke capacity, and sightline dominance
242
+ - objective approach diversity, defense depth, contest readability, and overtime state
243
+ - callout landmarks and team-color/accessibility readability
244
+ - player-count scaling, uneven teams, join-in-progress, spectators, and disconnects
245
+ - novice traffic versus expert routes; both cohorts can expose different failures
246
+ - exploits involving geometry, camera, collision, projectiles, AI, or destruction
247
+
248
+ Use heatmaps and outcome data to locate questions, then watch matches to explain them. A 50% win rate can conceal miserable spawns or a dominant strategy mirrored by both teams.
249
+
250
+ ## 10. Accessibility and comfort
251
+
252
+ Treat navigation, challenge, and sensory load as adjustable dimensions:
253
+
254
+ - communicate objectives and progress without requiring memory alone
255
+ - offer map, breadcrumb, high-contrast, audio/visual cue, and route-assist options
256
+ - avoid color-only teams, routes, hazards, or puzzle state
257
+ - provide timing, precision, damage, resource, puzzle, and navigation assists separately
258
+ - reduce repeated travel and execution after demonstrated mastery
259
+ - support pause where topology permits and provide safe stopping points
260
+ - validate motion, camera acceleration, head bob, FOV, flashing, visual clutter, and audio dependence
261
+ - make shortcuts, skips, and recovery usable without stigma or lost progress
262
+
263
+ Accessibility options can preserve fantasy while changing the barrier. Test with affected players rather than guessing from a checklist.
264
+
265
+ ## 11. JGengine authoring passes
266
+
267
+ Use `jgengine-editor` GUI or RPC/CLI for every pass; never hand-edit `editor.scene.json`.
268
+
269
+ 1. **Metrics:** author the movement/camera/interaction test space.
270
+ 2. **Topology:** place stable semantic markers, zones, paths, starts, goals, gates, and checkpoints.
271
+ 3. **Graybox:** establish terrain, boundaries, traversal, sightlines, encounter volume, and reset routes.
272
+ 4. **Gameplay:** bind systems to authored ids/kinds; keep coordinates in the document.
273
+ 5. **Wayfinding:** add landmarks, composition, lighting/material hierarchy, signage, and redundant cues.
274
+ 6. **Population:** place actors, resources, hazards, rewards, foliage, and props with budget awareness.
275
+ 7. **Art:** refine materials, asset variation, environmental story, and atmosphere without erasing readability.
276
+ 8. **Optimization:** validate dense views, navigation, collision, instancing, streaming, and bounded runtime queries.
277
+
278
+ Use stable names such as `spawn:*`, `goal:*`, `route:*`, `zone:*`, or project-specific semantic kinds consistently. Runtime and tests query semantics; they do not duplicate positions. If an authoring kind is missing, file the editor feature issue first.
279
+
280
+ ## 12. Playtesting and evidence
281
+
282
+ ### Test in layers
283
+
284
+ - **metric test:** can the controller perform intended movement reliably?
285
+ - **first-look test:** where does a fresh player look and go before prompting?
286
+ - **route test:** what choices, reversals, loops, and missed paths occur?
287
+ - **encounter test:** what positions, verbs, resources, failures, and exploits dominate?
288
+ - **pacing test:** where do hesitation, fatigue, pressure, relief, and payoff occur?
289
+ - **return test:** can players navigate backward or after world-state changes?
290
+ - **accessibility test:** which sensory, motor, cognitive, or timing barriers are avoidable?
291
+ - **multiplayer test:** how do skill, team state, count, latency, and social behavior alter flow?
292
+
293
+ Capture build/version, player profile, timestamps, path/position samples, deaths/damage, retries, objective state, interaction failures, camera direction at decisions, dwell, resource state, and completion time only when they answer a design question.
294
+
295
+ Watch without coaching. Ask afterward what the player believed the goal, rule, route, risk, and consequence were. Diagnose the smallest upstream cause:
296
+
297
+ - missing or competing information
298
+ - misleading affordance or composition
299
+ - bad geometry/metrics
300
+ - mechanic not learned
301
+ - execution burden
302
+ - encounter or economy tuning
303
+ - reset/recovery friction
304
+ - runtime bug
305
+
306
+ Change one causal variable, preserve before/after evidence, and retest. A polished screenshot proves composition, not playability; a scene summary proves authored presence, not visual quality.
307
+
308
+ ## 13. Diagnostic checklist
309
+
310
+ - Does the level serve a named loop, pillar, skill, or story purpose?
311
+ - Are dimensions derived from measured controller, camera, actor, and network behavior?
312
+ - Can players see enough to choose before commitment?
313
+ - Does every required mechanic appear before it is demanded?
314
+ - Are critical path, optional paths, return routes, and reset routes all valid?
315
+ - Do beats vary across separate intensity axes and include recovery?
316
+ - Do art, lighting, audio, and UI reinforce rather than contradict spatial guidance?
317
+ - Does every encounter support intended verbs, counterplay, retreat, and fair recovery?
318
+ - Are exploration rewards meaningful and route choices distinct?
319
+ - Are objectives, hazards, footing, interactables, and teams readable without one sensory channel?
320
+ - Do multiplayer spawns, timings, objectives, and power positions survive different skill bands and counts?
321
+ - Does `editor.scene.json` remain the sole source of authored spatial truth?
322
+ - Has a fresh target player completed the scenario without designer coaching?
323
+
324
+ ## 14. Research basis
325
+
326
+ - David Shaver and Robert Yang, GDC [“Invisible Intuition”](https://www.gdcvault.com/play/1025360/Level-): blockmesh, composition, light, effects, audio, and early tests as aligned wayfinding channels.
327
+ - Jane Ng, GDC [“Designing for Exploration and Choice in Firewatch”](https://www.gdcvault.com/play/1022409/Designing-for-Exploration-and-Choice): world structure, goals, gating, and encounter thinking for non-combat exploration.
328
+ - Matt Thorson, GDC [“Designing Celeste”](https://www.gdcvault.com/play/1024307/Level-Design-Workshop-Designing-Celeste): movement-driven stages, area arrangement, progression, and story integration.
329
+ - Mike Booth / Valve, [“The AI Systems of Left 4 Dead”](https://steamcdn-a.akamaihd.net/apps/valve/2009/ai_systems_of_l4d_mike_booth.pdf): measure player intensity and separate adaptive pacing frequency from difficulty amplitude.
330
+ - Jolie Menzel, GDC [“Solving Puzzle Design”](https://www.gdcvault.com/play/1023139/Level-): information, goals, teaching, iteration, and frustration diagnosis in spatial puzzles.
331
+ - Andrew Yoder, GDC [“The Holy Grail of Multiplayer Level Design”](https://gdcvault.com/play/1025183/Level-Design-Workshop-The-Holy): test graybox maps with casual and competitive cohorts and combine data with observation.
332
+ - Clemence Maurer, GDC [“Rewarding Exploration in Deus Ex: Mankind Divided”](https://gdcvault.com/play/1024305/Level-Design-Workshop-Rewarding-Exploration): connect optional navigation, systemic advantage, world coherence, and environmental story.
333
+ - Dave Feltham, GDC [“Emotional Journey”](https://gdcvault.com/play/1018166/Emotional-Journey-BioWare-s-Methods): use measured level pacing and flow to reinforce narrative arcs.
334
+ - Matthias Worch, GDC [“Decisions That Matter”](https://gdcvault.com/play/1020570/): informed spatial choices, consequences, possibility space, and agency.
335
+ - Brendon Chung, GDC [“Wayfinding & Storytelling Techniques”](https://www.gdcvault.com/play/1022117/level-design-in-a-day): architecture, navigation, environmental information, and common wayfinding failures.
336
+ - Luke McMillan, [“The Metrics of Space”](https://www.gamedeveloper.com/design/the-metrics-of-space-tactical-level-design): derive tactical space from viewpoint, awareness, and gameplay metrics rather than decoration.
337
+ - Microsoft, [Xbox Accessibility Guidelines](https://learn.microsoft.com/en-us/xbox/accessibility/guidelines): objective clarity, input, object readability, redundant cues, timing, challenge options, and sensory comfort.
@@ -0,0 +1,179 @@
1
+ # Live editor loop
2
+
3
+ Use this protocol to turn live play evidence into safe, executable instructions for an agent controlling the JGengine editor. The level designer owns the hypothesis and acceptance criteria; the editor agent owns document operations. Never send “make this area better” or infer that a successful tool call changed runtime play.
4
+
5
+ ## Contents
6
+
7
+ 1. Closed loop
8
+ 2. Authority and capability gate
9
+ 3. Evidence packet
10
+ 4. Editor-agent prompt contract
11
+ 5. Mutation policy
12
+ 6. Verification and iteration
13
+ 7. Example: spawn repair
14
+
15
+ ## 1. Closed loop
16
+
17
+ Repeat this sequence until the acceptance scenario passes or a stop condition is reached:
18
+
19
+ `capture -> inspect -> map pixels to scene/runtime ids -> form one spatial hypothesis -> brief editor agent -> author -> inspect document -> replay same scenario -> compare`
20
+
21
+ Preserve the same game state, viewport size, player pose, camera orientation, input sequence, and capture moment when comparing iterations. A changed screenshot from a different pose is not before/after evidence.
22
+
23
+ The parent level-design agent must retain authority over the player journey. Do not delegate “improve the level” without first defining the behavior to change, evidence, scope, and falsifier.
24
+
25
+ ## 2. Authority and capability gate
26
+
27
+ Before composing mutations, establish all of the following:
28
+
29
+ - authoritative persisted scene path and current document revision
30
+ - whether the live editor session loaded that same document
31
+ - whether runtime consumes the target marker/path/volume/meta directly
32
+ - visible objects mapped to stable document ids, runtime ids, or catalog ids
33
+ - registered marker, volume, and path kinds plus their schemas
34
+ - available placeable assets, prefabs, collections, terrain materials, and catalog entries
35
+ - whether target changes are scene, asset/catalog, gameplay, UI, or renderer concerns
36
+
37
+ In the editor agent, begin with `editor_status`, `document_revision` with the document included, `scene_summary`, `list_layers`, `list_assets`, `list_catalogs`, and—when connected to play—`runtime_summary`. Use `runtime_get` for specific visible entities.
38
+
39
+ Stop instead of mutating when:
40
+
41
+ - the live document differs from the runtime's persisted scene
42
+ - a target position exists only in code or is duplicated between code and scene
43
+ - an expected stable id is missing or ambiguous
44
+ - a required kind/schema or mutation cannot be expressed
45
+ - another human/agent changed the base revision
46
+ - the requested fix belongs to model scale/pivot, animation, UI, gameplay rules, or rendering rather than scene authoring
47
+
48
+ Report the precise ownership or editor capability gap. File the required `[FEATURE]` issue before a code fallback. A scene prompt cannot repair an authority split.
49
+
50
+ ## 3. Evidence packet
51
+
52
+ Build the editor handoff from observed evidence:
53
+
54
+ | Field | Required content |
55
+ | --- | --- |
56
+ | scenario | game state, objective, player pose, camera, input sequence, capture time |
57
+ | artifacts | absolute before-capture paths and relevant document/runtime summaries |
58
+ | observation | what dominates, hides, contradicts, confuses, delays, or misleads |
59
+ | player consequence | the wrong belief/action or missing decision caused by the space |
60
+ | hypothesis | one causal spatial change expected to alter that behavior |
61
+ | metrics | controller/camera/interaction/encounter measurements with units |
62
+ | target ids | existing semantic ids and catalog/runtime links; no guessed ids |
63
+ | preserve | working routes, landmarks, content, silhouettes, performance budget, and unrelated user work |
64
+ | scope | one spawn, route, zone, encounter, or teaching sequence |
65
+ | acceptance | document assertions plus replay, timing, comprehension, and visual thresholds |
66
+ | falsifier | observation that means the hypothesis failed |
67
+
68
+ Distinguish direct observation (“the NPC fills the left half of the spawn frame”) from inference (“the player may assume the NPC is the immediate objective”). Require replay or player evidence for the inference.
69
+
70
+ ## 4. Editor-agent prompt contract
71
+
72
+ Send this structure to the agent controlling the live editor:
73
+
74
+ ```text
75
+ You are authoring <game>/<scene> through the provided JGengine editor tools.
76
+ Do not hand-edit JSON or source code. Do not invent unavailable ids, kinds, assets, or metadata.
77
+
78
+ PLAYER OUTCOME
79
+ <observable behavior the revised space must cause>
80
+
81
+ FIXED REPLAY SCENARIO
82
+ <state, spawn/pose, camera, inputs, capture moment, viewport>
83
+
84
+ EVIDENCE
85
+ <capture paths, document/runtime summaries, observations separated from inferences>
86
+
87
+ SPATIAL HYPOTHESIS
88
+ <one causal claim; why these scene changes should alter player behavior>
89
+
90
+ METRICS AND CONSTRAINTS
91
+ <measured movement/camera/interaction/AI values, scope, performance, accessibility>
92
+
93
+ PRESERVE
94
+ <ids, routes, content, authored work, silhouettes, and behavior that must not change>
95
+
96
+ TARGET SEMANTICS
97
+ <existing ids to inspect plus required stable ids/kinds/meta; describe additions semantically>
98
+
99
+ AUTHORING SEQUENCE
100
+ 1. Call editor_status, document_revision(includeDocument=true), scene_summary, list_layers,
101
+ list_assets, list_catalogs, and runtime_summary when available.
102
+ 2. Verify that the live document is authoritative and every target maps to a stable id/runtime consumer.
103
+ If not, stop and return an ownership/capability report without mutation.
104
+ 3. Focus the camera on the target and inspect its nearby ids, hierarchy, collections, terrain, and runtime entities.
105
+ 4. Return a concise proposed operation list tied to the hypothesis. Then execute one coherent undoable patch group.
106
+ 5. Re-read the document revision and target objects. Check reachability, collision space, sightlines,
107
+ spawn safety, reverse travel, encounter reset, and performance implications.
108
+ 6. Switch to walk/play mode only when safe, exercise the fixed scenario, and report what still requires
109
+ the parent agent's external capture or gameplay verification.
110
+
111
+ MUTATION RULES
112
+ - Use direct setters for existing objects, place_asset for registered assets, and terrain/prefab tools for their domains.
113
+ - For structural add/remove operations, use push_document_patch with type=commands and the exact current
114
+ baseRevision. Use validated EditorCommand shapes; never force a revision mismatch.
115
+ - Prefer stable semantic ids and one coherent patch over many anonymous props.
116
+ - Do not use import_document for ordinary iteration or replace unrelated scene content.
117
+ - Keep every edit undoable. On a rejected operation or broken invariant, stop or undo rather than improvising in code.
118
+
119
+ REQUIRED OPERATIONS
120
+ <ordered add/move/remove/retag/terrain operations; state purpose and intended downstream consumer,
121
+ but let the editor resolve exact coordinates from inspected scene context and measured constraints>
122
+
123
+ ACCEPTANCE
124
+ <document assertions, play behavior, timing, wayfinding, visual composition, reset/exploit checks>
125
+
126
+ FALSIFIER
127
+ <what observed result means this patch should be undone or revised>
128
+
129
+ RETURN
130
+ - before and after document revisions
131
+ - exact successful operations and affected ids
132
+ - rejected/skipped operations with reasons
133
+ - unresolved non-scene ownership
134
+ - document assertions performed
135
+ - the exact replay/capture request for the parent agent
136
+ ```
137
+
138
+ Do not put speculative coordinates into the brief. Give topology, relative relationships, measured clearances, timing targets, and target semantics. The editor agent inspects the current scene and chooses positions that satisfy them; the parent verifies the result live.
139
+
140
+ ## 5. Mutation policy
141
+
142
+ Use the narrowest safe editor operation:
143
+
144
+ | Intent | Preferred operation |
145
+ | --- | --- |
146
+ | inspect authority/state | `document_revision`, `scene_summary`, `list_layers`, `runtime_summary` |
147
+ | inspect target | `get_marker`, `get_volume`, `runtime_get`, `camera_goto`, `hierarchy` |
148
+ | move/rotate existing | `set_transform` |
149
+ | retag or patch semantics | `set_marker`, `set_volume`, `set_path`, `set_meta` |
150
+ | place registered prop | `place_asset` |
151
+ | add/remove semantic object | versioned `push_document_patch` with `addMarker`/`addVolume`/`addPath`/`remove` commands |
152
+ | repeated authored kit | prefab and collection tools |
153
+ | terrain form/material | sculpt/paint/layer tools |
154
+ | test an uncertain live pose | runtime override, then `write_back_override` only after validation |
155
+ | recover failed hypothesis | `undo` and re-run the fixed scenario |
156
+
157
+ Never use runtime overrides as completion evidence: they are ephemeral until written back, and write-back is valid only for document-linked entities.
158
+
159
+ ## 6. Verification and iteration
160
+
161
+ After the editor agent returns:
162
+
163
+ 1. Confirm the persisted document diff contains only intended ids and semantics.
164
+ 2. Assert the required marker/path/volume/prefab/terrain state.
165
+ 3. Confirm runtime consumers query those ids rather than copied positions.
166
+ 4. Replay the identical scenario and capture the identical views.
167
+ 5. Inspect both images for first-look hierarchy, goal/threat/affordance readability, occlusion, safe footing, and world/UI agreement.
168
+ 6. Measure the behavioral claim: orientation time, first action/decision, route completion, encounter result, failure/retry, or reverse navigation.
169
+ 7. Keep, revise, or undo the patch based on the falsifier. Change one causal hypothesis per iteration.
170
+
171
+ Scene counts prove ownership, tests prove transitions, driven play proves progress, and screenshots prove visible composition. Require all applicable rungs.
172
+
173
+ ## 7. Example: spawn repair
174
+
175
+ For a spawn capture where an oversized NPC, placeholder prop, and weapon silhouette obscure the destination, do not send “make the spawn prettier.” Send a brief whose outcome is: “within five seconds, a fresh player looks toward the first route, recognizes the NPC as optional/safe, and has clear movement space.”
176
+
177
+ The editor agent must first resolve the player-spawn marker, NPC runtime/document link, visible prop ids, route id, camera direction, and persisted scene revision. It may then move or regroup authored props, establish a foreground-to-landmark composition, preserve collision clearance, and add semantic onboarding markers only when the runtime consumes them. It must report model-scale or viewmodel defects as asset/rendering work, not fake their repair with arbitrary scene transforms.
178
+
179
+ If the player/NPC coordinates come from code while nearby props come from the scene, stop: migrate authority before spatial polish. Otherwise the before capture and editor session describe different worlds.
@@ -1,14 +0,0 @@
1
- import type { EditorSession } from "@jgengine/core/editor/index";
2
- import type { EditorHostApi } from "./session.js";
3
- /**
4
- * The scene outliner as an isolated, selector-subscribed, virtualized panel. It reads only the
5
- * document + selection slices via `useStoreSelector`, so UI-only churn (gizmo mode, snapping, tool
6
- * changes) never rerenders it, and it windows its row list with `virtualWindow`, so a scene with
7
- * thousands of objects only ever mounts the visible handful of rows. Memoized against its stable
8
- * `session`/`api` props.
9
- * @internal — mounted by `EditorChrome`.
10
- */
11
- export declare const OutlinerPanel: import("react").MemoExoticComponent<({ session, api, }: {
12
- session: EditorSession;
13
- api: EditorHostApi;
14
- }) => import("react").JSX.Element>;
@@ -1,84 +0,0 @@
1
- import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { memo, useEffect, useMemo, useRef, useState } from "react";
3
- import { MATERIAL_DRAG_MIME } from "./AssetBrowser.js";
4
- import { flattenOutliner } from "./outlinerModel.js";
5
- import { shallowArrayEqual, useStoreSelector, virtualWindow } from "./useStoreSelector.js";
6
- const ROW_HEIGHT = 26;
7
- const INPUT = "rounded-md border border-white/10 bg-black/40 px-2 py-1 outline-none transition-colors placeholder:text-neutral-600 focus:border-cyan-400/60 focus:bg-black/60";
8
- /**
9
- * The scene outliner as an isolated, selector-subscribed, virtualized panel. It reads only the
10
- * document + selection slices via `useStoreSelector`, so UI-only churn (gizmo mode, snapping, tool
11
- * changes) never rerenders it, and it windows its row list with `virtualWindow`, so a scene with
12
- * thousands of objects only ever mounts the visible handful of rows. Memoized against its stable
13
- * `session`/`api` props.
14
- * @internal — mounted by `EditorChrome`.
15
- */
16
- export const OutlinerPanel = memo(function OutlinerPanel({ session, api, }) {
17
- const document = useStoreSelector(session, (state) => state.document);
18
- const selection = useStoreSelector(session, (state) => state.selection, shallowArrayEqual);
19
- const selectedId = selection[0];
20
- const [view, setView] = useState("kind");
21
- const [query, setQuery] = useState("");
22
- const [collapsedKinds, setCollapsedKinds] = useState({});
23
- const [collapsedNodes, setCollapsedNodes] = useState({});
24
- const rows = useMemo(() => flattenOutliner(document, { view, query, collapsedKinds, collapsedNodes }), [document, view, query, collapsedKinds, collapsedNodes]);
25
- const scrollRef = useRef(null);
26
- const [scrollTop, setScrollTop] = useState(0);
27
- const [viewportHeight, setViewportHeight] = useState(320);
28
- useEffect(() => {
29
- const element = scrollRef.current;
30
- if (element === null)
31
- return;
32
- const measure = () => setViewportHeight(element.clientHeight);
33
- measure();
34
- const observer = new ResizeObserver(measure);
35
- observer.observe(element);
36
- return () => observer.disconnect();
37
- }, []);
38
- const win = virtualWindow(scrollTop, viewportHeight, ROW_HEIGHT, rows.length);
39
- const visible = rows.slice(win.start, win.end);
40
- const selectRow = (id, additive) => {
41
- if (additive) {
42
- const current = session.getState().selection;
43
- const next = current.includes(id) ? current.filter((entry) => entry !== id) : [...current, id];
44
- session.dispatch({ type: "select", ids: next });
45
- return;
46
- }
47
- session.dispatch({ type: "select", ids: [id] });
48
- api.handle({ method: "camera_goto", id });
49
- };
50
- const [materialDropTarget, setMaterialDropTarget] = useState(null);
51
- const materialDropProps = (ids, key) => ({
52
- onDragOver: (event) => {
53
- if (!event.dataTransfer.types.includes(MATERIAL_DRAG_MIME))
54
- return;
55
- event.preventDefault();
56
- event.dataTransfer.dropEffect = "copy";
57
- if (materialDropTarget !== key)
58
- setMaterialDropTarget(key);
59
- },
60
- onDragLeave: () => setMaterialDropTarget((current) => (current === key ? null : current)),
61
- onDrop: (event) => {
62
- const materialId = event.dataTransfer.getData(MATERIAL_DRAG_MIME);
63
- if (materialId.length === 0)
64
- return;
65
- event.preventDefault();
66
- setMaterialDropTarget(null);
67
- api.handle({ method: "assign_material", ids: [...ids], materialId });
68
- },
69
- });
70
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-1.5 border-b border-white/[0.08] p-2", children: [_jsx("input", { type: "search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search objects and kinds\u2026", className: `w-full ${INPUT} px-2.5 py-1.5` }), _jsx("div", { className: "flex gap-0.5 rounded-md bg-black/40 p-0.5 ring-1 ring-inset ring-white/[0.06]", children: ["kind", "tree"].map((mode) => (_jsx("button", { type: "button", className: `flex-1 rounded px-2 py-0.5 text-[10px] transition-colors ${view === mode ? "bg-cyan-500/80 text-white" : "text-neutral-400 hover:text-neutral-200"}`, onClick: () => setView(mode), children: mode === "kind" ? "By kind" : "Tree" }, mode))) })] }), _jsx("div", { ref: scrollRef, className: "min-h-0 flex-1 overflow-auto p-2", onScroll: (event) => setScrollTop(event.currentTarget.scrollTop), children: rows.length === 0 ? (_jsx("div", { className: "p-3 text-center text-neutral-600", children: query.length > 0 ? "No matching objects" : "No objects yet" })) : (_jsx("div", { style: { height: win.totalHeight, position: "relative" }, children: _jsx("div", { style: { transform: `translateY(${win.offsetTop}px)` }, children: visible.map((row) => {
71
- if (row.type === "group") {
72
- return (_jsxs("button", { type: "button", style: { height: ROW_HEIGHT }, className: "flex w-full items-center gap-1 rounded-md px-1.5 text-left font-semibold text-neutral-300 transition-colors hover:bg-white/[0.06]", onClick: () => setCollapsedKinds((previous) => ({ ...previous, [row.kind]: !(previous[row.kind] === true) })), children: [_jsx("span", { className: "w-3 text-neutral-500", children: row.collapsed ? "▸" : "▾" }), _jsx("span", { children: row.kind }), _jsx("span", { className: "ml-auto text-neutral-500", children: row.total })] }, row.key));
73
- }
74
- if (row.type === "kindItem") {
75
- const rowSelected = selectedId !== undefined && row.ids.includes(selectedId);
76
- const cycleIndex = rowSelected ? row.ids.indexOf(selectedId) + 1 : 0;
77
- const dropActive = materialDropTarget === row.key;
78
- return (_jsxs("button", { type: "button", style: { height: ROW_HEIGHT }, className: `block w-full truncate rounded-md pl-5 pr-1.5 text-left transition-colors ${dropActive ? "bg-cyan-500/25 ring-1 ring-inset ring-cyan-300/50" : rowSelected ? "bg-cyan-500/15 text-cyan-100 ring-1 ring-inset ring-cyan-400/20" : "text-neutral-300 hover:bg-white/[0.06]"}`, onClick: (event) => selectRow(row.ids[0], event.ctrlKey || event.metaKey || event.shiftKey), ...materialDropProps(row.ids, row.key), children: [row.label, row.ids.length > 1 ? _jsxs("span", { className: "text-neutral-500", children: [" \u00D7", row.ids.length, rowSelected ? ` · ${cycleIndex}/${row.ids.length} · N next` : ""] }) : null] }, row.key));
79
- }
80
- const rowSelected = selection.includes(row.id);
81
- const dropActive = materialDropTarget === row.key;
82
- return (_jsxs("div", { style: { height: ROW_HEIGHT, paddingLeft: `${row.depth * 12}px` }, className: "flex items-center", children: [row.hasChildren ? (_jsx("button", { type: "button", className: "w-4 shrink-0 text-neutral-500 transition-colors hover:text-neutral-200", onClick: () => setCollapsedNodes((previous) => ({ ...previous, [row.id]: !(previous[row.id] === true) })), children: collapsedNodes[row.id] === true ? "▸" : "▾" })) : _jsx("span", { className: "w-4 shrink-0" }), _jsxs("button", { type: "button", className: `flex-1 truncate rounded-md px-1.5 text-left transition-colors ${dropActive ? "bg-cyan-500/25 ring-1 ring-inset ring-cyan-300/50" : rowSelected ? "bg-cyan-500/15 text-cyan-100 ring-1 ring-inset ring-cyan-400/20" : "text-neutral-300 hover:bg-white/[0.06]"}`, onClick: (event) => selectRow(row.id, event.ctrlKey || event.metaKey || event.shiftKey), title: row.id, ...materialDropProps([row.id], row.key), children: [row.label, _jsx("span", { className: "ml-1 text-[9px] text-neutral-600", children: row.kind })] })] }, row.key));
83
- }) }) })) })] }));
84
- });