@longstoryshort/vtt-sdk 0.2.0 → 0.4.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 +13 -11
- package/dist/adapters/owlbear/index.d.ts +35 -3
- package/dist/adapters/owlbear/index.js +48 -34
- package/dist/index.d.ts +19 -9
- package/dist/index.js +6 -3
- package/dist/{types-DQndRf3e.d.ts → types-w2_82sqo.d.ts} +28 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,13 +28,13 @@ npm install @owlbear-rodeo/sdk
|
|
|
28
28
|
|
|
29
29
|
| Import | Contents |
|
|
30
30
|
|--------|----------|
|
|
31
|
-
| `@longstoryshort/vtt-sdk` | Core: types, `
|
|
31
|
+
| `@longstoryshort/vtt-sdk` | Core: types, `createRollBridge`, `createSheetClient`, `createBridgeSheetSource`, `formatRollMessage` |
|
|
32
32
|
| `@longstoryshort/vtt-sdk/owlbear` | `OwlbearAdapter`, `syncObrref`, constants |
|
|
33
33
|
|
|
34
34
|
## Quick start — bridge side
|
|
35
35
|
|
|
36
36
|
```ts
|
|
37
|
-
import {
|
|
37
|
+
import { createRollBridge, createBridgeSheetSource } from '@longstoryshort/vtt-sdk';
|
|
38
38
|
import { OwlbearAdapter } from '@longstoryshort/vtt-sdk/owlbear';
|
|
39
39
|
|
|
40
40
|
const adapter = new OwlbearAdapter();
|
|
@@ -43,7 +43,7 @@ const source = createBridgeSheetSource({
|
|
|
43
43
|
allowedOrigins: ['https://longstoryshort.app'],
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
-
const dispose =
|
|
46
|
+
const dispose = createRollBridge(source, adapter, {
|
|
47
47
|
messages: {
|
|
48
48
|
connected: '🎲 Sheet connected',
|
|
49
49
|
labelHint: 'Select exactly one token to place a roll label',
|
|
@@ -63,11 +63,11 @@ import { createSheetClient } from '@longstoryshort/vtt-sdk';
|
|
|
63
63
|
const client = createSheetClient();
|
|
64
64
|
|
|
65
65
|
// emit a roll to the bridge
|
|
66
|
-
client.send({ type: '
|
|
66
|
+
client.send({ type: 'dnd:roll', payload: { ... } });
|
|
67
67
|
|
|
68
68
|
// receive inbound commands from the bridge
|
|
69
69
|
const unsub = client.onEvent((event) => {
|
|
70
|
-
if (event.type === '
|
|
70
|
+
if (event.type === 'dnd:command') { /* handle damage, conditions, … */ }
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
// cleanup
|
|
@@ -96,12 +96,14 @@ To adapt for your own VTT: copy `bridges/dnd/src/main.ts`, swap `OwlbearAdapter`
|
|
|
96
96
|
|
|
97
97
|
## Protocol events
|
|
98
98
|
|
|
99
|
-
| Type | Direction | Description |
|
|
100
|
-
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
99
|
+
| Type | Status | Direction | Description |
|
|
100
|
+
|------|--------|-----------|-------------|
|
|
101
|
+
| `dnd:roll` | ✅ stable | sheet → host | A roll result |
|
|
102
|
+
| `dnd:manifest` | 🧪 reserved | sheet → host | Sheet capabilities at handshake |
|
|
103
|
+
| `dnd:health` | 🧪 reserved | sheet → host | HP after an adjust/set |
|
|
104
|
+
| `dnd:command` | 🧪 reserved | host → sheet | Narrow inbound ops (adjust HP, toggle condition, …) |
|
|
105
|
+
|
|
106
|
+
Reserved events are typed and functional — the sheet implements them — but the bridge-side wiring is considered experimental API and may change. Wire them directly via `BridgeSheetSource.onEvent` rather than through `createRollBridge`.
|
|
105
107
|
|
|
106
108
|
## License
|
|
107
109
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { V as VTTAdapter, a as VTTUser, S as SheetEvent, N as NotifyVariant } from '../../types-
|
|
1
|
+
import { V as VTTAdapter, a as VTTUser, S as SheetEvent, N as NotifyVariant } from '../../types-w2_82sqo.js';
|
|
2
|
+
import * as _owlbear_rodeo_sdk from '@owlbear-rodeo/sdk';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Owlbear Rodeo implementation of {@link VTTAdapter}.
|
|
@@ -19,7 +20,6 @@ declare class OwlbearAdapter implements VTTAdapter {
|
|
|
19
20
|
get isAvailable(): boolean;
|
|
20
21
|
ready(): Promise<boolean>;
|
|
21
22
|
private init;
|
|
22
|
-
private loadSdk;
|
|
23
23
|
getSessionId(): string | undefined;
|
|
24
24
|
getCurrentUser(): VTTUser | undefined;
|
|
25
25
|
broadcast(event: SheetEvent): void;
|
|
@@ -48,4 +48,36 @@ declare const LABEL_METADATA_KEY = "rodeo.lss/label";
|
|
|
48
48
|
/** How long a roll label floats over a token before it is removed. */
|
|
49
49
|
declare const DEFAULT_LABEL_TTL_MS = 4000;
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
interface ObrLike {
|
|
52
|
+
onReady(cb: () => void): void;
|
|
53
|
+
}
|
|
54
|
+
/** Promisifies `OBR.onReady` — resolves once the OBR_READY handshake completes. */
|
|
55
|
+
declare function whenObrReady(obr: ObrLike): Promise<void>;
|
|
56
|
+
|
|
57
|
+
type OwlbearSdk = typeof _owlbear_rodeo_sdk;
|
|
58
|
+
/**
|
|
59
|
+
* The window shape expected by the SDK preload contract.
|
|
60
|
+
* Set `__lssObrSdk` at the app entry (before any navigation) so
|
|
61
|
+
* `loadObrSdk` can reuse the already-imported module and avoid missing
|
|
62
|
+
* the one-shot OBR_READY handshake.
|
|
63
|
+
*/
|
|
64
|
+
interface ObrPreloadWindow {
|
|
65
|
+
__lssObrSdk?: OwlbearSdk;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Call this at the bridge entry module — before any client-side navigation —
|
|
69
|
+
* to stash the already-imported OBR SDK so `loadObrSdk` can find it.
|
|
70
|
+
*/
|
|
71
|
+
declare function preloadObrSdk(sdk: OwlbearSdk): void;
|
|
72
|
+
/**
|
|
73
|
+
* Retrieves the OBR SDK for use inside an OBR extension frame.
|
|
74
|
+
*
|
|
75
|
+
* Prefers the copy stashed by `preloadObrSdk` (imported early enough to catch
|
|
76
|
+
* the one-shot OBR_READY handshake). Falls back to a dynamic import if the
|
|
77
|
+
* stash is absent, with a warning — the dynamic path may miss OBR_READY.
|
|
78
|
+
*
|
|
79
|
+
* Returns `null` when not running in a browser or when the import fails.
|
|
80
|
+
*/
|
|
81
|
+
declare function loadObrSdk(): Promise<OwlbearSdk | null>;
|
|
82
|
+
|
|
83
|
+
export { BROADCAST_CHANNEL, DEFAULT_LABEL_TTL_MS, LABEL_METADATA_KEY, type ObrPreloadWindow, OwlbearAdapter, loadObrSdk, preloadObrSdk, syncObrref, whenObrReady };
|
|
@@ -44,8 +44,44 @@ function syncObrref() {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
// src/adapters/owlbear/
|
|
47
|
+
// src/adapters/owlbear/loadObrSdk.ts
|
|
48
48
|
var DEV2 = process.env["NODE_ENV"] !== "production";
|
|
49
|
+
function preloadObrSdk(sdk) {
|
|
50
|
+
window.__lssObrSdk = sdk;
|
|
51
|
+
}
|
|
52
|
+
async function loadObrSdk() {
|
|
53
|
+
if (typeof window === "undefined") {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const host = window;
|
|
57
|
+
for (let i = 0; i < 10 && !host.__lssObrSdk; i += 1) {
|
|
58
|
+
await new Promise((resolve) => {
|
|
59
|
+
window.setTimeout(resolve, 50);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (host.__lssObrSdk) {
|
|
63
|
+
return host.__lssObrSdk;
|
|
64
|
+
}
|
|
65
|
+
if (DEV2) {
|
|
66
|
+
console.warn("[LSS/OBR] no preloaded SDK \u2014 importing now (may miss OBR_READY)");
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return await import('@owlbear-rodeo/sdk');
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error("[LSS/OBR] SDK import failed:", error);
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// src/adapters/owlbear/whenObrReady.ts
|
|
77
|
+
function whenObrReady(obr) {
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
obr.onReady(resolve);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/adapters/owlbear/OwlbearAdapter.ts
|
|
84
|
+
var DEV3 = process.env["NODE_ENV"] !== "production";
|
|
49
85
|
var NOTIFY_VARIANT = {
|
|
50
86
|
info: "INFO",
|
|
51
87
|
success: "SUCCESS",
|
|
@@ -73,13 +109,13 @@ var OwlbearAdapter = class {
|
|
|
73
109
|
return false;
|
|
74
110
|
}
|
|
75
111
|
syncObrref();
|
|
76
|
-
const sdk = await
|
|
112
|
+
const sdk = await loadObrSdk();
|
|
77
113
|
if (!sdk) {
|
|
78
114
|
return false;
|
|
79
115
|
}
|
|
80
116
|
this.sdk = sdk;
|
|
81
117
|
this.obr = sdk.default;
|
|
82
|
-
if (
|
|
118
|
+
if (DEV3) {
|
|
83
119
|
console.info(
|
|
84
120
|
"[LSS/OBR] init \u2014 isAvailable:",
|
|
85
121
|
this.obr.isAvailable,
|
|
@@ -88,18 +124,16 @@ var OwlbearAdapter = class {
|
|
|
88
124
|
);
|
|
89
125
|
}
|
|
90
126
|
if (!this.obr.isAvailable) {
|
|
91
|
-
if (
|
|
127
|
+
if (DEV3) {
|
|
92
128
|
console.warn("[LSS/OBR] not embedded (origin empty) \u2014 obrref missing at SDK load.");
|
|
93
129
|
}
|
|
94
130
|
return false;
|
|
95
131
|
}
|
|
96
|
-
if (
|
|
132
|
+
if (DEV3) {
|
|
97
133
|
console.info("[LSS/OBR] awaiting onReady (isReady =", this.obr.isReady, ")");
|
|
98
134
|
}
|
|
99
|
-
await
|
|
100
|
-
|
|
101
|
-
});
|
|
102
|
-
if (DEV2) {
|
|
135
|
+
await whenObrReady(this.obr);
|
|
136
|
+
if (DEV3) {
|
|
103
137
|
console.info("[LSS/OBR] onReady fired \u2014 fetching player\u2026");
|
|
104
138
|
}
|
|
105
139
|
if (this.disposed) {
|
|
@@ -112,31 +146,11 @@ var OwlbearAdapter = class {
|
|
|
112
146
|
this.obr.player.getRole()
|
|
113
147
|
]);
|
|
114
148
|
this.user = { id, name, role: role === "GM" ? "gm" : "player" };
|
|
115
|
-
if (
|
|
149
|
+
if (DEV3) {
|
|
116
150
|
console.info("[LSS/OBR] ready \u2014 room:", this.sessionId, "user:", this.user);
|
|
117
151
|
}
|
|
118
152
|
return true;
|
|
119
153
|
}
|
|
120
|
-
async loadSdk() {
|
|
121
|
-
const host = window;
|
|
122
|
-
for (let i = 0; i < 10 && !host.__lssObrSdk; i += 1) {
|
|
123
|
-
await new Promise((resolve) => {
|
|
124
|
-
window.setTimeout(resolve, 50);
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
if (host.__lssObrSdk) {
|
|
128
|
-
return host.__lssObrSdk;
|
|
129
|
-
}
|
|
130
|
-
if (DEV2) {
|
|
131
|
-
console.warn("[LSS/OBR] no preloaded SDK \u2014 importing now (may miss OBR_READY)");
|
|
132
|
-
}
|
|
133
|
-
try {
|
|
134
|
-
return await import('@owlbear-rodeo/sdk');
|
|
135
|
-
} catch (error) {
|
|
136
|
-
console.error("[LSS/OBR] SDK import failed:", error);
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
154
|
getSessionId() {
|
|
141
155
|
return this.sessionId;
|
|
142
156
|
}
|
|
@@ -168,14 +182,14 @@ var OwlbearAdapter = class {
|
|
|
168
182
|
try {
|
|
169
183
|
const selection = await obr.player.getSelection();
|
|
170
184
|
if (!selection || selection.length !== 1) {
|
|
171
|
-
if (
|
|
185
|
+
if (DEV3) {
|
|
172
186
|
console.warn("[OwlbearAdapter] label skipped \u2014 selected tokens:", selection?.length ?? 0);
|
|
173
187
|
}
|
|
174
188
|
return false;
|
|
175
189
|
}
|
|
176
190
|
const [token] = await obr.scene.items.getItems(selection);
|
|
177
191
|
if (!token) {
|
|
178
|
-
if (
|
|
192
|
+
if (DEV3) {
|
|
179
193
|
console.warn("[OwlbearAdapter] label skipped \u2014 selected item not found in scene");
|
|
180
194
|
}
|
|
181
195
|
return false;
|
|
@@ -188,7 +202,7 @@ var OwlbearAdapter = class {
|
|
|
188
202
|
}, ttlMs);
|
|
189
203
|
return true;
|
|
190
204
|
} catch (error) {
|
|
191
|
-
if (
|
|
205
|
+
if (DEV3) {
|
|
192
206
|
console.warn("[OwlbearAdapter] label error:", error);
|
|
193
207
|
}
|
|
194
208
|
return false;
|
|
@@ -199,4 +213,4 @@ var OwlbearAdapter = class {
|
|
|
199
213
|
}
|
|
200
214
|
};
|
|
201
215
|
|
|
202
|
-
export { BROADCAST_CHANNEL, DEFAULT_LABEL_TTL_MS, LABEL_METADATA_KEY, OwlbearAdapter, syncObrref };
|
|
216
|
+
export { BROADCAST_CHANNEL, DEFAULT_LABEL_TTL_MS, LABEL_METADATA_KEY, OwlbearAdapter, loadObrSdk, preloadObrSdk, syncObrref, whenObrReady };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
-
import { b as SheetSource, V as VTTAdapter,
|
|
2
|
-
export {
|
|
1
|
+
import { b as SheetSource, V as VTTAdapter, R as RollBridgeOptions, c as SheetClientOptions, d as SheetClient, C as CapabilityManifest, S as SheetEvent, M as MessageHost, D as DiceRollPayload, N as NotifyVariant } from './types-w2_82sqo.js';
|
|
2
|
+
export { e as CapabilityDescriptor, f as CapabilityOpAddTag, g as CapabilityOpAdjust, h as CapabilityOpName, i as CapabilityOpRemoveTag, j as CapabilityOpRequestRoll, k as CapabilityOpSet, l as CapabilityOpToggle, m as CapabilityOperation, H as HealthChangedPayload, n as MessageTarget, o as RollBridgeMessages, a as VTTUser, p as VTTUserRole } from './types-w2_82sqo.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Minimum sandbox tokens required on the sheet iframe.
|
|
6
|
+
* `allow-same-origin` is essential — without it the sheet gets an opaque origin
|
|
7
|
+
* and auth (cookies / localStorage) breaks entirely.
|
|
8
|
+
*/
|
|
9
|
+
declare const SHEET_IFRAME_SANDBOX = "allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The default roll bridge — one opinionated policy, not the full protocol.
|
|
8
13
|
*
|
|
14
|
+
* Wires {@link SheetSource} → {@link VTTAdapter} for the `dnd:roll` event only:
|
|
9
15
|
* - a roll on the sheet → local toast for the roller + broadcast to other
|
|
10
16
|
* clients + a transient label over the roller's selected token;
|
|
11
17
|
* - a roll broadcast by another client → local toast.
|
|
12
18
|
*
|
|
19
|
+
* Inbound capability wiring (`dnd:command`, `dnd:manifest`, `dnd:health`) is
|
|
20
|
+
* deliberately out of scope here — wire those directly via
|
|
21
|
+
* {@link BridgeSheetSource.onEvent} when your bridge needs them.
|
|
22
|
+
*
|
|
13
23
|
* Returns a dispose fn that tears down every subscription it created. The
|
|
14
24
|
* adapter is left untouched — it may be shared and longer-lived than the bridge.
|
|
15
25
|
*/
|
|
16
|
-
declare function
|
|
26
|
+
declare function createRollBridge(source: SheetSource, adapter: VTTAdapter, options?: RollBridgeOptions): () => void;
|
|
17
27
|
|
|
18
28
|
/**
|
|
19
29
|
* Sheet-side half of the postMessage transport. The character sheet (running in
|
|
@@ -43,7 +53,7 @@ interface BridgeSheetSourceOptions {
|
|
|
43
53
|
/** Origin to post inbound commands to. Default `'*'`. */
|
|
44
54
|
targetOrigin?: string;
|
|
45
55
|
}
|
|
46
|
-
/** A `SheetSource` (for `
|
|
56
|
+
/** A `SheetSource` (for `createRollBridge`) plus raw access and inbound `send`. */
|
|
47
57
|
interface BridgeSheetSource extends SheetSource {
|
|
48
58
|
/** Subscribe to the sheet's capability manifest (sent once at handshake). Returns an unsubscribe fn. */
|
|
49
59
|
onManifest(handler: (manifest: CapabilityManifest) => void): () => void;
|
|
@@ -56,7 +66,7 @@ interface BridgeSheetSource extends SheetSource {
|
|
|
56
66
|
/**
|
|
57
67
|
* Bridge-side half of the postMessage transport. Runs in the bridge frame (the
|
|
58
68
|
* VTT extension), listens to the embedded sheet iframe, and exposes a
|
|
59
|
-
* `SheetSource` so `
|
|
69
|
+
* `SheetSource` so `createRollBridge` can drive the VTT adapter — without the
|
|
60
70
|
* bridge ever touching the sheet's internals.
|
|
61
71
|
*
|
|
62
72
|
* `contentWindow` is read live on every message/send, so it survives the sheet
|
|
@@ -69,4 +79,4 @@ declare function formatRollMessage(payload: DiceRollPayload): string;
|
|
|
69
79
|
/** Maps a roll's crit state onto a toast variant. */
|
|
70
80
|
declare function rollVariant(payload: DiceRollPayload): NotifyVariant;
|
|
71
81
|
|
|
72
|
-
export { type BridgeSheetSource, type BridgeSheetSourceOptions, CapabilityManifest, DiceRollPayload, MessageHost, NotifyVariant,
|
|
82
|
+
export { type BridgeSheetSource, type BridgeSheetSourceOptions, CapabilityManifest, DiceRollPayload, MessageHost, NotifyVariant, RollBridgeOptions, SHEET_IFRAME_SANDBOX, SheetClient, SheetClientOptions, SheetEvent, type SheetFrameRef, SheetSource, VTTAdapter, createBridgeSheetSource, createRollBridge, createSheetClient, formatRollMessage, rollVariant };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var SHEET_IFRAME_SANDBOX = "allow-same-origin allow-scripts allow-popups allow-popups-to-escape-sandbox allow-forms allow-modals";
|
|
3
|
+
|
|
1
4
|
// src/formatRoll.ts
|
|
2
5
|
function formatRollMessage(payload) {
|
|
3
6
|
let crit = "";
|
|
@@ -20,12 +23,12 @@ function rollVariant(payload) {
|
|
|
20
23
|
return "info";
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
// src/
|
|
26
|
+
// src/createRollBridge.ts
|
|
24
27
|
var DEFAULT_MESSAGES = {
|
|
25
28
|
connected: "\u{1F3B2} Sheet connected to the table",
|
|
26
29
|
labelHint: "No label placed \u2014 select exactly one of your tokens on the map"
|
|
27
30
|
};
|
|
28
|
-
function
|
|
31
|
+
function createRollBridge(source, adapter, options = {}) {
|
|
29
32
|
const messages = { ...DEFAULT_MESSAGES, ...options.messages };
|
|
30
33
|
const cleanups = [];
|
|
31
34
|
let cancelled = false;
|
|
@@ -185,4 +188,4 @@ function createBridgeSheetSource(options) {
|
|
|
185
188
|
};
|
|
186
189
|
}
|
|
187
190
|
|
|
188
|
-
export { createBridgeSheetSource,
|
|
191
|
+
export { SHEET_IFRAME_SANDBOX, createBridgeSheetSource, createRollBridge, createSheetClient, formatRollMessage, rollVariant };
|
|
@@ -29,6 +29,7 @@ interface DiceRollPayload {
|
|
|
29
29
|
}
|
|
30
30
|
type CapabilityOpName = 'adjust' | 'set' | 'toggle' | 'add-tag' | 'remove-tag' | 'request-roll';
|
|
31
31
|
/**
|
|
32
|
+
* @experimental
|
|
32
33
|
* adjust: apply a signed delta to a numeric capability.
|
|
33
34
|
* Sign convention: negative delta = subtract (damage), positive delta = add (heal/gain).
|
|
34
35
|
* The sheet applies its own rules (temp HP absorption, resistances, clamping).
|
|
@@ -38,18 +39,19 @@ interface CapabilityOpAdjust {
|
|
|
38
39
|
capabilityId: string;
|
|
39
40
|
delta: number;
|
|
40
41
|
}
|
|
41
|
-
/** set: overwrite a capability value outright. */
|
|
42
|
+
/** @experimental set: overwrite a capability value outright. */
|
|
42
43
|
interface CapabilityOpSet {
|
|
43
44
|
op: 'set';
|
|
44
45
|
capabilityId: string;
|
|
45
46
|
value: number | boolean | string;
|
|
46
47
|
}
|
|
47
|
-
/** toggle: flip a boolean capability. */
|
|
48
|
+
/** @experimental toggle: flip a boolean capability. */
|
|
48
49
|
interface CapabilityOpToggle {
|
|
49
50
|
op: 'toggle';
|
|
50
51
|
capabilityId: string;
|
|
51
52
|
}
|
|
52
53
|
/**
|
|
54
|
+
* @experimental
|
|
53
55
|
* add-tag: append a label to a tag-track capability (e.g. a condition).
|
|
54
56
|
* Idempotent — if the value is already present, the sheet must ignore the command.
|
|
55
57
|
*/
|
|
@@ -59,6 +61,7 @@ interface CapabilityOpAddTag {
|
|
|
59
61
|
value: string;
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
64
|
+
* @experimental
|
|
62
65
|
* remove-tag: remove a label from a tag-track capability.
|
|
63
66
|
* Removes the first occurrence. Assumes a well-behaved set (no duplicates in DnD5e conditions).
|
|
64
67
|
*/
|
|
@@ -67,7 +70,7 @@ interface CapabilityOpRemoveTag {
|
|
|
67
70
|
capabilityId: string;
|
|
68
71
|
value: string;
|
|
69
72
|
}
|
|
70
|
-
/** request-roll: ask the sheet to roll a d20 check against an optional DC. */
|
|
73
|
+
/** @experimental request-roll: ask the sheet to roll a d20 check against an optional DC. */
|
|
71
74
|
interface CapabilityOpRequestRoll {
|
|
72
75
|
op: 'request-roll';
|
|
73
76
|
capabilityId: string;
|
|
@@ -75,12 +78,13 @@ interface CapabilityOpRequestRoll {
|
|
|
75
78
|
dc?: number;
|
|
76
79
|
}
|
|
77
80
|
type CapabilityOperation = CapabilityOpAdjust | CapabilityOpSet | CapabilityOpToggle | CapabilityOpAddTag | CapabilityOpRemoveTag | CapabilityOpRequestRoll;
|
|
78
|
-
/** One capability entry in the sheet manifest. */
|
|
81
|
+
/** @experimental One capability entry in the sheet manifest. */
|
|
79
82
|
interface CapabilityDescriptor {
|
|
80
83
|
id: string;
|
|
81
84
|
operations: CapabilityOpName[];
|
|
82
85
|
}
|
|
83
86
|
/**
|
|
87
|
+
* @experimental
|
|
84
88
|
* Capability manifest — the sheet's public declaration of what the host may do.
|
|
85
89
|
* Sent outbound by the sheet at handshake time; the host reads it to know which
|
|
86
90
|
* dnd:command operations are valid for this member.
|
|
@@ -91,7 +95,7 @@ interface CapabilityManifest {
|
|
|
91
95
|
sheetSystem: string;
|
|
92
96
|
capabilities: CapabilityDescriptor[];
|
|
93
97
|
}
|
|
94
|
-
/** Outbound fact: HP values after the sheet has applied an adjust/set command. */
|
|
98
|
+
/** @experimental Outbound fact: HP values after the sheet has applied an adjust/set command. */
|
|
95
99
|
interface HealthChangedPayload {
|
|
96
100
|
characterId: string;
|
|
97
101
|
current: number;
|
|
@@ -101,19 +105,29 @@ interface HealthChangedPayload {
|
|
|
101
105
|
/**
|
|
102
106
|
* Everything that crosses the sheet↔VTT boundary.
|
|
103
107
|
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
108
|
+
* | Type | Status | Direction |
|
|
109
|
+
* |------|--------|-----------|
|
|
110
|
+
* | `dnd:roll` | stable | sheet → host |
|
|
111
|
+
* | `dnd:manifest` | experimental | sheet → host |
|
|
112
|
+
* | `dnd:health` | experimental | sheet → host |
|
|
113
|
+
* | `dnd:command` | experimental | host → sheet |
|
|
106
114
|
*/
|
|
107
115
|
type SheetEvent = {
|
|
108
116
|
type: 'dnd:roll';
|
|
109
117
|
payload: DiceRollPayload;
|
|
110
|
-
}
|
|
118
|
+
}
|
|
119
|
+
/** @experimental */
|
|
120
|
+
| {
|
|
111
121
|
type: 'dnd:manifest';
|
|
112
122
|
payload: CapabilityManifest;
|
|
113
|
-
}
|
|
123
|
+
}
|
|
124
|
+
/** @experimental */
|
|
125
|
+
| {
|
|
114
126
|
type: 'dnd:health';
|
|
115
127
|
payload: HealthChangedPayload;
|
|
116
|
-
}
|
|
128
|
+
}
|
|
129
|
+
/** @experimental */
|
|
130
|
+
| {
|
|
117
131
|
type: 'dnd:command';
|
|
118
132
|
payload: CapabilityOperation;
|
|
119
133
|
};
|
|
@@ -127,14 +141,14 @@ interface SheetSource {
|
|
|
127
141
|
onRoll(handler: (roll: DiceRollPayload) => void): () => void;
|
|
128
142
|
}
|
|
129
143
|
/** Human-facing strings surfaced by the bridge — override to localize. */
|
|
130
|
-
interface
|
|
144
|
+
interface RollBridgeMessages {
|
|
131
145
|
/** Toast shown once the sheet connects to the table. */
|
|
132
146
|
connected: string;
|
|
133
147
|
/** Toast shown to the roller when a token label could not be placed. */
|
|
134
148
|
labelHint: string;
|
|
135
149
|
}
|
|
136
|
-
interface
|
|
137
|
-
messages?: Partial<
|
|
150
|
+
interface RollBridgeOptions {
|
|
151
|
+
messages?: Partial<RollBridgeMessages>;
|
|
138
152
|
}
|
|
139
153
|
/**
|
|
140
154
|
* The seam every VTT implements. The sheet talks only to this interface; each
|
|
@@ -204,4 +218,4 @@ interface SheetClient {
|
|
|
204
218
|
dispose(): void;
|
|
205
219
|
}
|
|
206
220
|
|
|
207
|
-
export type { CapabilityManifest as C, DiceRollPayload as D, HealthChangedPayload as H, MessageHost as M, NotifyVariant as N, SheetEvent as S, VTTAdapter as V, VTTUser as a, SheetSource as b,
|
|
221
|
+
export type { CapabilityManifest as C, DiceRollPayload as D, HealthChangedPayload as H, MessageHost as M, NotifyVariant as N, RollBridgeOptions as R, SheetEvent as S, VTTAdapter as V, VTTUser as a, SheetSource as b, SheetClientOptions as c, SheetClient as d, CapabilityDescriptor as e, CapabilityOpAddTag as f, CapabilityOpAdjust as g, CapabilityOpName as h, CapabilityOpRemoveTag as i, CapabilityOpRequestRoll as j, CapabilityOpSet as k, CapabilityOpToggle as l, CapabilityOperation as m, MessageTarget as n, RollBridgeMessages as o, VTTUserRole as p };
|