@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.
- package/dist/editor/EditorBridge.d.ts +15 -0
- package/dist/editor/EditorBridge.js +20 -5
- package/dist/editor/EditorOverlayScene.d.ts +2 -0
- package/dist/editor/EditorOverlayScene.js +27 -1
- package/dist/protocol.d.ts +20 -0
- package/dist/scene/HudRuntime.js +1 -1
- package/dist/scene/types.d.ts +2 -0
- package/package.json +1 -1
|
@@ -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,
|
|
1467
|
+
scene.load.atlasXML(asset.textureKey, loadFrom, atlasFrom);
|
|
1454
1468
|
}
|
|
1455
1469
|
else if (asset.atlasPath) {
|
|
1456
|
-
scene.load.atlas(asset.textureKey, loadFrom,
|
|
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,
|
|
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.
|
package/dist/protocol.d.ts
CHANGED
|
@@ -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
|
package/dist/scene/HudRuntime.js
CHANGED
|
@@ -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
|
});
|
package/dist/scene/types.d.ts
CHANGED
|
@@ -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 {
|