@umicat/phaser-sdk 1.0.10 → 1.0.12

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.
@@ -3,6 +3,21 @@ import { AssetRecord } from '../scene/types.js';
3
3
  import { TilemapEditOp } from '../protocol.js';
4
4
  export declare function setupEditorBridge(game: Phaser.Game): void;
5
5
  export declare function postSelectionRect(game: Phaser.Game): void;
6
+ /**
7
+ * Compute the entity's DOM screen rect — viewport pixels relative to the
8
+ * iframe's top-left. The host adds the iframe's bounding rect to translate
9
+ * into page coords.
10
+ *
11
+ * Path: world coords → camera transform → canvas pixels → iframe pixels.
12
+ * Phaser's Scale.FIT may letterbox/pillarbox the canvas inside the iframe,
13
+ * so we factor in the canvas's parent offset too.
14
+ */
15
+ export declare function computeScreenRect(game: Phaser.Game, go: Phaser.GameObjects.GameObject): {
16
+ x: number;
17
+ y: number;
18
+ width: number;
19
+ height: number;
20
+ } | null;
6
21
  /**
7
22
  * FB.9a — return ALL entities at the given world coords, sorted by depth
8
23
  * DESC (topmost first). Used by EditorOverlayScene's Alt+click handler
@@ -574,7 +574,7 @@ function doPostSelectionRect(game) {
574
574
  * Phaser's Scale.FIT may letterbox/pillarbox the canvas inside the iframe,
575
575
  * so we factor in the canvas's parent offset too.
576
576
  */
577
- function computeScreenRect(game, go) {
577
+ export function computeScreenRect(game, go) {
578
578
  // Pull entity-local bounds — same logic the editor uses for hit-test.
579
579
  const hitW = go.getData('editorHitWidth');
580
580
  const hitH = go.getData('editorHitHeight');
@@ -1405,6 +1405,17 @@ function pickLoadUrl(asset) {
1405
1405
  const loadUrl = asset.loadUrl;
1406
1406
  return loadUrl || asset.path;
1407
1407
  }
1408
+ /**
1409
+ * Atlas JSON URL for editor lazy-load. Mirrors `pickLoadUrl` for the companion
1410
+ * atlas/ninepatch file: `atlasLoadUrl` (off-record, set by the host's Bg-image
1411
+ * picker from `AssetSummary.atlasUrl`) loads the JSON from the CDN so a freshly
1412
+ * generated + region-tagged asset renders LIVE — its bytes aren't in the
1413
+ * workspace until save. Falls back to the workspace `atlasPath` for saved assets.
1414
+ */
1415
+ function pickAtlasUrl(asset) {
1416
+ const atlasLoadUrl = asset.atlasLoadUrl;
1417
+ return atlasLoadUrl || asset.atlasPath;
1418
+ }
1408
1419
  function loadAssetIntoScene(scene, asset) {
1409
1420
  return new Promise((resolve, reject) => {
1410
1421
  if (scene.textures.exists(asset.textureKey)) {
@@ -1448,21 +1459,25 @@ function loadAssetIntoScene(scene, asset) {
1448
1459
  scene.load.image(asset.textureKey, loadFrom);
1449
1460
  }
1450
1461
  break;
1451
- case 'atlas':
1462
+ case 'atlas': {
1463
+ // The companion JSON loads from `atlasLoadUrl` (CDN) when present so a
1464
+ // freshly generated + 9-sliced asset renders live, not just the texture.
1465
+ const atlasFrom = pickAtlasUrl(asset);
1452
1466
  if (asset.atlasPath && asset.atlasFormat === 'xml') {
1453
- scene.load.atlasXML(asset.textureKey, loadFrom, asset.atlasPath);
1467
+ scene.load.atlasXML(asset.textureKey, loadFrom, atlasFrom);
1454
1468
  }
1455
1469
  else if (asset.atlasPath) {
1456
- scene.load.atlas(asset.textureKey, loadFrom, asset.atlasPath);
1470
+ scene.load.atlas(asset.textureKey, loadFrom, atlasFrom);
1457
1471
  // Also fetch the raw JSON into the side-cache for per-region
1458
1472
  // ninePatch lookups (slice 10). Phaser's atlas parser drops
1459
1473
  // unknown per-frame fields, so we keep the original JSON around.
1460
1474
  const sidecarKey = `umicat:atlas-ninepatch:${asset.textureKey}`;
1461
1475
  if (!scene.cache.json.exists(sidecarKey)) {
1462
- scene.load.json(sidecarKey, asset.atlasPath);
1476
+ scene.load.json(sidecarKey, atlasFrom);
1463
1477
  }
1464
1478
  }
1465
1479
  break;
1480
+ }
1466
1481
  case 'audio':
1467
1482
  scene.load.audio(asset.textureKey, loadFrom);
1468
1483
  break;
@@ -84,6 +84,8 @@ export declare class EditorOverlayScene extends Phaser.Scene {
84
84
  private postDragEnd;
85
85
  private postShortcut;
86
86
  private applyPanZoom;
87
+ /** Last pointerdown's entity + time — used to detect a double-click (inline text edit). */
88
+ private lastDownInfo;
87
89
  private panActive;
88
90
  private spaceHeld;
89
91
  private panStartScreen;
@@ -4,7 +4,7 @@ import { findHudEntity, findHudRegistry, UMICAT_HUD_SCENE_KEY, } from '../scene/
4
4
  import { isPerFrameHitbox } from '../scene/types.js';
5
5
  import { getManifest } from '../scene/SceneLoader.js';
6
6
  import { getEditorState, startDrag, clearDrag, getDrag, getSelection, getEditorMode, getDebugOverlayState, getTilemapToolState, isTilemapPaintMode, beginTilemapStroke, beginAutotileStroke, appendTilemapStrokeCell, appendAutotileStrokeClick, endTilemapStroke, getTilemapStroke, beginTilemapRect, updateTilemapRect, endTilemapRect, getTilemapRect, getTilemapResize, beginTilemapResize, updateTilemapResize, endTilemapResize, getEntityResize, beginEntityResize, updateEntityResize, endEntityResize, } from './EditorState.js';
7
- import { applyTilemapOp, findTilemapLayerById, handleEditTilemap, postSelectionRect } from './EditorBridge.js';
7
+ import { applyTilemapOp, computeScreenRect, findTilemapLayerById, handleEditTilemap, postSelectionRect } from './EditorBridge.js';
8
8
  import { applyAutotile, findTerrain, getAutotileKind } from '../scene/autotile.js';
9
9
  /**
10
10
  * Editor overlay — slice 2.
@@ -65,6 +65,8 @@ const ANCHOR_CROSS_LEN = 6;
65
65
  export class EditorOverlayScene extends Phaser.Scene {
66
66
  constructor() {
67
67
  super({ key: EDITOR_OVERLAY_KEY });
68
+ /** Last pointerdown's entity + time — used to detect a double-click (inline text edit). */
69
+ this.lastDownInfo = null;
68
70
  // P1 infinite canvas — input state. Pan can be triggered by middle-button
69
71
  // drag OR by space+left-drag. We accumulate a "pan active" flag so the
70
72
  // existing selection / drag-to-move handlers don't fire while panning.
@@ -289,6 +291,30 @@ export class EditorOverlayScene extends Phaser.Scene {
289
291
  this.postPick(null, modifiers);
290
292
  return;
291
293
  }
294
+ // Double-click a text-bearing HUD widget (icon-button label / text widget) →
295
+ // tell the host to open an inline text editor over it (Figma convention —
296
+ // beats hunting for the Inspector's Label field). The first click already
297
+ // selected it; the second opens the editor instead of starting a drag.
298
+ const dblKind = hit.getData('entityKind');
299
+ const downTime = pointer.downTime;
300
+ const isDoubleClick = this.lastDownInfo?.id === entityId && downTime - this.lastDownInfo.time < 350;
301
+ this.lastDownInfo = { id: entityId, time: downTime };
302
+ if (isDoubleClick &&
303
+ getEditorMode(this.game) === 'hud' &&
304
+ (dblKind === 'icon-button' || dblKind === 'text')) {
305
+ const rect = computeScreenRect(this.game, hit);
306
+ if (rect) {
307
+ const msg = {
308
+ type: 'umicat:editor:editText',
309
+ entityId,
310
+ kind: dblKind,
311
+ rect,
312
+ };
313
+ window.parent.postMessage(msg, '*');
314
+ event?.preventDefault?.();
315
+ return;
316
+ }
317
+ }
292
318
  // Surface the prefab id when the picked GameObject was spawned via
293
319
  // `spawnPrefab` (set on the data manager at spawn — see Prefabs.ts).
294
320
  // The host routes prefab-instance picks to the Prefab Inspector.
@@ -715,6 +715,26 @@ export interface EditorEntityResizedMessage {
715
715
  height: number;
716
716
  };
717
717
  }
718
+ /**
719
+ * Double-click on a text-bearing HUD widget (icon-button label / text widget) —
720
+ * the host renders an inline `<input>` over the widget so the user edits the
721
+ * text directly on the canvas (Figma/Canva convention) instead of hunting for
722
+ * the Inspector field. `rect` is iframe-relative px (host adds the iframe offset
723
+ * to position the overlay). The host reads the CURRENT text from its scene state
724
+ * and commits the edit via the normal applyEdit/patch path.
725
+ */
726
+ export interface EditorEditTextMessage {
727
+ type: 'umicat:editor:editText';
728
+ entityId: string;
729
+ /** Which HUD widget kind — tells the host which field to edit (label vs source). */
730
+ kind: 'icon-button' | 'text';
731
+ rect: {
732
+ x: number;
733
+ y: number;
734
+ width: number;
735
+ height: number;
736
+ };
737
+ }
718
738
  /**
719
739
  * Editor keyboard shortcut intercepted inside the iframe (user clicked the
720
740
  * canvas, so focus is on the iframe and the host window doesn't see the
@@ -428,7 +428,7 @@ function createIconButton(ctx, entity, pos) {
428
428
  }
429
429
  if (v.label) {
430
430
  const label = ctx.scene.add.text(0, 0, v.label, {
431
- fontFamily: 'sans-serif',
431
+ fontFamily: v.fontFamily ?? 'sans-serif',
432
432
  fontSize: `${v.fontSize ?? 16}px`,
433
433
  color: v.textColor ?? '#ffffff',
434
434
  });
@@ -1213,6 +1213,8 @@ export interface HudIconButtonEntity extends HudEntityBase {
1213
1213
  strokeWidth?: number;
1214
1214
  pressedFillColor?: string;
1215
1215
  textColor?: string;
1216
+ /** Label font family (matches a loaded font; default `sans-serif`). */
1217
+ fontFamily?: string;
1216
1218
  fontSize?: number;
1217
1219
  }
1218
1220
  export interface HudProgressBarEntity extends HudEntityBase {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umicat/phaser-sdk",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "Umicat Phaser 3 SDK — game infrastructure for the Umicat platform",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",