@zylem/editor 0.8.8 → 0.9.2

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.
@@ -1,23 +1,25 @@
1
1
  import * as _zylem_bridge from '@zylem/bridge';
2
- import { BridgePose, BridgeVec3, BridgeQuat, SnapSettingsPayload, BridgeDebugTool, SceneOperationPayload, EntityTypeDescriptor } from '@zylem/bridge';
2
+ import { CameraViewPreset, BridgePose, EntitySelectMode, BridgeVec3, BridgeQuat, LevelBuffer, SnapSettingsPayload, BridgeDebugTool, SceneOperationPayload, EntityTypeDescriptor } from '@zylem/bridge';
3
3
  import { LucideProps } from 'lucide-solid';
4
4
  import { Component } from 'solid-js';
5
5
 
6
- declare const channel: _zylem_bridge.BridgeChannel;
7
6
  /**
8
- * Subscribe the editor stores to game→editor bridge messages and hydrate
9
- * from the channel's retained state.
7
+ * The editor's handle on the shared editor ↔ game bridge channel.
10
8
  *
11
- * Reference-counted: the game gates expensive editor-only work (thumbnails,
12
- * entity summaries) on these subscriptions existing, so the last editor to
13
- * unmount must actually release them, while any editor still mounted must
14
- * keep them alive.
9
+ * Owned here, and only here, so the game→editor sync modules, the
10
+ * editor→game commands, and stores such as `history-store` can all reach the
11
+ * same singleton without importing each other.
12
+ */
13
+ declare const channel: _zylem_bridge.BridgeChannel;
14
+
15
+ /**
16
+ * Editor → game commands.
15
17
  *
16
- * @returns A release function; safe to call more than once.
18
+ * Thin, typed wrappers over `channel.send` so UI code never has to spell out
19
+ * message names or payload shapes, and has no runtime dependency on game-lib
20
+ * internals.
17
21
  */
18
- declare function connectEditorBridge(): () => void;
19
- /** Tear down all bridge subscriptions, ignoring the reference count. */
20
- declare function disconnectEditorBridge(): void;
22
+
21
23
  /** Toggle the game's debug mode. */
22
24
  declare function sendDebugEnabled(enabled: boolean): void;
23
25
  /** Switch the active editor tool in the game. */
@@ -26,6 +28,12 @@ declare function sendTool(tool: BridgeDebugTool): void;
26
28
  declare function sendPlayback(paused: boolean): void;
27
29
  /** Select an entity in the game (null clears the selection). */
28
30
  declare function sendEntitySelect(uuid: string | null): void;
31
+ /**
32
+ * Replace, extend, reduce, or toggle the game's selection with several
33
+ * entities at once. `uuid` mirrors the first entry for single-select
34
+ * consumers.
35
+ */
36
+ declare function sendEntitySelectMany(uuids: string[], mode?: EntitySelectMode): void;
29
37
  /** Focus/frame the game debug camera on an entity. */
30
38
  declare function sendEntityFocus(uuid: string): void;
31
39
  /** Write a stage variable in the running game. */
@@ -44,6 +52,8 @@ declare function sendEntityTransform(uuid: string, transform: {
44
52
  }): void;
45
53
  /** Arm the game's add tool with a catalog type, or `null` to disarm it. */
46
54
  declare function sendAddType(typeId: string | null, props?: Record<string, unknown>): void;
55
+ /** Stream a recorded level into the running stage. */
56
+ declare function sendLevelLoad(buffer: LevelBuffer): void;
47
57
  /** Spawn a catalog entity without a placement click. */
48
58
  declare function sendEntityCreate(typeId: string, options?: {
49
59
  props?: Record<string, unknown>;
@@ -53,6 +63,30 @@ declare function sendEntityCreate(typeId: string, options?: {
53
63
  declare function sendSnapSettings(snap: SnapSettingsPayload): void;
54
64
  /** Show or hide the game's construction-plane grid. */
55
65
  declare function sendGridVisible(visible: boolean): void;
66
+ /** Lock the game's debug orbit camera to a view preset. */
67
+ declare function sendCameraView(preset: CameraViewPreset): void;
68
+
69
+ /**
70
+ * Connection lifecycle for the editor side of the bridge.
71
+ *
72
+ * Subscribes the game→editor sync modules to the channel and hydrates from
73
+ * `channel.getState()` on connect, so an editor mounted after the game started
74
+ * shows current config/entities immediately.
75
+ */
76
+ /**
77
+ * Subscribe the editor stores to game→editor bridge messages and hydrate
78
+ * from the channel's retained state.
79
+ *
80
+ * Reference-counted: the game gates expensive editor-only work (thumbnails,
81
+ * entity summaries) on these subscriptions existing, so the last editor to
82
+ * unmount must actually release them, while any editor still mounted must
83
+ * keep them alive.
84
+ *
85
+ * @returns A release function; safe to call more than once.
86
+ */
87
+ declare function connectEditorBridge(): () => void;
88
+ /** Tear down all bridge subscriptions, ignoring the reference count. */
89
+ declare function disconnectEditorBridge(): void;
56
90
 
57
91
  /**
58
92
  * Shared types for the editor store.
@@ -135,6 +169,11 @@ interface DockRect {
135
169
  height: number;
136
170
  }
137
171
  declare const DOCK_SIDES: readonly DockSide[];
172
+ /**
173
+ * The toolbar window. When it is the only panel on its edge it takes that
174
+ * whole edge, and every other dock lays out in the space left over.
175
+ */
176
+ declare const TOOLBAR_PANEL_ID = "toolbar";
138
177
  declare const findDockedSide: (registry: DockRegistry, panelId: DockPanelId) => DockSide | null;
139
178
  interface DockLayoutOptions {
140
179
  /** Smallest slot extent along a zone's long axis. */
@@ -146,6 +185,11 @@ interface DockLayoutOptions {
146
185
  * Left and right zones take the full viewport height and win over top and
147
186
  * bottom, which inset horizontally to the space between them. Panels sharing a
148
187
  * zone split its long axis evenly.
188
+ *
189
+ * The toolbar is the exception when it is alone on its edge: that strip runs
190
+ * the full length of the edge, and the rules above apply to everyone else
191
+ * inside the leftover rectangle. Sharing its edge with another panel drops
192
+ * the exception and the toolbar splits that edge like any other panel.
149
193
  */
150
194
  declare const computeDockLayout: (registry: DockRegistry, viewport: Viewport, options?: DockLayoutOptions) => Record<DockPanelId, DockRect>;
151
195
 
@@ -204,6 +248,7 @@ declare const gameState: GameState;
204
248
 
205
249
  /** Registry id of the main editor panel; detached sections use their panel id. */
206
250
  declare const MAIN_PANEL_ID = "main";
251
+
207
252
  interface DetachedPanelState {
208
253
  position: {
209
254
  x: number;
@@ -243,6 +288,9 @@ declare const debugStore: {
243
288
  panelZOrder: string[];
244
289
  docks: DockRegistry;
245
290
  dockDefaultsApplied: boolean;
291
+ toolbarSeeded: boolean;
292
+ toolbarCollapsed: boolean;
293
+ mainCollapsed: boolean;
246
294
  draggingPanelId: string | null;
247
295
  dropTargetIndex: number | null;
248
296
  };
@@ -359,6 +407,55 @@ declare function redo(): boolean;
359
407
  */
360
408
  declare function clearHistory(): void;
361
409
 
410
+ /**
411
+ * Level-editor mode.
412
+ *
413
+ * Build mode owns a buffer of catalog placements. Turning it on seeds that
414
+ * buffer from the stage currently loaded; turning it off freezes the buffer
415
+ * so later stage changes do not rewrite a level the user has stepped out of.
416
+ * Turning it on again reseeds.
417
+ */
418
+
419
+ /**
420
+ * How a recorded entry was placed.
421
+ *
422
+ * `level` is the only role marked for bake. The editor does not merge meshes;
423
+ * the game bakes later. `actor` and `doodad` are recorded and left out of that set.
424
+ */
425
+ type LevelRole = 'actor' | 'level' | 'doodad';
426
+ /** Role and actor props, kept beside `LevelEntry` because the bridge entry has neither. */
427
+ interface LevelAnnotation {
428
+ role: LevelRole;
429
+ props: Record<string, string>;
430
+ }
431
+ interface LevelState {
432
+ active: boolean;
433
+ /** Present after Build has been turned on at least once. Frozen while inactive. */
434
+ buffer: LevelBuffer | null;
435
+ /** Keyed by entry id. Entries that leave the buffer lose their annotation. */
436
+ annotations: Record<string, LevelAnnotation>;
437
+ /**
438
+ * Role stamped on entries that appear while the Add tool is armed.
439
+ * Cleared when the tool leaves Add, so a later stage update is not mis-tagged.
440
+ */
441
+ placementRole: LevelRole | null;
442
+ }
443
+ /** A plain copy of the buffer, safe to post across a frame or write to a file. */
444
+ interface LevelSnapshot {
445
+ buffer: LevelBuffer;
446
+ annotations: Record<string, LevelAnnotation>;
447
+ /** Stage id the buffer was recorded against, when the game has published one. */
448
+ stageId: string | null;
449
+ }
450
+ declare const levelState: LevelState;
451
+ /**
452
+ * Copy the recorded level out of the editor.
453
+ *
454
+ * `null` when Build has never been turned on. A buffer frozen by leaving Build
455
+ * mode is still returned. The copy is plain data, not the live proxy.
456
+ */
457
+ declare function snapshotLevelBuffer(): LevelSnapshot | null;
458
+
362
459
  /**
363
460
  * Stage export.
364
461
  *
@@ -437,6 +534,176 @@ declare function setArmedType(typeId: string | null): void;
437
534
  /** Drop the armed placement type and return to the neutral tool. */
438
535
  declare function disarmTools(): boolean;
439
536
 
537
+ /**
538
+ * What each toolbar button does when pressed, as plain functions.
539
+ *
540
+ * Kept out of the components so the keyboard shortcuts run the same code. Where
541
+ * a key and its button differ — the tool keys select, the tool buttons toggle —
542
+ * both variants live here over one shared primitive, so the two cannot drift.
543
+ * The components keep the reactive reads that drive `selected` and `disabled`,
544
+ * and call in here on click.
545
+ *
546
+ * Everything reads the valtio proxies rather than their Solid mirrors. These run
547
+ * outside any tracking scope — from a `keydown` on `window` as often as from a
548
+ * click — and the mirrors lag the proxies by a microtask.
549
+ */
550
+
551
+ type TransformTool = Extract<DebugTools, 'translate' | 'rotate' | 'scale'>;
552
+ type ClickTool = Extract<DebugTools, 'select' | 'delete'>;
553
+ /** Debug mode on or off, in the editor and the game. */
554
+ declare function toggleDebug(): void;
555
+ /**
556
+ * Make a click tool the active one. Pressing the key of the tool already active
557
+ * keeps it, so the keyboard behaves like a row of radio buttons; Escape is the
558
+ * way out.
559
+ */
560
+ declare function selectTool(tool: ClickTool): void;
561
+ /** Toggle a click tool from its button: a second press returns to the neutral tool. */
562
+ declare function toggleTool(tool: ClickTool): void;
563
+ /**
564
+ * Enter a gizmo mode from its key. Pressing the key of the mode already active
565
+ * keeps it — and still counts as handled, so the keystroke goes nowhere else.
566
+ */
567
+ declare function selectTransformTool(tool: TransformTool): boolean;
568
+ /** Toggle a gizmo mode from its button: a second press returns to the neutral tool. */
569
+ declare function toggleTransformTool(tool: TransformTool): boolean;
570
+ /**
571
+ * Arm placement with a catalog type and the role those new entries are tagged
572
+ * with. Arming an armed tool leaves it armed — Escape is the way out — so
573
+ * placing ten of something is one arm and ten clicks.
574
+ *
575
+ * @returns `false` when the catalog has no such type.
576
+ */
577
+ declare function armAddType(typeId: string, role?: LevelRole): boolean;
578
+ /**
579
+ * Arm placement with what the Add button's face shows.
580
+ *
581
+ * @returns `false` on an empty catalog, the one state with nothing to arm.
582
+ */
583
+ declare function armAddTool(role?: LevelRole): boolean;
584
+ /** Open the Add palette, if a toolbar is showing one. */
585
+ declare function openAddPalette(): boolean;
586
+ declare function toggleSnap(): void;
587
+ declare function toggleGrid(): void;
588
+ /** Pause or resume the simulation, in the editor and the game. */
589
+ declare function togglePlayback(): void;
590
+
591
+ /**
592
+ * Keyboard shortcuts for the toolbar.
593
+ *
594
+ * The tool row follows the Godot/Unreal layout — Q select, W move, E rotate,
595
+ * R scale — with mnemonics for the rest: A add, B build, X delete, D debug,
596
+ * S snap, G grid. Every key sits under the left hand on a QWERTY board, so
597
+ * the right hand can stay on the mouse.
598
+ *
599
+ * The keys are live whenever the editor panel is open, playing or paused: the
600
+ * panel being up is the signal that the keyboard is addressing the editor. The
601
+ * same letters are gameplay input — game-lib's keyboard provider listens on the
602
+ * same `window`, maps `a`, `s`, `q`, `e`, `x`, `z` and `Enter` for its default
603
+ * pad, and pays no attention to `preventDefault` — so while the panel is open
604
+ * the shortcuts are stopped here, in the capture phase, before any other
605
+ * listener sees them. Close the panel and the keys are the game's again.
606
+ *
607
+ * Undo and redo keep Cmd/Ctrl+Z and Cmd/Ctrl+Shift+Z from `history-shortcuts`,
608
+ * behind their own option; they are listed here so the tooltips can show them.
609
+ */
610
+ interface ShortcutChord {
611
+ /** `KeyboardEvent.key`, matched case-insensitively so Caps Lock changes nothing. */
612
+ key: string;
613
+ shift?: boolean;
614
+ /** Cmd on macOS, Ctrl elsewhere; either is accepted. */
615
+ mod?: boolean;
616
+ }
617
+ /** Every toolbar action's chord, keyed by the button it belongs to. */
618
+ declare const TOOLBAR_SHORTCUTS: {
619
+ readonly debug: {
620
+ readonly key: "d";
621
+ };
622
+ readonly build: {
623
+ readonly key: "b";
624
+ };
625
+ readonly select: {
626
+ readonly key: "q";
627
+ };
628
+ readonly translate: {
629
+ readonly key: "w";
630
+ };
631
+ readonly rotate: {
632
+ readonly key: "e";
633
+ };
634
+ readonly scale: {
635
+ readonly key: "r";
636
+ };
637
+ readonly add: {
638
+ readonly key: "a";
639
+ };
640
+ readonly addPalette: {
641
+ readonly key: "a";
642
+ readonly shift: true;
643
+ };
644
+ readonly delete: {
645
+ readonly key: "x";
646
+ };
647
+ readonly snap: {
648
+ readonly key: "s";
649
+ };
650
+ readonly grid: {
651
+ readonly key: "g";
652
+ };
653
+ readonly undo: {
654
+ readonly key: "z";
655
+ readonly mod: true;
656
+ };
657
+ readonly redo: {
658
+ readonly key: "z";
659
+ readonly mod: true;
660
+ readonly shift: true;
661
+ };
662
+ readonly playback: {
663
+ readonly key: "Enter";
664
+ readonly mod: true;
665
+ };
666
+ };
667
+ type ToolbarShortcutId = keyof typeof TOOLBAR_SHORTCUTS;
668
+ /** The shortcuts this module runs; undo and redo are `history-shortcuts`'. */
669
+ declare const TOOLBAR_ACTIONS: readonly ["debug", "build", "select", "translate", "rotate", "scale", "add", "addPalette", "delete", "snap", "grid", "playback"];
670
+ type ToolbarAction = (typeof TOOLBAR_ACTIONS)[number];
671
+ /**
672
+ * A chord as tooltip text: `W`, `Shift+A`, `Cmd+Enter`.
673
+ *
674
+ * Words rather than ⌘ and ⇧, because the toolbar buttons use one string for
675
+ * both the tooltip and the accessible name.
676
+ */
677
+ declare function formatShortcut(chord: ShortcutChord, apple?: boolean): string;
678
+ /**
679
+ * Run the action a shortcut stands for.
680
+ *
681
+ * The tool keys select rather than toggle: W in Move stays in Move, and Escape
682
+ * is the way out. The buttons keep their toggle, since a lit button is its own
683
+ * "press again to leave" affordance. Everything else is a toggle by nature.
684
+ *
685
+ * @returns Whether anything happened. `false` mirrors a disabled button: Move
686
+ * with nothing to move, Add with an empty catalog, the palette with no toolbar.
687
+ */
688
+ declare function runToolbarAction(action: ToolbarAction): boolean;
689
+ interface ToolbarShortcutOptions {
690
+ /** @default true */
691
+ enabled?: boolean;
692
+ /**
693
+ * Whether the keys belong to the editor right now — the web component passes
694
+ * "the panel is open". Read per keystroke, so it can follow UI state.
695
+ * @default always
696
+ */
697
+ isActive?: () => boolean;
698
+ target?: Window;
699
+ }
700
+ /**
701
+ * Install the toolbar shortcuts.
702
+ *
703
+ * @returns An uninstall function.
704
+ */
705
+ declare function installToolbarShortcuts(options?: ToolbarShortcutOptions): () => void;
706
+
440
707
  /**
441
708
  * Keeps the gizmo modes from outliving the selection they act on.
442
709
  *
@@ -535,6 +802,18 @@ interface ZylemEditorConfig {
535
802
  * @default true
536
803
  */
537
804
  enableEscapeShortcut?: boolean;
805
+ /**
806
+ * Whether the toolbar's keys are installed: Q/W/E/R for the tools, A to arm
807
+ * placement, X for delete, D/S/G for debug, snap and grid, and Cmd/Ctrl+Enter
808
+ * for play/pause. They are live while the editor panel is open, whether the
809
+ * simulation is playing or paused, and are stopped before the game's own key
810
+ * bindings see them; with the panel closed the keys are the game's.
811
+ *
812
+ * Disable it to bind the keys yourself; the actions are exported as
813
+ * `selectTool`, `selectTransformTool`, `togglePlayback` and friends.
814
+ * @default true
815
+ */
816
+ enableToolbarShortcuts?: boolean;
538
817
  }
539
818
  /**
540
819
  * ZylemEditorElement - A web component that wraps the Zylem Editor.
@@ -596,6 +875,8 @@ declare class ZylemEditorElement extends HTMLElement {
596
875
  openPanel(): void;
597
876
  closePanel(): void;
598
877
  togglePanel(): void;
878
+ /** Whether either editor window is showing. Collapsed counts as showing; closed does not. */
879
+ isPanelOpen(): boolean;
599
880
  /**
600
881
  * Dock a panel to a viewport edge, or pass `null` to float it again.
601
882
  * Defaults to the main editor panel.
@@ -705,13 +986,14 @@ interface MountZylemEditorOptions extends EditorStateBridgeOptions {
705
986
  */
706
987
  launcherMode?: EditorLauncherMode;
707
988
  /**
708
- * Dock layout to seed on first run, keyed by viewport edge. `'main'` is the
709
- * editor panel; any other id is a section such as `'console'`. Ignored once
710
- * the user has a saved layout.
989
+ * Dock layout to seed on first run, keyed by viewport edge. `'toolbar'` is
990
+ * the tool strip, `'main'` is the panel container, and any other id is a
991
+ * section such as `'console'`. A host that omits the toolbar still gets it
992
+ * on top. Ignored once the user has a saved layout.
711
993
  *
712
994
  * @example
713
995
  * ```ts
714
- * mountZylemEditor({ defaultDocks: { left: ['main'], bottom: ['console'] } });
996
+ * mountZylemEditor({ defaultDocks: { top: ['toolbar'], left: ['main'] } });
715
997
  * ```
716
998
  */
717
999
  defaultDocks?: EditorDockDefaults;
@@ -756,4 +1038,4 @@ type SceneOperationListener = (op: SceneOperationPayload) => void;
756
1038
  */
757
1039
  declare function onSceneOperation(listener: SceneOperationListener): () => void;
758
1040
 
759
- export { type CatalogState, DEFAULT_ROTATE_SNAP, DOCK_SIDES, type DockPanelId, type DockRect, type DockRegistry, type DockSide, type DockZoneState, type EditorDockDefaults, type EditorEvent, type EditorEventType, type EditorStateBridge, type EditorStateBridgeOptions, type EditorUpdatePayload, type HistoryState, Icon, MAIN_PANEL_ID, MAX_HISTORY_DEPTH, type MountZylemEditorOptions, type MountedZylemEditor, type SceneOperationListener, type StageExport, type StageExportEntity, type TransformState, type TransformToolGuardOptions, type ZylemEditorConfig, ZylemEditorElement, attachEditorStateBridge, channel as bridgeChannel, buildStageExport, canRedo, canUndo, catalogState, clearHistory, computeDockLayout, connectEditorBridge, copyStageExport, debugState, debugStore, disarmTools, disconnectEditorBridge, dispatchEditorUpdate, dispatchToEditor, dockPanelToSide, editorEvents, findDockedSide, gameState, getDockedSide, historyState, installTransformToolGuard, mountZylemEditor, onSceneOperation, redo, redoLabel, registerZylemEditor, releaseOrphanedTransformTool, sendAddType, sendDebugEnabled, sendEntityCreate, sendEntityFocus, sendEntitySelect, sendEntityTransform, sendGridVisible, sendPlayback, sendSnapSettings, sendStageVariable, sendTool, setArmedType, setEntityCatalog, setGridVisible, setSnapEnabled, setSnapIncrements, stageExportToString, stageState, transformState, undo, undoLabel, undockPanelFromSides };
1041
+ export { type CatalogState, type ClickTool, DEFAULT_ROTATE_SNAP, DOCK_SIDES, type DockPanelId, type DockRect, type DockRegistry, type DockSide, type DockZoneState, type EditorDockDefaults, type EditorEvent, type EditorEventType, type EditorStateBridge, type EditorStateBridgeOptions, type EditorUpdatePayload, type HistoryState, Icon, type LevelSnapshot, MAIN_PANEL_ID, MAX_HISTORY_DEPTH, type MountZylemEditorOptions, type MountedZylemEditor, type SceneOperationListener, type ShortcutChord, type StageExport, type StageExportEntity, TOOLBAR_PANEL_ID, TOOLBAR_SHORTCUTS, type ToolbarAction, type ToolbarShortcutId, type ToolbarShortcutOptions, type TransformState, type TransformTool, type TransformToolGuardOptions, type ZylemEditorConfig, ZylemEditorElement, armAddTool, armAddType, attachEditorStateBridge, channel as bridgeChannel, buildStageExport, canRedo, canUndo, catalogState, clearHistory, computeDockLayout, connectEditorBridge, copyStageExport, debugState, debugStore, disarmTools, disconnectEditorBridge, dispatchEditorUpdate, dispatchToEditor, dockPanelToSide, editorEvents, findDockedSide, formatShortcut, gameState, getDockedSide, historyState, installToolbarShortcuts, installTransformToolGuard, levelState, mountZylemEditor, onSceneOperation, openAddPalette, redo, redoLabel, registerZylemEditor, releaseOrphanedTransformTool, runToolbarAction, selectTool, selectTransformTool, sendAddType, sendCameraView, sendDebugEnabled, sendEntityCreate, sendEntityFocus, sendEntitySelect, sendEntitySelectMany, sendEntityTransform, sendGridVisible, sendLevelLoad, sendPlayback, sendSnapSettings, sendStageVariable, sendTool, setArmedType, setEntityCatalog, setGridVisible, setSnapEnabled, setSnapIncrements, snapshotLevelBuffer, stageExportToString, stageState, toggleDebug, toggleGrid, togglePlayback, toggleSnap, toggleTool, toggleTransformTool, transformState, undo, undoLabel, undockPanelFromSides };