bonkjs 0.2.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.
Files changed (88) hide show
  1. package/README.md +94 -0
  2. package/dist/audio/AudioManager.d.ts +105 -0
  3. package/dist/audio/AudioManager.d.ts.map +1 -0
  4. package/dist/audio/AudioSource.d.ts +69 -0
  5. package/dist/audio/AudioSource.d.ts.map +1 -0
  6. package/dist/audio/index.d.ts +3 -0
  7. package/dist/audio/index.d.ts.map +1 -0
  8. package/dist/bonkjs.js +4307 -0
  9. package/dist/bonkjs.js.map +1 -0
  10. package/dist/devtools/Tweaker.d.ts +61 -0
  11. package/dist/devtools/Tweaker.d.ts.map +1 -0
  12. package/dist/devtools/TweakerOverlay.d.ts +66 -0
  13. package/dist/devtools/TweakerOverlay.d.ts.map +1 -0
  14. package/dist/devtools/index.d.ts +3 -0
  15. package/dist/devtools/index.d.ts.map +1 -0
  16. package/dist/devtools/tweaker-styles.d.ts +5 -0
  17. package/dist/devtools/tweaker-styles.d.ts.map +1 -0
  18. package/dist/devtools/types.d.ts +37 -0
  19. package/dist/devtools/types.d.ts.map +1 -0
  20. package/dist/index.d.ts +13 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/input/Input.d.ts +114 -0
  23. package/dist/input/Input.d.ts.map +1 -0
  24. package/dist/input/Keys.d.ts +63 -0
  25. package/dist/input/Keys.d.ts.map +1 -0
  26. package/dist/input/index.d.ts +3 -0
  27. package/dist/input/index.d.ts.map +1 -0
  28. package/dist/math/index.d.ts +2 -0
  29. package/dist/math/index.d.ts.map +1 -0
  30. package/dist/math/vec2.d.ts +27 -0
  31. package/dist/math/vec2.d.ts.map +1 -0
  32. package/dist/physics/CollisionLayers.d.ts +25 -0
  33. package/dist/physics/CollisionLayers.d.ts.map +1 -0
  34. package/dist/physics/MatterPhysicsWorld.d.ts +24 -0
  35. package/dist/physics/MatterPhysicsWorld.d.ts.map +1 -0
  36. package/dist/physics/PhysicsWorld.d.ts +101 -0
  37. package/dist/physics/PhysicsWorld.d.ts.map +1 -0
  38. package/dist/physics/RigidBody.d.ts +72 -0
  39. package/dist/physics/RigidBody.d.ts.map +1 -0
  40. package/dist/physics/index.d.ts +6 -0
  41. package/dist/physics/index.d.ts.map +1 -0
  42. package/dist/render/AnimatedSprite.d.ts +87 -0
  43. package/dist/render/AnimatedSprite.d.ts.map +1 -0
  44. package/dist/render/Camera.d.ts +76 -0
  45. package/dist/render/Camera.d.ts.map +1 -0
  46. package/dist/render/PixiRenderer.d.ts +80 -0
  47. package/dist/render/PixiRenderer.d.ts.map +1 -0
  48. package/dist/render/Renderer.d.ts +151 -0
  49. package/dist/render/Renderer.d.ts.map +1 -0
  50. package/dist/render/Sprite.d.ts +49 -0
  51. package/dist/render/Sprite.d.ts.map +1 -0
  52. package/dist/render/index.d.ts +6 -0
  53. package/dist/render/index.d.ts.map +1 -0
  54. package/dist/runtime/EventSystem.d.ts +38 -0
  55. package/dist/runtime/EventSystem.d.ts.map +1 -0
  56. package/dist/runtime/Game.d.ts +59 -0
  57. package/dist/runtime/Game.d.ts.map +1 -0
  58. package/dist/runtime/Scheduler.d.ts +50 -0
  59. package/dist/runtime/Scheduler.d.ts.map +1 -0
  60. package/dist/runtime/Time.d.ts +30 -0
  61. package/dist/runtime/Time.d.ts.map +1 -0
  62. package/dist/runtime/Transform.d.ts +47 -0
  63. package/dist/runtime/Transform.d.ts.map +1 -0
  64. package/dist/runtime/index.d.ts +6 -0
  65. package/dist/runtime/index.d.ts.map +1 -0
  66. package/dist/types.d.ts +54 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/ui/UIElement.d.ts +211 -0
  69. package/dist/ui/UIElement.d.ts.map +1 -0
  70. package/dist/ui/UIManager.d.ts +102 -0
  71. package/dist/ui/UIManager.d.ts.map +1 -0
  72. package/dist/ui/index.d.ts +11 -0
  73. package/dist/ui/index.d.ts.map +1 -0
  74. package/dist/ui/layout/UIHBox.d.ts +42 -0
  75. package/dist/ui/layout/UIHBox.d.ts.map +1 -0
  76. package/dist/ui/layout/UIVBox.d.ts +52 -0
  77. package/dist/ui/layout/UIVBox.d.ts.map +1 -0
  78. package/dist/ui/primitives/UIButton.d.ts +77 -0
  79. package/dist/ui/primitives/UIButton.d.ts.map +1 -0
  80. package/dist/ui/primitives/UIImage.d.ts +54 -0
  81. package/dist/ui/primitives/UIImage.d.ts.map +1 -0
  82. package/dist/ui/primitives/UIPanel.d.ts +71 -0
  83. package/dist/ui/primitives/UIPanel.d.ts.map +1 -0
  84. package/dist/ui/primitives/UIText.d.ts +57 -0
  85. package/dist/ui/primitives/UIText.d.ts.map +1 -0
  86. package/dist/ui/types.d.ts +138 -0
  87. package/dist/ui/types.d.ts.map +1 -0
  88. package/package.json +54 -0
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # bonkjs
2
+
3
+ A 2D game toolkit for AI collaboration. TypeScript-first. No scene format, no component hierarchy — Claude decides the architecture per game.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install bonkjs
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { Game, Sprite, Camera, RigidBody, Input, Transform } from 'bonkjs';
15
+
16
+ const game = new Game();
17
+ const canvas = await game.init({ width: 800, height: 600 });
18
+ document.getElementById('app')!.appendChild(canvas);
19
+
20
+ const player = new Transform({ position: [400, 300] });
21
+ const sprite = new Sprite(game.renderer, { width: 48, height: 64, color: 0x00ff00, transform: player });
22
+ const body = game.createBody(player, { type: 'dynamic', fixedRotation: true });
23
+ body.addCollider({ type: 'box', width: 48, height: 64 });
24
+
25
+ const camera = new Camera(game.renderer, { followSmoothing: 8 });
26
+ camera.follow(() => player.worldPosition);
27
+
28
+ game.onFixedUpdate(() => { body.syncFromPhysics(); });
29
+ game.onUpdate(() => {
30
+ const h = Input.getAxisRaw('horizontal');
31
+ body.velocity = [h * 300, body.velocity[1]];
32
+ sprite.sync();
33
+ });
34
+ game.onLateUpdate(() => { camera.update(); });
35
+
36
+ game.start();
37
+ ```
38
+
39
+ No scene format. No component hierarchy. Import what you need — tree-shaking handles the rest.
40
+
41
+ ## What It Provides
42
+
43
+ - **Rendering** — Sprites, animated sprites, camera follow/zoom/bounds, z-ordering, screen shake (PixiJS v8)
44
+ - **Physics** — Rigid body, collider shapes, collision layers, triggers, raycasting (Matter.js)
45
+ - **Input** — Named axes and buttons, raw key/mouse access, smoothed variants
46
+ - **Audio** — Music, SFX, spatial audio, browser autoplay handling (Howler.js)
47
+ - **Math** — vec2 operations (add, sub, normalize, dot, cross, lerp, rotate, distance)
48
+ - **Game Loop** — Fixed timestep physics (60Hz), variable render, time scaling, coroutines
49
+
50
+ ## What It Does NOT Provide
51
+
52
+ - Scene format or scene loader
53
+ - GameObject/Component/Behavior hierarchy
54
+ - Editor panels or inspector
55
+ - Entity-component framework
56
+
57
+ Games build their own architecture. The game code IS the scene.
58
+
59
+ ## Architecture — The Sandwich Model
60
+
61
+ ```
62
+ ┌─────────────────────────────────────────────────────────┐
63
+ │ Layer 3: Bonk Overlay (game-agnostic dev tools) │
64
+ │ Debug wireframes, performance overlays, build targets │
65
+ ├─────────────────────────────────────────────────────────┤
66
+ │ Layer 2: Game Code (your code, game-specific) │
67
+ │ Whatever architecture THIS game needs │
68
+ ├─────────────────────────────────────────────────────────┤
69
+ │ Layer 1: Bonk Runtime (game-agnostic tools) │
70
+ │ Rendering, physics, input, audio, math, camera, UI │
71
+ └─────────────────────────────────────────────────────────┘
72
+ ```
73
+
74
+ Layer 1 is the npm package. Layer 2 is whatever your game needs. Layer 3 is optional dev tooling (planned).
75
+
76
+ ## Commands
77
+
78
+ ```bash
79
+ npm run dev # Hot-reload dev server (port 3000)
80
+ npm run build # Library build (ESM + declarations → dist/)
81
+ npm run build:watch # Library build with file watching
82
+ npm run typecheck # Type check only
83
+ ```
84
+
85
+ ## Documentation
86
+
87
+ - [CLAUDE.md](./CLAUDE.md) — AI collaboration context, new game setup recipe, npm link workflow
88
+ - [docs/ARCHITECTURE.md](./docs/ARCHITECTURE.md) — Full architecture
89
+ - [docs/CAMERA.md](./docs/CAMERA.md) — Camera system
90
+ - [docs/PHYSICS.md](./docs/PHYSICS.md) — Physics and collision
91
+ - [docs/INPUT.md](./docs/INPUT.md) — Input system
92
+ - [docs/AUDIO-SYSTEM.md](./docs/AUDIO-SYSTEM.md) — Audio
93
+ - [docs/EVENTS.md](./docs/EVENTS.md) — Event system
94
+ - [docs/UI-SYSTEM.md](./docs/UI-SYSTEM.md) — UI system
@@ -0,0 +1,105 @@
1
+ import { Howl } from 'howler';
2
+ /** Volume categories for audio */
3
+ export type VolumeCategory = 'master' | 'music' | 'sfx';
4
+ /** Audio events emitted by the system */
5
+ export declare const AudioEvents: {
6
+ /** Fired when audio context is unlocked by user interaction */
7
+ readonly UNLOCKED: "audio:unlocked";
8
+ /** Fired when a volume category changes */
9
+ readonly VOLUME_CHANGED: "audio:volume:changed";
10
+ };
11
+ declare class AudioManagerImpl {
12
+ /** Cache of loaded sounds by path */
13
+ private cache;
14
+ /** Volume levels for each category */
15
+ private volumes;
16
+ /** Whether audio context has been unlocked */
17
+ private unlocked;
18
+ /** Callbacks waiting for unlock */
19
+ private unlockCallbacks;
20
+ /** Whether init has been called */
21
+ private initialized;
22
+ /**
23
+ * Initialize the audio manager.
24
+ * Sets up browser autoplay unlock listeners.
25
+ * Safe to call multiple times.
26
+ */
27
+ init(): void;
28
+ /** Handle successful audio unlock */
29
+ private handleUnlock;
30
+ /**
31
+ * Set volume for a category.
32
+ * @param category - The volume category
33
+ * @param value - Volume level (0-1)
34
+ */
35
+ setVolume(category: VolumeCategory, value: number): void;
36
+ /**
37
+ * Get volume for a category.
38
+ * @param category - The volume category
39
+ * @returns Volume level (0-1)
40
+ */
41
+ getVolume(category: VolumeCategory): number;
42
+ /**
43
+ * Get effective volume for a category (master * category).
44
+ * @param category - The volume category
45
+ * @returns Effective volume level (0-1)
46
+ */
47
+ getEffectiveVolume(category: VolumeCategory): number;
48
+ /**
49
+ * Preload audio files.
50
+ * @param paths - Array of audio file paths to preload
51
+ * @returns Promise that resolves when all files are loaded
52
+ */
53
+ preload(paths: string[]): Promise<void>;
54
+ /**
55
+ * Get or load a sound (async).
56
+ * @param path - Path to the audio file
57
+ * @returns Promise that resolves to the Howl instance
58
+ */
59
+ getSound(path: string): Promise<Howl>;
60
+ /**
61
+ * Get cached sound (sync).
62
+ * Returns null if not loaded.
63
+ * @param path - Path to the audio file
64
+ * @returns Howl instance or null
65
+ */
66
+ getSoundSync(path: string): Howl | null;
67
+ /**
68
+ * Check if audio context is unlocked.
69
+ * @returns True if audio can play without user interaction
70
+ */
71
+ isUnlocked(): boolean;
72
+ /**
73
+ * Register a callback for when audio is unlocked.
74
+ * Calls immediately if already unlocked.
75
+ * @param callback - Function to call when unlocked
76
+ */
77
+ onUnlock(callback: () => void): void;
78
+ /**
79
+ * Unload a specific sound from cache.
80
+ * @param path - Path to the audio file
81
+ */
82
+ unload(path: string): void;
83
+ /**
84
+ * Destroy and cleanup all sounds.
85
+ */
86
+ destroy(): void;
87
+ /**
88
+ * Pause all sounds.
89
+ */
90
+ pauseAll(): void;
91
+ /**
92
+ * Resume all sounds that were playing.
93
+ */
94
+ resumeAll(): void;
95
+ /**
96
+ * Stop all sounds.
97
+ */
98
+ stopAll(): void;
99
+ }
100
+ /** Global AudioManager singleton */
101
+ export declare const AudioManager: AudioManagerImpl;
102
+ /** Convenience function to get the AudioManager */
103
+ export declare function getAudioManager(): AudioManagerImpl;
104
+ export {};
105
+ //# sourceMappingURL=AudioManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioManager.d.ts","sourceRoot":"","sources":["../../src/audio/AudioManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,IAAI,EAAU,MAAM,QAAQ,CAAC;AAGtC,kCAAkC;AAClC,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,KAAK,CAAC;AAExD,yCAAyC;AACzC,eAAO,MAAM,WAAW;IACtB,+DAA+D;;IAE/D,2CAA2C;;CAEnC,CAAC;AAEX,cAAM,gBAAgB;IACpB,qCAAqC;IACrC,OAAO,CAAC,KAAK,CAA2B;IAExC,sCAAsC;IACtC,OAAO,CAAC,OAAO,CAIb;IAEF,8CAA8C;IAC9C,OAAO,CAAC,QAAQ,CAAS;IAEzB,mCAAmC;IACnC,OAAO,CAAC,eAAe,CAAyB;IAEhD,mCAAmC;IACnC,OAAO,CAAC,WAAW,CAAS;IAE5B;;;;OAIG;IACH,IAAI,IAAI,IAAI;IAmCZ,qCAAqC;IACrC,OAAO,CAAC,YAAY;IAiBpB;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAWxD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM;IAI3C;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM;IAOpD;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7C;;;;OAIG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB3C;;;;;OAKG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIvC;;;OAGG;IACH,UAAU,IAAI,OAAO;IAIrB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAQpC;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAQ1B;;OAEG;IACH,OAAO,IAAI,IAAI;IAQf;;OAEG;IACH,QAAQ,IAAI,IAAI;IAMhB;;OAEG;IACH,SAAS,IAAI,IAAI;IAajB;;OAEG;IACH,OAAO,IAAI,IAAI;CAKhB;AAED,oCAAoC;AACpC,eAAO,MAAM,YAAY,kBAAyB,CAAC;AAEnD,mDAAmD;AACnD,wBAAgB,eAAe,IAAI,gBAAgB,CAElD"}
@@ -0,0 +1,69 @@
1
+ import { Renderer } from '../render/Renderer';
2
+ import { Transform } from '../runtime/Transform';
3
+ /** Configuration for creating an AudioSource */
4
+ export interface AudioSourceConfig {
5
+ /** Audio file source path */
6
+ src?: string;
7
+ /** Base volume (0-1) */
8
+ volume?: number;
9
+ /** Whether to loop playback */
10
+ loop?: boolean;
11
+ /** Volume category */
12
+ category?: 'music' | 'sfx';
13
+ /** Enable spatial audio */
14
+ spatial?: boolean;
15
+ /** Minimum distance for full volume (spatial) */
16
+ minDistance?: number;
17
+ /** Maximum distance before silent (spatial) */
18
+ maxDistance?: number;
19
+ }
20
+ export declare class AudioSource {
21
+ /** Audio file source path */
22
+ src: string;
23
+ /** Base volume (0-1) */
24
+ volume: number;
25
+ /** Whether to loop */
26
+ loop: boolean;
27
+ /** Volume category */
28
+ category: 'music' | 'sfx';
29
+ /** Spatial audio enabled */
30
+ spatial: boolean;
31
+ /** Minimum distance for full volume */
32
+ minDistance: number;
33
+ /** Maximum distance before silent */
34
+ maxDistance: number;
35
+ private sound;
36
+ private soundId;
37
+ private effectiveVolume;
38
+ private _transform;
39
+ constructor(config?: AudioSourceConfig);
40
+ /** Set the transform for spatial audio positioning. */
41
+ setTransform(transform: Transform): void;
42
+ /** Load the audio file. Call before play(). */
43
+ load(): Promise<void>;
44
+ /** Play the sound. */
45
+ play(): void;
46
+ /** Pause playback. */
47
+ pause(): void;
48
+ /** Stop playback and reset. */
49
+ stop(): void;
50
+ /** Resume paused playback. */
51
+ resume(): void;
52
+ /** Seek to a specific time in seconds. */
53
+ seek(time: number): void;
54
+ /** Fade volume over time. */
55
+ fade(from: number, to: number, durationMs: number): void;
56
+ /** Check if currently playing. */
57
+ get playing(): boolean;
58
+ /** Get current playback time in seconds. */
59
+ getTime(): number;
60
+ /** Get total duration in seconds. */
61
+ getDuration(): number;
62
+ /** Play a one-shot sound. */
63
+ playOneShot(path?: string): Promise<void>;
64
+ /** Update spatial audio (call each frame if spatial is enabled). */
65
+ updateSpatialAudio(renderer: Renderer | null): void;
66
+ /** Destroy and clean up. */
67
+ destroy(): void;
68
+ }
69
+ //# sourceMappingURL=AudioSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioSource.d.ts","sourceRoot":"","sources":["../../src/audio/AudioSource.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,gDAAgD;AAChD,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B,2BAA2B;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,WAAW;IACtB,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IAEZ,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;IAEf,sBAAsB;IACtB,IAAI,EAAE,OAAO,CAAC;IAEd,sBAAsB;IACtB,QAAQ,EAAE,OAAO,GAAG,KAAK,CAAC;IAE1B,4BAA4B;IAC5B,OAAO,EAAE,OAAO,CAAC;IAEjB,uCAAuC;IACvC,WAAW,EAAE,MAAM,CAAC;IAEpB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IAEpB,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,UAAU,CAA0B;gBAEhC,MAAM,CAAC,EAAE,iBAAiB;IAUtC,uDAAuD;IACvD,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAIxC,+CAA+C;IACzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,sBAAsB;IACtB,IAAI,IAAI,IAAI;IAoBZ,sBAAsB;IACtB,KAAK,IAAI,IAAI;IAMb,+BAA+B;IAC/B,IAAI,IAAI,IAAI;IAOZ,8BAA8B;IAC9B,MAAM,IAAI,IAAI;IAMd,0CAA0C;IAC1C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAMxB,6BAA6B;IAC7B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAQxD,kCAAkC;IAClC,IAAI,OAAO,IAAI,OAAO,CAGrB;IAED,4CAA4C;IAC5C,OAAO,IAAI,MAAM;IAMjB,qCAAqC;IACrC,WAAW,IAAI,MAAM;IAKrB,6BAA6B;IACvB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc/C,oEAAoE;IACpE,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,IAAI;IA4BnD,4BAA4B;IAC5B,OAAO,IAAI,IAAI;CAIhB"}
@@ -0,0 +1,3 @@
1
+ export { AudioManager, getAudioManager, AudioEvents, type VolumeCategory, } from './AudioManager';
2
+ export { AudioSource, type AudioSourceConfig } from './AudioSource';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/audio/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,eAAe,EACf,WAAW,EACX,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC"}