@zylem/editor 0.6.8 → 0.7.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.
@@ -1,116 +1,7 @@
1
1
  import { Component } from 'solid-js';
2
2
  import { LucideProps } from 'lucide-solid';
3
-
4
- type EditorLauncherMode = 'floating' | 'hidden';
5
-
6
- /**
7
- * Configuration options for the ZylemEditorElement
8
- */
9
- interface ZylemEditorConfig {
10
- /**
11
- * External debug state to sync with.
12
- * If provided, the editor will use this state instead of its internal state.
13
- */
14
- debugState?: any;
15
- /**
16
- * External game state to sync with.
17
- */
18
- gameState?: any;
19
- /**
20
- * External stage state to sync with.
21
- */
22
- stageState?: any;
23
- /**
24
- * Whether to include default styles.
25
- * @default true
26
- */
27
- includeStyles?: boolean;
28
- /**
29
- * Controls whether the internal floating launcher is rendered.
30
- * @default 'floating'
31
- */
32
- launcherMode?: EditorLauncherMode;
33
- }
34
- /**
35
- * ZylemEditorElement - A web component that wraps the Zylem Editor.
36
- *
37
- * This component is fully self-contained with bundled styles.
38
- * It can be imported and used in any project without additional CSS configuration.
39
- *
40
- * @example
41
- * ```html
42
- * <!-- Standalone usage -->
43
- * <zylem-editor></zylem-editor>
44
- * ```
45
- *
46
- * @example
47
- * ```js
48
- * // Configured usage
49
- * const editor = document.querySelector('zylem-editor');
50
- * editor.config = {
51
- * debugState: myGame.debugState,
52
- * gameState: myGame.gameState,
53
- * stageState: myGame.stageState,
54
- * };
55
- * ```
56
- *
57
- * @example
58
- * ```js
59
- * // Manual registration in another project
60
- * import { ZylemEditorElement } from '@zylem/editor';
61
- *
62
- * // Optionally customize before registering
63
- * customElements.define('my-editor', ZylemEditorElement);
64
- * ```
65
- */
66
- declare class ZylemEditorElement extends HTMLElement {
67
- private dispose;
68
- private _config;
69
- private _initialized;
70
- private controller;
71
- constructor();
72
- /**
73
- * Set configuration for the editor.
74
- * Must be set before the element is connected to the DOM, or call `reinitialize()` after.
75
- */
76
- set config(value: ZylemEditorConfig);
77
- get config(): ZylemEditorConfig;
78
- set launcherMode(value: EditorLauncherMode);
79
- get launcherMode(): EditorLauncherMode;
80
- /**
81
- * Observed attributes for the web component
82
- */
83
- static get observedAttributes(): string[];
84
- attributeChangedCallback(name: string, _oldValue: string, newValue: string): void;
85
- connectedCallback(): void;
86
- private initialize;
87
- openPanel(): void;
88
- closePanel(): void;
89
- togglePanel(): void;
90
- /**
91
- * Reinitialize the editor with new configuration.
92
- * Call this after changing the `config` property if the element is already connected.
93
- */
94
- reinitialize(): void;
95
- disconnectedCallback(): void;
96
- }
97
- /**
98
- * Register the ZylemEditorElement as a custom element.
99
- * Call this function to use the default 'zylem-editor' tag name.
100
- *
101
- * @example
102
- * ```js
103
- * import { registerZylemEditor } from '@zylem/editor';
104
- * registerZylemEditor();
105
- * // Now you can use <zylem-editor> in your HTML
106
- * ```
107
- */
108
- declare function registerZylemEditor(tagName?: string): void;
109
-
110
- interface IconProps extends LucideProps {
111
- icon: Component<LucideProps>;
112
- }
113
- declare const Icon: Component<IconProps>;
3
+ import * as _zylem_bridge from '@zylem/bridge';
4
+ import { BridgeDebugTool } from '@zylem/bridge';
114
5
 
115
6
  /**
116
7
  * Editor Event Bus
@@ -164,53 +55,11 @@ declare class EditorEventBusWrapper {
164
55
  */
165
56
  declare const editorEvents: EditorEventBusWrapper;
166
57
 
167
- /**
168
- * Editor Events Module
169
- *
170
- * Bidirectional window CustomEvents for communication between @zylem/editor and consuming apps.
171
- *
172
- * EDITOR_STATE_DISPATCH: Editor sends state changes → external apps listen
173
- * EDITOR_STATE_RECEIVE: External apps send updates → editor listens
174
- */
175
- /**
176
- * Payload structure for editor state events.
177
- * Matches the nested state structure for both directions.
178
- */
179
- interface EditorUpdatePayload {
180
- gameState?: {
181
- debugFlag?: boolean;
182
- [key: string]: unknown;
183
- };
184
- toolbarState?: {
185
- tool?: 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'none';
186
- paused?: boolean;
187
- };
188
- [key: string]: unknown;
189
- }
190
- /**
191
- * Event name constants
192
- */
193
- declare const EDITOR_STATE_DISPATCH = "editor-state-dispatch";
194
- declare const EDITOR_STATE_RECEIVE = "editor-state-receive";
195
- declare const EDITOR_UPDATE_EVENT = "editor-state-dispatch";
196
- /**
197
- * Dispatch state changes from the editor to external consumers.
198
- *
199
- * @param payload - The state update payload
200
- *
201
- * @example
202
- * ```ts
203
- * // From GameSection when debug checkbox changes
204
- * dispatchEditorUpdate({ gameState: { debugFlag: true } });
205
- * ```
206
- */
207
- declare function dispatchEditorUpdate(payload: EditorUpdatePayload): void;
208
-
209
58
  /**
210
59
  * Entities state for the entities section.
211
60
  * Manages debug state and entity selection/hover.
212
61
  */
213
- type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'none';
62
+ type DebugTools = 'select' | 'translate' | 'rotate' | 'scale' | 'delete' | 'add' | 'none';
214
63
  interface DebugState {
215
64
  paused: boolean;
216
65
  tool: DebugTools;
@@ -260,9 +109,11 @@ interface BaseEntityInterface {
260
109
  position?: Vector3Like | undefined;
261
110
  rotation?: Vector3Like | undefined;
262
111
  scale?: Vector3Like | undefined;
263
- /** PNG data URL of a framed entity preview. */
264
- thumbnail?: string | null | undefined;
265
- /** World-space AABB size for thumbnail rulers. */
112
+ /**
113
+ * World-space AABB size for thumbnail rulers. Preview images themselves
114
+ * live in the thumbnail side-channel store, keyed by uuid, so entity state
115
+ * stays small enough to diff cheaply.
116
+ */
266
117
  bounds?: {
267
118
  width: number;
268
119
  height: number;
@@ -294,6 +145,58 @@ interface StageStateInterface {
294
145
 
295
146
  declare const stageState: StageStateInterface;
296
147
 
148
+ /**
149
+ * Dock layout geometry.
150
+ *
151
+ * Docking used to be per-panel: each panel snapped itself to a viewport edge
152
+ * and sized itself from `window.innerWidth`. That cannot express two panels
153
+ * sharing a side, or a bottom strip that stops at a left-docked panel, so the
154
+ * dock state lives in one registry (persisted in the editor store) and every
155
+ * panel's rect comes from `computeDockLayout`.
156
+ *
157
+ * This module is deliberately free of Solid and `window` so the geometry can
158
+ * be unit tested directly.
159
+ */
160
+ type DockSide = 'left' | 'right' | 'top' | 'bottom';
161
+ /** `'main'` is the editor panel; any other id is a detached section id. */
162
+ type DockPanelId = string;
163
+ interface DockZoneState {
164
+ /** Occupants in slot order along the zone's long axis. */
165
+ panels: DockPanelId[];
166
+ /**
167
+ * Width for left/right, height for top/bottom.
168
+ * `0` means "not chosen yet"; the layout falls back to a viewport quarter.
169
+ */
170
+ thickness: number;
171
+ }
172
+ type DockRegistry = Record<DockSide, DockZoneState>;
173
+ interface Viewport {
174
+ width: number;
175
+ height: number;
176
+ }
177
+ interface DockRect {
178
+ x: number;
179
+ y: number;
180
+ width: number;
181
+ height: number;
182
+ }
183
+ declare const DOCK_SIDES: readonly DockSide[];
184
+ declare const findDockedSide: (registry: DockRegistry, panelId: DockPanelId) => DockSide | null;
185
+ interface DockLayoutOptions {
186
+ /** Smallest slot extent along a zone's long axis. */
187
+ minSlotExtent?: number;
188
+ }
189
+ /**
190
+ * Rect for every docked panel.
191
+ *
192
+ * Left and right zones take the full viewport height and win over top and
193
+ * bottom, which inset horizontally to the space between them. Panels sharing a
194
+ * zone split its long axis evenly.
195
+ */
196
+ declare const computeDockLayout: (registry: DockRegistry, viewport: Viewport, options?: DockLayoutOptions) => Record<DockPanelId, DockRect>;
197
+
198
+ /** Registry id of the main editor panel; detached sections use their panel id. */
199
+ declare const MAIN_PANEL_ID = "main";
297
200
  interface DetachedPanelState {
298
201
  position: {
299
202
  x: number;
@@ -304,6 +207,8 @@ interface DetachedPanelState {
304
207
  height: number;
305
208
  };
306
209
  }
210
+ /** First-run dock assignments a host can request; see `applyDefaultDocks`. */
211
+ type EditorDockDefaults = Partial<Record<DockSide, DockPanelId[]>>;
307
212
  declare const debugStore: {
308
213
  debug: boolean;
309
214
  tool: DebugTools;
@@ -314,6 +219,11 @@ declare const debugStore: {
314
219
  x: number;
315
220
  y: number;
316
221
  } | null;
222
+ /** Main panel's floating size; its docked size comes from the registry. */
223
+ panelSize: {
224
+ width: number;
225
+ height: number;
226
+ } | null;
317
227
  toggleButtonPosition: {
318
228
  x: number;
319
229
  y: number;
@@ -322,40 +232,231 @@ declare const debugStore: {
322
232
  detachedPanels: Record<string, DetachedPanelState>;
323
233
  openSections: string[];
324
234
  panelZOrder: string[];
235
+ docks: DockRegistry;
236
+ dockDefaultsApplied: boolean;
325
237
  draggingPanelId: string | null;
326
238
  dropTargetIndex: number | null;
327
239
  };
240
+ declare const dockPanelToSide: (panelId: DockPanelId, side: DockSide, index?: number) => void;
241
+ declare const undockPanelFromSides: (panelId: DockPanelId) => void;
242
+ declare const getDockedSide: (panelId: DockPanelId) => DockSide | null;
243
+
244
+ type EditorLauncherMode = 'floating' | 'hidden';
245
+
246
+ /**
247
+ * Configuration options for the ZylemEditorElement
248
+ */
249
+ interface ZylemEditorConfig {
250
+ /**
251
+ * External debug state to sync with.
252
+ * If provided, the editor will use this state instead of its internal state.
253
+ */
254
+ debugState?: any;
255
+ /**
256
+ * External game state to sync with.
257
+ */
258
+ gameState?: any;
259
+ /**
260
+ * External stage state to sync with.
261
+ */
262
+ stageState?: any;
263
+ /**
264
+ * Whether to include default styles.
265
+ * @default true
266
+ */
267
+ includeStyles?: boolean;
268
+ /**
269
+ * Controls whether the internal floating launcher is rendered.
270
+ * @default 'floating'
271
+ */
272
+ launcherMode?: EditorLauncherMode;
273
+ /**
274
+ * Dock layout to seed the first time the editor runs in this browser, keyed
275
+ * by viewport edge. Use `'main'` for the editor panel itself; any other id is
276
+ * a section (for example `'console'`), which gets detached from the accordion
277
+ * so it can hold a dock slot.
278
+ *
279
+ * Ignored once the user has a saved layout, so it sets a starting point
280
+ * rather than forcing one.
281
+ */
282
+ defaultDocks?: EditorDockDefaults;
283
+ }
284
+ /**
285
+ * ZylemEditorElement - A web component that wraps the Zylem Editor.
286
+ *
287
+ * This component is fully self-contained with bundled styles.
288
+ * It can be imported and used in any project without additional CSS configuration.
289
+ *
290
+ * @example
291
+ * ```html
292
+ * <!-- Standalone usage -->
293
+ * <zylem-editor></zylem-editor>
294
+ * ```
295
+ *
296
+ * @example
297
+ * ```js
298
+ * // Configured usage
299
+ * const editor = document.querySelector('zylem-editor');
300
+ * editor.config = {
301
+ * debugState: myGame.debugState,
302
+ * gameState: myGame.gameState,
303
+ * stageState: myGame.stageState,
304
+ * };
305
+ * ```
306
+ *
307
+ * @example
308
+ * ```js
309
+ * // Manual registration in another project
310
+ * import { ZylemEditorElement } from '@zylem/editor';
311
+ *
312
+ * // Optionally customize before registering
313
+ * customElements.define('my-editor', ZylemEditorElement);
314
+ * ```
315
+ */
316
+ declare class ZylemEditorElement extends HTMLElement {
317
+ private dispose;
318
+ private _config;
319
+ private _initialized;
320
+ private controller;
321
+ /** Releases this element's bridge subscription (reference-counted). */
322
+ private releaseBridge;
323
+ constructor();
324
+ /**
325
+ * Set configuration for the editor.
326
+ * Must be set before the element is connected to the DOM, or call `reinitialize()` after.
327
+ */
328
+ set config(value: ZylemEditorConfig);
329
+ get config(): ZylemEditorConfig;
330
+ set launcherMode(value: EditorLauncherMode);
331
+ get launcherMode(): EditorLauncherMode;
332
+ /**
333
+ * Observed attributes for the web component
334
+ */
335
+ static get observedAttributes(): string[];
336
+ attributeChangedCallback(name: string, _oldValue: string, newValue: string): void;
337
+ connectedCallback(): void;
338
+ private initialize;
339
+ openPanel(): void;
340
+ closePanel(): void;
341
+ togglePanel(): void;
342
+ /**
343
+ * Dock a panel to a viewport edge, or pass `null` to float it again.
344
+ * Defaults to the main editor panel.
345
+ */
346
+ dockPanel(side: DockSide | null, panelId?: DockPanelId): void;
347
+ /**
348
+ * Reinitialize the editor with new configuration.
349
+ * Call this after changing the `config` property if the element is already connected.
350
+ */
351
+ reinitialize(): void;
352
+ disconnectedCallback(): void;
353
+ private teardown;
354
+ }
355
+ /**
356
+ * Register the ZylemEditorElement as a custom element.
357
+ * Call this function to use the default 'zylem-editor' tag name.
358
+ *
359
+ * @example
360
+ * ```js
361
+ * import { registerZylemEditor } from '@zylem/editor';
362
+ * registerZylemEditor();
363
+ * // Now you can use <zylem-editor> in your HTML
364
+ * ```
365
+ */
366
+ declare function registerZylemEditor(tagName?: string): void;
367
+
368
+ interface IconProps extends LucideProps {
369
+ icon: Component<LucideProps>;
370
+ }
371
+ declare const Icon: Component<IconProps>;
372
+
373
+ declare const channel: _zylem_bridge.BridgeChannel;
374
+ /**
375
+ * Subscribe the editor stores to game→editor bridge messages and hydrate
376
+ * from the channel's retained state.
377
+ *
378
+ * Reference-counted: the game gates expensive editor-only work (thumbnails,
379
+ * entity summaries) on these subscriptions existing, so the last editor to
380
+ * unmount must actually release them, while any editor still mounted must
381
+ * keep them alive.
382
+ *
383
+ * @returns A release function; safe to call more than once.
384
+ */
385
+ declare function connectEditorBridge(): () => void;
386
+ /** Tear down all bridge subscriptions, ignoring the reference count. */
387
+ declare function disconnectEditorBridge(): void;
388
+ /** Toggle the game's debug mode. */
389
+ declare function sendDebugEnabled(enabled: boolean): void;
390
+ /** Switch the active editor tool in the game. */
391
+ declare function sendTool(tool: BridgeDebugTool): void;
392
+ /** Pause or resume the game loop. */
393
+ declare function sendPlayback(paused: boolean): void;
394
+ /** Select an entity in the game (null clears the selection). */
395
+ declare function sendEntitySelect(uuid: string | null): void;
396
+ /** Focus/frame the game debug camera on an entity. */
397
+ declare function sendEntityFocus(uuid: string): void;
398
+ /** Write a stage variable in the running game. */
399
+ declare function sendStageVariable(key: string, value: unknown): void;
328
400
 
329
401
  /**
330
402
  * Editor Host Helpers
331
403
  *
332
404
  * Reusable wiring for apps that embed the <zylem-editor> overlay next to a
333
- * running Zylem game. Consolidates the mount + state-bridge boilerplate that
334
- * was previously duplicated across consuming apps (examples, creator preview).
405
+ * running Zylem game. These helpers are thin wrappers over the shared
406
+ * `@zylem/bridge` channel: the game applies editor commands itself (via its
407
+ * bridge adapter), so hosts only need this module to mount the overlay,
408
+ * mirror editor commands into their own stores, or push state into the
409
+ * editor UI.
335
410
  */
336
411
 
412
+ /**
413
+ * Nested state payload used by host-facing helpers.
414
+ * Mirrors the historical `editor-state-dispatch` window-event shape so
415
+ * existing hosts keep working; internally everything rides the bridge.
416
+ */
417
+ interface EditorUpdatePayload {
418
+ gameState?: {
419
+ debugFlag?: boolean;
420
+ [key: string]: unknown;
421
+ };
422
+ toolbarState?: {
423
+ tool?: BridgeDebugTool;
424
+ paused?: boolean;
425
+ };
426
+ [key: string]: unknown;
427
+ }
337
428
  interface EditorStateBridgeOptions {
338
429
  /**
339
- * Called after each editor state dispatch has been applied to the
340
- * game-lib debug state, so hosts can mirror the payload into their own
341
- * stores (e.g. a SolidJS store).
430
+ * Called after each editor command has been sent to the game, so hosts
431
+ * can mirror the payload into their own stores (e.g. a SolidJS store).
342
432
  */
343
433
  onStateDispatch?: (payload: EditorUpdatePayload) => void;
344
434
  }
345
435
  interface EditorStateBridge {
346
436
  /** Push external app state back into the editor UI. */
347
437
  dispatchToEditor(payload: EditorUpdatePayload): void;
348
- /** Remove the window listener installed by the bridge. */
438
+ /** Remove the bridge subscriptions installed by this helper. */
349
439
  dispose(): void;
350
440
  }
351
441
  /**
352
- * Dispatch a state update payload to the editor (EDITOR_STATE_RECEIVE).
353
- * Safe to call in non-browser environments (no-op).
442
+ * Push a state update payload into the editor UI (stores + game command).
443
+ * Safe to call in non-browser environments (no-op side effects only).
354
444
  */
355
445
  declare function dispatchToEditor(payload: EditorUpdatePayload): void;
356
446
  /**
357
- * Listen for editor state dispatches (EDITOR_STATE_DISPATCH) and sync them
358
- * into the game-lib debug state: debug flag, active tool, and pause state.
447
+ * Observe editor→game commands on the shared bridge.
448
+ *
449
+ * The game side applies commands itself; this helper exists so external
450
+ * hosts can mirror editor actions (debug toggle, tool, pause) into their own
451
+ * state via `onStateDispatch`.
452
+ *
453
+ * This deliberately does **not** open the editor's game→editor store
454
+ * subscriptions. Those are owned by `<zylem-editor>` for the duration it is
455
+ * mounted, and the game gates expensive work (thumbnail renders, entity
456
+ * summaries) on them being present. A long-lived host helper holding one open
457
+ * would keep that work running forever, even with no editor on screen. Hosts
458
+ * that render editor components without the custom element should call
459
+ * `connectEditorBridge()` directly and release it themselves.
359
460
  *
360
461
  * @example
361
462
  * ```ts
@@ -365,12 +466,31 @@ declare function dispatchToEditor(payload: EditorUpdatePayload): void;
365
466
  * ```
366
467
  */
367
468
  declare function attachEditorStateBridge(options?: EditorStateBridgeOptions): EditorStateBridge;
469
+ /**
470
+ * Send an editor state payload as bridge commands to the running game.
471
+ *
472
+ * @deprecated Use the typed bridge commands (`sendDebugEnabled`, `sendTool`,
473
+ * `sendPlayback`) instead. Kept for hosts migrating off the old
474
+ * window-CustomEvent API.
475
+ */
476
+ declare function dispatchEditorUpdate(payload: EditorUpdatePayload): void;
368
477
  interface MountZylemEditorOptions extends EditorStateBridgeOptions {
369
478
  /**
370
479
  * Controls the editor's floating launcher button.
371
480
  * @default 'floating'
372
481
  */
373
482
  launcherMode?: EditorLauncherMode;
483
+ /**
484
+ * Dock layout to seed on first run, keyed by viewport edge. `'main'` is the
485
+ * editor panel; any other id is a section such as `'console'`. Ignored once
486
+ * the user has a saved layout.
487
+ *
488
+ * @example
489
+ * ```ts
490
+ * mountZylemEditor({ defaultDocks: { left: ['main'], bottom: ['console'] } });
491
+ * ```
492
+ */
493
+ defaultDocks?: EditorDockDefaults;
374
494
  /**
375
495
  * Element the <zylem-editor> overlay is appended to.
376
496
  * @default document.body
@@ -379,6 +499,8 @@ interface MountZylemEditorOptions extends EditorStateBridgeOptions {
379
499
  }
380
500
  interface MountedZylemEditor extends EditorStateBridge {
381
501
  element: ZylemEditorElement;
502
+ /** Dock a panel to a viewport edge, or pass `null` to float it again. */
503
+ dockPanel(side: DockSide | null, panelId?: DockPanelId): void;
382
504
  }
383
505
  /**
384
506
  * Imperatively mount a <zylem-editor> overlay and attach the state bridge.
@@ -392,4 +514,4 @@ interface MountedZylemEditor extends EditorStateBridge {
392
514
  */
393
515
  declare function mountZylemEditor(options?: MountZylemEditorOptions): MountedZylemEditor;
394
516
 
395
- export { EDITOR_STATE_DISPATCH, EDITOR_STATE_RECEIVE, EDITOR_UPDATE_EVENT, type EditorEvent, type EditorEventType, type EditorStateBridge, type EditorStateBridgeOptions, type EditorUpdatePayload, Icon, type MountZylemEditorOptions, type MountedZylemEditor, type ZylemEditorConfig, ZylemEditorElement, attachEditorStateBridge, debugState, debugStore, dispatchEditorUpdate, dispatchToEditor, editorEvents, gameState, mountZylemEditor, registerZylemEditor, stageState };
517
+ export { DOCK_SIDES, type DockPanelId, type DockRect, type DockRegistry, type DockSide, type DockZoneState, type EditorDockDefaults, type EditorEvent, type EditorEventType, type EditorStateBridge, type EditorStateBridgeOptions, type EditorUpdatePayload, Icon, MAIN_PANEL_ID, type MountZylemEditorOptions, type MountedZylemEditor, type ZylemEditorConfig, ZylemEditorElement, attachEditorStateBridge, channel as bridgeChannel, computeDockLayout, connectEditorBridge, debugState, debugStore, disconnectEditorBridge, dispatchEditorUpdate, dispatchToEditor, dockPanelToSide, editorEvents, findDockedSide, gameState, getDockedSide, mountZylemEditor, registerZylemEditor, sendDebugEnabled, sendEntityFocus, sendEntitySelect, sendPlayback, sendStageVariable, sendTool, stageState, undockPanelFromSides };