@fabi-play/sdk 0.3.0 → 0.5.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/README.md +84 -0
- package/dist/avatars.d.ts +26 -14
- package/dist/avatars.js +1 -1
- package/dist/chunks/{chunk-GFNOIOSX.js → chunk-H3BKYVPL.js} +301 -22
- package/dist/fabi-play.d.ts +193 -10
- package/dist/fabi-play.js +1 -1
- package/dist/fabi-play.schema.json +32 -3
- package/dist/fabi-play.umd.js +453 -26
- package/dist/fabi-play.umd.min.js +1 -1
- package/dist/index.d.ts +212 -11
- package/dist/index.js +166 -4
- package/dist/integrity.json +6 -6
- package/package.json +3 -3
package/dist/fabi-play.d.ts
CHANGED
|
@@ -67,6 +67,20 @@ declare namespace FabiPlay {
|
|
|
67
67
|
/** Where the events go: by default the page, with a round cursor (domPointer()). */
|
|
68
68
|
target?: PointerTarget;
|
|
69
69
|
}
|
|
70
|
+
/** The events the remote's cursor makes in the game's page, as a Magic Remote's (a mouse). */
|
|
71
|
+
export type CursorEventType = "pointermove" | "mousemove" | "pointerdown" | "mousedown" | "pointerup" | "mouseup" | "click" | "pointercancel";
|
|
72
|
+
/** Where the cursor's events go (connect's options.cursor): by default the page (domCursor()). */
|
|
73
|
+
export interface CursorTarget {
|
|
74
|
+
/** The game's viewport in px. */
|
|
75
|
+
size(): {
|
|
76
|
+
width: number;
|
|
77
|
+
height: number;
|
|
78
|
+
};
|
|
79
|
+
/** One event at x, y (px); buttons: 1 while OK is held, else 0. */
|
|
80
|
+
dispatch(type: CursorEventType, x: number, y: number, buttons: number): void;
|
|
81
|
+
}
|
|
82
|
+
/** What became of a picture the game asked to save (HostMessage picture-result, Play.savePicture). */
|
|
83
|
+
export type PictureResult = "sent" | "unavailable" | "too-big" | "error";
|
|
70
84
|
/**
|
|
71
85
|
* One body: for each landmark x, y (0–1, y downwards) and visibility (0–1), so 33 × 3 numbers.
|
|
72
86
|
* In "player space": mirrored like a mirror, whatever camera took it, so a child moving to their left moves left.
|
|
@@ -208,7 +222,18 @@ declare namespace FabiPlay {
|
|
|
208
222
|
*/
|
|
209
223
|
/**
|
|
210
224
|
* pointer: only with input 'remote', true when the TV's remote also points (LG's Magic Remote): it reaches the game
|
|
211
|
-
* as ordinary pointer and mouse events (hover, click with OK)
|
|
225
|
+
* as ordinary pointer and mouse events (hover, click with OK); or, for a game whose manifest says "remoteCursor":
|
|
226
|
+
* true, when the screen moves a cursor with the arrows (cursor messages, which the SDK turns into the same pointer
|
|
227
|
+
* and mouse events). Absent = false.
|
|
228
|
+
*/
|
|
229
|
+
/**
|
|
230
|
+
* pictures: a phone that can save pictures is linked now (its hello said pictures), so the game may send picture
|
|
231
|
+
* messages; it changes with a capabilities message. Absent = false.
|
|
232
|
+
*/
|
|
233
|
+
/**
|
|
234
|
+
* orientation: the screen's orientation when the game starts (width ≥ height: 'landscape', see orientationOf);
|
|
235
|
+
* only a game whose manifest lists 'portrait' gets 'portrait'. Informational, to choose a layout: lay out from the
|
|
236
|
+
* real viewport size all the same. Absent (older hosts) = 'landscape'.
|
|
212
237
|
*/
|
|
213
238
|
{
|
|
214
239
|
type: "init";
|
|
@@ -225,6 +250,8 @@ declare namespace FabiPlay {
|
|
|
225
250
|
input?: GameInput;
|
|
226
251
|
turns?: number[];
|
|
227
252
|
pointer?: boolean;
|
|
253
|
+
pictures?: boolean;
|
|
254
|
+
orientation?: Orientation;
|
|
228
255
|
}
|
|
229
256
|
/** The backdrop or the view changed mid-game (video switched on or off, the camera image's fit or framing changed). */
|
|
230
257
|
| {
|
|
@@ -278,6 +305,36 @@ declare namespace FabiPlay {
|
|
|
278
305
|
| {
|
|
279
306
|
type: "turn";
|
|
280
307
|
players: number[];
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* What the screen can do for the game changed mid-game: pictures, whether a phone that can save pictures is linked
|
|
311
|
+
* now (one joined, or the last one left). init carries the first value.
|
|
312
|
+
*/
|
|
313
|
+
| {
|
|
314
|
+
type: "capabilities";
|
|
315
|
+
pictures: boolean;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* The answer to a picture message, with its id (absent if the picture had none): 'sent', the phone has it (saved,
|
|
319
|
+
* or saved once the parent allows it); 'unavailable', no phone that saves pictures is linked; 'too-big', over
|
|
320
|
+
* MAX_PICTURE_BYTES; 'error', anything else (the link dropped, the phone failed, no answer in time).
|
|
321
|
+
*/
|
|
322
|
+
| {
|
|
323
|
+
type: "picture-result";
|
|
324
|
+
id?: number;
|
|
325
|
+
result: PictureResult;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Remote mode with "remoteCursor": true: the cursor the arrows move (see KeyCursor), x and y in fractions of the
|
|
329
|
+
* game's frame (0–1), down while OK is held. Sent when it moves and when OK goes down or up; the SDK turns it into
|
|
330
|
+
* pointer and mouse events in the game's page (pointermove, pointerdown, pointerup, click, …), as a Magic Remote's.
|
|
331
|
+
* The screen draws the cursor itself, above the game.
|
|
332
|
+
*/
|
|
333
|
+
| {
|
|
334
|
+
type: "cursor";
|
|
335
|
+
x: number;
|
|
336
|
+
y: number;
|
|
337
|
+
down: boolean;
|
|
281
338
|
};
|
|
282
339
|
/** Why a game is paused (see HostMessage pause). */
|
|
283
340
|
export type PauseReason = "user" | "distance";
|
|
@@ -319,7 +376,20 @@ declare namespace FabiPlay {
|
|
|
319
376
|
| ({
|
|
320
377
|
type: "stats";
|
|
321
378
|
renderFps: number;
|
|
322
|
-
} & GameStats)
|
|
379
|
+
} & GameStats)
|
|
380
|
+
/**
|
|
381
|
+
* Save a picture on the family's phone (a drawing, the photo of a wall). data: a base64 data URL, data:image/jpeg
|
|
382
|
+
* or data:image/png, at most MAX_PICTURE_BYTES, whose bytes really are that type (checkPicture); title: a name for
|
|
383
|
+
* it (MAX_PICTURE_TITLE characters); id: the game's own, echoed in picture-result. Anything else is dropped: a game
|
|
384
|
+
* can send only an image this way. The screen sends it to the phone over their direct link only, never to a
|
|
385
|
+
* server, and answers picture-result.
|
|
386
|
+
*/
|
|
387
|
+
| {
|
|
388
|
+
type: "picture";
|
|
389
|
+
data: string;
|
|
390
|
+
title?: string;
|
|
391
|
+
id?: number;
|
|
392
|
+
};
|
|
323
393
|
/** The movements a game is played with, as its manifest lists them. */
|
|
324
394
|
export type Movement = "hands" | "jump" | "squat" | "clap" | "freeze" | "lanes" | "shapes";
|
|
325
395
|
/**
|
|
@@ -373,6 +443,14 @@ declare namespace FabiPlay {
|
|
|
373
443
|
backdrop?: GameBackdrop[];
|
|
374
444
|
/** Optional: Fabi explains the game on the TV before calibration (see GameIntro). Absent: straight to calibration. */
|
|
375
445
|
intro?: GameIntro;
|
|
446
|
+
/**
|
|
447
|
+
* Optional: the game's cover, the card in the catalogue (TV carousel, phone, admin): a path relative to the game's
|
|
448
|
+
* address (no scheme, no leading /, no "." or ".." segments, no backslash, up to COVER_MAX_PATH characters, see
|
|
449
|
+
* isCoverPath), ending in lowercase .jpg, .jpeg, .png or .webp: cover.jpg. A 16:9 image (COVER_ASPECT, within
|
|
450
|
+
* COVER_ASPECT_TOLERANCE), recommended 1280×720 and at most COVER_MAX_BYTES; JPEG is the safest for old TV
|
|
451
|
+
* browsers. The review checks the file; absent or broken, the catalogue falls back to another image.
|
|
452
|
+
*/
|
|
453
|
+
cover?: string;
|
|
376
454
|
/** Optional: how it can be played; absent = ['body']. Must include 'body' (Fabi Play is a movement platform); add 'remote' to be offered without a phone. */
|
|
377
455
|
inputs?: GameInput[];
|
|
378
456
|
/**
|
|
@@ -389,7 +467,27 @@ declare namespace FabiPlay {
|
|
|
389
467
|
* a game drawn on exact spots of the live video).
|
|
390
468
|
*/
|
|
391
469
|
framing?: GameFraming;
|
|
470
|
+
/**
|
|
471
|
+
* Optional, only with 'remote' in inputs: true and, in remote mode, the arrows move a free cursor instead of making
|
|
472
|
+
* gestures (they speed up when held, see KeyCursor) and OK presses and releases it: the game gets it as pointer and
|
|
473
|
+
* mouse events, the same as a Magic Remote's (init.pointer true), so a game written for the pointer works with
|
|
474
|
+
* both. Back is still the platform's pause. For games picked or drawn with a pointer (a painting game). Absent or
|
|
475
|
+
* false: arrows and OK make gestures.
|
|
476
|
+
*/
|
|
477
|
+
remoteCursor?: boolean;
|
|
478
|
+
/**
|
|
479
|
+
* Optional: the screen orientations the game can be played in; absent = ['landscape'] (see gameOrientations).
|
|
480
|
+
* Non-empty, without repeats, from ORIENTATIONS. A phone alone («Gioca qui / Play here») is held upright, so it
|
|
481
|
+
* offers only games that list 'portrait'; the TV and the tablet are landscape. A game that lists both still lays
|
|
482
|
+
* out from its real viewport size; init.orientation says which one it started in.
|
|
483
|
+
*/
|
|
484
|
+
orientations?: Orientation[];
|
|
392
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* A screen's orientation: 'landscape' when its width is at least its height, else 'portrait' (see orientationOf).
|
|
488
|
+
* GameManifest.orientations lists those a game supports; HostMessage init.orientation says the one it starts in.
|
|
489
|
+
*/
|
|
490
|
+
export type Orientation = "landscape" | "portrait";
|
|
393
491
|
/** How the screen frames the camera image for a game: see GameManifest.framing. */
|
|
394
492
|
export type GameFraming = "auto" | "fixed";
|
|
395
493
|
/**
|
|
@@ -469,6 +567,8 @@ declare namespace FabiPlay {
|
|
|
469
567
|
export type DrawAvatar = (ctx: CanvasRenderingContext2D, who: PlayerFrame | Body, player?: number | string, box?: AvatarBox | null, options?: DrawAvatarOptions) => Body | null;
|
|
470
568
|
/** A frame's body smoothed (and interpolated) for its player, or a player id's last one; null if none. */
|
|
471
569
|
export type SmoothedBody = (who: PlayerFrame | number, options?: Pick<DrawAvatarOptions, "smooth" | "interpolate">) => Body | null;
|
|
570
|
+
/** What savePicture takes: a canvas (any size) or a data URL (data:image/jpeg or data:image/png, base64). */
|
|
571
|
+
export type PictureSource = HTMLCanvasElement | string;
|
|
472
572
|
/** What the 'start' event says: who plays and how, as it was at the start (late listeners get the same; see Easy for now). */
|
|
473
573
|
export interface StartInfo {
|
|
474
574
|
/** The players' age range: simpler rules for 3-5, scores and challenges for 9-12. */
|
|
@@ -477,8 +577,10 @@ declare namespace FabiPlay {
|
|
|
477
577
|
players: readonly number[];
|
|
478
578
|
/** The language to show text in. */
|
|
479
579
|
locale: Locale;
|
|
480
|
-
/** 'tv': on a TV, children standing back; 'solo': a tablet in front of
|
|
580
|
+
/** 'tv': on a TV, children standing back; 'solo': a tablet or a phone in front of the children. */
|
|
481
581
|
mode: "tv" | "solo";
|
|
582
|
+
/** 'landscape' or 'portrait' (a phone held upright): the screen's orientation at the start, see Play.orientation. */
|
|
583
|
+
orientation: Orientation;
|
|
482
584
|
/** 'video': mirror mode, paint no opaque background; 'none': paint your own. See the backdrop event. */
|
|
483
585
|
backdrop: Backdrop;
|
|
484
586
|
/** Where the camera image is (with 'video', the video; with 'none', where the avatars stand), see toScreen; null if unknown. */
|
|
@@ -489,8 +591,12 @@ declare namespace FabiPlay {
|
|
|
489
591
|
turns: readonly number[];
|
|
490
592
|
/** 'body' (the default) or 'remote': played with the TV remote, keys turned into gestures (see Play.input). */
|
|
491
593
|
input: GameInput;
|
|
492
|
-
/** With input 'remote', whether the remote also points (LG's Magic Remote; see Play.pointer). */
|
|
594
|
+
/** With input 'remote', whether the remote also points (LG's Magic Remote, or the arrows as a cursor; see Play.pointer). */
|
|
493
595
|
pointer: boolean;
|
|
596
|
+
/** Whether savePicture can reach a phone at the start (see Play.canSavePicture; the capabilities event says when it changes). */
|
|
597
|
+
canSavePicture: boolean;
|
|
598
|
+
/** The screen's sound is off: play no sound, and make every cue that sound would give visible instead. */
|
|
599
|
+
muted: boolean;
|
|
494
600
|
}
|
|
495
601
|
/**
|
|
496
602
|
* The events of FabiPlay.on, by name, with what each listener gets. Every gesture (jump, land, squat, stand, clap,
|
|
@@ -549,6 +655,10 @@ declare namespace FabiPlay {
|
|
|
549
655
|
turn: {
|
|
550
656
|
players: number[];
|
|
551
657
|
};
|
|
658
|
+
/** What the screen can do changed: pictures, whether savePicture can reach a phone now (see canSavePicture). */
|
|
659
|
+
capabilities: {
|
|
660
|
+
pictures: boolean;
|
|
661
|
+
};
|
|
552
662
|
};
|
|
553
663
|
/** An event name of FabiPlay.on. */
|
|
554
664
|
export type EasyEvent = keyof EasyEvents;
|
|
@@ -565,7 +675,7 @@ declare namespace FabiPlay {
|
|
|
565
675
|
export interface Easy {
|
|
566
676
|
/**
|
|
567
677
|
* Listens to an event: a gesture by its name ('jump', 'clap', 'lane-left', …), 'start', 'hands', 'shape',
|
|
568
|
-
* 'players', 'pause', 'resume', 'break-start', 'break-end', 'backdrop', 'turn' (see EasyEvents). Returns a function that
|
|
678
|
+
* 'players', 'pause', 'resume', 'break-start', 'break-end', 'backdrop', 'turn', 'capabilities' (see EasyEvents). Returns a function that
|
|
569
679
|
* stops listening.
|
|
570
680
|
*
|
|
571
681
|
* @example
|
|
@@ -606,8 +716,26 @@ declare namespace FabiPlay {
|
|
|
606
716
|
readonly turns: readonly number[];
|
|
607
717
|
/** How this session is played (see Play.input); 'body' before start. */
|
|
608
718
|
readonly input: GameInput;
|
|
609
|
-
/**
|
|
719
|
+
/**
|
|
720
|
+
* With input 'remote', whether the remote also points (Play.pointer: a Magic Remote, or the arrows as a cursor with
|
|
721
|
+
* "remoteCursor": true; either way plain pointer and mouse events). pointer() is the hand adapter. False before start.
|
|
722
|
+
*/
|
|
610
723
|
readonly hasPointer: boolean;
|
|
724
|
+
/** Whether savePicture can reach a phone now (see Play.canSavePicture); false before start. */
|
|
725
|
+
readonly canSavePicture: boolean;
|
|
726
|
+
/**
|
|
727
|
+
* Saves a picture on the family's phone: a canvas (scaled to 1920 px at most, JPEG) or a data URL (JPEG or PNG),
|
|
728
|
+
* with an optional title. Resolves 'sent', 'unavailable', 'too-big' or 'error' (see Play.savePicture); called before
|
|
729
|
+
* start, it waits for it. Only TV → phone over their direct link, never to a server.
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* if (FabiPlay.canSavePicture) FabiPlay.savePicture(canvas, { title: 'Il nostro muro' })
|
|
733
|
+
*/
|
|
734
|
+
savePicture(source: PictureSource, opts?: {
|
|
735
|
+
title?: string;
|
|
736
|
+
}): Promise<PictureResult>;
|
|
737
|
+
/** The screen's sound is off (see StartInfo.muted); false before start. */
|
|
738
|
+
readonly muted: boolean;
|
|
611
739
|
/** Where the camera image is, with any backdrop; null if the host didn't say, and before start. */
|
|
612
740
|
readonly view: BackdropView | null;
|
|
613
741
|
/** What the screen knows about the device (see Play.device); null if it said nothing, and before start. */
|
|
@@ -675,6 +803,20 @@ declare namespace FabiPlay {
|
|
|
675
803
|
turn: Extract<HostMessage, {
|
|
676
804
|
type: "turn";
|
|
677
805
|
}>;
|
|
806
|
+
/**
|
|
807
|
+
* What the screen can do for the game changed: pictures, whether savePicture can reach a phone now (see
|
|
808
|
+
* Play.canSavePicture). Only when it changes.
|
|
809
|
+
*/
|
|
810
|
+
capabilities: Extract<HostMessage, {
|
|
811
|
+
type: "capabilities";
|
|
812
|
+
}>;
|
|
813
|
+
/**
|
|
814
|
+
* Remote mode with "remoteCursor": true: the cursor the arrows move (x, y in fractions of the page, down while OK
|
|
815
|
+
* is held). Nothing to do with it: the SDK already makes the pointer and mouse events a Magic Remote would.
|
|
816
|
+
*/
|
|
817
|
+
cursor: Extract<HostMessage, {
|
|
818
|
+
type: "cursor";
|
|
819
|
+
}>;
|
|
678
820
|
};
|
|
679
821
|
/**
|
|
680
822
|
* The game's side of Fabi Play, from connect(): who plays, their movements, and how the game tells it is ready,
|
|
@@ -719,10 +861,43 @@ declare namespace FabiPlay {
|
|
|
719
861
|
/**
|
|
720
862
|
* Only with input 'remote': the remote also points (LG's Magic Remote), reaching the game as ordinary pointer and
|
|
721
863
|
* mouse events (hover, click with OK). Then let what the game has to pick be clicked too (large hit areas, a hover
|
|
722
|
-
* highlight).
|
|
723
|
-
* the mouse
|
|
864
|
+
* highlight). Also true for a game whose manifest says "remoteCursor": true, when the arrows move a cursor: the SDK
|
|
865
|
+
* makes the same pointer and mouse events from it (OK held: pointerdown … pointerup, then click), so a game written
|
|
866
|
+
* for the pointer works with both. false otherwise, and from hosts that don't say. Not the pointer() adapter, which
|
|
867
|
+
* makes a child's hand the mouse.
|
|
724
868
|
*/
|
|
725
869
|
readonly pointer: boolean;
|
|
870
|
+
/**
|
|
871
|
+
* Whether savePicture can reach a phone now: true when the screen says a phone that saves pictures is linked (the
|
|
872
|
+
* app; the web phone can't). It can change mid-game: listen to 'capabilities'. Hide or grey a "save" button while
|
|
873
|
+
* it is false, and never make the children wait on it.
|
|
874
|
+
*/
|
|
875
|
+
readonly canSavePicture: boolean;
|
|
876
|
+
/**
|
|
877
|
+
* Saves a picture on the family's phone, in its gallery (a drawing, the photo of a wall). source: a canvas (scaled
|
|
878
|
+
* down to MAX_PICTURE_SIDE, 1920 px on its longer side, drawn on white and encoded as JPEG at quality 0.85, lower
|
|
879
|
+
* if it would be over 3 MiB) or a data URL (data:image/jpeg or data:image/png, base64, sent as it is). title: a
|
|
880
|
+
* name for it (up to 60 characters). Resolves with what became of it: 'sent' (the phone has it), 'unavailable' (no
|
|
881
|
+
* phone that saves pictures: canSavePicture is false), 'too-big' (over 3 MiB), 'error' (a canvas that can't be read,
|
|
882
|
+
* not an image, the link dropped, no answer in 2 minutes). Never rejects.
|
|
883
|
+
*
|
|
884
|
+
* Privacy: the picture goes only from the TV to the phone over their direct link (WebRTC, encrypted), never to a
|
|
885
|
+
* server, and nothing else can be sent this way. If the phone can't save it (permission denied), Fabi Play tells the
|
|
886
|
+
* parent on the phone: don't tell the children.
|
|
887
|
+
*
|
|
888
|
+
* @example
|
|
889
|
+
* if (play.canSavePicture) await play.savePicture(canvas, { title: 'Our wall' })
|
|
890
|
+
*/
|
|
891
|
+
savePicture(source: PictureSource, opts?: {
|
|
892
|
+
title?: string;
|
|
893
|
+
}): Promise<PictureResult>;
|
|
894
|
+
/**
|
|
895
|
+
* The screen's orientation when the game started: 'landscape' (width ≥ height; the TV, the tablet, and what older
|
|
896
|
+
* hosts mean) or 'portrait' (a phone held upright, only for games whose manifest lists it in orientations). Use it
|
|
897
|
+
* to choose a layout, but size everything from the real viewport (innerWidth × innerHeight): it is not updated if
|
|
898
|
+
* the screen turns.
|
|
899
|
+
*/
|
|
900
|
+
readonly orientation: Orientation;
|
|
726
901
|
/** The screen's sound is off: start the game muted. */
|
|
727
902
|
readonly muted: boolean;
|
|
728
903
|
/** The players' age range: simpler rules for 3-5, scores and challenges for 9-12. */
|
|
@@ -784,8 +959,8 @@ declare namespace FabiPlay {
|
|
|
784
959
|
/** Called when backdrop or view changes; returns a function that stops listening. */
|
|
785
960
|
onBackdrop(listener: (backdrop: Backdrop, view: BackdropView | null) => void): () => void;
|
|
786
961
|
/**
|
|
787
|
-
* Listens to the host: 'frame', 'gesture', 'pause', 'resume', 'break-start', 'break-done', 'backdrop', 'turn'
|
|
788
|
-
* (see Events). Returns a function that stops listening.
|
|
962
|
+
* Listens to the host: 'frame', 'gesture', 'pause', 'resume', 'break-start', 'break-done', 'backdrop', 'turn',
|
|
963
|
+
* 'capabilities', 'cursor' (see Events). Returns a function that stops listening.
|
|
789
964
|
*
|
|
790
965
|
* @example
|
|
791
966
|
* const off = play.on('frame', (f) => (players = f.players))
|
|
@@ -834,6 +1009,14 @@ declare namespace FabiPlay {
|
|
|
834
1009
|
*/
|
|
835
1010
|
readonly avatars: Readonly<Record<string, unknown>>;
|
|
836
1011
|
}
|
|
1012
|
+
/** connect()'s options. */
|
|
1013
|
+
export interface ConnectOptions {
|
|
1014
|
+
/**
|
|
1015
|
+
* Where the remote's cursor events go ("remoteCursor": true): by default the page (domCursor()), null for nowhere
|
|
1016
|
+
* (the game reads the 'cursor' event itself). Tests pass their own.
|
|
1017
|
+
*/
|
|
1018
|
+
cursor?: CursorTarget | null;
|
|
1019
|
+
}
|
|
837
1020
|
}
|
|
838
1021
|
|
|
839
1022
|
/**
|