@streamorchard/grapevine 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/PROTOCOL.md CHANGED
@@ -181,7 +181,8 @@ A capability is a named feature one app provides and others use, defined by a JS
181
181
  | ----------- | ----------------------------------------------------------------- |
182
182
  | `Timestamp` | ISO 8601, in UTC |
183
183
  | `Platform` | A streaming site: `kick`, `twitch`, `youtube`, or one added later |
184
- | `Channel` | `{ platform, slug, name? }` |
184
+ | `Channel` | `{ platform, slug, name?, avatar? }` |
185
+ | `Image` | A picture's `https` address, as the site gives it |
185
186
  | `Account` | `{ platform, user }`: the username only, never a token or cookie |
186
187
  | `Progress` | `{ done, total, unit }`, where `unit` is `minutes` for now |
187
188
 
@@ -194,14 +195,22 @@ A capability is a named feature one app provides and others use, defined by a JS
194
195
 
195
196
  ### `drops.farming` 1.0
196
197
 
197
- What a drops app is farming. Provided by Pear Drops.
198
-
199
- | Field | |
200
- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
201
- | `state` | `farming` (its player is watching a stream for drop credit), `waiting`, `paused`, `done`, `signed-out`, or one added later |
202
- | `account` | The account it farms for, or `null` |
203
- | `channel` | The stream it's watching while `farming`, else `null` |
204
- | `drop` | `{ campaign, category, progress }` while farming, else `null` |
198
+ What a drops app is farming, with enough for another app to show it, in a top-bar widget say. Provided by Pear Drops.
199
+
200
+ | Field | |
201
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
202
+ | `state` | `farming` (its player is watching a stream for drop credit), `waiting`, `paused`, `done`, `signed-out`, or one added later |
203
+ | `account` | The account it farms for, or `null` |
204
+ | `channel` | The channel it's watching while `farming` (with its `avatar`, optional), else `null` |
205
+ | `stream` | Optional, while farming: `{ title?, viewers? }` |
206
+ | `drop` | While farming, `{ campaign, category, progress }`, else `null`. Optional extras below |
207
+ | `drop.image` | The category's picture |
208
+ | `drop.endsAt` | When the drop ends |
209
+ | `drop.rewards` | `{ claimed, total, items? }`. `items` lists every reward in the order they unlock (up to 20): `{ name, image?, required, state, unlocksAt? }`, where `state` is `claimed`, `ready` (unlocked, waiting to be claimed), `locked`, or one added later, and a locked one's `unlocksAt` is when it should unlock at the current rate |
210
+ | `drop.next` | The reward being worked towards: `{ name, image?, required, unlocksAt }`, where `required` is minutes and `unlocksAt` is when it should unlock at the current rate |
211
+ | `drop.doneAt` | When every reward should be earned at the current rate, or `null` |
212
+
213
+ `drops.farming.show {}` asks the drops app to bring its window to the front, for a click on that widget. It replies `{ shown }`.
205
214
 
206
215
  ### `players` 1.0
207
216
 
package/README.md CHANGED
@@ -53,12 +53,37 @@ const link = createLink({
53
53
 
54
54
  await link.start();
55
55
 
56
- // Tell every app that subscribed. seq and at are added for you.
56
+ // Tell every app that subscribed. seq and at are added for you, and the last one also answers
57
+ // drops.farming.get. Everything past progress is optional: it's what a widget would show.
57
58
  link.publish('drops.farming.changed', {
58
59
  state: 'farming',
59
60
  account: { platform: 'kick', user: 'pearfan' },
60
- channel: { platform: 'kick', slug: 'pixelpear' },
61
- drop: null,
61
+ channel: { platform: 'kick', slug: 'pixelpear', name: 'PixelPear', avatar: 'https://files.kick.com/…/pixelpear.webp' },
62
+ stream: { title: 'Neon Rush ranked, chill vibes', viewers: 1240 },
63
+ drop: {
64
+ campaign: 'Neon Rush Launch Week',
65
+ category: 'Neon Rush',
66
+ progress: { done: 48, total: 120, unit: 'minutes' },
67
+ image: 'https://files.kick.com/…/neon-rush.webp',
68
+ endsAt: '2026-10-21T18:00:00Z',
69
+ rewards: {
70
+ claimed: 1,
71
+ total: 3,
72
+ items: [
73
+ { name: 'Pear Badge', required: 30, state: 'claimed' },
74
+ { name: 'Hype Rocket Emote', required: 60, state: 'locked', unlocksAt: '2026-10-18T09:24:00Z' },
75
+ { name: 'Neon Crown', required: 120, state: 'locked', unlocksAt: '2026-10-18T10:24:00Z' },
76
+ ],
77
+ },
78
+ next: { name: 'Hype Rocket Emote', required: 60, unlocksAt: '2026-10-18T09:24:00Z' },
79
+ doneAt: '2026-10-18T10:24:00Z',
80
+ },
81
+ });
82
+
83
+ // A click on another app's widget asks to see Pear Drops.
84
+ link.handle('drops.farming.show', () => {
85
+ mainWindow.show();
86
+ return { shown: true };
62
87
  });
63
88
 
64
89
  // Hear what the other apps provide.
@@ -69,6 +94,10 @@ link.on('event', (fromAppId, name, params) => {
69
94
  // Ask one of them something.
70
95
  const players = await link.request('com.glowingpear.streammultiview', 'players.get');
71
96
 
97
+ // On the other side, an app showing a farming widget (Stream Multiview, say):
98
+ // link.on('event', (from, name, f) => name === 'drops.farming.changed' && drawWidget(f));
99
+ // widget.onclick = () => link.request('com.streamorchard.peardrops', 'drops.farming.show');
100
+
72
101
  // When a Connected apps switch changes:
73
102
  link.refresh();
74
103
 
@@ -123,10 +152,10 @@ Example messages for every capability version, good and bad, are in `@streamorch
123
152
 
124
153
  ## Capabilities
125
154
 
126
- | Capability | Provided by | What it says |
127
- | ------------------- | ---------------- | ------------------------------------------------------------------ |
128
- | `drops.farming` 1.0 | Pear Drops | Whether it's farming, which channel, and the drop's progress |
129
- | `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open |
155
+ | Capability | Provided by | What it says |
156
+ | ------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
157
+ | `drops.farming` 1.0 | Pear Drops | Whether it's farming: the channel, stream and drop, with pictures and every reward's state and when it unlocks, enough for a widget. Also `show`, to bring Pear Drops to the front |
158
+ | `players` 1.0 | Stream Multiview | Its players by site: signed in or not, and which channels are open |
130
159
 
131
160
  To add one:
132
161
 
@@ -0,0 +1,11 @@
1
+ {
2
+ "event": "drops.farming.changed",
3
+ "params": {
4
+ "seq": 1,
5
+ "at": "2026-10-18T09:12:04Z",
6
+ "state": "farming",
7
+ "account": null,
8
+ "channel": { "platform": "kick", "slug": "pixelpear", "avatar": "pd-img://x.png" },
9
+ "drop": null
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "event": "drops.farming.changed",
3
+ "params": {
4
+ "seq": 1,
5
+ "at": "2026-10-18T09:12:04Z",
6
+ "state": "farming",
7
+ "account": null,
8
+ "channel": null,
9
+ "drop": { "campaign": "c", "category": "c", "progress": { "done": 1, "total": 2, "unit": "minutes" }, "next": { "required": 60 } }
10
+ }
11
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "event": "drops.farming.changed",
3
+ "params": {
4
+ "seq": 1,
5
+ "at": "2026-10-18T09:12:04Z",
6
+ "state": "farming",
7
+ "account": null,
8
+ "channel": null,
9
+ "drop": {
10
+ "campaign": "c",
11
+ "category": "c",
12
+ "progress": { "done": 1, "total": 2, "unit": "minutes" },
13
+ "rewards": { "claimed": 0, "total": 1, "items": [{ "name": "Pear Badge", "required": 30 }] }
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "event": "drops.farming.changed",
3
+ "note": "Only the required fields: the rest are optional",
4
+ "params": {
5
+ "seq": 41,
6
+ "at": "2026-10-18T09:10:00Z",
7
+ "state": "farming",
8
+ "account": null,
9
+ "channel": { "platform": "kick", "slug": "pixelpear" },
10
+ "drop": { "campaign": "Neon Rush Launch Week", "category": "Neon Rush", "progress": { "done": 46, "total": 120, "unit": "minutes" } }
11
+ }
12
+ }
@@ -5,7 +5,36 @@
5
5
  "at": "2026-10-18T09:12:04.120Z",
6
6
  "state": "farming",
7
7
  "account": { "platform": "kick", "user": "pearfan" },
8
- "channel": { "platform": "kick", "slug": "pixelpear", "name": "PixelPear" },
9
- "drop": { "campaign": "Neon Rush Launch Week", "category": "Neon Rush", "progress": { "done": 48, "total": 120, "unit": "minutes" } }
8
+ "channel": { "platform": "kick", "slug": "pixelpear", "name": "PixelPear", "avatar": "https://files.kick.com/images/user/1/profile_image/pixelpear.webp" },
9
+ "stream": { "title": "Neon Rush ranked, chill vibes", "viewers": 1240 },
10
+ "drop": {
11
+ "campaign": "Neon Rush Launch Week",
12
+ "category": "Neon Rush",
13
+ "progress": { "done": 48, "total": 120, "unit": "minutes" },
14
+ "image": "https://files.kick.com/images/subcategories/1/banner/neon-rush.webp",
15
+ "endsAt": "2026-10-21T18:00:00Z",
16
+ "doneAt": "2026-10-18T10:24:00Z",
17
+ "rewards": {
18
+ "claimed": 1,
19
+ "total": 3,
20
+ "items": [
21
+ { "name": "Pear Badge", "image": "https://files.kick.com/drops/reward/pear-badge.png", "required": 30, "state": "claimed" },
22
+ {
23
+ "name": "Hype Rocket Emote",
24
+ "image": "https://files.kick.com/drops/reward/hype-rocket.png",
25
+ "required": 60,
26
+ "state": "locked",
27
+ "unlocksAt": "2026-10-18T09:24:00Z"
28
+ },
29
+ { "name": "Neon Crown", "required": 120, "state": "locked", "unlocksAt": "2026-10-18T10:24:00Z" }
30
+ ]
31
+ },
32
+ "next": {
33
+ "name": "Hype Rocket Emote",
34
+ "image": "https://files.kick.com/drops/reward/hype-rocket.png",
35
+ "required": 60,
36
+ "unlocksAt": "2026-10-18T09:24:00Z"
37
+ }
38
+ }
10
39
  }
11
40
  }
@@ -0,0 +1 @@
1
+ { "result": "drops.farming.show", "params": { "shown": true } }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamorchard/grapevine",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Lets desktop apps on the same computer find each other and talk, privately: local sockets, JSON-RPC, pairing and versioned capabilities.",
5
5
  "license": "GPL-3.0-only",
6
6
  "author": "StreamOrchard",
@@ -1,7 +1,17 @@
1
1
  {
2
2
  "description": "Types every capability shares. Lists marked x-open may gain values: an app must accept a value it doesn't know.",
3
3
  "defs": {
4
- "Timestamp": { "type": "string", "format": "date-time", "description": "ISO 8601, in UTC" },
4
+ "Timestamp": {
5
+ "type": "string",
6
+ "format": "date-time",
7
+ "description": "ISO 8601, in UTC"
8
+ },
9
+ "Image": {
10
+ "type": "string",
11
+ "pattern": "^https://",
12
+ "maxLength": 2000,
13
+ "description": "A picture's https address, as the site gives it"
14
+ },
5
15
  "Platform": {
6
16
  "type": "string",
7
17
  "pattern": "^[a-z0-9-]{1,32}$",
@@ -12,9 +22,24 @@
12
22
  "type": "object",
13
23
  "required": ["platform", "slug"],
14
24
  "properties": {
15
- "platform": { "$ref": "common:Platform" },
16
- "slug": { "type": "string", "minLength": 1, "maxLength": 100, "description": "As in the channel's address" },
17
- "name": { "type": "string", "maxLength": 200, "description": "As shown on the site" }
25
+ "platform": {
26
+ "$ref": "common:Platform"
27
+ },
28
+ "slug": {
29
+ "type": "string",
30
+ "minLength": 1,
31
+ "maxLength": 100,
32
+ "description": "As in the channel's address"
33
+ },
34
+ "name": {
35
+ "type": "string",
36
+ "maxLength": 200,
37
+ "description": "As shown on the site"
38
+ },
39
+ "avatar": {
40
+ "$ref": "common:Image",
41
+ "description": "The channel's picture"
42
+ }
18
43
  }
19
44
  },
20
45
  "Account": {
@@ -22,19 +47,36 @@
22
47
  "required": ["platform", "user"],
23
48
  "description": "Who an app is signed in as: the username only, never a token or cookie",
24
49
  "properties": {
25
- "platform": { "$ref": "common:Platform" },
26
- "user": { "type": "string", "minLength": 1, "maxLength": 100 }
50
+ "platform": {
51
+ "$ref": "common:Platform"
52
+ },
53
+ "user": {
54
+ "type": "string",
55
+ "minLength": 1,
56
+ "maxLength": 100
57
+ }
27
58
  }
28
59
  },
29
60
  "Progress": {
30
61
  "type": "object",
31
62
  "required": ["done", "total", "unit"],
32
63
  "properties": {
33
- "done": { "type": "number", "minimum": 0 },
34
- "total": { "type": "number", "minimum": 0 },
35
- "unit": { "type": "string", "x-open": ["minutes"] }
64
+ "done": {
65
+ "type": "number",
66
+ "minimum": 0
67
+ },
68
+ "total": {
69
+ "type": "number",
70
+ "minimum": 0
71
+ },
72
+ "unit": {
73
+ "type": "string",
74
+ "x-open": ["minutes"]
75
+ }
36
76
  }
37
77
  },
38
- "Empty": { "type": "object" }
78
+ "Empty": {
79
+ "type": "object"
80
+ }
39
81
  }
40
82
  }
@@ -2,20 +2,50 @@
2
2
  "capability": "drops.farming",
3
3
  "major": 1,
4
4
  "minor": 0,
5
- "description": "What a drops app is farming. Provided by Pear Drops.",
5
+ "description": "What a drops app is farming, with enough to show it (a top-bar widget, say). Provided by Pear Drops.",
6
6
  "events": {
7
- "drops.farming.changed": { "$ref": "#State" }
7
+ "drops.farming.changed": {
8
+ "$ref": "#State"
9
+ }
8
10
  },
9
11
  "methods": {
10
- "drops.farming.get": { "params": { "$ref": "common:Empty" }, "result": { "$ref": "#State" } }
12
+ "drops.farming.get": {
13
+ "params": {
14
+ "$ref": "common:Empty"
15
+ },
16
+ "result": {
17
+ "$ref": "#State"
18
+ }
19
+ },
20
+ "drops.farming.show": {
21
+ "params": {
22
+ "$ref": "common:Empty"
23
+ },
24
+ "result": {
25
+ "type": "object",
26
+ "required": ["shown"],
27
+ "properties": {
28
+ "shown": {
29
+ "type": "boolean",
30
+ "description": "Whether its window came to the front"
31
+ }
32
+ }
33
+ }
34
+ }
11
35
  },
12
36
  "defs": {
13
37
  "State": {
14
38
  "type": "object",
15
39
  "required": ["seq", "at", "state", "account", "channel", "drop"],
16
40
  "properties": {
17
- "seq": { "type": "integer", "minimum": 0, "description": "Counts up with each change; 0 for a reply to drops.farming.get" },
18
- "at": { "$ref": "common:Timestamp" },
41
+ "seq": {
42
+ "type": "integer",
43
+ "minimum": 0,
44
+ "description": "Counts up with each change; 0 for a reply to drops.farming.get"
45
+ },
46
+ "at": {
47
+ "$ref": "common:Timestamp"
48
+ },
19
49
  "state": {
20
50
  "type": "string",
21
51
  "minLength": 1,
@@ -23,21 +53,185 @@
23
53
  "x-open": ["farming", "waiting", "paused", "done", "signed-out"],
24
54
  "description": "farming: its player is watching a stream for drop credit. Any other value, known or not, means it isn't."
25
55
  },
26
- "account": { "anyOf": [{ "$ref": "common:Account" }, { "type": "null" }] },
27
- "channel": { "anyOf": [{ "$ref": "common:Channel" }, { "type": "null" }], "description": "The stream being watched, while farming" },
56
+ "account": {
57
+ "anyOf": [
58
+ {
59
+ "$ref": "common:Account"
60
+ },
61
+ {
62
+ "type": "null"
63
+ }
64
+ ]
65
+ },
66
+ "channel": {
67
+ "anyOf": [
68
+ {
69
+ "$ref": "common:Channel"
70
+ },
71
+ {
72
+ "type": "null"
73
+ }
74
+ ],
75
+ "description": "The stream being watched, while farming"
76
+ },
28
77
  "drop": {
29
78
  "anyOf": [
30
79
  {
31
80
  "type": "object",
32
81
  "required": ["campaign", "category", "progress"],
33
82
  "properties": {
34
- "campaign": { "type": "string", "maxLength": 200 },
35
- "category": { "type": "string", "maxLength": 200 },
36
- "progress": { "$ref": "common:Progress" }
83
+ "campaign": {
84
+ "type": "string",
85
+ "maxLength": 200
86
+ },
87
+ "category": {
88
+ "type": "string",
89
+ "maxLength": 200
90
+ },
91
+ "progress": {
92
+ "$ref": "common:Progress"
93
+ },
94
+ "image": {
95
+ "$ref": "common:Image",
96
+ "description": "The category's picture"
97
+ },
98
+ "endsAt": {
99
+ "$ref": "common:Timestamp",
100
+ "description": "When the drop ends"
101
+ },
102
+ "doneAt": {
103
+ "anyOf": [
104
+ {
105
+ "$ref": "common:Timestamp"
106
+ },
107
+ {
108
+ "type": "null"
109
+ }
110
+ ],
111
+ "description": "When every reward should be earned, at the current rate"
112
+ },
113
+ "rewards": {
114
+ "type": "object",
115
+ "required": ["claimed", "total"],
116
+ "properties": {
117
+ "claimed": {
118
+ "type": "integer",
119
+ "minimum": 0
120
+ },
121
+ "total": {
122
+ "type": "integer",
123
+ "minimum": 0
124
+ },
125
+ "items": {
126
+ "type": "array",
127
+ "maxItems": 20,
128
+ "items": {
129
+ "$ref": "#Reward"
130
+ },
131
+ "description": "Every reward of the drop, in the order they unlock"
132
+ }
133
+ }
134
+ },
135
+ "next": {
136
+ "anyOf": [
137
+ {
138
+ "type": "object",
139
+ "required": ["name", "required"],
140
+ "properties": {
141
+ "name": {
142
+ "type": "string",
143
+ "maxLength": 200
144
+ },
145
+ "image": {
146
+ "$ref": "common:Image"
147
+ },
148
+ "required": {
149
+ "type": "number",
150
+ "minimum": 0,
151
+ "description": "Minutes of watching it unlocks at"
152
+ },
153
+ "unlocksAt": {
154
+ "anyOf": [
155
+ {
156
+ "$ref": "common:Timestamp"
157
+ },
158
+ {
159
+ "type": "null"
160
+ }
161
+ ],
162
+ "description": "When it should unlock, at the current rate"
163
+ }
164
+ }
165
+ },
166
+ {
167
+ "type": "null"
168
+ }
169
+ ],
170
+ "description": "The reward being worked towards"
171
+ }
37
172
  }
38
173
  },
39
- { "type": "null" }
174
+ {
175
+ "type": "null"
176
+ }
40
177
  ]
178
+ },
179
+ "stream": {
180
+ "anyOf": [
181
+ {
182
+ "type": "object",
183
+ "properties": {
184
+ "title": {
185
+ "type": "string",
186
+ "maxLength": 300
187
+ },
188
+ "viewers": {
189
+ "type": "integer",
190
+ "minimum": 0
191
+ }
192
+ }
193
+ },
194
+ {
195
+ "type": "null"
196
+ }
197
+ ],
198
+ "description": "The stream being watched, while farming: its title and viewers"
199
+ }
200
+ }
201
+ },
202
+ "Reward": {
203
+ "type": "object",
204
+ "required": ["name", "required", "state"],
205
+ "properties": {
206
+ "name": {
207
+ "type": "string",
208
+ "maxLength": 200
209
+ },
210
+ "image": {
211
+ "$ref": "common:Image"
212
+ },
213
+ "required": {
214
+ "type": "number",
215
+ "minimum": 0,
216
+ "description": "Minutes of watching it unlocks at"
217
+ },
218
+ "state": {
219
+ "type": "string",
220
+ "minLength": 1,
221
+ "maxLength": 32,
222
+ "x-open": ["claimed", "ready", "locked"],
223
+ "description": "ready: unlocked, waiting to be claimed"
224
+ },
225
+ "unlocksAt": {
226
+ "anyOf": [
227
+ {
228
+ "$ref": "common:Timestamp"
229
+ },
230
+ {
231
+ "type": "null"
232
+ }
233
+ ],
234
+ "description": "For a locked one: when it should unlock, at the current rate"
41
235
  }
42
236
  }
43
237
  }
@@ -3,6 +3,9 @@
3
3
  /** ISO 8601, in UTC */
4
4
  export type Timestamp = string;
5
5
 
6
+ /** A picture's https address, as the site gives it */
7
+ export type Image = string;
8
+
6
9
  /** A streaming site */
7
10
  export type Platform = "kick" | "twitch" | "youtube" | (string & {});
8
11
 
@@ -12,6 +15,8 @@ export type Channel = {
12
15
  slug: string;
13
16
  /** As shown on the site */
14
17
  name?: string;
18
+ /** The channel's picture */
19
+ avatar?: Image;
15
20
  };
16
21
 
17
22
  /** Who an app is signed in as: the username only, never a token or cookie */
@@ -28,7 +33,7 @@ export type Progress = {
28
33
 
29
34
  export type Empty = Record<string, unknown>;
30
35
 
31
- // drops.farming 1.0: What a drops app is farming. Provided by Pear Drops.
36
+ // drops.farming 1.0: What a drops app is farming, with enough to show it (a top-bar widget, say). Provided by Pear Drops.
32
37
  export type DropsFarmingV1State = {
33
38
  /** Counts up with each change; 0 for a reply to drops.farming.get */
34
39
  seq: number;
@@ -42,9 +47,46 @@ export type DropsFarmingV1State = {
42
47
  campaign: string;
43
48
  category: string;
44
49
  progress: Progress;
50
+ /** The category's picture */
51
+ image?: Image;
52
+ /** When the drop ends */
53
+ endsAt?: Timestamp;
54
+ /** When every reward should be earned, at the current rate */
55
+ doneAt?: Timestamp | null;
56
+ rewards?: {
57
+ claimed: number;
58
+ total: number;
59
+ /** Every reward of the drop, in the order they unlock */
60
+ items?: Array<DropsFarmingV1Reward>;
61
+ };
62
+ /** The reward being worked towards */
63
+ next?: {
64
+ name: string;
65
+ image?: Image;
66
+ /** Minutes of watching it unlocks at */
67
+ required: number;
68
+ /** When it should unlock, at the current rate */
69
+ unlocksAt?: Timestamp | null;
70
+ } | null;
71
+ } | null;
72
+ /** The stream being watched, while farming: its title and viewers */
73
+ stream?: {
74
+ title?: string;
75
+ viewers?: number;
45
76
  } | null;
46
77
  };
47
78
 
79
+ export type DropsFarmingV1Reward = {
80
+ name: string;
81
+ image?: Image;
82
+ /** Minutes of watching it unlocks at */
83
+ required: number;
84
+ /** ready: unlocked, waiting to be claimed */
85
+ state: "claimed" | "ready" | "locked" | (string & {});
86
+ /** For a locked one: when it should unlock, at the current rate */
87
+ unlocksAt?: Timestamp | null;
88
+ };
89
+
48
90
  // players 1.0: An app's stream players, by site: whether they're signed in, and which channels are open. Provided by Stream Multiview.
49
91
  export type PlayersV1State = {
50
92
  seq: number;
@@ -73,5 +115,9 @@ export interface Events {
73
115
  /** Every method, by name: its params and result. */
74
116
  export interface Methods {
75
117
  "drops.farming.get": { params: Empty; result: DropsFarmingV1State };
118
+ "drops.farming.show": { params: Empty; result: {
119
+ /** Whether its window came to the front */
120
+ shown: boolean;
121
+ } };
76
122
  "players.get": { params: Empty; result: PlayersV1State };
77
123
  }