@umicat/platform-sdk 0.2.2 → 0.4.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.
- package/dist/core/UmicatCore.d.ts +4 -0
- package/dist/core/UmicatCore.js +2 -0
- package/dist/fold/FoldModule.d.ts +58 -0
- package/dist/fold/FoldModule.js +87 -0
- package/dist/fold/ScreensModule.d.ts +21 -0
- package/dist/fold/ScreensModule.js +101 -0
- package/dist/fold/foldVariables.d.ts +37 -0
- package/dist/fold/foldVariables.js +67 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { RealtimeModule } from '../realtime/RealtimeModule.js';
|
|
|
5
5
|
import { AiModule } from '../ai/AiModule.js';
|
|
6
6
|
import { PlatformModule } from '../platform/PlatformModule.js';
|
|
7
7
|
import { VoiceModule } from '../voice/VoiceModule.js';
|
|
8
|
+
import { FoldModule } from '../fold/FoldModule.js';
|
|
8
9
|
import type { UmicatUser } from '../protocol.js';
|
|
9
10
|
export interface UmicatInitOptions {
|
|
10
11
|
/** Handshake timeout in ms when running inside a host. Default 5000. */
|
|
@@ -46,6 +47,9 @@ export declare class UmicatCore {
|
|
|
46
47
|
/** Voice input (speech-to-text) with a live mic level for waveform UIs.
|
|
47
48
|
* Browser recognition in a browser, native platform recognizer in the app. */
|
|
48
49
|
readonly voice: VoiceModule;
|
|
50
|
+
/** Where the device's fold falls across the play area. Always present;
|
|
51
|
+
* reports `dualScreen: false` on every surface without a hinge. */
|
|
52
|
+
readonly fold: FoldModule;
|
|
49
53
|
protected constructor(transport: Transport);
|
|
50
54
|
/** The current authenticated user, or `null` if anonymous / standalone. */
|
|
51
55
|
get user(): UmicatUser | null;
|
package/dist/core/UmicatCore.js
CHANGED
|
@@ -6,6 +6,7 @@ import { RealtimeModule } from '../realtime/RealtimeModule.js';
|
|
|
6
6
|
import { AiModule } from '../ai/AiModule.js';
|
|
7
7
|
import { PlatformModule } from '../platform/PlatformModule.js';
|
|
8
8
|
import { VoiceModule } from '../voice/VoiceModule.js';
|
|
9
|
+
import { FoldModule } from '../fold/FoldModule.js';
|
|
9
10
|
/**
|
|
10
11
|
* Umicat platform services bound to the current (game, user) — with no
|
|
11
12
|
* rendering engine anywhere in sight.
|
|
@@ -48,6 +49,7 @@ export class UmicatCore {
|
|
|
48
49
|
this.ai = new AiModule(transport);
|
|
49
50
|
this.platform = new PlatformModule(transport);
|
|
50
51
|
this.voice = new VoiceModule(transport);
|
|
52
|
+
this.fold = new FoldModule(transport);
|
|
51
53
|
}
|
|
52
54
|
/** The current authenticated user, or `null` if anonymous / standalone. */
|
|
53
55
|
get user() { return this.transport.user; }
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { Transport } from '../core/Transport.js';
|
|
2
|
+
/** A rectangle in CSS pixels, in the game viewport's own coordinate space. */
|
|
3
|
+
export interface FoldRect {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export interface FoldLayout {
|
|
10
|
+
/** Whether the play area should present itself as two screens right now. */
|
|
11
|
+
dualScreen: boolean;
|
|
12
|
+
/** Hinge angle in degrees: 0 shut, 180 flat. Absent on a device with no hinge. */
|
|
13
|
+
angle?: number;
|
|
14
|
+
/** The crease, margins INCLUDED. A band, not a line. */
|
|
15
|
+
division?: FoldRect;
|
|
16
|
+
/** The usable panel above the crease. */
|
|
17
|
+
top?: FoldRect;
|
|
18
|
+
/** The usable panel below it. */
|
|
19
|
+
bottom?: FoldRect;
|
|
20
|
+
/**
|
|
21
|
+
* Cut-outs content must not sit under — the front cameras.
|
|
22
|
+
*
|
|
23
|
+
* Only the ACTIVE ones: a folding phone reports a camera on the display it is
|
|
24
|
+
* not currently using, and reserving space for a lens that is facing away is
|
|
25
|
+
* just lost screen.
|
|
26
|
+
*/
|
|
27
|
+
occlusions: FoldRect[];
|
|
28
|
+
viewport?: {
|
|
29
|
+
width: number;
|
|
30
|
+
height: number;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Live fold state, and a subscription to changes.
|
|
35
|
+
*
|
|
36
|
+
* Reports `dualScreen: false` forever on a host that never pushes — a browser
|
|
37
|
+
* tab, an older native build, a phone that does not fold. Callers do not
|
|
38
|
+
* feature-detect; they lay out against whatever `layout` currently says.
|
|
39
|
+
*/
|
|
40
|
+
export declare class FoldModule {
|
|
41
|
+
private readonly transport;
|
|
42
|
+
private current;
|
|
43
|
+
private readonly listeners;
|
|
44
|
+
constructor(transport: Transport);
|
|
45
|
+
/** What the fold is doing right now. Never null. */
|
|
46
|
+
get layout(): FoldLayout;
|
|
47
|
+
/**
|
|
48
|
+
* Calls `listener` on every change, and ONCE immediately with the current
|
|
49
|
+
* value.
|
|
50
|
+
*
|
|
51
|
+
* The immediate call is the point: a game that only hears about changes comes
|
|
52
|
+
* up laid out for a flat screen on a device that was already folded when it
|
|
53
|
+
* launched, and then looks correct forever after the first hinge movement —
|
|
54
|
+
* which is the worst possible version of this bug, because it fixes itself
|
|
55
|
+
* the moment anyone tries to reproduce it.
|
|
56
|
+
*/
|
|
57
|
+
subscribe(listener: (layout: FoldLayout) => void): () => void;
|
|
58
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const FOLD_EVENT = 'umicat:fold';
|
|
2
|
+
/** The flat, unfolded answer — what every surface without a hinge reports. */
|
|
3
|
+
const NO_FOLD = { dualScreen: false, occlusions: [] };
|
|
4
|
+
/**
|
|
5
|
+
* Live fold state, and a subscription to changes.
|
|
6
|
+
*
|
|
7
|
+
* Reports `dualScreen: false` forever on a host that never pushes — a browser
|
|
8
|
+
* tab, an older native build, a phone that does not fold. Callers do not
|
|
9
|
+
* feature-detect; they lay out against whatever `layout` currently says.
|
|
10
|
+
*/
|
|
11
|
+
export class FoldModule {
|
|
12
|
+
constructor(transport) {
|
|
13
|
+
this.transport = transport;
|
|
14
|
+
this.current = NO_FOLD;
|
|
15
|
+
this.listeners = new Set();
|
|
16
|
+
transport.on?.(FOLD_EVENT, (payload) => {
|
|
17
|
+
const next = parse(payload);
|
|
18
|
+
if (!next)
|
|
19
|
+
return;
|
|
20
|
+
this.current = next;
|
|
21
|
+
for (const listener of this.listeners)
|
|
22
|
+
listener(next);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** What the fold is doing right now. Never null. */
|
|
26
|
+
get layout() {
|
|
27
|
+
return this.current;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Calls `listener` on every change, and ONCE immediately with the current
|
|
31
|
+
* value.
|
|
32
|
+
*
|
|
33
|
+
* The immediate call is the point: a game that only hears about changes comes
|
|
34
|
+
* up laid out for a flat screen on a device that was already folded when it
|
|
35
|
+
* launched, and then looks correct forever after the first hinge movement —
|
|
36
|
+
* which is the worst possible version of this bug, because it fixes itself
|
|
37
|
+
* the moment anyone tries to reproduce it.
|
|
38
|
+
*/
|
|
39
|
+
subscribe(listener) {
|
|
40
|
+
this.listeners.add(listener);
|
|
41
|
+
listener(this.current);
|
|
42
|
+
return () => { this.listeners.delete(listener); };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function rect(value) {
|
|
46
|
+
if (!value || typeof value !== 'object')
|
|
47
|
+
return undefined;
|
|
48
|
+
const r = value;
|
|
49
|
+
const nums = ['x', 'y', 'width', 'height'].map((k) => r[k]);
|
|
50
|
+
if (nums.some((n) => typeof n !== 'number' || !Number.isFinite(n)))
|
|
51
|
+
return undefined;
|
|
52
|
+
const [x, y, width, height] = nums;
|
|
53
|
+
return { x, y, width, height };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Host payload → `FoldLayout`, or null if it is not one.
|
|
57
|
+
*
|
|
58
|
+
* Validated rather than cast: this crosses a postMessage boundary, and a
|
|
59
|
+
* half-shaped payload that reaches layout code is a crash in the middle of
|
|
60
|
+
* someone's game rather than a line in a console.
|
|
61
|
+
*/
|
|
62
|
+
function parse(payload) {
|
|
63
|
+
if (!payload || typeof payload !== 'object')
|
|
64
|
+
return null;
|
|
65
|
+
const p = payload;
|
|
66
|
+
if (typeof p.dualScreen !== 'boolean')
|
|
67
|
+
return null;
|
|
68
|
+
const occlusions = Array.isArray(p.occlusions)
|
|
69
|
+
? p.occlusions.map(rect).filter((r) => !!r)
|
|
70
|
+
: [];
|
|
71
|
+
const viewport = p.viewport && typeof p.viewport === 'object'
|
|
72
|
+
? p.viewport
|
|
73
|
+
: undefined;
|
|
74
|
+
const top = rect(p.top);
|
|
75
|
+
const bottom = rect(p.bottom);
|
|
76
|
+
return {
|
|
77
|
+
// A split with only one side is not a split. Guarding here keeps every
|
|
78
|
+
// consumer from having to check both panels before using either.
|
|
79
|
+
dualScreen: p.dualScreen && !!top && !!bottom,
|
|
80
|
+
angle: typeof p.angle === 'number' ? p.angle : undefined,
|
|
81
|
+
division: rect(p.division),
|
|
82
|
+
top,
|
|
83
|
+
bottom,
|
|
84
|
+
occlusions,
|
|
85
|
+
viewport,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FoldModule } from './FoldModule.js';
|
|
2
|
+
export declare class ScreensModule {
|
|
3
|
+
/** Above the crease when folded; the whole viewport when flat. */
|
|
4
|
+
readonly top: HTMLElement;
|
|
5
|
+
/** Below the crease. Hidden — but still yours, and still populated — when
|
|
6
|
+
* the device is flat. */
|
|
7
|
+
readonly bottom: HTMLElement;
|
|
8
|
+
private second;
|
|
9
|
+
private readonly listeners;
|
|
10
|
+
constructor(fold: FoldModule);
|
|
11
|
+
/** Whether `bottom` is a real second screen right now. */
|
|
12
|
+
get hasSecondScreen(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Calls `listener` on every change, and once immediately — same contract as
|
|
15
|
+
* `FoldModule.subscribe`, and for the same reason: a game told only about
|
|
16
|
+
* CHANGES lays itself out for one screen on a device that was already folded
|
|
17
|
+
* when it launched.
|
|
18
|
+
*/
|
|
19
|
+
subscribe(listener: (screens: ScreensModule) => void): () => void;
|
|
20
|
+
private apply;
|
|
21
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Two screens a game can put ANYTHING on.
|
|
2
|
+
//
|
|
3
|
+
// `FoldModule` answers "where is the crease"; this answers "here are the two
|
|
4
|
+
// surfaces". A game that wants a map, an inventory, a text box or a second
|
|
5
|
+
// rendered view on the lower screen appends it to `bottom` and is done — the
|
|
6
|
+
// SDK keeps both boxes glued to the panels as the hinge moves.
|
|
7
|
+
//
|
|
8
|
+
// **Both elements always exist, and neither is ever replaced.** The obvious
|
|
9
|
+
// alternative — `bottom: HTMLElement | null`, null when the device is flat —
|
|
10
|
+
// is more honest about the hardware and much worse to use: every game would
|
|
11
|
+
// branch at startup, and anything already appended would be destroyed on the
|
|
12
|
+
// first unfold. Instead `bottom` is simply hidden while there is no second
|
|
13
|
+
// screen, so a game appends once and its content survives folding, unfolding
|
|
14
|
+
// and folding again. `hasSecondScreen` is there for the game that genuinely
|
|
15
|
+
// wants to say something different when there is only one.
|
|
16
|
+
//
|
|
17
|
+
// **`position: fixed` children land inside their screen, not the window.** The
|
|
18
|
+
// containers carry a `transform`, and a transformed ancestor becomes the
|
|
19
|
+
// containing block for fixed descendants. That is what lets an existing HUD —
|
|
20
|
+
// authored as fixed, anchored to "bottom-right" — move onto a panel whole,
|
|
21
|
+
// without touching a single widget.
|
|
22
|
+
/** Pointer-events are off on the container and on for what the game adds, so
|
|
23
|
+
* an empty top screen does not swallow taps meant for the world below it. */
|
|
24
|
+
const BASE_STYLE = {
|
|
25
|
+
position: 'fixed',
|
|
26
|
+
pointerEvents: 'none',
|
|
27
|
+
// Clips to the panel: content must not bleed across the crease, which is the
|
|
28
|
+
// one place on this device where "slightly too tall" is physically bent.
|
|
29
|
+
overflow: 'hidden',
|
|
30
|
+
// Establishes the containing block (see above) AND its own stacking context.
|
|
31
|
+
transform: 'translateZ(0)',
|
|
32
|
+
// Under the SDK's own touch layer (z-index 10), which shares the lower panel.
|
|
33
|
+
zIndex: '5',
|
|
34
|
+
};
|
|
35
|
+
export class ScreensModule {
|
|
36
|
+
constructor(fold) {
|
|
37
|
+
this.second = false;
|
|
38
|
+
this.listeners = new Set();
|
|
39
|
+
this.top = make('top');
|
|
40
|
+
this.bottom = make('bottom');
|
|
41
|
+
fold.subscribe((layout) => this.apply(layout));
|
|
42
|
+
}
|
|
43
|
+
/** Whether `bottom` is a real second screen right now. */
|
|
44
|
+
get hasSecondScreen() {
|
|
45
|
+
return this.second;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Calls `listener` on every change, and once immediately — same contract as
|
|
49
|
+
* `FoldModule.subscribe`, and for the same reason: a game told only about
|
|
50
|
+
* CHANGES lays itself out for one screen on a device that was already folded
|
|
51
|
+
* when it launched.
|
|
52
|
+
*/
|
|
53
|
+
subscribe(listener) {
|
|
54
|
+
this.listeners.add(listener);
|
|
55
|
+
listener(this);
|
|
56
|
+
return () => { this.listeners.delete(listener); };
|
|
57
|
+
}
|
|
58
|
+
apply(layout) {
|
|
59
|
+
const split = layout.dualScreen && !!layout.top && !!layout.bottom;
|
|
60
|
+
this.second = split;
|
|
61
|
+
if (split) {
|
|
62
|
+
box(this.top, layout.top);
|
|
63
|
+
box(this.bottom, layout.bottom);
|
|
64
|
+
this.bottom.style.display = '';
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Flat: the top screen IS the window, and there is no second one.
|
|
68
|
+
Object.assign(this.top.style, { left: '0', top: '0', width: '100%', height: '100%' });
|
|
69
|
+
this.bottom.style.display = 'none';
|
|
70
|
+
}
|
|
71
|
+
for (const listener of this.listeners)
|
|
72
|
+
listener(this);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function make(name) {
|
|
76
|
+
// Headless (tests, SSR): hand back a detached stub rather than throwing. A
|
|
77
|
+
// game is never rendering in that case, so the box has nothing to be glued
|
|
78
|
+
// to — but `screens.top` must still be an element, or every caller needs a
|
|
79
|
+
// null check for a situation that only exists in a test runner.
|
|
80
|
+
if (typeof document === 'undefined') {
|
|
81
|
+
return { style: {}, dataset: {}, setAttribute() { }, appendChild() { } };
|
|
82
|
+
}
|
|
83
|
+
const el = document.createElement('div');
|
|
84
|
+
el.dataset.umicatScreen = name;
|
|
85
|
+
// Survives the editor's "hide every static element" sweep, which otherwise
|
|
86
|
+
// takes anything it does not recognise in index.html.
|
|
87
|
+
el.setAttribute('data-umicat-keep', '');
|
|
88
|
+
Object.assign(el.style, BASE_STYLE);
|
|
89
|
+
if (name === 'bottom')
|
|
90
|
+
el.style.display = 'none';
|
|
91
|
+
(document.body ?? document.documentElement).appendChild(el);
|
|
92
|
+
return el;
|
|
93
|
+
}
|
|
94
|
+
function box(el, r) {
|
|
95
|
+
Object.assign(el.style, {
|
|
96
|
+
left: `${r.x}px`,
|
|
97
|
+
top: `${r.y}px`,
|
|
98
|
+
width: `${r.width}px`,
|
|
99
|
+
height: `${r.height}px`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FoldModule, FoldLayout } from './FoldModule.js';
|
|
2
|
+
/**
|
|
3
|
+
* Publishes the fold geometry as CSS custom properties, so a game's own DOM can
|
|
4
|
+
* follow the crease without reading the SDK at all:
|
|
5
|
+
*
|
|
6
|
+
* --umicat-top-height, --umicat-bottom-top, --umicat-bottom-height,
|
|
7
|
+
* --umicat-fold-top, --umicat-fold-height
|
|
8
|
+
*
|
|
9
|
+
* plus `data-umicat-dual-screen` on `<html>`, because a stylesheet cannot ask a
|
|
10
|
+
* module a question.
|
|
11
|
+
*
|
|
12
|
+
* **Engine-agnostic on purpose.** These NAMES are a contract between the SDK and
|
|
13
|
+
* a game's stylesheet — Gomoku's chat panel moves to the lower screen with no
|
|
14
|
+
* JavaScript at all — and a contract with two definitions drifts. It lived in
|
|
15
|
+
* `@umicat/three-sdk` while 3D was the only consumer; it moved here the moment
|
|
16
|
+
* the Phaser side needed the same names.
|
|
17
|
+
*
|
|
18
|
+
* All values are `0` when there is no fold, so a stylesheet that uses them is
|
|
19
|
+
* correct on a flat screen too.
|
|
20
|
+
*
|
|
21
|
+
* **Never give these a fallback in `var()`.** Without the property the whole
|
|
22
|
+
* `calc()` is invalid, the declaration is dropped, and the unfolded rule stands
|
|
23
|
+
* — which is right. A `0px` fallback computes a real but wrong position: for a
|
|
24
|
+
* bottom-anchored element, one off the screen.
|
|
25
|
+
*/
|
|
26
|
+
export declare function installFoldVariables(fold: FoldModule): () => void;
|
|
27
|
+
/**
|
|
28
|
+
* Puts a canvas in the panel above the crease, or back where it was.
|
|
29
|
+
*
|
|
30
|
+
* The CSS BOX only — never the drawing buffer. Which half of the job that is
|
|
31
|
+
* depends on the engine: Phaser's scale manager sizes the buffer itself once
|
|
32
|
+
* the box changes, while a three.js game has to be told (`viewportSize`).
|
|
33
|
+
* Shared because the positioning is identical and the trap is too: a buffer
|
|
34
|
+
* sized to the upper panel inside a canvas still stretched across the window
|
|
35
|
+
* draws the picture at twice its height.
|
|
36
|
+
*/
|
|
37
|
+
export declare function placeCanvasInPanel(canvas: HTMLCanvasElement, layout: FoldLayout): void;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publishes the fold geometry as CSS custom properties, so a game's own DOM can
|
|
3
|
+
* follow the crease without reading the SDK at all:
|
|
4
|
+
*
|
|
5
|
+
* --umicat-top-height, --umicat-bottom-top, --umicat-bottom-height,
|
|
6
|
+
* --umicat-fold-top, --umicat-fold-height
|
|
7
|
+
*
|
|
8
|
+
* plus `data-umicat-dual-screen` on `<html>`, because a stylesheet cannot ask a
|
|
9
|
+
* module a question.
|
|
10
|
+
*
|
|
11
|
+
* **Engine-agnostic on purpose.** These NAMES are a contract between the SDK and
|
|
12
|
+
* a game's stylesheet — Gomoku's chat panel moves to the lower screen with no
|
|
13
|
+
* JavaScript at all — and a contract with two definitions drifts. It lived in
|
|
14
|
+
* `@umicat/three-sdk` while 3D was the only consumer; it moved here the moment
|
|
15
|
+
* the Phaser side needed the same names.
|
|
16
|
+
*
|
|
17
|
+
* All values are `0` when there is no fold, so a stylesheet that uses them is
|
|
18
|
+
* correct on a flat screen too.
|
|
19
|
+
*
|
|
20
|
+
* **Never give these a fallback in `var()`.** Without the property the whole
|
|
21
|
+
* `calc()` is invalid, the declaration is dropped, and the unfolded rule stands
|
|
22
|
+
* — which is right. A `0px` fallback computes a real but wrong position: for a
|
|
23
|
+
* bottom-anchored element, one off the screen.
|
|
24
|
+
*/
|
|
25
|
+
export function installFoldVariables(fold) {
|
|
26
|
+
return fold.subscribe(publish);
|
|
27
|
+
}
|
|
28
|
+
function publish(layout) {
|
|
29
|
+
if (typeof document === 'undefined')
|
|
30
|
+
return;
|
|
31
|
+
const root = document.documentElement;
|
|
32
|
+
const px = (n) => `${Math.round(n ?? 0)}px`;
|
|
33
|
+
const on = layout.dualScreen;
|
|
34
|
+
root.style.setProperty('--umicat-top-height', px(on ? layout.top?.height : 0));
|
|
35
|
+
root.style.setProperty('--umicat-bottom-top', px(on ? layout.bottom?.y : 0));
|
|
36
|
+
root.style.setProperty('--umicat-bottom-height', px(on ? layout.bottom?.height : 0));
|
|
37
|
+
root.style.setProperty('--umicat-fold-top', px(on ? layout.division?.y : 0));
|
|
38
|
+
root.style.setProperty('--umicat-fold-height', px(on ? layout.division?.height : 0));
|
|
39
|
+
if (on)
|
|
40
|
+
root.dataset.umicatDualScreen = '';
|
|
41
|
+
else
|
|
42
|
+
delete root.dataset.umicatDualScreen;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Puts a canvas in the panel above the crease, or back where it was.
|
|
46
|
+
*
|
|
47
|
+
* The CSS BOX only — never the drawing buffer. Which half of the job that is
|
|
48
|
+
* depends on the engine: Phaser's scale manager sizes the buffer itself once
|
|
49
|
+
* the box changes, while a three.js game has to be told (`viewportSize`).
|
|
50
|
+
* Shared because the positioning is identical and the trap is too: a buffer
|
|
51
|
+
* sized to the upper panel inside a canvas still stretched across the window
|
|
52
|
+
* draws the picture at twice its height.
|
|
53
|
+
*/
|
|
54
|
+
export function placeCanvasInPanel(canvas, layout) {
|
|
55
|
+
if (layout.dualScreen && layout.top) {
|
|
56
|
+
Object.assign(canvas.style, {
|
|
57
|
+
position: 'fixed',
|
|
58
|
+
left: `${layout.top.x}px`,
|
|
59
|
+
top: `${layout.top.y}px`,
|
|
60
|
+
width: `${layout.top.width}px`,
|
|
61
|
+
height: `${layout.top.height}px`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
Object.assign(canvas.style, { position: '', left: '', top: '', width: '', height: '' });
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,9 @@ export type { JoinOptions, RoomListEntry, RoomListOptions } from './realtime/Rea
|
|
|
16
16
|
export { AiModule, Npc } from './ai/AiModule.js';
|
|
17
17
|
export type { NpcConfig } from './ai/AiModule.js';
|
|
18
18
|
export { PlatformModule } from './platform/PlatformModule.js';
|
|
19
|
+
export { FoldModule } from './fold/FoldModule.js';
|
|
20
|
+
export { installFoldVariables, placeCanvasInPanel } from './fold/foldVariables.js';
|
|
21
|
+
export type { FoldLayout, FoldRect } from './fold/FoldModule.js';
|
|
19
22
|
export { VoiceModule, webVoiceSupported } from './voice/VoiceModule.js';
|
|
20
23
|
export type { VoiceCallbacks, VoiceSession, VoiceLang } from './voice/VoiceModule.js';
|
|
21
24
|
export { DialogueModule } from './dialogue/DialogueModule.js';
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,8 @@ export { RealtimeModule } from './realtime/RealtimeModule.js';
|
|
|
15
15
|
export { UmicatRoom, PlayerDataFacade, RoomDataFacade, ChatFacade, MAX_CHAT_TEXT_LEN, } from './realtime/UmicatRoom.js';
|
|
16
16
|
export { AiModule, Npc } from './ai/AiModule.js';
|
|
17
17
|
export { PlatformModule } from './platform/PlatformModule.js';
|
|
18
|
+
export { FoldModule } from './fold/FoldModule.js';
|
|
19
|
+
export { installFoldVariables, placeCanvasInPanel } from './fold/foldVariables.js';
|
|
18
20
|
export { VoiceModule, webVoiceSupported } from './voice/VoiceModule.js';
|
|
19
21
|
export { DialogueModule } from './dialogue/DialogueModule.js';
|
|
20
22
|
export { DialogueRunner, resolveLangText } from './dialogue/runner.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umicat/platform-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Umicat's platform layer, independent of any rendering engine: identity, cloud saves, shared game data, multiplayer, runtime AI, voice and scripted dialogue. Consumed by @umicat/phaser-sdk and @umicat/three-sdk alike.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|