@ksteinstudio/game-controller 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ksteinstudio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,348 @@
1
+ # Universal Game Controller Engine
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@ksteinstudio/game-controller)](https://www.npmjs.com/package/@ksteinstudio/game-controller)
4
+ [![license](https://img.shields.io/npm/l/@ksteinstudio/game-controller)](LICENSE)
5
+
6
+ A lightweight, zero-dependency engine for rendering interactive virtual game controllers via JSON configuration. Renders inside an `<iframe>` and communicates with the parent window through `postMessage`. Perfect for mobile web games, browser-based emulators, and remote control interfaces.
7
+
8
+ **Key features:**
9
+ - Percentage-based positioning (0–100) for resolution independence
10
+ - Multi-touch support via PointerEvents API
11
+ - JSON-driven layout — define controllers as data
12
+ - Iframe-sandboxed renderer — secure and isolated
13
+ - GPU-accelerated CSS transforms for low-latency input
14
+ - Zero runtime dependencies
15
+ - TypeScript-first with full type exports
16
+
17
+ ---
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ # npm
23
+ npm install @ksteinstudio/game-controller
24
+
25
+ # yarn
26
+ yarn add @ksteinstudio/game-controller
27
+
28
+ # pnpm
29
+ pnpm add @ksteinstudio/game-controller
30
+ ```
31
+
32
+ - Works with any framework: React, Vue, Svelte, Angular, or vanilla JS/TS
33
+ - Supports ESM and CommonJS
34
+ - TypeScript types included out of the box
35
+ - Browser-only (requires DOM APIs)
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ### 1. Define a Controller Layout (JSON)
42
+
43
+ ```json
44
+ {
45
+ "version": "1.0.0",
46
+ "name": "My-Layout",
47
+ "canvas": {
48
+ "aspectRatio": "16:9",
49
+ "backgroundColor": "rgba(0,0,0,0.5)"
50
+ },
51
+ "elements": [
52
+ {
53
+ "id": "left-stick",
54
+ "type": "joystick",
55
+ "position": { "x": 15, "y": 60 },
56
+ "zIndex": 1,
57
+ "radius": 12,
58
+ "innerStickSize": 5,
59
+ "deadzone": 0.1,
60
+ "mode": "static",
61
+ "style": { "color": "rgba(85, 85, 85, 0.6)" }
62
+ },
63
+ {
64
+ "id": "btn-a",
65
+ "type": "button",
66
+ "position": { "x": 85, "y": 65 },
67
+ "zIndex": 1,
68
+ "shape": "circle",
69
+ "size": 8,
70
+ "label": "A",
71
+ "actionKey": "JOY_A",
72
+ "style": { "color": "#4CAF50" }
73
+ }
74
+ ]
75
+ }
76
+ ```
77
+
78
+ ### 2. Embed with the SDK
79
+
80
+ ```typescript
81
+ import { createControllerSDK } from '@ksteinstudio/game-controller';
82
+ import layout from './controller.json';
83
+
84
+ const controller = createControllerSDK({
85
+ config: layout,
86
+ container: document.getElementById('controller-container'),
87
+ onInput: (event) => {
88
+ console.log('Input:', event.type, event.payload);
89
+ },
90
+ onReady: () => {
91
+ console.log('Controller is ready');
92
+ },
93
+ });
94
+
95
+ // Update the layout dynamically
96
+ controller.updateConfig(newLayout);
97
+
98
+ // Clean up
99
+ controller.destroy();
100
+ ```
101
+
102
+ ---
103
+
104
+ ## API Reference
105
+
106
+ ### `createControllerSDK(options)`
107
+
108
+ Creates and embeds a controller inside a container element.
109
+
110
+ | Option | Type | Required | Description |
111
+ |--------|------|----------|-------------|
112
+ | `config` | `ControllerConfig` | ✅ | The JSON layout configuration |
113
+ | `container` | `HTMLElement` | ✅ | DOM element to mount the iframe into |
114
+ | `iframeSrc` | `string` | ❌ | Custom URL for the renderer (uses embedded renderer by default) |
115
+ | `onInput` | `(event: InputEvent) => void` | ❌ | Callback for all input events |
116
+ | `onReady` | `() => void` | ❌ | Called when the renderer is initialized |
117
+ | `width` | `string` | ❌ | CSS width for the iframe (default: `100%`) |
118
+ | `height` | `string` | ❌ | CSS height for the iframe (default: `100%`) |
119
+
120
+ **Returns** `ControllerSDKInstance`:
121
+
122
+ | Method | Description |
123
+ |--------|-------------|
124
+ | `updateConfig(config)` | Send a new layout to the renderer |
125
+ | `destroy()` | Remove the iframe and clean up listeners |
126
+ | `getIframe()` | Access the underlying HTMLIFrameElement |
127
+
128
+ ---
129
+
130
+ ## Type Definitions
131
+
132
+ ### Position
133
+
134
+ All positions use a **percentage-based coordinate system** (0–100) for resolution independence.
135
+
136
+ ```typescript
137
+ interface Position {
138
+ x: number; // 0 to 100 (% of canvas width)
139
+ y: number; // 0 to 100 (% of canvas height)
140
+ }
141
+ ```
142
+
143
+ ### Element Types
144
+
145
+ | Type | Description | Key Properties |
146
+ |------|-------------|----------------|
147
+ | `button` | Pressable button (circle or square) | `shape`, `size`, `label`, `actionKey` |
148
+ | `joystick` | Analog stick with normalized output | `radius`, `innerStickSize`, `deadzone`, `mode` |
149
+ | `dpad` | 4-directional pad | `size`, `actionKeys` |
150
+ | `slider` | Linear input control | `length`, `orientation`, `actionKey` |
151
+
152
+ ### Events Emitted
153
+
154
+ | Event | Payload | When |
155
+ |-------|---------|------|
156
+ | `INPUT_START` | `{ elementId, actionKey, timestamp }` | Button pressed |
157
+ | `INPUT_END` | `{ elementId, actionKey, timestamp }` | Button released |
158
+ | `JOYSTICK_MOVE` | `{ elementId, vector: {x,y}, angle, magnitude, timestamp }` | Joystick moves (vector normalized -1 to 1) |
159
+ | `DPAD_PRESS` | `{ elementId, direction, actionKey, timestamp }` | DPad direction pressed |
160
+ | `DPAD_RELEASE` | `{ elementId, direction, actionKey, timestamp }` | DPad direction released |
161
+
162
+ ---
163
+
164
+ ## Math Utilities
165
+
166
+ Exported utilities for building custom layout editors or tools.
167
+
168
+ ### Snap-to-Grid
169
+
170
+ ```typescript
171
+ import { snapToGrid, snapPositionToGrid } from '@ksteinstudio/game-controller';
172
+
173
+ const snapped = snapToGrid(37, 20); // → 35
174
+ const pos = snapPositionToGrid({ x: 37.2, y: 62.8 }, 20); // → { x: 35, y: 65 }
175
+ ```
176
+
177
+ ### Coordinate Conversion
178
+
179
+ ```typescript
180
+ import { pixelToPercentage, percentageToPixel } from '@ksteinstudio/game-controller';
181
+
182
+ const pct = pixelToPercentage(320, 1920); // → 16.67
183
+ const px = percentageToPixel(50, 1080); // → 540
184
+ ```
185
+
186
+ ### Alignment Guides
187
+
188
+ ```typescript
189
+ import { findAlignmentGuides } from '@ksteinstudio/game-controller';
190
+
191
+ const result = findAlignmentGuides(draggedElement, cursorPosition, otherElements);
192
+ // result.position → snapped position
193
+ // result.guides → array of active alignment lines
194
+ ```
195
+
196
+ ---
197
+
198
+ ## Framework Examples
199
+
200
+ ### React
201
+
202
+ ```tsx
203
+ import { useEffect, useRef } from 'react';
204
+ import { createControllerSDK, ControllerConfig, InputEvent } from '@ksteinstudio/game-controller';
205
+
206
+ const layout: ControllerConfig = {
207
+ version: '1.0.0',
208
+ name: 'react-controller',
209
+ canvas: { aspectRatio: '16:9', backgroundColor: 'rgba(0,0,0,0.4)' },
210
+ elements: [
211
+ {
212
+ id: 'stick', type: 'joystick', position: { x: 20, y: 65 },
213
+ zIndex: 1, radius: 12, innerStickSize: 5, deadzone: 0.1, mode: 'static',
214
+ style: { color: 'rgba(80,80,80,0.6)' },
215
+ },
216
+ {
217
+ id: 'btn-a', type: 'button', position: { x: 85, y: 60 },
218
+ zIndex: 1, shape: 'circle', size: 10, label: 'A', actionKey: 'JUMP',
219
+ style: { color: '#4CAF50' },
220
+ },
221
+ ],
222
+ };
223
+
224
+ function GameController() {
225
+ const containerRef = useRef<HTMLDivElement>(null);
226
+
227
+ useEffect(() => {
228
+ if (!containerRef.current) return;
229
+
230
+ const sdk = createControllerSDK({
231
+ config: layout,
232
+ container: containerRef.current,
233
+ onInput: (event: InputEvent) => {
234
+ console.log(event.type, event.payload);
235
+ },
236
+ });
237
+
238
+ return () => sdk.destroy();
239
+ }, []);
240
+
241
+ return <div ref={containerRef} style={{ width: '100%', height: '300px' }} />;
242
+ }
243
+ ```
244
+
245
+ ### Vue 3
246
+
247
+ ```vue
248
+ <template>
249
+ <div ref="controllerRef" style="width: 100%; height: 300px" />
250
+ </template>
251
+
252
+ <script setup lang="ts">
253
+ import { ref, onMounted, onUnmounted } from 'vue';
254
+ import { createControllerSDK, type ControllerSDKInstance } from '@ksteinstudio/game-controller';
255
+
256
+ const controllerRef = ref<HTMLDivElement>();
257
+ let sdk: ControllerSDKInstance;
258
+
259
+ onMounted(() => {
260
+ if (!controllerRef.value) return;
261
+
262
+ sdk = createControllerSDK({
263
+ config: {
264
+ version: '1.0.0',
265
+ name: 'vue-controller',
266
+ canvas: { aspectRatio: '16:9' },
267
+ elements: [
268
+ {
269
+ id: 'joy', type: 'joystick', position: { x: 20, y: 65 },
270
+ zIndex: 1, radius: 12, innerStickSize: 5, deadzone: 0.1, mode: 'static',
271
+ },
272
+ ],
273
+ },
274
+ container: controllerRef.value,
275
+ onInput: (event) => console.log(event),
276
+ });
277
+ });
278
+
279
+ onUnmounted(() => sdk?.destroy());
280
+ </script>
281
+ ```
282
+
283
+ ### Vanilla JavaScript (No Bundler)
284
+
285
+ ```html
286
+ <div id="controller" style="width: 100%; height: 400px;"></div>
287
+
288
+ <script type="module">
289
+ import { createControllerSDK } from 'https://esm.sh/@ksteinstudio/game-controller';
290
+
291
+ createControllerSDK({
292
+ config: {
293
+ version: '1.0.0',
294
+ name: 'vanilla-layout',
295
+ canvas: { aspectRatio: '16:9', backgroundColor: 'rgba(0,0,0,0.3)' },
296
+ elements: [
297
+ {
298
+ id: 'dpad', type: 'dpad', position: { x: 15, y: 50 },
299
+ zIndex: 1, size: 15, style: { color: '#333' },
300
+ },
301
+ {
302
+ id: 'btn-a', type: 'button', position: { x: 85, y: 55 },
303
+ zIndex: 1, shape: 'circle', size: 10, label: 'A', actionKey: 'A',
304
+ style: { color: '#4CAF50' },
305
+ },
306
+ ],
307
+ },
308
+ container: document.getElementById('controller'),
309
+ onInput: (e) => console.log(e.type, e.payload),
310
+ });
311
+ </script>
312
+ ```
313
+
314
+ ---
315
+
316
+ ## All Exports
317
+
318
+ ```typescript
319
+ import { createControllerSDK } from '@ksteinstudio/game-controller';
320
+
321
+ import type {
322
+ ControllerConfig,
323
+ ButtonElement,
324
+ JoystickElement,
325
+ DpadElement,
326
+ InputEvent,
327
+ Position,
328
+ } from '@ksteinstudio/game-controller';
329
+
330
+ import {
331
+ snapToGrid,
332
+ snapPositionToGrid,
333
+ pixelToPercentage,
334
+ percentageToPixel,
335
+ findAlignmentGuides,
336
+ calculateCanvasDimensions,
337
+ } from '@ksteinstudio/game-controller';
338
+
339
+ import { createParentBridge, createIframeBridge } from '@ksteinstudio/game-controller';
340
+
341
+ import { renderControllerFromConfig, destroyRenderer } from '@ksteinstudio/game-controller';
342
+ ```
343
+
344
+ ---
345
+
346
+ ## License
347
+
348
+ [MIT](LICENSE) © Ksteinstudio
@@ -0,0 +1,272 @@
1
+ interface Position {
2
+ x: number;
3
+ y: number;
4
+ }
5
+ interface ElementStyle {
6
+ color?: string;
7
+ opacity?: number;
8
+ scale?: number;
9
+ borderColor?: string;
10
+ borderWidth?: number;
11
+ fontSize?: number;
12
+ fontFamily?: string;
13
+ }
14
+ interface BaseElement {
15
+ id: string;
16
+ type: 'button' | 'joystick' | 'dpad' | 'slider';
17
+ position: Position;
18
+ zIndex: number;
19
+ style?: ElementStyle;
20
+ }
21
+ interface ButtonElement extends BaseElement {
22
+ type: 'button';
23
+ label?: string;
24
+ shape: 'circle' | 'square';
25
+ size: number;
26
+ actionKey: string;
27
+ }
28
+ interface JoystickElement extends BaseElement {
29
+ type: 'joystick';
30
+ radius: number;
31
+ innerStickSize: number;
32
+ deadzone: number;
33
+ mode: 'static' | 'floating';
34
+ }
35
+ interface DpadElement extends BaseElement {
36
+ type: 'dpad';
37
+ size: number;
38
+ actionKeys?: {
39
+ up: string;
40
+ down: string;
41
+ left: string;
42
+ right: string;
43
+ };
44
+ }
45
+ interface SliderElement extends BaseElement {
46
+ type: 'slider';
47
+ length: number;
48
+ orientation: 'horizontal' | 'vertical';
49
+ actionKey: string;
50
+ }
51
+ type ControllerElement = ButtonElement | JoystickElement | DpadElement | SliderElement;
52
+ interface CanvasSettings {
53
+ aspectRatio: string;
54
+ backgroundColor?: string;
55
+ gridDensity?: number;
56
+ }
57
+ interface ControllerConfig {
58
+ version: string;
59
+ name: string;
60
+ canvas: CanvasSettings;
61
+ elements: ControllerElement[];
62
+ }
63
+
64
+ declare const enum MessageType {
65
+ CONFIG_LOAD = "CONFIG_LOAD",
66
+ INPUT_START = "INPUT_START",
67
+ INPUT_END = "INPUT_END",
68
+ JOYSTICK_MOVE = "JOYSTICK_MOVE",
69
+ DPAD_PRESS = "DPAD_PRESS",
70
+ DPAD_RELEASE = "DPAD_RELEASE",
71
+ SLIDER_CHANGE = "SLIDER_CHANGE",
72
+ RENDERER_READY = "RENDERER_READY",
73
+ CONFIG_UPDATE = "CONFIG_UPDATE"
74
+ }
75
+ interface Vector2D {
76
+ x: number;
77
+ y: number;
78
+ }
79
+ interface ConfigLoadMessage {
80
+ type: MessageType.CONFIG_LOAD;
81
+ payload: ControllerConfig;
82
+ }
83
+ interface InputStartMessage {
84
+ type: MessageType.INPUT_START;
85
+ payload: {
86
+ elementId: string;
87
+ actionKey: string;
88
+ timestamp: number;
89
+ };
90
+ }
91
+ interface InputEndMessage {
92
+ type: MessageType.INPUT_END;
93
+ payload: {
94
+ elementId: string;
95
+ actionKey: string;
96
+ timestamp: number;
97
+ };
98
+ }
99
+ interface JoystickMoveMessage {
100
+ type: MessageType.JOYSTICK_MOVE;
101
+ payload: {
102
+ elementId: string;
103
+ vector: Vector2D;
104
+ angle: number;
105
+ magnitude: number;
106
+ timestamp: number;
107
+ };
108
+ }
109
+ interface DpadPressMessage {
110
+ type: MessageType.DPAD_PRESS;
111
+ payload: {
112
+ elementId: string;
113
+ direction: 'up' | 'down' | 'left' | 'right';
114
+ actionKey: string;
115
+ timestamp: number;
116
+ };
117
+ }
118
+ interface DpadReleaseMessage {
119
+ type: MessageType.DPAD_RELEASE;
120
+ payload: {
121
+ elementId: string;
122
+ direction: 'up' | 'down' | 'left' | 'right';
123
+ actionKey: string;
124
+ timestamp: number;
125
+ };
126
+ }
127
+ interface SliderChangeMessage {
128
+ type: MessageType.SLIDER_CHANGE;
129
+ payload: {
130
+ elementId: string;
131
+ actionKey: string;
132
+ value: number;
133
+ timestamp: number;
134
+ };
135
+ }
136
+ interface RendererReadyMessage {
137
+ type: MessageType.RENDERER_READY;
138
+ }
139
+ interface ConfigUpdateMessage {
140
+ type: MessageType.CONFIG_UPDATE;
141
+ payload: ControllerConfig;
142
+ }
143
+ type ControllerMessage = ConfigLoadMessage | InputStartMessage | InputEndMessage | JoystickMoveMessage | DpadPressMessage | DpadReleaseMessage | SliderChangeMessage | RendererReadyMessage | ConfigUpdateMessage;
144
+ type InputEvent = InputStartMessage | InputEndMessage | JoystickMoveMessage | DpadPressMessage | DpadReleaseMessage | SliderChangeMessage;
145
+
146
+ type MessageHandler = (message: ControllerMessage) => void;
147
+ declare function createParentBridge(iframe: HTMLIFrameElement): {
148
+ sendConfig(config: ControllerConfig): void;
149
+ updateConfig(config: ControllerConfig): void;
150
+ onMessage(handler: MessageHandler): () => boolean;
151
+ onInput(handler: (event: InputEvent) => void): () => boolean;
152
+ destroy(): void;
153
+ };
154
+ declare function createIframeBridge(): {
155
+ emitControllerEvent(message: ControllerMessage): void;
156
+ emitInputStart(elementId: string, actionKey: string): void;
157
+ emitInputEnd(elementId: string, actionKey: string): void;
158
+ emitJoystickMove(elementId: string, x: number, y: number, angle: number, magnitude: number): void;
159
+ emitDpadPress(elementId: string, direction: "up" | "down" | "left" | "right", actionKey: string): void;
160
+ emitDpadRelease(elementId: string, direction: "up" | "down" | "left" | "right", actionKey: string): void;
161
+ emitSliderChange(elementId: string, actionKey: string, value: number): void;
162
+ signalReady(): void;
163
+ onMessage(handler: MessageHandler): () => boolean;
164
+ destroy(): void;
165
+ };
166
+ type ParentBridge = ReturnType<typeof createParentBridge>;
167
+ type IframeBridge = ReturnType<typeof createIframeBridge>;
168
+
169
+ declare function snapToGrid(value: number, gridDensity: number): number;
170
+ declare function snapPositionToGrid(position: Position, gridDensity: number): Position;
171
+ declare function pixelToPercentage(pixelValue: number, containerSize: number): number;
172
+ declare function percentageToPixel(percentageValue: number, containerSize: number): number;
173
+ declare function convertPixelPositionToPercentage(pixelX: number, pixelY: number, containerWidth: number, containerHeight: number): Position;
174
+ declare function convertPercentagePositionToPixel(position: Position, containerWidth: number, containerHeight: number): {
175
+ x: number;
176
+ y: number;
177
+ };
178
+ declare function clampPercentage(value: number): number;
179
+ declare function clamp(value: number, min: number, max: number): number;
180
+ declare function distance(a: Position, b: Position): number;
181
+ declare function normalizeVector(x: number, y: number): {
182
+ x: number;
183
+ y: number;
184
+ magnitude: number;
185
+ };
186
+ declare function angleFromVector(x: number, y: number): number;
187
+ declare function applyDeadzone(value: number, deadzone: number): number;
188
+ declare function applyDeadzoneToVector(x: number, y: number, deadzone: number): {
189
+ x: number;
190
+ y: number;
191
+ };
192
+ declare function parseAspectRatio(aspectRatio: string): {
193
+ width: number;
194
+ height: number;
195
+ };
196
+ declare function calculateCanvasDimensions(containerWidth: number, containerHeight: number, aspectRatio: string): {
197
+ width: number;
198
+ height: number;
199
+ offsetX: number;
200
+ offsetY: number;
201
+ };
202
+
203
+ interface AlignmentGuide {
204
+ axis: 'horizontal' | 'vertical';
205
+ position: number;
206
+ sourceElementId: string;
207
+ targetElementId: string;
208
+ }
209
+ interface SnapResult {
210
+ position: Position;
211
+ guides: AlignmentGuide[];
212
+ }
213
+ declare function findAlignmentGuides(draggedElement: ControllerElement, dragPosition: Position, otherElements: ControllerElement[], threshold?: number): SnapResult;
214
+ declare function generateGridLines(gridDensity: number): number[];
215
+
216
+ interface ButtonRenderContext {
217
+ element: ButtonElement;
218
+ canvasWidth: number;
219
+ canvasHeight: number;
220
+ onInputStart: (elementId: string, actionKey: string) => void;
221
+ onInputEnd: (elementId: string, actionKey: string) => void;
222
+ }
223
+ declare function createButtonElement(context: ButtonRenderContext): HTMLElement;
224
+ declare function updateButtonElement(container: HTMLElement, element: ButtonElement, canvasWidth: number, canvasHeight: number): void;
225
+
226
+ interface JoystickRenderContext {
227
+ element: JoystickElement;
228
+ canvasWidth: number;
229
+ canvasHeight: number;
230
+ onJoystickMove: (elementId: string, vector: Vector2D, angle: number, magnitude: number) => void;
231
+ }
232
+ declare function createJoystickElement(context: JoystickRenderContext): HTMLElement;
233
+ declare function updateJoystickElement(container: HTMLElement, element: JoystickElement, canvasWidth: number, canvasHeight: number): void;
234
+
235
+ interface DpadRenderContext {
236
+ element: DpadElement;
237
+ canvasWidth: number;
238
+ canvasHeight: number;
239
+ onDpadPress: (elementId: string, direction: 'up' | 'down' | 'left' | 'right', actionKey: string) => void;
240
+ onDpadRelease: (elementId: string, direction: 'up' | 'down' | 'left' | 'right', actionKey: string) => void;
241
+ }
242
+ declare function createDpadElement(context: DpadRenderContext): HTMLElement;
243
+ declare function updateDpadElement(container: HTMLElement, element: DpadElement, canvasWidth: number, canvasHeight: number): void;
244
+
245
+ interface RendererState {
246
+ config: ControllerConfig | null;
247
+ canvas: HTMLElement | null;
248
+ elementMap: Map<string, HTMLElement>;
249
+ bridge: IframeBridge;
250
+ resizeObserver: ResizeObserver | null;
251
+ }
252
+ declare function initializeRenderer(rootElement: HTMLElement): RendererState;
253
+ declare function renderControllerFromConfig(rootElement: HTMLElement, config: ControllerConfig): RendererState;
254
+ declare function destroyRenderer(state: RendererState): void;
255
+
256
+ interface ControllerSDKOptions {
257
+ config: ControllerConfig;
258
+ container: HTMLElement;
259
+ iframeSrc?: string;
260
+ onInput?: (event: InputEvent) => void;
261
+ onReady?: () => void;
262
+ width?: string;
263
+ height?: string;
264
+ }
265
+ interface ControllerSDKInstance {
266
+ updateConfig: (config: ControllerConfig) => void;
267
+ destroy: () => void;
268
+ getIframe: () => HTMLIFrameElement;
269
+ }
270
+ declare function createControllerSDK(options: ControllerSDKOptions): ControllerSDKInstance;
271
+
272
+ export { type AlignmentGuide, type BaseElement, type ButtonElement, type ButtonRenderContext, type CanvasSettings, type ConfigLoadMessage, type ConfigUpdateMessage, type ControllerConfig, type ControllerElement, type ControllerMessage, type ControllerSDKInstance, type ControllerSDKOptions, type DpadElement, type DpadPressMessage, type DpadReleaseMessage, type DpadRenderContext, type ElementStyle, type IframeBridge, type InputEndMessage, type InputEvent, type InputStartMessage, type JoystickElement, type JoystickMoveMessage, type JoystickRenderContext, MessageType, type ParentBridge, type Position, type RendererReadyMessage, type SliderChangeMessage, type SliderElement, type SnapResult, type Vector2D, angleFromVector, applyDeadzone, applyDeadzoneToVector, calculateCanvasDimensions, clamp, clampPercentage, convertPercentagePositionToPixel, convertPixelPositionToPercentage, createButtonElement, createControllerSDK, createDpadElement, createIframeBridge, createJoystickElement, createParentBridge, destroyRenderer, distance, findAlignmentGuides, generateGridLines, initializeRenderer, normalizeVector, parseAspectRatio, percentageToPixel, pixelToPercentage, renderControllerFromConfig, snapPositionToGrid, snapToGrid, updateButtonElement, updateDpadElement, updateJoystickElement };