@longstoryshort/vtt-sdk 0.1.0 → 0.3.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 CHANGED
@@ -28,13 +28,13 @@ npm install @owlbear-rodeo/sdk
28
28
 
29
29
  | Import | Contents |
30
30
  |--------|----------|
31
- | `@longstoryshort/vtt-sdk` | Core: types, `createSheetBridge`, `createSheetClient`, `createBridgeSheetSource`, `formatRollMessage` |
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 { createSheetBridge, createBridgeSheetSource } from '@longstoryshort/vtt-sdk';
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 = createSheetBridge(source, adapter, {
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: 'DICE_ROLL', payload: { ... } });
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 === 'CAPABILITY_COMMAND') { /* handle damage, conditions, … */ }
70
+ if (event.type === 'dnd:command') { /* handle damage, conditions, … */ }
71
71
  });
72
72
 
73
73
  // cleanup
@@ -84,18 +84,26 @@ sandbox="allow-same-origin allow-scripts allow-popups allow-popups-to-escape-san
84
84
 
85
85
  `allow-same-origin` is required so the sheet can read its auth cookie and access localStorage. Without it the sheet gets an opaque origin and auth breaks.
86
86
 
87
- ## Reference bridge
87
+ ## Reference bridge — Owlbear Rodeo (D&D 5e)
88
88
 
89
- A ready-to-adapt React bridge page for Owlbear Rodeo lives in [`bridges/owlbear/index.tsx`](bridges/owlbear/index.tsx). It is NOT published to npm deploy it as a separate page from your own infrastructure.
89
+ A deployable vanilla-TS bridge for Owlbear Rodeo lives in [`bridges/dnd/`](bridges/dnd/). It is automatically deployed to GitHub Pages on every push to `master`.
90
+
91
+ **Live manifest:** `https://bridge.longstoryshort.app/dnd/obr/manifest.json`
92
+
93
+ To install the extension in OBR: Extensions → Add extension → paste the manifest URL above.
94
+
95
+ To adapt for your own VTT: copy `bridges/dnd/src/main.ts`, swap `OwlbearAdapter` for your own `VTTAdapter` implementation, and deploy as a static page.
90
96
 
91
97
  ## Protocol events
92
98
 
93
- | Type | Direction | Description |
94
- |------|-----------|-------------|
95
- | `DICE_ROLL` | sheet → bridge | A roll result |
96
- | `MANIFEST` | sheet → bridge | Sheet capabilities at handshake |
97
- | `HEALTH_CHANGED` | sheet → bridge | HP after an adjust/set |
98
- | `CAPABILITY_COMMAND` | bridge → sheet | Narrow inbound ops (adjust HP, toggle condition, …) |
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`.
99
107
 
100
108
  ## License
101
109
 
@@ -1,4 +1,4 @@
1
- import { V as VTTAdapter, a as VTTUser, S as SheetEvent, N as NotifyVariant } from '../../types-CS2uTxCW.js';
1
+ import { V as VTTAdapter, a as VTTUser, S as SheetEvent, N as NotifyVariant } from '../../types-w2_82sqo.js';
2
2
 
3
3
  /**
4
4
  * Owlbear Rodeo implementation of {@link VTTAdapter}.
package/dist/index.d.ts CHANGED
@@ -1,19 +1,22 @@
1
- import { b as SheetSource, V as VTTAdapter, c as SheetBridgeOptions, d as SheetClientOptions, e as SheetClient, C as CapabilityManifest, S as SheetEvent, M as MessageHost, D as DiceRollPayload, N as NotifyVariant } from './types-CS2uTxCW.js';
2
- export { f as CapabilityDescriptor, g as CapabilityOpAddTag, h as CapabilityOpAdjust, i as CapabilityOpName, j as CapabilityOpRemoveTag, k as CapabilityOpRequestRoll, l as CapabilityOpSet, m as CapabilityOpToggle, n as CapabilityOperation, H as HealthChangedPayload, o as MessageTarget, p as SheetBridgeMessages, a as VTTUser, q as VTTUserRole } from './types-CS2uTxCW.js';
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
- * Wires a host {@link SheetSource} to a {@link VTTAdapter}. This is the whole
6
- * integration in one place, and it knows nothing about any specific sheet app or
7
- * any specific VTT:
5
+ * The default roll bridge one opinionated policy, not the full protocol.
8
6
  *
7
+ * Wires {@link SheetSource} → {@link VTTAdapter} for the `dnd:roll` event only:
9
8
  * - a roll on the sheet → local toast for the roller + broadcast to other
10
9
  * clients + a transient label over the roller's selected token;
11
10
  * - a roll broadcast by another client → local toast.
12
11
  *
12
+ * Inbound capability wiring (`dnd:command`, `dnd:manifest`, `dnd:health`) is
13
+ * deliberately out of scope here — wire those directly via
14
+ * {@link BridgeSheetSource.onEvent} when your bridge needs them.
15
+ *
13
16
  * Returns a dispose fn that tears down every subscription it created. The
14
17
  * adapter is left untouched — it may be shared and longer-lived than the bridge.
15
18
  */
16
- declare function createSheetBridge(source: SheetSource, adapter: VTTAdapter, options?: SheetBridgeOptions): () => void;
19
+ declare function createRollBridge(source: SheetSource, adapter: VTTAdapter, options?: RollBridgeOptions): () => void;
17
20
 
18
21
  /**
19
22
  * Sheet-side half of the postMessage transport. The character sheet (running in
@@ -43,20 +46,20 @@ interface BridgeSheetSourceOptions {
43
46
  /** Origin to post inbound commands to. Default `'*'`. */
44
47
  targetOrigin?: string;
45
48
  }
46
- /** A `SheetSource` (for `createSheetBridge`) plus raw access and inbound `send`. */
49
+ /** A `SheetSource` (for `createRollBridge`) plus raw access and inbound `send`. */
47
50
  interface BridgeSheetSource extends SheetSource {
48
51
  /** Subscribe to the sheet's capability manifest (sent once at handshake). Returns an unsubscribe fn. */
49
52
  onManifest(handler: (manifest: CapabilityManifest) => void): () => void;
50
53
  /** Subscribe to every event coming from the sheet (not only rolls). */
51
54
  onEvent(handler: (event: SheetEvent) => void): () => void;
52
- /** Post an inbound command to the sheet (e.g. `CAPABILITY_COMMAND`). */
55
+ /** Post an inbound command to the sheet (e.g. `dnd:command`). */
53
56
  send(event: SheetEvent): void;
54
57
  dispose(): void;
55
58
  }
56
59
  /**
57
60
  * Bridge-side half of the postMessage transport. Runs in the bridge frame (the
58
61
  * VTT extension), listens to the embedded sheet iframe, and exposes a
59
- * `SheetSource` so `createSheetBridge` can drive the VTT adapter — without the
62
+ * `SheetSource` so `createRollBridge` can drive the VTT adapter — without the
60
63
  * bridge ever touching the sheet's internals.
61
64
  *
62
65
  * `contentWindow` is read live on every message/send, so it survives the sheet
@@ -69,4 +72,4 @@ declare function formatRollMessage(payload: DiceRollPayload): string;
69
72
  /** Maps a roll's crit state onto a toast variant. */
70
73
  declare function rollVariant(payload: DiceRollPayload): NotifyVariant;
71
74
 
72
- export { type BridgeSheetSource, type BridgeSheetSourceOptions, CapabilityManifest, DiceRollPayload, MessageHost, NotifyVariant, SheetBridgeOptions, SheetClient, SheetClientOptions, SheetEvent, type SheetFrameRef, SheetSource, VTTAdapter, createBridgeSheetSource, createSheetBridge, createSheetClient, formatRollMessage, rollVariant };
75
+ export { type BridgeSheetSource, type BridgeSheetSourceOptions, CapabilityManifest, DiceRollPayload, MessageHost, NotifyVariant, RollBridgeOptions, SheetClient, SheetClientOptions, SheetEvent, type SheetFrameRef, SheetSource, VTTAdapter, createBridgeSheetSource, createRollBridge, createSheetClient, formatRollMessage, rollVariant };
package/dist/index.js CHANGED
@@ -20,12 +20,12 @@ function rollVariant(payload) {
20
20
  return "info";
21
21
  }
22
22
 
23
- // src/createSheetBridge.ts
23
+ // src/createRollBridge.ts
24
24
  var DEFAULT_MESSAGES = {
25
25
  connected: "\u{1F3B2} Sheet connected to the table",
26
26
  labelHint: "No label placed \u2014 select exactly one of your tokens on the map"
27
27
  };
28
- function createSheetBridge(source, adapter, options = {}) {
28
+ function createRollBridge(source, adapter, options = {}) {
29
29
  const messages = { ...DEFAULT_MESSAGES, ...options.messages };
30
30
  const cleanups = [];
31
31
  let cancelled = false;
@@ -34,7 +34,7 @@ function createSheetBridge(source, adapter, options = {}) {
34
34
  return;
35
35
  }
36
36
  adapter.notify(formatRollMessage(roll), rollVariant(roll));
37
- adapter.broadcast({ type: "DICE_ROLL", payload: roll });
37
+ adapter.broadcast({ type: "dnd:roll", payload: roll });
38
38
  void adapter.labelOverSelection(roll.total).then((placed) => {
39
39
  if (!placed) {
40
40
  adapter.notify(messages.labelHint, "warning");
@@ -47,7 +47,7 @@ function createSheetBridge(source, adapter, options = {}) {
47
47
  }
48
48
  adapter.notify(messages.connected, "success");
49
49
  cleanups.push(adapter.onEvent((event) => {
50
- if (event.type !== "DICE_ROLL") {
50
+ if (event.type !== "dnd:roll") {
51
51
  return;
52
52
  }
53
53
  adapter.notify(formatRollMessage(event.payload), rollVariant(event.payload));
@@ -61,7 +61,7 @@ function createSheetBridge(source, adapter, options = {}) {
61
61
 
62
62
  // src/postMessageProtocol.ts
63
63
  var MARKER = "__lssSheetSdk";
64
- var VERSION = 1;
64
+ var VERSION = 2;
65
65
  function wrapEnvelope(event) {
66
66
  return { __lssSheetSdk: VERSION, event };
67
67
  }
@@ -147,7 +147,7 @@ function createBridgeSheetSource(options) {
147
147
  return {
148
148
  onRoll(handler) {
149
149
  const wrapped = (event) => {
150
- if (event.type === "DICE_ROLL") {
150
+ if (event.type === "dnd:roll") {
151
151
  handler(event.payload);
152
152
  }
153
153
  };
@@ -158,7 +158,7 @@ function createBridgeSheetSource(options) {
158
158
  },
159
159
  onManifest(handler) {
160
160
  const wrapped = (event) => {
161
- if (event.type === "MANIFEST") {
161
+ if (event.type === "dnd:manifest") {
162
162
  handler(event.payload);
163
163
  }
164
164
  };
@@ -185,4 +185,4 @@ function createBridgeSheetSource(options) {
185
185
  };
186
186
  }
187
187
 
188
- export { createBridgeSheetSource, createSheetBridge, createSheetClient, formatRollMessage, rollVariant };
188
+ export { 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,15 +78,16 @@ 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
- * CAPABILITY_COMMAND operations are valid for this member.
90
+ * dnd:command operations are valid for this member.
87
91
  */
88
92
  interface CapabilityManifest {
89
93
  version: '1';
@@ -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,20 +105,30 @@ interface HealthChangedPayload {
101
105
  /**
102
106
  * Everything that crosses the sheet↔VTT boundary.
103
107
  *
104
- * Outbound (sheet host): DICE_ROLL · MANIFEST · HEALTH_CHANGED
105
- * Inbound (host → sheet): CAPABILITY_COMMAND
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
- type: 'DICE_ROLL';
116
+ type: 'dnd:roll';
109
117
  payload: DiceRollPayload;
110
- } | {
111
- type: 'MANIFEST';
118
+ }
119
+ /** @experimental */
120
+ | {
121
+ type: 'dnd:manifest';
112
122
  payload: CapabilityManifest;
113
- } | {
114
- type: 'HEALTH_CHANGED';
123
+ }
124
+ /** @experimental */
125
+ | {
126
+ type: 'dnd:health';
115
127
  payload: HealthChangedPayload;
116
- } | {
117
- type: 'CAPABILITY_COMMAND';
128
+ }
129
+ /** @experimental */
130
+ | {
131
+ type: 'dnd:command';
118
132
  payload: CapabilityOperation;
119
133
  };
120
134
  /**
@@ -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 SheetBridgeMessages {
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 SheetBridgeOptions {
137
- messages?: Partial<SheetBridgeMessages>;
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, SheetBridgeOptions as c, SheetClientOptions as d, SheetClient as e, CapabilityDescriptor as f, CapabilityOpAddTag as g, CapabilityOpAdjust as h, CapabilityOpName as i, CapabilityOpRemoveTag as j, CapabilityOpRequestRoll as k, CapabilityOpSet as l, CapabilityOpToggle as m, CapabilityOperation as n, MessageTarget as o, SheetBridgeMessages as p, VTTUserRole as q };
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longstoryshort/vtt-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Embed a longstoryshort.app character sheet in any virtual tabletop via iframe and postMessage",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,7 +18,9 @@
18
18
  },
19
19
  "typesVersions": {
20
20
  "*": {
21
- "owlbear": ["./dist/adapters/owlbear/index.d.ts"]
21
+ "owlbear": [
22
+ "./dist/adapters/owlbear/index.d.ts"
23
+ ]
22
24
  }
23
25
  },
24
26
  "files": [