spice-html5-react 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.
Files changed (64) hide show
  1. package/README.md +262 -0
  2. package/dist/components/SpiceDisplay.d.ts +109 -0
  3. package/dist/components/SpiceDisplay.d.ts.map +1 -0
  4. package/dist/hooks/useSpice.d.ts +107 -0
  5. package/dist/hooks/useSpice.d.ts.map +1 -0
  6. package/dist/index.cjs +14 -0
  7. package/dist/index.cjs.map +1 -0
  8. package/dist/index.d.ts +8 -0
  9. package/dist/index.d.ts.map +1 -0
  10. package/dist/index.mjs +6745 -0
  11. package/dist/index.mjs.map +1 -0
  12. package/dist/protocol/bitmap.d.ts +20 -0
  13. package/dist/protocol/bitmap.d.ts.map +1 -0
  14. package/dist/protocol/cursor.d.ts +16 -0
  15. package/dist/protocol/cursor.d.ts.map +1 -0
  16. package/dist/protocol/display.d.ts +311 -0
  17. package/dist/protocol/display.d.ts.map +1 -0
  18. package/dist/protocol/enums.d.ts +677 -0
  19. package/dist/protocol/enums.d.ts.map +1 -0
  20. package/dist/protocol/filexfer.d.ts +61 -0
  21. package/dist/protocol/filexfer.d.ts.map +1 -0
  22. package/dist/protocol/index.d.ts +34 -0
  23. package/dist/protocol/index.d.ts.map +1 -0
  24. package/dist/protocol/inputs.d.ts +95 -0
  25. package/dist/protocol/inputs.d.ts.map +1 -0
  26. package/dist/protocol/lz.d.ts +24 -0
  27. package/dist/protocol/lz.d.ts.map +1 -0
  28. package/dist/protocol/main.d.ts +205 -0
  29. package/dist/protocol/main.d.ts.map +1 -0
  30. package/dist/protocol/playback.d.ts +51 -0
  31. package/dist/protocol/playback.d.ts.map +1 -0
  32. package/dist/protocol/png.d.ts +14 -0
  33. package/dist/protocol/png.d.ts.map +1 -0
  34. package/dist/protocol/port.d.ts +46 -0
  35. package/dist/protocol/port.d.ts.map +1 -0
  36. package/dist/protocol/quic.d.ts +105 -0
  37. package/dist/protocol/quic.d.ts.map +1 -0
  38. package/dist/protocol/resize.d.ts +61 -0
  39. package/dist/protocol/resize.d.ts.map +1 -0
  40. package/dist/protocol/simulatecursor.d.ts +49 -0
  41. package/dist/protocol/simulatecursor.d.ts.map +1 -0
  42. package/dist/protocol/spicearraybuffer.d.ts +44 -0
  43. package/dist/protocol/spicearraybuffer.d.ts.map +1 -0
  44. package/dist/protocol/spiceconn.d.ts +118 -0
  45. package/dist/protocol/spiceconn.d.ts.map +1 -0
  46. package/dist/protocol/spicedataview.d.ts +144 -0
  47. package/dist/protocol/spicedataview.d.ts.map +1 -0
  48. package/dist/protocol/spicemsg.d.ts +435 -0
  49. package/dist/protocol/spicemsg.d.ts.map +1 -0
  50. package/dist/protocol/spicetype.d.ts +166 -0
  51. package/dist/protocol/spicetype.d.ts.map +1 -0
  52. package/dist/protocol/ticket.d.ts +14 -0
  53. package/dist/protocol/ticket.d.ts.map +1 -0
  54. package/dist/protocol/webm.d.ts +118 -0
  55. package/dist/protocol/webm.d.ts.map +1 -0
  56. package/dist/protocol/wire.d.ts +81 -0
  57. package/dist/protocol/wire.d.ts.map +1 -0
  58. package/dist/utils/atKeynames.d.ts +96 -0
  59. package/dist/utils/atKeynames.d.ts.map +1 -0
  60. package/dist/utils/codeToScancode.d.ts +2 -0
  61. package/dist/utils/codeToScancode.d.ts.map +1 -0
  62. package/dist/utils/debug.d.ts +84 -0
  63. package/dist/utils/debug.d.ts.map +1 -0
  64. package/package.json +60 -0
package/README.md ADDED
@@ -0,0 +1,262 @@
1
+ # spice-html5-react
2
+
3
+ A React + TypeScript client library for the [SPICE](https://www.spice-space.org) remote desktop protocol. Connects to SPICE servers over WebSocket (via [websockify](https://github.com/novnc/websockify)) and renders the remote display in a browser.
4
+
5
+ ## Features
6
+
7
+ - Drop-in `<SpiceDisplay>` React component with full connection lifecycle management
8
+ - Low-level `useSpice` hook for custom UI and fine-grained control
9
+ - Keyboard, mouse, and scroll input forwarding
10
+ - Clipboard sharing between host and guest
11
+ - File transfer via drag-and-drop
12
+ - Audio playback (Opus codec)
13
+ - Dynamic guest display resizing
14
+ - SPICE ticket (RSA) authentication
15
+ - SSR-safe (guards against `document`/`WebSocket` in server environments)
16
+ - Ships as ESM, CJS, and TypeScript declarations
17
+
18
+ ## Requirements
19
+
20
+ - React 18+
21
+ - A running SPICE server (e.g. QEMU with `-spice` flag)
22
+ - A WebSocket proxy such as [websockify](https://github.com/novnc/websockify) bridging WebSocket traffic to the SPICE server's TCP port
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install spice-html5-react
28
+ ```
29
+
30
+ Peer dependencies: `react` and `react-dom` (>= 18.0.0).
31
+
32
+ ## Quick Start
33
+
34
+ ### `<SpiceDisplay>` Component
35
+
36
+ The simplest way to embed a SPICE session:
37
+
38
+ ```tsx
39
+ import { useRef } from 'react';
40
+ import { SpiceDisplay } from 'spice-html5-react';
41
+ import type { SpiceDisplayHandle } from 'spice-html5-react';
42
+
43
+ function RemoteDesktop() {
44
+ const displayRef = useRef<SpiceDisplayHandle>(null);
45
+
46
+ return (
47
+ <SpiceDisplay
48
+ ref={displayRef}
49
+ connection={{
50
+ host: "localhost",
51
+ port: 5959,
52
+ password: "secret",
53
+ }}
54
+ display={{ fitContainer: true }}
55
+ features={{
56
+ enableKeyboard: true,
57
+ enableMouse: true,
58
+ enableClipboard: true,
59
+ enableFileTransfer: true,
60
+ }}
61
+ onConnect={() => console.log("connected")}
62
+ onDisconnect={() => console.log("disconnected")}
63
+ onError={(e) => console.error(e)}
64
+ onResize={(w, h) => console.log(`${w}x${h}`)}
65
+ style={{ width: "100%", height: 600 }}
66
+ />
67
+ );
68
+ }
69
+ ```
70
+
71
+ The imperative handle exposes `sendCtrlAltDel()`, `disconnect()`, `reconnect()`, and `getConnection()`.
72
+
73
+ ### `useSpice` Hook
74
+
75
+ For full control over the connection lifecycle and input events:
76
+
77
+ ```tsx
78
+ import { useEffect, useRef } from 'react';
79
+ import { useSpice } from 'spice-html5-react';
80
+
81
+ function CustomDisplay() {
82
+ const containerRef = useRef<HTMLDivElement>(null);
83
+ const { status, resolution, controls } = useSpice({
84
+ canvasRef: containerRef,
85
+ callbacks: {
86
+ onConnect: () => console.log("connected"),
87
+ onError: (e) => console.error(e),
88
+ },
89
+ });
90
+
91
+ useEffect(() => {
92
+ controls.connect({ uri: "ws://localhost:5959", password: "secret" });
93
+ return () => controls.disconnect();
94
+ }, [controls]);
95
+
96
+ return (
97
+ <div>
98
+ <p>Status: {status}</p>
99
+ {resolution && <p>Resolution: {resolution.width}x{resolution.height}</p>}
100
+ <div ref={containerRef} style={{ width: "100%", height: 600 }} />
101
+ <button onClick={() => controls.sendCtrlAltDel()}>Ctrl+Alt+Del</button>
102
+ </div>
103
+ );
104
+ }
105
+ ```
106
+
107
+ The hook returns reactive `status`, `error`, `resolution`, and `surfaces` state, plus a `controls` object with methods for input, clipboard, resolution changes, and connection management.
108
+
109
+ ## SPICE Server Setup
110
+
111
+ 1. Start a SPICE-enabled VM (example using QEMU):
112
+
113
+ ```bash
114
+ qemu-system-x86_64 \
115
+ -spice port=5900,disable-ticketing=on \
116
+ -drive file=disk.qcow2,format=qcow2
117
+ ```
118
+
119
+ 2. Start websockify to proxy WebSocket connections to the SPICE TCP port:
120
+
121
+ ```bash
122
+ websockify 5959 localhost:5900
123
+ ```
124
+
125
+ 3. Point the client at `ws://localhost:5959`.
126
+
127
+ ## Development
128
+
129
+ ```bash
130
+ # Install dependencies
131
+ npm install
132
+
133
+ # Dev server
134
+ npm run dev
135
+
136
+ # Type-check (no emit)
137
+ npm run typecheck
138
+
139
+ # Build library (ESM + CJS + .d.ts)
140
+ npm run build
141
+
142
+ # Run tests
143
+ npm run test
144
+ ```
145
+
146
+ ## Example App
147
+
148
+ A full working example lives in the `example/` directory. It demonstrates how to integrate `<SpiceDisplay>` into a React application with a connection form, status indicator, error handling, and imperative controls.
149
+
150
+ ### What It Shows
151
+
152
+ - Embedding `<SpiceDisplay>` with grouped props (`connection`, `display`, `features`)
153
+ - Using a `ref` to access the imperative handle (`sendCtrlAltDel`, `disconnect`, `reconnect`)
154
+ - Handling lifecycle callbacks (`onConnect`, `onDisconnect`, `onError`, `onResize`)
155
+ - Conditionally mounting/unmounting the display based on user actions
156
+ - Resilient error handling that keeps the component mounted on transient errors
157
+
158
+ ### Running the Example
159
+
160
+ The example links to the parent library via `"file:.."`, so you need to build the library first:
161
+
162
+ ```bash
163
+ # From the repository root
164
+ npm install
165
+ npm run build
166
+
167
+ # Then start the example
168
+ cd example
169
+ npm install
170
+ npm run dev
171
+ ```
172
+
173
+ The Vite dev server starts at `http://localhost:3000`. Enter the host and port matching your websockify proxy (defaults to `localhost:5959`), optionally a password, and click **Connect**.
174
+
175
+ ### Example Structure
176
+
177
+ ```
178
+ example/
179
+ index.html # HTML entry point
180
+ package.json # Dependencies (links to parent library via file:..)
181
+ tsconfig.json # TypeScript config
182
+ vite.config.ts # Vite dev server config
183
+ src/
184
+ main.tsx # React root mount
185
+ App.tsx # Main app — connection form, SpiceDisplay, controls
186
+ ```
187
+
188
+ `App.tsx` is the main file to look at. It wires up connection form state, mounts `<SpiceDisplay>` on connect, and exposes Disconnect, Reconnect, and Ctrl+Alt+Del buttons through the imperative ref.
189
+
190
+ ## API Reference
191
+
192
+ ### `<SpiceDisplay>` Props
193
+
194
+ | Prop | Type | Description |
195
+ |------|------|-------------|
196
+ | `connection` | `SpiceConnectionProps` | Host, port, password, scheme, path |
197
+ | `display` | `SpiceDisplayOptions` | `scale`, `resize`, `fitContainer` |
198
+ | `features` | `SpiceFeatureToggles` | Toggle keyboard, mouse, clipboard, file transfer, audio |
199
+ | `audio` | `SpiceAudioProps` | `volume` (0-1), `muted` |
200
+ | `onConnect` | `() => void` | Fired when the SPICE connection is established |
201
+ | `onDisconnect` | `() => void` | Fired when the connection is closed |
202
+ | `onError` | `(error: Error) => void` | Fired on connection errors |
203
+ | `onResize` | `(width, height) => void` | Fired when the display resolution changes |
204
+ | `onClipboard` | `(text: string) => void` | Fired when clipboard text is received from the guest |
205
+ | `onFileTransferProgress` | `(taskId, bytes, total, filename) => void` | File transfer progress |
206
+ | `onFileTransferComplete` | `(taskId, filename, error?) => void` | File transfer completion |
207
+ | `className` | `string` | CSS class for the outer container |
208
+ | `style` | `CSSProperties` | Inline styles for the outer container |
209
+
210
+ ### `SpiceDisplayHandle` (imperative ref)
211
+
212
+ | Method | Description |
213
+ |--------|-------------|
214
+ | `sendCtrlAltDel()` | Send Ctrl+Alt+Delete to the guest |
215
+ | `disconnect()` | Close the SPICE connection |
216
+ | `reconnect()` | Reconnect using current props |
217
+ | `getConnection()` | Get the underlying `SpiceMainConn` instance |
218
+
219
+ ### `useSpice` Return Value
220
+
221
+ | Field | Type | Description |
222
+ |-------|------|-------------|
223
+ | `status` | `"disconnected" \| "connecting" \| "connected" \| "error"` | Current connection status |
224
+ | `error` | `Error \| null` | Last connection error |
225
+ | `resolution` | `{ width, height } \| null` | Current display resolution |
226
+ | `surfaces` | `number` | Number of active display surfaces |
227
+ | `controls` | `SpiceControls` | Methods: `connect`, `disconnect`, `sendKeyDown`, `sendKeyUp`, `sendMouseMove`, `sendMouseButton`, `sendClipboard`, `setResolution`, `sendCtrlAltDel`, `getConnection` |
228
+
229
+ ## Project Structure
230
+
231
+ ```
232
+ src/
233
+ index.ts # Package entry point / barrel export
234
+ components/
235
+ SpiceDisplay.tsx # Drop-in React component
236
+ hooks/
237
+ useSpice.ts # Low-level React hook
238
+ protocol/ # SPICE protocol implementation
239
+ spiceconn.ts # Base WebSocket connection
240
+ main.ts # Main channel, agent, clipboard
241
+ display.ts # Display rendering (canvas 2D)
242
+ inputs.ts # Keyboard and mouse input
243
+ cursor.ts # Cursor rendering
244
+ playback.ts # Audio playback (Opus/WebM)
245
+ port.ts # Port channel
246
+ enums.ts # Protocol constants
247
+ spicemsg.ts # Message serialization
248
+ spicetype.ts # SPICE data types
249
+ wire.ts # Binary framing / wire reader
250
+ ticket.ts # RSA ticket authentication
251
+ quic.ts, lz.ts # Image decompression
252
+ bitmap.ts, png.ts # Image format conversion
253
+ webm.ts # WebM container for VP8
254
+ resize.ts # Guest screen resizing
255
+ filexfer.ts # File transfer (drag-and-drop)
256
+ simulatecursor.ts # Software cursor
257
+ utils/
258
+ debug.ts # Debug flags and utilities
259
+ atKeynames.ts # AT keyboard scancode names
260
+ codeToScancode.ts # KeyboardEvent.code to scancode map
261
+ ```
262
+
@@ -0,0 +1,109 @@
1
+ import { default as React } from 'react';
2
+ import { SpiceMainConn, ClipboardCallback, FileXferProgressCallback, FileXferCompleteCallback } from '../protocol/main';
3
+
4
+ /** Connection configuration props. */
5
+ export interface SpiceConnectionProps {
6
+ /** WebSocket host (e.g. "localhost"). */
7
+ host: string;
8
+ /** WebSocket port number. */
9
+ port: number;
10
+ /** SPICE password for ticket authentication. */
11
+ password?: string;
12
+ /** Authentication token (set as a cookie for nova-spiceproxy). */
13
+ token?: string;
14
+ /** WebSocket protocol scheme ("ws" or "wss"). Defaults to "ws". */
15
+ scheme?: "ws" | "wss";
16
+ /** WebSocket path (e.g. "websockify"). */
17
+ path?: string;
18
+ }
19
+ /** Display behaviour props. */
20
+ export interface SpiceDisplayOptions {
21
+ /** Whether to scale the display to fit the container. */
22
+ scale?: boolean;
23
+ /** Whether to enable dynamic guest resizing to match the container. */
24
+ resize?: boolean;
25
+ /** Whether to resize the guest to fit the container div. */
26
+ fitContainer?: boolean;
27
+ }
28
+ /** Feature-toggle props. */
29
+ export interface SpiceFeatureToggles {
30
+ /** Enable keyboard input forwarding. Default: true. */
31
+ enableKeyboard?: boolean;
32
+ /** Enable mouse input forwarding. Default: true. */
33
+ enableMouse?: boolean;
34
+ /** Enable clipboard sharing. Default: true. */
35
+ enableClipboard?: boolean;
36
+ /** Enable file transfer (drag-and-drop). Default: true. */
37
+ enableFileTransfer?: boolean;
38
+ /** Enable audio playback. Default: true. */
39
+ enableAudio?: boolean;
40
+ }
41
+ /** Audio props. */
42
+ export interface SpiceAudioProps {
43
+ /** Audio volume (0.0 – 1.0). */
44
+ volume?: number;
45
+ /** Whether audio is muted. */
46
+ muted?: boolean;
47
+ }
48
+ /** Event callback props. */
49
+ export interface SpiceEventCallbacks {
50
+ /** Called when the SPICE connection is established. */
51
+ onConnect?: () => void;
52
+ /** Called when the SPICE connection is closed. */
53
+ onDisconnect?: () => void;
54
+ /** Called when a connection error occurs. */
55
+ onError?: (error: Error) => void;
56
+ /** Called when the display is resized (width, height in pixels). */
57
+ onResize?: (width: number, height: number) => void;
58
+ /** Called when clipboard text is received from the guest. */
59
+ onClipboard?: ClipboardCallback;
60
+ /** Called to report file transfer progress. */
61
+ onFileTransferProgress?: FileXferProgressCallback;
62
+ /** Called when a file transfer completes. */
63
+ onFileTransferComplete?: FileXferCompleteCallback;
64
+ /** Called when data is received on a port channel. */
65
+ onPortData?: (portName: string, data: ArrayBuffer) => void;
66
+ }
67
+ /** Full set of SpiceDisplay component props. */
68
+ export interface SpiceDisplayProps extends SpiceEventCallbacks {
69
+ /** Connection settings. */
70
+ connection: SpiceConnectionProps;
71
+ /** Display options. */
72
+ display?: SpiceDisplayOptions;
73
+ /** Feature toggles. */
74
+ features?: SpiceFeatureToggles;
75
+ /** Audio settings. */
76
+ audio?: SpiceAudioProps;
77
+ /** CSS class name applied to the outer div. */
78
+ className?: string;
79
+ /** Inline styles applied to the outer div. */
80
+ style?: React.CSSProperties;
81
+ }
82
+ /** Imperative handle exposed via ref. */
83
+ export interface SpiceDisplayHandle {
84
+ /** Send Ctrl+Alt+Delete to the guest. */
85
+ sendCtrlAltDel: () => void;
86
+ /** Disconnect from the SPICE server. */
87
+ disconnect: () => void;
88
+ /** Reconnect using current props. */
89
+ reconnect: () => void;
90
+ /** Get the underlying SpiceMainConn instance, if connected. */
91
+ getConnection: () => SpiceMainConn | null;
92
+ }
93
+ /**
94
+ * React component that renders a SPICE remote desktop session.
95
+ *
96
+ * Usage:
97
+ * ```tsx
98
+ * <SpiceDisplay
99
+ * ref={spiceRef}
100
+ * connection={{ host: "localhost", port: 5900, password: "secret" }}
101
+ * features={{ enableKeyboard: true, enableMouse: true }}
102
+ * onConnect={() => console.log("connected")}
103
+ * onError={(e) => console.error(e)}
104
+ * />
105
+ * ```
106
+ */
107
+ export declare const SpiceDisplay: React.ForwardRefExoticComponent<SpiceDisplayProps & React.RefAttributes<SpiceDisplayHandle>>;
108
+ export default SpiceDisplay;
109
+ //# sourceMappingURL=SpiceDisplay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpiceDisplay.d.ts","sourceRoot":"","sources":["../../src/components/SpiceDisplay.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import { SpiceMainConn, ClipboardCallback, FileXferProgressCallback, FileXferCompleteCallback } from '../protocol/main';
2
+
3
+ /** SPICE connection status. */
4
+ export type SpiceStatus = "disconnected" | "connecting" | "connected" | "error";
5
+ /** Display resolution. */
6
+ export interface SpiceResolution {
7
+ width: number;
8
+ height: number;
9
+ }
10
+ /** Configuration object for connect(). */
11
+ export interface SpiceConfig {
12
+ /** WebSocket URI (e.g. "ws://localhost:5900"). */
13
+ uri: string;
14
+ /** SPICE password. */
15
+ password?: string;
16
+ /** DOM element ID where canvases will be appended. */
17
+ screenId?: string;
18
+ /** DOM element ID for debug output. */
19
+ dumpId?: string;
20
+ /** DOM element ID for log messages. */
21
+ messageId?: string;
22
+ }
23
+ /** Callbacks the hook consumer can supply. */
24
+ export interface UseSpiceCallbacks {
25
+ /** Called when the SPICE connection succeeds. */
26
+ onConnect?: () => void;
27
+ /** Called when the connection is closed. */
28
+ onDisconnect?: () => void;
29
+ /** Called on connection error. */
30
+ onError?: (error: Error) => void;
31
+ /** Called when the display resolution changes. */
32
+ onResize?: (width: number, height: number) => void;
33
+ /** Called when clipboard text is received from the guest. */
34
+ onClipboard?: ClipboardCallback;
35
+ /** Called to report file transfer progress. */
36
+ onFileTransferProgress?: FileXferProgressCallback;
37
+ /** Called when a file transfer completes. */
38
+ onFileTransferComplete?: FileXferCompleteCallback;
39
+ }
40
+ /** Options accepted by the useSpice hook. */
41
+ export interface UseSpiceOptions {
42
+ /** React ref to a canvas or div element for display rendering. */
43
+ canvasRef?: React.RefObject<HTMLElement | null>;
44
+ /** Callbacks. */
45
+ callbacks?: UseSpiceCallbacks;
46
+ }
47
+ /** Control methods returned by the hook. */
48
+ export interface SpiceControls {
49
+ /** Initiate a SPICE connection. */
50
+ connect: (config: SpiceConfig) => void;
51
+ /** Disconnect from the SPICE server. */
52
+ disconnect: () => void;
53
+ /** Send an AT scancode key-down event. */
54
+ sendKeyDown: (scancode: number) => void;
55
+ /** Send an AT scancode key-up event. */
56
+ sendKeyUp: (scancode: number) => void;
57
+ /** Send an absolute mouse position. */
58
+ sendMouseMove: (x: number, y: number) => void;
59
+ /** Send a mouse button press or release. */
60
+ sendMouseButton: (button: number, pressed: boolean) => void;
61
+ /** Send clipboard text to the guest. */
62
+ sendClipboard: (text: string) => void;
63
+ /** Request a guest display resolution change. */
64
+ setResolution: (width: number, height: number) => void;
65
+ /** Send Ctrl+Alt+Delete. */
66
+ sendCtrlAltDel: () => void;
67
+ /** Get the underlying SpiceMainConn instance, if connected. */
68
+ getConnection: () => SpiceMainConn | null;
69
+ }
70
+ /** Return value of the useSpice hook. */
71
+ export interface UseSpiceReturn {
72
+ /** Current connection status. */
73
+ status: SpiceStatus;
74
+ /** Last connection error, or null. */
75
+ error: Error | null;
76
+ /** Current display resolution, or null if no primary surface. */
77
+ resolution: SpiceResolution | null;
78
+ /** Number of active surfaces. */
79
+ surfaces: number;
80
+ /** Control methods. */
81
+ controls: SpiceControls;
82
+ }
83
+ /**
84
+ * Low-level React hook for managing a SPICE connection with full control
85
+ * over the connection lifecycle and input events.
86
+ *
87
+ * Usage:
88
+ * ```tsx
89
+ * function MyComponent() {
90
+ * const containerRef = useRef<HTMLDivElement>(null);
91
+ * const { status, resolution, controls } = useSpice({
92
+ * canvasRef: containerRef,
93
+ * callbacks: { onConnect: () => console.log("connected") },
94
+ * });
95
+ *
96
+ * useEffect(() => {
97
+ * controls.connect({ uri: "ws://localhost:5900", password: "secret" });
98
+ * return () => controls.disconnect();
99
+ * }, [controls]);
100
+ *
101
+ * return <div ref={containerRef} />;
102
+ * }
103
+ * ```
104
+ */
105
+ export declare function useSpice(options?: UseSpiceOptions): UseSpiceReturn;
106
+ export default useSpice;
107
+ //# sourceMappingURL=useSpice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSpice.d.ts","sourceRoot":"","sources":["../../src/hooks/useSpice.ts"],"names":[],"mappings":"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"}