@caperjs/plugin-3d 0.0.0-stage → 0.8.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 CHANGED
@@ -1,3 +1,191 @@
1
- # Temporary Holding Version
1
+ # @caperjs/plugin-3d
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Experimental** — `@pixi/3d` is in open beta; this package pins an exact version and the API
4
+ we expose may change until upstream stabilizes.
5
+
6
+ Official 3D for [Caper](https://github.com/anthonysapp/caper) games, built on Pixi's
7
+ [`@pixi/3d`](https://pixijs.com/3d). A `View3D` is a normal node on the 2D stage; lights,
8
+ models, and meshes live in a separate tree inside it. Interactive 2D UI you add **after** the
9
+ view draws on top and receives taps first.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npx caper add 3d
15
+ ```
16
+
17
+ Then `npx caper doctor`. The command:
18
+
19
+ - Installs `@caperjs/plugin-3d`, `@pixi/3d` at the pin this package expects, and `pixi.js` only if
20
+ yours is older than the version Caper pins; announces packages and the install command before running it.
21
+ - Adds `'3d'` to `plugins` in `caper.config.ts` (or prints the line if there is no `plugins` array).
22
+ - Creates `src/scenes/Sample3DScene.ts` when missing (never overwrites).
23
+ - On pnpm ≥ 10.16, may add `@pixi/3d` to `minimumReleaseAgeExclude` in `pnpm-workspace.yaml`.
24
+
25
+ ### By hand
26
+
27
+ ```bash
28
+ pnpm add @caperjs/plugin-3d @pixi/3d@0.1.0
29
+ ```
30
+
31
+ Peers: `@caperjs/core` (requires `pixi.js@8.23.0`), `pixi.js`, `@pixi/3d@0.1.0` (exact). Add `'3d'`
32
+ to `plugins` in `caper.config.ts`. On **pnpm 11**, you may need
33
+ `minimumReleaseAgeExclude: ['@pixi/3d@0.1.0']` in `pnpm-workspace.yaml`; `caper add 3d` adds it when
34
+ needed.
35
+
36
+ ## Use
37
+
38
+ ```ts
39
+ // caper.config.ts
40
+ export default defineConfig({
41
+ plugins: [
42
+ '3d',
43
+ // or, with options:
44
+ // ['3d', { options: { quality: 'low' } }],
45
+ ],
46
+ });
47
+ ```
48
+
49
+ ```ts
50
+ // any Scene
51
+ const view = this.add.view3d({
52
+ camera: { position: [0, 1.5, 4], lookAt: [0, 0, 0] },
53
+ });
54
+ const hero = view.add.model({ asset: 'robot', y: 0 });
55
+ view.add.light({ type: 'directional', intensity: 2, castShadow: true });
56
+ const cube = view.add.mesh({ geometry: 'box', color: 0xe85d5d, x: -1 });
57
+ hero.play('Walking');
58
+ ```
59
+
60
+ Put self-contained `.glb` files in your assets folder (AssetPack pass-through). v1 supports
61
+ **.glb only** — multi-file `.gltf` and `.hdr` are not tested.
62
+
63
+ ## Animation
64
+
65
+ `model.play(name, options?)` replaces whatever is playing and restarts the clip from the start
66
+ (unlike 0.7, which layered by default). Use `layer: true` to blend on purpose. Options
67
+ (`loop`, `fade`, `onComplete`, …) do not carry over to the next call. A replaced clip does not run
68
+ its previous `onComplete`.
69
+
70
+ ```ts
71
+ model.play('Walking');
72
+ model.play('Wave', { loop: false, fade: 0.2, onComplete: () => model.play('Walking') });
73
+ ```
74
+
75
+ ## Entities
76
+
77
+ Declare a 3D game object the way you declare any Caper entity, on the `Entity3D` base class.
78
+ Scaffold with `npx caper add entity <Name> --3d`.
79
+
80
+ ```ts
81
+ // src/entities/Car.ts
82
+ import { defineEntity } from '@caperjs/core';
83
+ import { Entity3D } from '@caperjs/plugin-3d';
84
+
85
+ export const entity = defineEntity({ id: 'car' });
86
+
87
+ export default class Car extends Entity3D<{ color?: number }> {
88
+ added() {
89
+ // this.add / this.make are the 3D factory, parented to the entity
90
+ this.add.mesh({ geometry: 'box', color: this.props.color ?? 0xd9534f, scale: [2, 0.6, 1] });
91
+ }
92
+ update() {
93
+ this.rotation.y += 0.02 * this.app.ticker.deltaTime;
94
+ }
95
+ }
96
+ ```
97
+
98
+ ```ts
99
+ const car = view.add.entity('car', { color: 0x4a90d9, x: -2, y: 0.3 });
100
+ ```
101
+
102
+ - `id`, props and the returned instance are typed from the class. Only ids whose class extends
103
+ `Container3D` are accepted; `this.add.entity('car')` on a 2D container is a type error.
104
+ - `view.add.entity` applies the placement props (`position`, `rotation`, `scale`, `x` / `y` / `z`,
105
+ `visible`, `lookAt`, `parent`); the rest are the entity's own, on `this.props`.
106
+ - Build parts in `added()`, not in the constructor: the entity's factory belongs to the view it is
107
+ in. What it creates is freed with that view, and its models' clips follow `app.ticker`.
108
+ - `update()` runs every frame while the entity is in the scene, and stops with `app.pause()`.
109
+ - `view.make.entity` builds without parenting; `this.add.entity(...)` inside an entity nests one.
110
+ - `entity.on('pointertap', …)` opts the entity into picking on itself and passive parts (set
111
+ `eventMode = 'static'` yourself for property handlers).
112
+
113
+ ## Many meshes: share one material
114
+
115
+ `color` gives each mesh its own material for the life of the view; materials are never shared by
116
+ colour behind your back. For a spawner, make one material and pass it. The view frees it.
117
+
118
+ ```ts
119
+ const red = view.make.material({ color: 0xd9534f, roughness: 0.6 });
120
+ for (let i = 0; i < 200; i++) view.add.mesh({ geometry: 'box', material: red, x: i });
121
+ ```
122
+
123
+ `color` is shorthand for `baseColor`; every other key is a raw `Material3D` option. Writing to
124
+ `red` recolours every mesh that uses it. (`view.add.material` is the same function: a material has
125
+ no parent.) A `new Material3D(...)` you build yourself also works and stays yours to destroy.
126
+
127
+ ## Portrait screens
128
+
129
+ Optional `camera.fitAspect` on props cameras (not a raw `Camera3D`) is the width/height ratio you
130
+ frame for (e.g. `16 / 9`). On a narrower view, vertical FOV grows so the same width stays in frame.
131
+
132
+ ```ts
133
+ const view = this.add.view3d({
134
+ camera: { position: [0, 1.5, 4], lookAt: [0, 0, 0], fitAspect: 16 / 9 },
135
+ });
136
+ ```
137
+
138
+ ## Quality
139
+
140
+ Plugin option `quality: 'auto' | 'low' | 'high'` (default `'auto'`):
141
+
142
+ | Value | Shadows | Resolution cap |
143
+ | --- | --- | --- |
144
+ | `high` | high | inherit |
145
+ | `low` | low | capped at 1 |
146
+ | `auto` | low on mobile, medium otherwise | cap 2 on mobile, inherit on desktop |
147
+
148
+ Per-view `shadowQuality` and `resolution` props override the global default.
149
+
150
+ ## Interaction
151
+
152
+ `interactive` on the view (default `true`):
153
+
154
+ - `true` → `eventMode: 'static'`: 3D picking on; the view's rectangle captures taps, so put
155
+ buttons and HUD **above** the view in the 2D tree.
156
+ - `false` → `eventMode: 'none'`: taps pass through to 2D behind the view; no 3D picking.
157
+
158
+ Do not use `'passive'` on the view: it disables picking and still blocks taps to 2D behind the view.
159
+
160
+ `view.pin(2dNode, target3d, { offset? })` keeps a 2D node over a 3D point (updates each frame;
161
+ the view owns `visible` while pinned).
162
+
163
+ ## Boot cost
164
+
165
+ Opting in loads `@pixi/3d` in the **pre-renderer** phase, before the Pixi renderer is created.
166
+ That is required for 3D to work, but it means every app start pays roughly **~490 KB raw /
167
+ ~155 KB gzip** even if no scene uses 3D. The import pulls both WebGL and WebGPU backend
168
+ chunks at boot. Apps without `'3d'` in `plugins` pay nothing.
169
+
170
+ ## Teardown
171
+
172
+ Never destroy a loaded model or the view with `{ material: true }` (or blanket geometry flags
173
+ on glTF nodes): glTF materials are shared on the cached source and the next visit crashes every
174
+ frame. The plugin destroys only geometry and materials **it** created; models use
175
+ `{ children: true }` only.
176
+
177
+ Nodes made by a view's factory belong to that view: the built-in geometry, `color` materials and
178
+ `make.material` materials are freed when the view is destroyed, so do not keep such a node alive past its view. A raw
179
+ `geometry` or `material` you pass in stays yours to destroy.
180
+
181
+ ## Types
182
+
183
+ No import needed in scenes: with `'3d'` in `plugins` and this package installed, regenerated
184
+ `src/types/caper-app.d.ts` includes `import type {} from '@caperjs/plugin-3d'` so
185
+ `this.add.view3d` typechecks. When AssetPack ships `.glb`/`.gltf` models, `caper-assets.d.ts`
186
+ defines `AssetModels`; `view.add.model({ asset: '…' })` only accepts known manifest names or a
187
+ loaded `Scene3DSource` (`asset: await Assets.load(url)`).
188
+
189
+ ## License
190
+
191
+ MIT
@@ -0,0 +1,28 @@
1
+ import { Container3D } from '@pixi/3d';
2
+ import { Factory3D } from './factory3d';
3
+ type Listen<Self> = (event: string | symbol, fn: (...args: any[]) => void, context?: any) => Self;
4
+ declare const Entity3D_base: typeof Container3D & import('@caperjs/core').Constructor<import('@caperjs/core').ISignalContainer> & import('@caperjs/core').Constructor<import('@caperjs/core').ILifecycle>;
5
+ /**
6
+ * Base class for 3D entities: a `Container3D` with the Caper lifecycle (`added` / `removed` / `update`),
7
+ * signals, and its own 3D factory. Declare one like any entity (`defineEntity` + default export under
8
+ * `src/entities/`) and build it with `view.add.entity(id, props)`.
9
+ *
10
+ * Build parts in `added()`, not in the constructor: `this.add` / `this.make` belong to the view the entity is
11
+ * in, so they work once it has been added to one. Parts are parented to the entity; the resources they
12
+ * create are freed with the view, and model animations are driven by the view.
13
+ * Adding a pointer listener makes a passive entity interactive so it hears taps on its parts.
14
+ */
15
+ export declare class Entity3D<Props = void> extends Entity3D_base {
16
+ readonly props: Props;
17
+ private factories?;
18
+ constructor(props?: Props);
19
+ on: Container3D['on'] & Listen<this>;
20
+ once: Container3D['once'] & Listen<this>;
21
+ /** Listening is the opt-in: a passive ancestor is skipped when a part's event bubbles. */
22
+ private enablePointerEvents;
23
+ get add(): Factory3D;
24
+ get make(): Factory3D;
25
+ private resolveFactories;
26
+ }
27
+ export {};
28
+ //# sourceMappingURL=Entity3D.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Entity3D.d.ts","sourceRoot":"","sources":["../src/Entity3D.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;;AAElG;;;;;;;;;GASG;AACH,qBAAa,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAE,SAAQ,aAAuC;IACjF,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,OAAO,CAAC,SAAS,CAAC,CAAsD;gBAE5D,KAAK,CAAC,EAAE,KAAK;IAQhB,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAGN;IAE9B,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,CAGR;IAEzC,0FAA0F;IAC1F,OAAO,CAAC,mBAAmB;IAM3B,IAAI,GAAG,IAAI,SAAS,CAEnB;IAED,IAAI,IAAI,IAAI,SAAS,CAEpB;IAED,OAAO,CAAC,gBAAgB;CAczB"}
package/lib/Model.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { Model3D, AnimationState } from '@pixi/3d';
2
+ import { Ticker } from 'pixi.js';
3
+ export type PlayOptions = Partial<Pick<AnimationState, 'loop' | 'animationSpeed' | 'weight' | 'onComplete' | 'onLoop'>> & {
4
+ fade?: number;
5
+ layer?: boolean;
6
+ };
7
+ export declare class Model extends Model3D {
8
+ private transition?;
9
+ play(name: string, options?: PlayOptions): AnimationState;
10
+ stop(): void;
11
+ update(ticker: Ticker): void;
12
+ }
13
+ //# sourceMappingURL=Model.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../src/Model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,GAAG,QAAQ,GAAG,YAAY,GAAG,QAAQ,CAAC,CAAC,GAAG;IACxH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAIF,qBAAa,KAAM,SAAQ,OAAO;IAChC,OAAO,CAAC,UAAU,CAAC,CAAO;IAE1B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,cAAc;IAuC7D,IAAI,IAAI,IAAI;IAKZ,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAc7B"}
@@ -0,0 +1,20 @@
1
+ import { Plugin, IApplication, IPlugin } from '@caperjs/core';
2
+ import { View3D, View3DProps } from './View3D';
3
+ import { Quality3D } from './quality';
4
+ export interface Plugin3DOptions {
5
+ quality: Quality3D;
6
+ }
7
+ export interface IPlugin3D extends IPlugin<Plugin3DOptions> {
8
+ }
9
+ declare module '@caperjs/core' {
10
+ interface FactoryMethodOverrides {
11
+ view3d: (props?: View3DProps) => View3D;
12
+ }
13
+ }
14
+ export declare class Plugin3D extends Plugin<Plugin3DOptions> implements IPlugin3D {
15
+ id: string;
16
+ initialize(options?: Partial<Plugin3DOptions>): void;
17
+ postInitialize(app: IApplication): void;
18
+ }
19
+ export default Plugin3D;
20
+ //# sourceMappingURL=Plugin3D.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Plugin3D.d.ts","sourceRoot":"","sources":["../src/Plugin3D.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAA0B,MAAM,eAAe,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAG3C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,SAAS,CAAC;CACpB;AACD,MAAM,WAAW,SAAU,SAAQ,OAAO,CAAC,eAAe,CAAC;CAAG;AAE9D,OAAO,QAAQ,eAAe,CAAC;IAC7B,UAAU,sBAAsB;QAC9B,MAAM,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC;KACzC;CACF;AAKD,qBAAa,QAAS,SAAQ,MAAM,CAAC,eAAe,CAAE,YAAW,SAAS;IACxE,EAAE,SAAQ;IAEV,UAAU,CAAC,OAAO,GAAE,OAAO,CAAC,eAAe,CAAM,GAAG,IAAI;IAKxD,cAAc,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI;CAGxC;AAED,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { Camera3D, Container3D, View3D as PixiView3D, View3DOptions } from '@pixi/3d';
2
+ import { Size } from '@caperjs/core';
3
+ import { Container, Ticker } from 'pixi.js';
4
+ import { Factory3D } from './factory3d';
5
+ import { Vec3 } from './props';
6
+ type CameraProps = {
7
+ position?: Vec3;
8
+ lookAt?: Vec3;
9
+ /** Vertical field of view in degrees. */
10
+ fov?: number;
11
+ near?: number;
12
+ far?: number;
13
+ /**
14
+ * The aspect ratio (width / height) the scene is framed for, e.g. `16 / 9`. On a narrower view (a phone
15
+ * held upright) the field of view grows so the sides stay in frame. Leave it out to keep `fov` fixed.
16
+ */
17
+ fitAspect?: number;
18
+ };
19
+ export interface View3DProps extends Omit<View3DOptions, 'camera'> {
20
+ camera?: Camera3D | CameraProps;
21
+ interactive?: boolean;
22
+ }
23
+ /** Something an add-on attaches to a view. The view drives it; games never call these. */
24
+ export interface View3DPart {
25
+ /** Every frame the view updates (on app.ticker), after model clips. */
26
+ update?(ticker: Ticker): void;
27
+ /** Once when owned with the current size, then on every view resize. */
28
+ resize?(size: Size): void;
29
+ /** Before the view frees its nodes and resources, while the view is still usable. */
30
+ destroy(): void;
31
+ }
32
+ declare const View3D_base: (new (options?: View3DOptions<Container3D> | undefined) => PixiView3D<Container3D>) & import('@caperjs/core').Constructor<import('@caperjs/core').ISignalContainer> & import('@caperjs/core').Constructor<import('@caperjs/core').ILifecycle>;
33
+ export declare class View3D extends View3D_base {
34
+ readonly add: Factory3D;
35
+ readonly make: Factory3D;
36
+ private readonly owned;
37
+ private readonly parts;
38
+ private readonly pins;
39
+ private readonly fillsScreen;
40
+ private readonly fit?;
41
+ constructor(props?: View3DProps);
42
+ /** For add-ons: the view updates `part` every frame, tells it about resizes, and destroys it with itself. */
43
+ own<T extends View3DPart>(part: T): T;
44
+ /** Keeps a 2D node over a 3D point, hidden while the point is behind the camera. Returns the function that unpins it. */
45
+ pin(node: Container, target: Container3D | Vec3, options?: {
46
+ offset?: Vec3;
47
+ }): () => void;
48
+ added(): void;
49
+ resize(_size?: Size): void;
50
+ update(_ticker?: Ticker | number): void;
51
+ private placePins;
52
+ destroy(_options?: Parameters<PixiView3D['destroy']>[0] | boolean): void;
53
+ private fitCamera;
54
+ private get viewSize();
55
+ private placeFullScreen;
56
+ }
57
+ export {};
58
+ //# sourceMappingURL=View3D.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"View3D.d.ts","sourceRoot":"","sources":["../src/View3D.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAW,MAAM,IAAI,UAAU,EAAE,MAAM,UAAU,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAS,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,SAAS,CAAC;AAG7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAMpC,KAAK,WAAW,GAAG;IACjB,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,MAAM,CAAC,EAAE,IAAI,CAAC;IACd,yCAAyC;IACzC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,WAAY,SAAQ,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC;IAChE,MAAM,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,0FAA0F;AAC1F,MAAM,WAAW,UAAU;IACzB,uEAAuE;IACvE,MAAM,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wEAAwE;IACxE,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,qFAAqF;IACrF,OAAO,IAAI,IAAI,CAAC;CACjB;;AAOD,qBAAa,MAAO,SAAQ,WAAsC;IAChE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA6B;IAClD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAkC;gBAE3C,KAAK,GAAE,WAAgB;IAqCnC,6GAA6G;IAC7G,GAAG,CAAC,CAAC,SAAS,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC;IAMrC,yHAAyH;IACzH,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,IAAI,CAAA;KAAO,GAAG,MAAM,IAAI;IAW7F,KAAK,IAAI,IAAI;IAIb,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,GAAG,IAAI;IAM1B,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAMvC,OAAO,CAAC,SAAS;IAmBjB,OAAO,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,IAAI;IASxE,OAAO,CAAC,SAAS;IAMjB,OAAO,KAAK,QAAQ,GAGnB;IAED,OAAO,CAAC,eAAe;CAGxB"}