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.
- package/README.md +262 -0
- package/dist/components/SpiceDisplay.d.ts +109 -0
- package/dist/components/SpiceDisplay.d.ts.map +1 -0
- package/dist/hooks/useSpice.d.ts +107 -0
- package/dist/hooks/useSpice.d.ts.map +1 -0
- package/dist/index.cjs +14 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +6745 -0
- package/dist/index.mjs.map +1 -0
- package/dist/protocol/bitmap.d.ts +20 -0
- package/dist/protocol/bitmap.d.ts.map +1 -0
- package/dist/protocol/cursor.d.ts +16 -0
- package/dist/protocol/cursor.d.ts.map +1 -0
- package/dist/protocol/display.d.ts +311 -0
- package/dist/protocol/display.d.ts.map +1 -0
- package/dist/protocol/enums.d.ts +677 -0
- package/dist/protocol/enums.d.ts.map +1 -0
- package/dist/protocol/filexfer.d.ts +61 -0
- package/dist/protocol/filexfer.d.ts.map +1 -0
- package/dist/protocol/index.d.ts +34 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/inputs.d.ts +95 -0
- package/dist/protocol/inputs.d.ts.map +1 -0
- package/dist/protocol/lz.d.ts +24 -0
- package/dist/protocol/lz.d.ts.map +1 -0
- package/dist/protocol/main.d.ts +205 -0
- package/dist/protocol/main.d.ts.map +1 -0
- package/dist/protocol/playback.d.ts +51 -0
- package/dist/protocol/playback.d.ts.map +1 -0
- package/dist/protocol/png.d.ts +14 -0
- package/dist/protocol/png.d.ts.map +1 -0
- package/dist/protocol/port.d.ts +46 -0
- package/dist/protocol/port.d.ts.map +1 -0
- package/dist/protocol/quic.d.ts +105 -0
- package/dist/protocol/quic.d.ts.map +1 -0
- package/dist/protocol/resize.d.ts +61 -0
- package/dist/protocol/resize.d.ts.map +1 -0
- package/dist/protocol/simulatecursor.d.ts +49 -0
- package/dist/protocol/simulatecursor.d.ts.map +1 -0
- package/dist/protocol/spicearraybuffer.d.ts +44 -0
- package/dist/protocol/spicearraybuffer.d.ts.map +1 -0
- package/dist/protocol/spiceconn.d.ts +118 -0
- package/dist/protocol/spiceconn.d.ts.map +1 -0
- package/dist/protocol/spicedataview.d.ts +144 -0
- package/dist/protocol/spicedataview.d.ts.map +1 -0
- package/dist/protocol/spicemsg.d.ts +435 -0
- package/dist/protocol/spicemsg.d.ts.map +1 -0
- package/dist/protocol/spicetype.d.ts +166 -0
- package/dist/protocol/spicetype.d.ts.map +1 -0
- package/dist/protocol/ticket.d.ts +14 -0
- package/dist/protocol/ticket.d.ts.map +1 -0
- package/dist/protocol/webm.d.ts +118 -0
- package/dist/protocol/webm.d.ts.map +1 -0
- package/dist/protocol/wire.d.ts +81 -0
- package/dist/protocol/wire.d.ts.map +1 -0
- package/dist/utils/atKeynames.d.ts +96 -0
- package/dist/utils/atKeynames.d.ts.map +1 -0
- package/dist/utils/codeToScancode.d.ts +2 -0
- package/dist/utils/codeToScancode.d.ts.map +1 -0
- package/dist/utils/debug.d.ts +84 -0
- package/dist/utils/debug.d.ts.map +1 -0
- 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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"}
|