@playus.club/games-sdk 0.1.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/LICENSE.md +35 -0
- package/README.md +129 -0
- package/dist/sdk/babylon/canvas.d.ts +30 -0
- package/dist/sdk/babylon/canvas.d.ts.map +1 -0
- package/dist/sdk/babylon/index.d.ts +3 -0
- package/dist/sdk/babylon/index.d.ts.map +1 -0
- package/dist/sdk/babylon.js +52 -0
- package/dist/sdk/bridge.d.ts +2 -0
- package/dist/sdk/bridge.d.ts.map +1 -0
- package/dist/sdk/chunks/background-DLy8kjVf.js +47 -0
- package/dist/sdk/chunks/debug-BKXPXMKn.js +92 -0
- package/dist/sdk/chunks/touch-hint-BZBB3COY.js +148 -0
- package/dist/sdk/helpers/timeFormat.d.ts +3 -0
- package/dist/sdk/helpers/timeFormat.d.ts.map +1 -0
- package/dist/sdk/i18n.d.ts +55 -0
- package/dist/sdk/i18n.d.ts.map +1 -0
- package/dist/sdk/index.d.ts +19 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +366 -0
- package/dist/sdk/mobile-interaction.d.ts +4 -0
- package/dist/sdk/mobile-interaction.d.ts.map +1 -0
- package/dist/sdk/native-bridge.d.ts +45 -0
- package/dist/sdk/native-bridge.d.ts.map +1 -0
- package/dist/sdk/overlay/debug.d.ts +31 -0
- package/dist/sdk/overlay/debug.d.ts.map +1 -0
- package/dist/sdk/overlay/index.d.ts +5 -0
- package/dist/sdk/overlay/index.d.ts.map +1 -0
- package/dist/sdk/overlay/touch-hint.d.ts +12 -0
- package/dist/sdk/overlay/touch-hint.d.ts.map +1 -0
- package/dist/sdk/overlay.js +3 -0
- package/dist/sdk/phaser/container.d.ts +25 -0
- package/dist/sdk/phaser/container.d.ts.map +1 -0
- package/dist/sdk/phaser/index.d.ts +3 -0
- package/dist/sdk/phaser/index.d.ts.map +1 -0
- package/dist/sdk/phaser.js +72 -0
- package/dist/sdk/random.d.ts +5 -0
- package/dist/sdk/random.d.ts.map +1 -0
- package/dist/sdk/sound.d.ts +36 -0
- package/dist/sdk/sound.d.ts.map +1 -0
- package/dist/sdk/tap-to-start.d.ts +19 -0
- package/dist/sdk/tap-to-start.d.ts.map +1 -0
- package/dist/sdk/three/canvas.d.ts +13 -0
- package/dist/sdk/three/canvas.d.ts.map +1 -0
- package/dist/sdk/three/index.d.ts +3 -0
- package/dist/sdk/three/index.d.ts.map +1 -0
- package/dist/sdk/three.js +36 -0
- package/dist/sdk/timing.d.ts +4 -0
- package/dist/sdk/timing.d.ts.map +1 -0
- package/dist/sdk/types/background.d.ts +33 -0
- package/dist/sdk/types/background.d.ts.map +1 -0
- package/dist/sdk/url-params.d.ts +5 -0
- package/dist/sdk/url-params.d.ts.map +1 -0
- package/package.json +77 -0
- package/src/playus/fonts.css +29 -0
- package/src/playus/global.css +14 -0
- package/src/playus/styles.css +61 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Playus Games SDK License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dapps Pte Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
## Grant
|
|
6
|
+
|
|
7
|
+
Subject to the terms below, Dapps Pte Ltd grants you a limited, non-exclusive, non-transferable, revocable, royalty-free license to use, reproduce, and modify this SDK solely to develop, test, and build web games intended for review by and distribution within the Playus apps operated by Dapps Pte Ltd ("Playus"), and to distribute the SDK only in compiled form as part of such a game bundle delivered to Playus.
|
|
8
|
+
|
|
9
|
+
## Restrictions
|
|
10
|
+
|
|
11
|
+
You may not:
|
|
12
|
+
|
|
13
|
+
- use the SDK, or any part of it (including the native bridge protocol implementation), to integrate or run games or other web content in any app, platform, or service other than Playus;
|
|
14
|
+
- redistribute, sublicense, sell, or publish the SDK or modified versions of it outside of a game bundle delivered to Playus;
|
|
15
|
+
- remove or alter copyright or license notices.
|
|
16
|
+
|
|
17
|
+
## Your games remain yours
|
|
18
|
+
|
|
19
|
+
This license covers the SDK only. You keep all rights to your own game code and assets. Terms for reviewing, publishing, and monetizing games inside Playus are agreed separately with Dapps Pte Ltd and take precedence over this license where they conflict.
|
|
20
|
+
|
|
21
|
+
## Contributions
|
|
22
|
+
|
|
23
|
+
By submitting a contribution to this repository you grant Dapps Pte Ltd a perpetual, worldwide, royalty-free license to use, modify, and distribute it as part of the SDK.
|
|
24
|
+
|
|
25
|
+
## Termination
|
|
26
|
+
|
|
27
|
+
This license terminates automatically if you breach it or when Dapps Pte Ltd withdraws it in writing. On termination you must stop using the SDK for new development; game bundles already accepted by Playus remain unaffected.
|
|
28
|
+
|
|
29
|
+
## No warranty
|
|
30
|
+
|
|
31
|
+
THE SDK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY LAW, DAPPS PTE LTD SHALL NOT BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM THE USE OF THE SDK.
|
|
32
|
+
|
|
33
|
+
## Contact
|
|
34
|
+
|
|
35
|
+
Dapps Pte Ltd — https://dapps.ltd — team@dapps.ltd
|
package/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/playus-web-preview.jpg" alt="Playus dice logo" width="720" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# Playus Games SDK
|
|
6
|
+
|
|
7
|
+
SDK and local host simulator for building Playus-compatible web games.
|
|
8
|
+
|
|
9
|
+
Partners build games in their own repository and deliver a pre-built static bundle to Playus. The bundle must already use the Playus SDK runtime so it works inside the Playus iOS and Android WebViews without a source-code adaptation step.
|
|
10
|
+
|
|
11
|
+
## Getting The SDK
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install @playus.club/games-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The npm package contains the runtime only. For the local host simulator and the example games, clone this repository (see Quick Start below).
|
|
18
|
+
|
|
19
|
+
The SDK speaks Playus bridge protocol v3. Note the SDK version you built with — it is part of the bundle delivery metadata.
|
|
20
|
+
|
|
21
|
+
## Quick Start (this repo)
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm install
|
|
25
|
+
npm run dev
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Open the local host simulator:
|
|
29
|
+
|
|
30
|
+
```txt
|
|
31
|
+
http://localhost:8091
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The simulator loads the included examples and validates the real Playus bridge flow, including `ready`, `hostReady`, `hostReadyAck`, live score updates, finish events, language params, debug mode, and host mute callbacks.
|
|
35
|
+
|
|
36
|
+
## SDK Usage
|
|
37
|
+
|
|
38
|
+
Import the SDK in your entry module (not behind a dynamic import — the host verifies the bridge exists right after page load):
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import {
|
|
42
|
+
createTapToStartOverlay,
|
|
43
|
+
nativeBridge,
|
|
44
|
+
sound,
|
|
45
|
+
} from '@playus.club/games-sdk';
|
|
46
|
+
import '@playus.club/games-sdk/styles.css';
|
|
47
|
+
|
|
48
|
+
nativeBridge.configure({ gameId: 'your-game-id' });
|
|
49
|
+
|
|
50
|
+
createTapToStartOverlay({
|
|
51
|
+
text: {
|
|
52
|
+
en: 'Tap to start',
|
|
53
|
+
de: 'Tippen zum Starten',
|
|
54
|
+
fr: 'Touchez pour commencer',
|
|
55
|
+
es: 'Toca para empezar',
|
|
56
|
+
it: 'Tocca per iniziare',
|
|
57
|
+
},
|
|
58
|
+
mode: 'dismiss-only',
|
|
59
|
+
onStart: () => {
|
|
60
|
+
nativeBridge.game.started();
|
|
61
|
+
nativeBridge.game.score(0);
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
await sound.preload(['positive-input']);
|
|
66
|
+
nativeBridge.game.ready({ version: '1.0.0' });
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
When the run ends:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
nativeBridge.game.finished(finalScore);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Engine helpers: `@playus.club/games-sdk/phaser`, `@playus.club/games-sdk/babylon`, and `@playus.club/games-sdk/three` provide ready-made containers and configs. See [the game contract](docs/game-contract.md) for the full runtime rules.
|
|
76
|
+
|
|
77
|
+
## Examples
|
|
78
|
+
|
|
79
|
+
Three small complete games. All follow the full [game contract](docs/game-contract.md) with localized overlays, sounds, and haptics — and each shows a different feature mix:
|
|
80
|
+
|
|
81
|
+
| | `games/starter-game` (plain TS) | `games/phaser-example` | `games/babylon-example` |
|
|
82
|
+
| --- | --- | --- | --- |
|
|
83
|
+
| Game | Hit 5 targets fast | Pop bubbles for 20s | Tap the odd cube, endless levels |
|
|
84
|
+
| Score | Time: negative seconds, `score(0)` at start, whole-second live updates, exact fractional final | Points with live updates | Levels with an endless difficulty ramp |
|
|
85
|
+
| Seeded random | New layout per try | Same pattern every try (`includePlayContext: false`) | `seededShuffle` + float ranges |
|
|
86
|
+
| Start overlay | `dismiss-only`, default tap hint | `dismiss-only`, `tap-rapid` hint | `pass-first-input`, board visible behind hint |
|
|
87
|
+
| Also shows | Real-time score timer (no clamping) | Delta clamping, countdown clock, warning sound | Transparent background, debug overlay, DPR cap, brief end feedback, `error()` |
|
|
88
|
+
|
|
89
|
+
## Testing Your Own Bundle
|
|
90
|
+
|
|
91
|
+
The simulator's full handshake only works same-origin. To test your built bundle:
|
|
92
|
+
|
|
93
|
+
1. Copy your build output into `public/<your-game-id>/` in this repo.
|
|
94
|
+
2. Run `npm run dev` and enter `/<your-game-id>/` as the Game URL in the simulator.
|
|
95
|
+
|
|
96
|
+
Cross-origin URLs (e.g. your own dev server) still show outgoing events, but `hostReady` cannot be delivered — the simulator marks this and skips handshake checks.
|
|
97
|
+
|
|
98
|
+
## Delivering A Bundle
|
|
99
|
+
|
|
100
|
+
Plain source delivery is preferred when you can share it — see [CONTRIBUTING.md](CONTRIBUTING.md). For bundle delivery, Playus expects a static web bundle:
|
|
101
|
+
|
|
102
|
+
```txt
|
|
103
|
+
dist/
|
|
104
|
+
index.html
|
|
105
|
+
assets/...
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The bundle must:
|
|
109
|
+
|
|
110
|
+
- run from a static host path with no backend required
|
|
111
|
+
- reference all assets **relatively** — with Vite set `base: './'` and check that the built `index.html` uses `./assets/...`, not `/assets/...` (Playus hosts serve bundles from a subpath)
|
|
112
|
+
- include the Playus SDK in the compiled output
|
|
113
|
+
- send its bundle version via `ready({ version })`
|
|
114
|
+
- call `ready()` only after required assets for the first playable frame are loaded
|
|
115
|
+
- send meaningful live `score()` updates
|
|
116
|
+
- call `finished(finalScore)` exactly once
|
|
117
|
+
- support `lang` from the URL hash for in-game text and overlays
|
|
118
|
+
|
|
119
|
+
Playus handles signing, hosting, game metadata, score type assignment, leaderboard UI, and final result UI.
|
|
120
|
+
|
|
121
|
+
## Docs
|
|
122
|
+
|
|
123
|
+
- [Game contract](docs/game-contract.md) — the runtime rules every bundle must follow
|
|
124
|
+
- [Assets and mobile performance](docs/assets-and-performance.md)
|
|
125
|
+
- [Submission checklist](docs/submission-checklist.md)
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
Source-available under the [Playus Games SDK License](LICENSE.md): free to use for building and delivering games for the Playus apps; not for use in other apps or platforms.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type BackgroundConfig } from '../types/background';
|
|
2
|
+
import { Color4 } from '@babylonjs/core/Maths/math.color';
|
|
3
|
+
export type CanvasOptions = {
|
|
4
|
+
background?: BackgroundConfig;
|
|
5
|
+
};
|
|
6
|
+
export declare function createCanvas(options?: CanvasOptions): HTMLCanvasElement;
|
|
7
|
+
/**
|
|
8
|
+
* Returns Engine options based on background config with performance optimizations.
|
|
9
|
+
* Use this when creating the Babylon.js Engine.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* const engineOpts = getEngineOptions(background);
|
|
13
|
+
* this.engine = new Engine(this.canvas, true, engineOpts);
|
|
14
|
+
*/
|
|
15
|
+
export declare function getEngineOptions(background?: BackgroundConfig): {
|
|
16
|
+
alpha: boolean;
|
|
17
|
+
antialias: boolean;
|
|
18
|
+
preserveDrawingBuffer: boolean;
|
|
19
|
+
stencil: boolean;
|
|
20
|
+
desynchronized: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Returns a Color4 for scene.clearColor based on background config.
|
|
24
|
+
* Use this when setting up the scene.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* this.scene.clearColor = getClearColor(background);
|
|
28
|
+
*/
|
|
29
|
+
export declare function getClearColor(background?: BackgroundConfig): Color4;
|
|
30
|
+
//# sourceMappingURL=canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canvas.d.ts","sourceRoot":"","sources":["../../../src/playus/babylon/canvas.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,gBAAgB,EAAqC,MAAM,qBAAqB,CAAC;AAC/F,OAAO,EAAE,MAAM,EAAE,MAAM,kCAAkC,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,iBAAiB,CAuDvE;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,CAAC,EAAE,gBAAgB;;;;;;EAS7D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAmBnE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/playus/babylon/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { i as e, n as t, o as n, r } from "./chunks/background-DLy8kjVf.js";
|
|
2
|
+
import { r as i } from "./chunks/debug-BKXPXMKn.js";
|
|
3
|
+
import { Color4 as a } from "@babylonjs/core/Maths/math.color";
|
|
4
|
+
//#region src/playus/babylon/canvas.ts
|
|
5
|
+
function o(r) {
|
|
6
|
+
let a = t(r?.background), o = document.createElement("div");
|
|
7
|
+
o.id = "game-root", Object.assign(o.style, {
|
|
8
|
+
position: "fixed",
|
|
9
|
+
inset: "0",
|
|
10
|
+
display: "grid",
|
|
11
|
+
placeItems: "center",
|
|
12
|
+
...a && { background: a }
|
|
13
|
+
}), e(o), document.body.appendChild(o);
|
|
14
|
+
let s = document.createElement("div");
|
|
15
|
+
if (s.id = "game-viewport", Object.assign(s.style, {
|
|
16
|
+
width: "min(100vw, calc(100vh * 0.625))",
|
|
17
|
+
aspectRatio: "0.625",
|
|
18
|
+
position: "relative",
|
|
19
|
+
...a && { background: a }
|
|
20
|
+
}), e(s), o.appendChild(s), i()) {
|
|
21
|
+
let e = document.createElement("div");
|
|
22
|
+
e.id = "debug-background", Object.assign(e.style, {
|
|
23
|
+
position: "absolute",
|
|
24
|
+
inset: "0",
|
|
25
|
+
background: "rgba(0, 0, 0, 0.1)",
|
|
26
|
+
pointerEvents: "none",
|
|
27
|
+
zIndex: "-1"
|
|
28
|
+
}), s.appendChild(e);
|
|
29
|
+
}
|
|
30
|
+
let c = document.createElement("canvas");
|
|
31
|
+
return c.id = "gameCanvas", Object.assign(c.style, {
|
|
32
|
+
width: "100%",
|
|
33
|
+
height: "100%",
|
|
34
|
+
display: "block"
|
|
35
|
+
}), e(c), n(c), s.appendChild(c), c;
|
|
36
|
+
}
|
|
37
|
+
function s(e) {
|
|
38
|
+
return {
|
|
39
|
+
alpha: r(e),
|
|
40
|
+
antialias: !0,
|
|
41
|
+
preserveDrawingBuffer: !1,
|
|
42
|
+
stencil: !1,
|
|
43
|
+
desynchronized: !1
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function c(e) {
|
|
47
|
+
if (r(e)) return new a(0, 0, 0, 0);
|
|
48
|
+
let n = t(e), i = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(n);
|
|
49
|
+
return i ? new a(parseInt(i[1], 16) / 255, parseInt(i[2], 16) / 255, parseInt(i[3], 16) / 255, 1) : new a(0, 0, 0, 1);
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { o as createCanvas, c as getClearColor, s as getEngineOptions };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/playus/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/playus/mobile-interaction.ts
|
|
2
|
+
var e = new URLSearchParams(window.location.search), t = d("playusNoMobilePolicy"), n = d("playusNoTouchDefaultGuard"), r = e.get("playusTouchAction"), i = [
|
|
3
|
+
"a[href]",
|
|
4
|
+
"button",
|
|
5
|
+
"input",
|
|
6
|
+
"select",
|
|
7
|
+
"textarea",
|
|
8
|
+
"[contenteditable]",
|
|
9
|
+
"[role=\"button\"]",
|
|
10
|
+
"[role=\"slider\"]"
|
|
11
|
+
].join(","), a = !1, o = /* @__PURE__ */ new WeakSet();
|
|
12
|
+
function s() {
|
|
13
|
+
t || a || (a = !0, u(document.documentElement), document.body ? u(document.body) : document.addEventListener("DOMContentLoaded", () => {
|
|
14
|
+
document.body && u(document.body);
|
|
15
|
+
}, { once: !0 }), document.addEventListener("selectstart", f, !0), document.addEventListener("contextmenu", f, !0), document.addEventListener("dragstart", f, !0));
|
|
16
|
+
}
|
|
17
|
+
function c(e) {
|
|
18
|
+
return t ? e : (u(e), e.style.touchAction = r || "none", e);
|
|
19
|
+
}
|
|
20
|
+
function l(e) {
|
|
21
|
+
return t || n || o.has(e) ? e : (o.add(e), e.addEventListener("touchstart", f, { passive: !1 }), e.addEventListener("touchmove", f, { passive: !1 }), e);
|
|
22
|
+
}
|
|
23
|
+
function u(e) {
|
|
24
|
+
let t = e.style;
|
|
25
|
+
t.userSelect = "none", t.webkitUserSelect = "none", t.webkitTouchCallout = "none", t.webkitTapHighlightColor = "transparent", t.overscrollBehavior = "none";
|
|
26
|
+
}
|
|
27
|
+
function d(t) {
|
|
28
|
+
let n = e.get(t);
|
|
29
|
+
return n === "" || n === "1" || n === "true";
|
|
30
|
+
}
|
|
31
|
+
function f(e) {
|
|
32
|
+
p(e.target) || e.preventDefault();
|
|
33
|
+
}
|
|
34
|
+
function p(e) {
|
|
35
|
+
return e instanceof Element ? e.closest(i) !== null : !1;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region src/playus/types/background.ts
|
|
39
|
+
var m = { transparent: !0 };
|
|
40
|
+
function h(e) {
|
|
41
|
+
return !e || e.transparent;
|
|
42
|
+
}
|
|
43
|
+
function g(e) {
|
|
44
|
+
if (!(!e || e.transparent === !0)) return e.color;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { s as a, c as i, g as n, l as o, h as r, m as t };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//#region src/playus/url-params.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
try {
|
|
4
|
+
let t = window.location.hash.substring(1);
|
|
5
|
+
return new URLSearchParams(t).get(e) || new URLSearchParams(window.location.search).get(e);
|
|
6
|
+
} catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function t(t) {
|
|
11
|
+
let n = e("groupgame");
|
|
12
|
+
if (n) return t?.includePlayContext === !1 ? n : `${n}.${e("playcontext")}`;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/playus/overlay/debug.ts
|
|
16
|
+
function n() {
|
|
17
|
+
try {
|
|
18
|
+
let e = document.createElement("canvas"), t = e.getContext("webgl2") || e.getContext("webgl");
|
|
19
|
+
if (!t) return {
|
|
20
|
+
supported: !1,
|
|
21
|
+
renderer: "Canvas 2D (no WebGL)",
|
|
22
|
+
vendor: "CPU"
|
|
23
|
+
};
|
|
24
|
+
let n = t.getExtension("WEBGL_debug_renderer_info");
|
|
25
|
+
return n ? {
|
|
26
|
+
supported: !0,
|
|
27
|
+
renderer: t.getParameter(n.UNMASKED_RENDERER_WEBGL),
|
|
28
|
+
vendor: t.getParameter(n.UNMASKED_VENDOR_WEBGL)
|
|
29
|
+
} : {
|
|
30
|
+
supported: !0,
|
|
31
|
+
renderer: "WebGL (details hidden)",
|
|
32
|
+
vendor: "Unknown"
|
|
33
|
+
};
|
|
34
|
+
} catch {
|
|
35
|
+
return {
|
|
36
|
+
supported: !1,
|
|
37
|
+
renderer: "Detection failed",
|
|
38
|
+
vendor: "Unknown"
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function r() {
|
|
43
|
+
return e("d") === "1";
|
|
44
|
+
}
|
|
45
|
+
function i(e) {
|
|
46
|
+
let t = document.createElement("div");
|
|
47
|
+
t.id = "debug-overlay", Object.assign(t.style, {
|
|
48
|
+
position: "absolute",
|
|
49
|
+
top: "5px",
|
|
50
|
+
left: "50%",
|
|
51
|
+
transform: "translateX(-50%)",
|
|
52
|
+
padding: "2px 6px",
|
|
53
|
+
background: "rgba(0,0,0,0.35)",
|
|
54
|
+
color: "#fff",
|
|
55
|
+
fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, sans-serif",
|
|
56
|
+
fontSize: "12px",
|
|
57
|
+
lineHeight: "1.2",
|
|
58
|
+
borderRadius: "6px",
|
|
59
|
+
pointerEvents: "none",
|
|
60
|
+
userSelect: "none",
|
|
61
|
+
display: "none",
|
|
62
|
+
zIndex: "9999"
|
|
63
|
+
}), e.appendChild(t);
|
|
64
|
+
let n = document.createElement("div");
|
|
65
|
+
n.textContent = "— fps", t.appendChild(n);
|
|
66
|
+
let r = document.createElement("div");
|
|
67
|
+
r.style.fontSize = "10px", r.style.opacity = "0.8", r.textContent = "", t.appendChild(r);
|
|
68
|
+
function i(e) {
|
|
69
|
+
n.textContent = `${e | 0} fps`;
|
|
70
|
+
}
|
|
71
|
+
function a(e) {
|
|
72
|
+
r.textContent = e;
|
|
73
|
+
}
|
|
74
|
+
function o() {
|
|
75
|
+
t.style.display = "block";
|
|
76
|
+
}
|
|
77
|
+
function s() {
|
|
78
|
+
t.style.display = "none";
|
|
79
|
+
}
|
|
80
|
+
function c() {
|
|
81
|
+
t.parentElement && t.parentElement.removeChild(t);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
setFps: i,
|
|
85
|
+
setRenderer: a,
|
|
86
|
+
show: o,
|
|
87
|
+
hide: s,
|
|
88
|
+
destroy: c
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { e as a, t as i, n, r, i as t };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
//#region src/playus/overlay/touch-hint.ts
|
|
2
|
+
var e = !1;
|
|
3
|
+
function t() {
|
|
4
|
+
if (e) return;
|
|
5
|
+
e = !0;
|
|
6
|
+
let t = document.createElement("style");
|
|
7
|
+
t.id = "touch-hint-keyframes", t.textContent = "\n @keyframes th-tap {\n 0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.4; }\n 50% { transform: translate(-50%, -50%) scale(1.35); opacity: 1.0; }\n }\n @keyframes th-tap-rapid {\n 0% { transform: translate(-50%, -50%) scale(1); opacity: 0; }\n 20% { transform: translate(-50%, -50%) scale(1.25); opacity: 1.0; }\n 50% { transform: translate(-50%, -50%) scale(1); opacity: 0; }\n 100% { transform: translate(-50%, -50%) scale(1); opacity: 0; }\n }\n @keyframes th-drag-h {\n 0%, 100% { left: 5%; }\n 50% { left: 95%; }\n }\n @keyframes th-tap-sides-l {\n 0%, 48%, 100% { opacity: 0.2; transform: translate(-50%, -50%) scale(1); }\n 12%, 38% { opacity: 1.0; transform: translate(-50%, -50%) scale(1.25); }\n }\n @keyframes th-tap-sides-r {\n 0%, 52%, 100% { opacity: 0.2; transform: translate(-50%, -50%) scale(1); }\n 62%, 88% { opacity: 1.0; transform: translate(-50%, -50%) scale(1.25); }\n }\n @keyframes th-tap-timed {\n 0%, 60% { transform: translate(-50%, -50%) scale(1); opacity: 0.25; }\n 70% { transform: translate(-50%, -50%) scale(1.4); opacity: 1.0; }\n 85%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.25; }\n }\n @keyframes th-drag-free {\n 0% { transform: translate(-50%, -50%) translate(90%, -70%); }\n 12% { transform: translate(-50%, -50%) translate(30%, 90%); }\n 25% { transform: translate(-50%, -50%) translate(-90%, 30%); }\n 37% { transform: translate(-50%, -50%) translate(-30%, -90%); }\n 50% { transform: translate(-50%, -50%) translate(90%, -30%); }\n 62% { transform: translate(-50%, -50%) translate(30%, 90%); }\n 75% { transform: translate(-50%, -50%) translate(-90%, 30%); }\n 87% { transform: translate(-50%, -50%) translate(-30%, -90%); }\n 100% { transform: translate(-50%, -50%) translate(90%, -70%); }\n }\n @keyframes th-swipe-4dir {\n 0% { transform: translate(-50%, -50%) translate(140%, 0); opacity: 0; }\n 5% { transform: translate(-50%, -50%) translate(90%, 0); opacity: 0.9; }\n 8% { transform: translate(-50%, -50%) translate(-90%, 0); opacity: 0.9; }\n 13% { transform: translate(-50%, -50%) translate(-140%, 0); opacity: 0; }\n\n 25% { transform: translate(-50%, -50%) translate(-140%, 0); opacity: 0; }\n 30% { transform: translate(-50%, -50%) translate(-90%, 0); opacity: 0.9; }\n 33% { transform: translate(-50%, -50%) translate(90%, 0); opacity: 0.9; }\n 38% { transform: translate(-50%, -50%) translate(140%, 0); opacity: 0; }\n\n 50% { transform: translate(-50%, -50%) translate(0, 80%); opacity: 0; }\n 55% { transform: translate(-50%, -50%) translate(0, 50%); opacity: 0.9; }\n 58% { transform: translate(-50%, -50%) translate(0, -50%); opacity: 0.9; }\n 63% { transform: translate(-50%, -50%) translate(0, -80%); opacity: 0; }\n\n 75% { transform: translate(-50%, -50%) translate(0, -80%); opacity: 0; }\n 80% { transform: translate(-50%, -50%) translate(0, -50%); opacity: 0.9; }\n 83% { transform: translate(-50%, -50%) translate(0, 50%); opacity: 0.9; }\n 88% { transform: translate(-50%, -50%) translate(0, 80%); opacity: 0; }\n 100% { transform: translate(-50%, -50%) translate(0, 80%); opacity: 0; }\n }\n @keyframes th-swipe-h {\n 0% { transform: translate(-50%, -50%) translate(140%, 0); opacity: 0; }\n 5% { transform: translate(-50%, -50%) translate(90%, 0); opacity: 0.9; }\n 8% { transform: translate(-50%, -50%) translate(-90%, 0); opacity: 0.9; }\n 13% { transform: translate(-50%, -50%) translate(-140%, 0); opacity: 0; }\n\n 50% { transform: translate(-50%, -50%) translate(-140%, 0); opacity: 0; }\n 55% { transform: translate(-50%, -50%) translate(-90%, 0); opacity: 0.9; }\n 58% { transform: translate(-50%, -50%) translate(90%, 0); opacity: 0.9; }\n 63% { transform: translate(-50%, -50%) translate(140%, 0); opacity: 0; }\n 100% { transform: translate(-50%, -50%) translate(140%, 0); opacity: 0; }\n }\n @keyframes th-swipe-down {\n 0% { transform: translate(-50%, -50%) translate(0, -80%); opacity: 0; }\n 8% { transform: translate(-50%, -50%) translate(0, -50%); opacity: 0.9; }\n 14% { transform: translate(-50%, -50%) translate(0, 50%); opacity: 0.9; }\n 22% { transform: translate(-50%, -50%) translate(0, 80%); opacity: 0; }\n 100% { transform: translate(-50%, -50%) translate(0, 80%); opacity: 0; }\n }\n ", document.head.appendChild(t);
|
|
8
|
+
}
|
|
9
|
+
var n = {
|
|
10
|
+
position: "absolute",
|
|
11
|
+
borderRadius: "50%",
|
|
12
|
+
background: "rgba(255, 255, 255, 1.0)",
|
|
13
|
+
pointerEvents: "none"
|
|
14
|
+
};
|
|
15
|
+
function r(e, t) {
|
|
16
|
+
let r = document.createElement("div");
|
|
17
|
+
return Object.assign(r.style, {
|
|
18
|
+
...n,
|
|
19
|
+
background: t,
|
|
20
|
+
width: e,
|
|
21
|
+
aspectRatio: "1"
|
|
22
|
+
}), r;
|
|
23
|
+
}
|
|
24
|
+
function i(e, t) {
|
|
25
|
+
let n = r("30%", t);
|
|
26
|
+
Object.assign(n.style, {
|
|
27
|
+
top: "50%",
|
|
28
|
+
left: "50%",
|
|
29
|
+
animation: "th-tap 1.2s ease-in-out infinite"
|
|
30
|
+
}), e.appendChild(n);
|
|
31
|
+
}
|
|
32
|
+
function a(e, t) {
|
|
33
|
+
let n = r("28%", t);
|
|
34
|
+
Object.assign(n.style, {
|
|
35
|
+
top: "50%",
|
|
36
|
+
left: "5%",
|
|
37
|
+
transform: "translate(-50%, -50%)",
|
|
38
|
+
opacity: "0.7",
|
|
39
|
+
animation: "th-drag-h 2s ease-in-out infinite"
|
|
40
|
+
}), e.appendChild(n);
|
|
41
|
+
}
|
|
42
|
+
function o(e, t) {
|
|
43
|
+
let n = r("22%", t);
|
|
44
|
+
Object.assign(n.style, {
|
|
45
|
+
top: "50%",
|
|
46
|
+
left: "15%",
|
|
47
|
+
opacity: "0.15",
|
|
48
|
+
animation: "th-tap-sides-l 2s ease-in-out infinite"
|
|
49
|
+
}), e.appendChild(n);
|
|
50
|
+
let i = r("22%", t);
|
|
51
|
+
Object.assign(i.style, {
|
|
52
|
+
top: "50%",
|
|
53
|
+
left: "85%",
|
|
54
|
+
opacity: "0.15",
|
|
55
|
+
animation: "th-tap-sides-r 2s ease-in-out infinite"
|
|
56
|
+
}), e.appendChild(i);
|
|
57
|
+
}
|
|
58
|
+
function s(e, t) {
|
|
59
|
+
let n = r("30%", t);
|
|
60
|
+
Object.assign(n.style, {
|
|
61
|
+
top: "50%",
|
|
62
|
+
left: "50%",
|
|
63
|
+
animation: "th-tap-timed 2s ease-in-out infinite"
|
|
64
|
+
}), e.appendChild(n);
|
|
65
|
+
}
|
|
66
|
+
function c(e, t) {
|
|
67
|
+
let n = r("26%", t);
|
|
68
|
+
Object.assign(n.style, {
|
|
69
|
+
top: "50%",
|
|
70
|
+
left: "50%",
|
|
71
|
+
opacity: "0.7",
|
|
72
|
+
animation: "th-drag-free 3.5s ease-in-out infinite"
|
|
73
|
+
}), e.appendChild(n);
|
|
74
|
+
}
|
|
75
|
+
function l(e, t) {
|
|
76
|
+
let n = r("18%", t);
|
|
77
|
+
Object.assign(n.style, {
|
|
78
|
+
top: "35%",
|
|
79
|
+
left: "50%",
|
|
80
|
+
animation: "th-swipe-4dir 4s linear infinite"
|
|
81
|
+
}), e.appendChild(n);
|
|
82
|
+
}
|
|
83
|
+
function u(e, t) {
|
|
84
|
+
let n = r("30%", t);
|
|
85
|
+
Object.assign(n.style, {
|
|
86
|
+
top: "50%",
|
|
87
|
+
left: "50%",
|
|
88
|
+
animation: "th-tap-rapid 0.9s ease-in-out infinite"
|
|
89
|
+
}), e.appendChild(n);
|
|
90
|
+
}
|
|
91
|
+
function d(e, t) {
|
|
92
|
+
let n = r("18%", t);
|
|
93
|
+
Object.assign(n.style, {
|
|
94
|
+
top: "50%",
|
|
95
|
+
left: "50%",
|
|
96
|
+
animation: "th-swipe-h 3s linear infinite"
|
|
97
|
+
}), e.appendChild(n);
|
|
98
|
+
}
|
|
99
|
+
function f(e, t) {
|
|
100
|
+
let n = r("24%", t);
|
|
101
|
+
Object.assign(n.style, {
|
|
102
|
+
top: "50%",
|
|
103
|
+
left: "50%",
|
|
104
|
+
animation: "th-swipe-down 2s linear infinite"
|
|
105
|
+
}), e.appendChild(n);
|
|
106
|
+
}
|
|
107
|
+
var p = {
|
|
108
|
+
tap: i,
|
|
109
|
+
"drag-horizontal": a,
|
|
110
|
+
"tap-sides": o,
|
|
111
|
+
"tap-timed": s,
|
|
112
|
+
"drag-free": c,
|
|
113
|
+
"swipe-4dir": l,
|
|
114
|
+
"swipe-horizontal": d,
|
|
115
|
+
"swipe-down": f,
|
|
116
|
+
"tap-rapid": u
|
|
117
|
+
}, m = {
|
|
118
|
+
"tap-sides": "75%",
|
|
119
|
+
"swipe-4dir": "85%",
|
|
120
|
+
"swipe-horizontal": "85%"
|
|
121
|
+
};
|
|
122
|
+
function h(e, n, r = "#ffffff", i) {
|
|
123
|
+
t();
|
|
124
|
+
let a = document.createElement("div");
|
|
125
|
+
return Object.assign(a.style, {
|
|
126
|
+
position: "absolute",
|
|
127
|
+
top: i?.top ?? "62%",
|
|
128
|
+
left: "50%",
|
|
129
|
+
transform: "translateX(-50%)",
|
|
130
|
+
width: i?.width ?? m[e] ?? "40%",
|
|
131
|
+
aspectRatio: "1",
|
|
132
|
+
pointerEvents: "none",
|
|
133
|
+
zIndex: "11",
|
|
134
|
+
display: "none"
|
|
135
|
+
}), p[e](a, r), n.appendChild(a), {
|
|
136
|
+
show() {
|
|
137
|
+
a.style.display = "block";
|
|
138
|
+
},
|
|
139
|
+
hide() {
|
|
140
|
+
a.style.display = "none";
|
|
141
|
+
},
|
|
142
|
+
destroy() {
|
|
143
|
+
a.remove();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { h as t };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timeFormat.d.ts","sourceRoot":"","sources":["../../../src/playus/helpers/timeFormat.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAWjE;AAED,wBAAgB,yBAAyB,CAAC,iBAAiB,EAAE,MAAM,GAAG,MAAM,CAO3E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported languages - single source of truth
|
|
3
|
+
*/
|
|
4
|
+
export declare const SUPPORTED_LANGUAGES: readonly ["en", "de", "fr", "es", "it"];
|
|
5
|
+
export type Language = typeof SUPPORTED_LANGUAGES[number];
|
|
6
|
+
export declare const DEFAULT_LANGUAGE: Language;
|
|
7
|
+
/**
|
|
8
|
+
* Translation dictionary type - key-first structure
|
|
9
|
+
* All translations for a key are grouped together
|
|
10
|
+
*/
|
|
11
|
+
export type TranslationDict<Keys extends string> = {
|
|
12
|
+
[K in Keys]: {
|
|
13
|
+
[L in Language]?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Get current language from URL parameter
|
|
18
|
+
* Reads ?lang=de from URL (follows debug.ts pattern)
|
|
19
|
+
*
|
|
20
|
+
* @returns Language code or default ('en')
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // URL: http://localhost:8080/?lang=de
|
|
24
|
+
* getCurrentLanguage(); // Returns 'de'
|
|
25
|
+
*
|
|
26
|
+
* // URL: http://localhost:8080/
|
|
27
|
+
* getCurrentLanguage(); // Returns 'en'
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCurrentLanguage(): Language;
|
|
30
|
+
/**
|
|
31
|
+
* Create a type-safe translation function with template support
|
|
32
|
+
*
|
|
33
|
+
* This is the main API that games use. It:
|
|
34
|
+
* 1. Detects current language from URL
|
|
35
|
+
* 2. Returns translation function with full type safety
|
|
36
|
+
* 3. Automatically falls back to English if translation missing
|
|
37
|
+
* 4. Supports template variables for dynamic strings
|
|
38
|
+
*
|
|
39
|
+
* @param translations - Dictionary of translations (key-first structure)
|
|
40
|
+
* @returns Translation function t(key) or t(key, vars)
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* const translations = {
|
|
44
|
+
* hint: { en: "Tap to start", de: "Tippen zum Starten" },
|
|
45
|
+
* level: { en: "Level {n}", de: "Level {n}" }
|
|
46
|
+
* };
|
|
47
|
+
* const t = createTranslator(translations);
|
|
48
|
+
* t('hint'); // Returns "Tap to start" or "Tippen zum Starten"
|
|
49
|
+
* t('level', { n: 5 }); // Returns "Level 5"
|
|
50
|
+
*/
|
|
51
|
+
export declare function createTranslator<Keys extends string>(translations: TranslationDict<Keys>): {
|
|
52
|
+
(key: Keys): string;
|
|
53
|
+
(key: Keys, vars: Record<string, string | number>): string;
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/playus/i18n.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,eAAO,MAAM,mBAAmB,yCAA0C,CAAC;AAC3E,MAAM,MAAM,QAAQ,GAAG,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,EAAE,QAAe,CAAC;AAE/C;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,IAAI,SAAS,MAAM,IAAI;KAChD,CAAC,IAAI,IAAI,GAAG;SACV,CAAC,IAAI,QAAQ,CAAC,CAAC,EAAE,MAAM;KACzB;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,IAAI,QAAQ,CAM7C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,SAAS,MAAM,EAClD,YAAY,EAAE,eAAe,CAAC,IAAI,CAAC,GAClC;IACD,CAAC,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAAC;CAC5D,CAoBA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { ColorConfig, NativeBridge, nativeBridge, roundScoreForBridge, } from './bridge';
|
|
2
|
+
export { createSeededRandom, seededBetween, seededFloatBetween, seededShuffle } from './random';
|
|
3
|
+
export { getGameSeed, getUrlParam } from './url-params';
|
|
4
|
+
export { clampGameplayDeltaMs, clampGameplayDeltaSeconds } from './timing';
|
|
5
|
+
export { createTranslator, getCurrentLanguage } from './i18n';
|
|
6
|
+
export type { Language, TranslationDict } from './i18n';
|
|
7
|
+
export { createTapToStartOverlay } from './tap-to-start';
|
|
8
|
+
export type { LocalizedText, TapToStartMode, TapToStartOverlay } from './tap-to-start';
|
|
9
|
+
export { sound } from './sound';
|
|
10
|
+
export type { SoundId, SoundPlayOptions } from './sound';
|
|
11
|
+
export { applyMobileSurfaceStyle, installMobileSelectionPolicy, installTouchDefaultGuard, } from './mobile-interaction';
|
|
12
|
+
export { createDebugOverlay, getRendererInfo, isDebugMode } from './overlay/debug';
|
|
13
|
+
export { createTouchHint } from './overlay/touch-hint';
|
|
14
|
+
export type { DebugOverlay } from './overlay/debug';
|
|
15
|
+
export type { TouchHint, TouchHintType } from './overlay/touch-hint';
|
|
16
|
+
export type { BackgroundConfig } from './types/background';
|
|
17
|
+
export { DEFAULT_BACKGROUND, getBackgroundColor, isTransparent } from './types/background';
|
|
18
|
+
export { formatMillisecondsAsClock, formatSecondsAsClock } from './helpers/timeFormat';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/playus/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,mBAAmB,GACpB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAC9D,YAAY,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACvF,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EACL,uBAAuB,EACvB,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAC3F,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|