@zylem/editor 0.7.0 → 0.7.4

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Timothy Cool
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,7 +1,7 @@
1
1
  import { Component } from 'solid-js';
2
2
  import { LucideProps } from 'lucide-solid';
3
3
  import * as _zylem_bridge from '@zylem/bridge';
4
- import { BridgeDebugTool } from '@zylem/bridge';
4
+ import { BridgeDebugTool, BridgeVec3, BridgeQuat, BridgePose, SnapSettingsPayload, SceneOperationPayload, EntityTypeDescriptor } from '@zylem/bridge';
5
5
 
6
6
  /**
7
7
  * Editor Event Bus
@@ -64,7 +64,22 @@ interface DebugState {
64
64
  paused: boolean;
65
65
  tool: DebugTools;
66
66
  selectedEntityId: string | null;
67
+ /**
68
+ * Full selection, mirroring the game. `selectedEntityId` is its first entry,
69
+ * kept so single-select consumers need no change.
70
+ */
71
+ selectedEntityIds: string[];
67
72
  hoveredEntityId: string | null;
73
+ /**
74
+ * Most recently selected or created entity, kept after it is deselected.
75
+ *
76
+ * What the gizmo tools fall back to: pressing Rotate with nothing selected
77
+ * should turn the thing you were last working on, rather than nothing. Never
78
+ * cleared on deselect — only overwritten — and validated against the current
79
+ * entity list at the point of use, since the entity it names can be deleted
80
+ * or undone away.
81
+ */
82
+ lastTouchedEntityId: string | null;
68
83
  flags: Set<string>;
69
84
  }
70
85
  declare const debugState: DebugState;
@@ -215,6 +230,8 @@ declare const debugStore: {
215
230
  paused: boolean;
216
231
  hovered: string | null;
217
232
  selected: string[];
233
+ /** Fallback target for the gizmo tools; see `DebugState.lastTouchedEntityId`. */
234
+ lastTouched: string | null;
218
235
  panelPosition: {
219
236
  x: number;
220
237
  y: number;
@@ -280,6 +297,19 @@ interface ZylemEditorConfig {
280
297
  * rather than forcing one.
281
298
  */
282
299
  defaultDocks?: EditorDockDefaults;
300
+ /**
301
+ * Whether the editor claims cmd/ctrl+z for its own undo stack.
302
+ *
303
+ * Disable it when embedding the editor in an app with its own history, and
304
+ * call the exported `undo()` / `redo()` from that app's handler instead.
305
+ * @default true
306
+ */
307
+ enableUndoShortcut?: boolean;
308
+ /**
309
+ * Whether Escape disarms the active tool.
310
+ * @default true
311
+ */
312
+ enableEscapeShortcut?: boolean;
283
313
  }
284
314
  /**
285
315
  * ZylemEditorElement - A web component that wraps the Zylem Editor.
@@ -320,6 +350,8 @@ declare class ZylemEditorElement extends HTMLElement {
320
350
  private controller;
321
351
  /** Releases this element's bridge subscription (reference-counted). */
322
352
  private releaseBridge;
353
+ /** Keyboard shortcuts and the snap/grid mirror, torn down with the element. */
354
+ private teardownHooks;
323
355
  constructor();
324
356
  /**
325
357
  * Set configuration for the editor.
@@ -397,6 +429,216 @@ declare function sendEntitySelect(uuid: string | null): void;
397
429
  declare function sendEntityFocus(uuid: string): void;
398
430
  /** Write a stage variable in the running game. */
399
431
  declare function sendStageVariable(key: string, value: unknown): void;
432
+ /**
433
+ * Set an entity's transform absolutely.
434
+ *
435
+ * `quaternion` is authoritative when supplied; `rotation` is Euler radians for
436
+ * the numeric fields, and round-trips less cleanly.
437
+ */
438
+ declare function sendEntityTransform(uuid: string, transform: {
439
+ position?: BridgeVec3;
440
+ rotation?: BridgeVec3;
441
+ quaternion?: BridgeQuat;
442
+ scale?: BridgeVec3;
443
+ }): void;
444
+ /** Arm the game's add tool with a catalog type, or `null` to disarm it. */
445
+ declare function sendAddType(typeId: string | null, props?: Record<string, unknown>): void;
446
+ /** Spawn a catalog entity without a placement click. */
447
+ declare function sendEntityCreate(typeId: string, options?: {
448
+ props?: Record<string, unknown>;
449
+ pose?: BridgePose;
450
+ }): void;
451
+ /** Push snap increments to the game's gizmo. */
452
+ declare function sendSnapSettings(snap: SnapSettingsPayload): void;
453
+ /** Show or hide the game's construction-plane grid. */
454
+ declare function sendGridVisible(visible: boolean): void;
455
+
456
+ /**
457
+ * Undo/redo history for scene edits.
458
+ *
459
+ * The editor owns the stack but never the inversion. Every edit originates in
460
+ * the game (gizmo drags, placement clicks, delete clicks), so the game publishes
461
+ * a committed before/after record and the editor stores it. Replaying one sends
462
+ * it back over the bridge: undoing a delete has to produce the original entity
463
+ * with its original uuid, which the editor could not do — all it ever receives
464
+ * is a summary of name, type, and transform.
465
+ *
466
+ * In-memory only. A reload drops history, which is correct: the game's entities
467
+ * are gone too, so the uuids every entry refers to would be dangling.
468
+ */
469
+
470
+ /**
471
+ * Depth cap. Each entry holds two poses per affected entity, so this is cheap;
472
+ * the limit exists so a long editing session cannot grow without bound. The
473
+ * game's detached-entity bin is sized above this, so a uuid still reachable
474
+ * from here is still restorable.
475
+ */
476
+ declare const MAX_HISTORY_DEPTH = 100;
477
+ interface HistoryState {
478
+ /** Oldest first; the last entry is what `undo()` reverts. */
479
+ undoStack: SceneOperationPayload[];
480
+ /** Operations that have been undone, newest last. */
481
+ redoStack: SceneOperationPayload[];
482
+ }
483
+ declare const historyState: HistoryState;
484
+ declare function canUndo(): boolean;
485
+ declare function canRedo(): boolean;
486
+ /** Label of the operation `undo()` would revert, for menu text. */
487
+ declare function undoLabel(): string | null;
488
+ /** Label of the operation `redo()` would replay. */
489
+ declare function redoLabel(): string | null;
490
+ /** Revert the most recent operation. */
491
+ declare function undo(): boolean;
492
+ /** Replay the most recently undone operation. */
493
+ declare function redo(): boolean;
494
+ /**
495
+ * Drop all history. Called on a fresh stage snapshot, because the entities the
496
+ * stack refers to are gone and replaying an entry would either do nothing or
497
+ * hit an unrelated entity that happens to share a uuid.
498
+ */
499
+ declare function clearHistory(): void;
500
+
501
+ /**
502
+ * The placeable entity types the game offers, and which one the Add tool is
503
+ * armed with.
504
+ *
505
+ * The list is game-owned: it arrives over the bridge as `catalog:snapshot`, so
506
+ * a host that registers its own entity types game-side gets them in the palette
507
+ * with no editor change. Icons come through as inline SVG markup because the
508
+ * editor and its hosts do not share an icon library.
509
+ */
510
+
511
+ interface CatalogState {
512
+ entities: EntityTypeDescriptor[];
513
+ /** Type the Add tool will place, or null when disarmed. */
514
+ armedTypeId: string | null;
515
+ /**
516
+ * Last type chosen from the palette, so re-arming the tool does not make the
517
+ * user re-pick. Survives disarming; cleared only when the catalog no longer
518
+ * contains it.
519
+ */
520
+ lastTypeId: string | null;
521
+ }
522
+ declare const catalogState: CatalogState;
523
+ declare function setEntityCatalog(entities: EntityTypeDescriptor[]): void;
524
+ declare function setArmedType(typeId: string | null): void;
525
+
526
+ /** Drop the armed placement type and return to the neutral tool. */
527
+ declare function disarmTools(): boolean;
528
+
529
+ /**
530
+ * Keeps the gizmo modes from outliving the selection they act on.
531
+ *
532
+ * The Move/Rotate/Scale buttons are only shown while something is selected, so
533
+ * a tool left active after the selection clears would be unreachable: no button
534
+ * to toggle it off, and the game keeps the simulation paused for the whole time
535
+ * a transform tool is held (see `StageTransformTool.setMode`). Escape already
536
+ * offers a manual way out via `disarmTools`; this is the automatic one.
537
+ */
538
+
539
+ /**
540
+ * Drop an active transform tool when nothing is selected.
541
+ *
542
+ * @returns `true` when a tool was reset.
543
+ */
544
+ declare function releaseOrphanedTransformTool(): boolean;
545
+ interface TransformToolGuardOptions {
546
+ /** @default true */
547
+ enabled?: boolean;
548
+ }
549
+ /**
550
+ * Watch the selection and release orphaned transform tools.
551
+ *
552
+ * @returns An uninstall function.
553
+ */
554
+ declare function installTransformToolGuard(options?: TransformToolGuardOptions): () => void;
555
+
556
+ /**
557
+ * Snap settings and grid visibility for the transform tools.
558
+ *
559
+ * Owned by the editor because it is the UI for them, and mirrored to the game
560
+ * over `snap:set` — the gizmo does the actual snapping, since only it knows the
561
+ * drag target.
562
+ */
563
+
564
+ /** 15 degrees, the default rotation increment. */
565
+ declare const DEFAULT_ROTATE_SNAP: number;
566
+ interface TransformState extends SnapSettingsPayload {
567
+ /** Whether the game draws its construction-plane grid. */
568
+ gridVisible: boolean;
569
+ }
570
+ declare const transformState: TransformState;
571
+ declare function setSnapEnabled(enabled: boolean): void;
572
+ declare function setSnapIncrements(increments: Partial<Pick<TransformState, 'translate' | 'rotate' | 'scale'>>): void;
573
+ declare function setGridVisible(visible: boolean): void;
574
+
575
+ /**
576
+ * Host observation of committed scene edits.
577
+ *
578
+ * Every edit the editor records is also announced here, so an embedding app can
579
+ * react to it — persisting a layout, marking a document dirty, or eventually
580
+ * writing changes back to source. Kept in its own module so `editor-bridge` does
581
+ * not have to import the host surface (which imports the bridge), which would be
582
+ * circular.
583
+ */
584
+
585
+ type SceneOperationListener = (op: SceneOperationPayload) => void;
586
+ /**
587
+ * Observe committed scene operations.
588
+ *
589
+ * @returns An unsubscribe function.
590
+ */
591
+ declare function onSceneOperation(listener: SceneOperationListener): () => void;
592
+
593
+ /**
594
+ * Stage export.
595
+ *
596
+ * Edits are runtime-only: moving a crate in the editor does not rewrite the
597
+ * source that created it. Export is the bridge between the two — it dumps the
598
+ * current stage as JSON so the values can be pasted back into the code that
599
+ * authored the scene. Source write-back is a later problem; this makes the work
600
+ * recoverable in the meantime.
601
+ *
602
+ * Built from the editor's own bridge state rather than `@zylem/game-lib/schema`,
603
+ * because the editor has no game-lib dependency at runtime and the entity
604
+ * blueprint schema stores 2D positions, which would drop the Z of every
605
+ * transform this feature exists to preserve.
606
+ */
607
+
608
+ interface StageExportEntity {
609
+ uuid: string;
610
+ name?: string;
611
+ type?: string;
612
+ position?: Vector3Like;
613
+ /** Euler angles in radians, matching the game's own representation. */
614
+ rotation?: Vector3Like;
615
+ scale?: Vector3Like;
616
+ }
617
+ interface StageExport {
618
+ /** Bumped when the shape changes, so an old file is recognisable. */
619
+ version: 1;
620
+ exportedAt: string;
621
+ stage: {
622
+ id: string;
623
+ backgroundColor: string;
624
+ backgroundImage: string | null;
625
+ gravity: Vector3Like;
626
+ variables: Record<string, unknown>;
627
+ } | null;
628
+ entities: StageExportEntity[];
629
+ }
630
+ /** Snapshot the current stage as a plain, serializable object. */
631
+ declare function buildStageExport(): StageExport;
632
+ /** The export as formatted JSON. */
633
+ declare function stageExportToString(): string;
634
+ /**
635
+ * Copy the export to the clipboard.
636
+ *
637
+ * @returns The serialized stage when the copy succeeded, or `null` when the
638
+ * clipboard was unavailable — over plain HTTP, or without a user gesture, the
639
+ * API is either missing or rejects, and the caller falls back to the console.
640
+ */
641
+ declare function copyStageExport(): Promise<string | null>;
400
642
 
401
643
  /**
402
644
  * Editor Host Helpers
@@ -514,4 +756,4 @@ interface MountedZylemEditor extends EditorStateBridge {
514
756
  */
515
757
  declare function mountZylemEditor(options?: MountZylemEditorOptions): MountedZylemEditor;
516
758
 
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 };
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 };