@opentray/spec 0.6.0 → 0.10.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 +24 -18
- package/dist/index.d.mts +58 -55
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +13 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,27 +4,33 @@ Shared TypeScript protocol and contract package for OpenTray.
|
|
|
4
4
|
|
|
5
5
|
## Role
|
|
6
6
|
|
|
7
|
-
- Define
|
|
8
|
-
- Define
|
|
9
|
-
- Define public `
|
|
10
|
-
- Keep protocol types reusable by
|
|
7
|
+
- Define newline-delimited JSON protocol payload shapes.
|
|
8
|
+
- Define protocol version and endpoint identity helpers.
|
|
9
|
+
- Define public `App`, `Tray`, `Session`, icon projection, menu, tooltip, and extension contract types.
|
|
10
|
+
- Keep protocol types reusable by `opentray` and official extensions.
|
|
11
11
|
|
|
12
|
-
This package
|
|
12
|
+
This package is platform-neutral and must not import native implementation packages.
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Tray Contract
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
pnpm --filter @opentray/spec example:parse
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
Endpoint identity is version-scoped during the current unstable broker stage:
|
|
16
|
+
`TrayOptions` uses `id` as the tray atom identity. Visible tray text belongs to the unified `icon` field:
|
|
23
17
|
|
|
24
18
|
```ts
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
import type { Icon, TrayOptions } from "@opentray/spec";
|
|
20
|
+
|
|
21
|
+
const icon: Icon = {
|
|
22
|
+
type: "file",
|
|
23
|
+
path: "./status.png",
|
|
24
|
+
text: "Status",
|
|
25
|
+
"icon-only": { type: "file", path: "./status-small.png" },
|
|
26
|
+
"text-only": "Status",
|
|
27
|
+
"icon-text": { type: "file", path: "./status.png", text: "Status" },
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const tray: TrayOptions = {
|
|
31
|
+
id: "com.example.status",
|
|
32
|
+
icon,
|
|
33
|
+
};
|
|
30
34
|
```
|
|
35
|
+
|
|
36
|
+
`Space`, `Surface`, `spaceId`, `create-space`, and top-level tray `title` are removed public vocabulary for v0.9.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
//#region src/index.d.ts
|
|
2
2
|
type SessionId = string;
|
|
3
3
|
type RequestId = string;
|
|
4
|
-
type
|
|
4
|
+
type AppId = string;
|
|
5
5
|
type TrayId = string;
|
|
6
6
|
type MenuItemId = number;
|
|
7
|
-
/** @deprecated Use `SessionId`. */
|
|
8
|
-
type LeaseId = SessionId;
|
|
9
|
-
/** @deprecated Use `SpaceId`. */
|
|
10
|
-
type SurfaceId = SpaceId;
|
|
11
7
|
declare const PROTOCOL_VERSION = 1;
|
|
12
8
|
declare const OPENTRAY_PROTOCOL_FAMILY = "opentray-protocol";
|
|
13
9
|
declare const OPENTRAY_PROTOCOL_LINE_MAJOR = 1;
|
|
@@ -80,23 +76,21 @@ declare const formatWindowsPipeName: (identity: BrokerEndpointIdentity) => strin
|
|
|
80
76
|
* spawn path so task managers show the owning application, not a generic name.
|
|
81
77
|
*/
|
|
82
78
|
declare const formatBrokerProcessTitle: (identity: BrokerEndpointIdentity) => string;
|
|
83
|
-
interface
|
|
84
|
-
id?:
|
|
85
|
-
|
|
79
|
+
interface AppOptions {
|
|
80
|
+
id?: AppId;
|
|
81
|
+
name?: string;
|
|
86
82
|
icon?: Icon;
|
|
87
83
|
default?: boolean;
|
|
88
84
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
interface AppRef {
|
|
86
|
+
appId: AppId;
|
|
87
|
+
}
|
|
88
|
+
interface AppIdentity {
|
|
89
|
+
appId: AppId;
|
|
90
|
+
appName: string;
|
|
93
91
|
}
|
|
94
|
-
/** @deprecated Use `SpaceRef`. */
|
|
95
|
-
type SurfaceRef = SpaceRef;
|
|
96
92
|
interface TrayOptions {
|
|
97
|
-
|
|
98
|
-
appId?: string;
|
|
99
|
-
title?: string;
|
|
93
|
+
id: string;
|
|
100
94
|
tooltip?: Tooltip;
|
|
101
95
|
icon?: Icon;
|
|
102
96
|
menu?: Menu;
|
|
@@ -136,7 +130,7 @@ type MenuItem = {
|
|
|
136
130
|
enabled?: boolean;
|
|
137
131
|
items: MenuItem[];
|
|
138
132
|
};
|
|
139
|
-
type
|
|
133
|
+
type IconImage = {
|
|
140
134
|
type: "rgba";
|
|
141
135
|
data: Uint8Array | number[];
|
|
142
136
|
width: number;
|
|
@@ -148,6 +142,18 @@ type Icon = {
|
|
|
148
142
|
type: "file";
|
|
149
143
|
path: string;
|
|
150
144
|
};
|
|
145
|
+
type SimpleIcon = IconImage & {
|
|
146
|
+
text?: string;
|
|
147
|
+
};
|
|
148
|
+
type IconText = IconImage & {
|
|
149
|
+
text: string;
|
|
150
|
+
};
|
|
151
|
+
interface IconCandidates {
|
|
152
|
+
"icon-only"?: IconImage;
|
|
153
|
+
"text-only"?: string;
|
|
154
|
+
"icon-text"?: IconText;
|
|
155
|
+
}
|
|
156
|
+
type Icon = IconCandidates & Partial<SimpleIcon>;
|
|
151
157
|
interface Rect {
|
|
152
158
|
x: number;
|
|
153
159
|
y: number;
|
|
@@ -163,29 +169,29 @@ interface TrayBoundsResult {
|
|
|
163
169
|
type MouseButton = "left" | "right" | "middle";
|
|
164
170
|
type TrayEvent = {
|
|
165
171
|
type: "ready";
|
|
166
|
-
|
|
172
|
+
appId: AppId;
|
|
167
173
|
} | {
|
|
168
174
|
type: "menuClick";
|
|
169
|
-
|
|
175
|
+
appId: AppId;
|
|
170
176
|
trayId: TrayId;
|
|
171
177
|
itemId: MenuItemId;
|
|
172
178
|
} | {
|
|
173
179
|
type: "trayClick";
|
|
174
|
-
|
|
180
|
+
appId: AppId;
|
|
175
181
|
trayId: TrayId;
|
|
176
182
|
button: MouseButton;
|
|
177
183
|
x: number;
|
|
178
184
|
y: number;
|
|
179
185
|
} | {
|
|
180
186
|
type: "trayDoubleClick";
|
|
181
|
-
|
|
187
|
+
appId: AppId;
|
|
182
188
|
trayId: TrayId;
|
|
183
189
|
button: MouseButton;
|
|
184
190
|
x: number;
|
|
185
191
|
y: number;
|
|
186
192
|
};
|
|
187
193
|
interface ExtensionScope {
|
|
188
|
-
|
|
194
|
+
appId: AppId;
|
|
189
195
|
trayId?: TrayId;
|
|
190
196
|
ext: string;
|
|
191
197
|
}
|
|
@@ -193,18 +199,21 @@ interface ExtensionEnvelope<TData = unknown> {
|
|
|
193
199
|
scope: ExtensionScope;
|
|
194
200
|
data: TData;
|
|
195
201
|
}
|
|
196
|
-
interface
|
|
202
|
+
interface RuntimeHostSessionHealth {
|
|
197
203
|
sessionId: number;
|
|
198
|
-
|
|
204
|
+
internalSessionId?: SessionId;
|
|
199
205
|
initialized: boolean;
|
|
200
206
|
}
|
|
201
|
-
interface
|
|
207
|
+
interface RuntimeHostHealth {
|
|
202
208
|
pid: number;
|
|
203
209
|
packageVersion: string;
|
|
204
210
|
protocolVersion: number;
|
|
205
211
|
endpoint: string;
|
|
212
|
+
appId: AppId;
|
|
213
|
+
appName: string;
|
|
214
|
+
callerLabel: string;
|
|
206
215
|
sessionCount: number;
|
|
207
|
-
sessions:
|
|
216
|
+
sessions: RuntimeHostSessionHealth[];
|
|
208
217
|
}
|
|
209
218
|
type ClientFrame = {
|
|
210
219
|
type: "init";
|
|
@@ -214,68 +223,62 @@ type ClientFrame = {
|
|
|
214
223
|
type: "exit";
|
|
215
224
|
};
|
|
216
225
|
type ClientRequestFrame = ({
|
|
217
|
-
type: "create-
|
|
226
|
+
type: "create-app";
|
|
218
227
|
requestId: RequestId;
|
|
219
|
-
} &
|
|
220
|
-
type: "resolve-default-
|
|
228
|
+
} & AppOptions) | {
|
|
229
|
+
type: "resolve-default-app";
|
|
221
230
|
requestId: RequestId;
|
|
222
231
|
} | {
|
|
223
232
|
type: "create-tray";
|
|
224
233
|
requestId: RequestId;
|
|
225
|
-
|
|
234
|
+
app: AppRef;
|
|
226
235
|
tray: TrayOptions;
|
|
227
236
|
} | {
|
|
228
237
|
type: "destroy-tray";
|
|
229
238
|
requestId: RequestId;
|
|
230
|
-
|
|
239
|
+
appId: AppId;
|
|
231
240
|
trayId: TrayId;
|
|
232
241
|
} | {
|
|
233
242
|
type: "get-tray-bounds";
|
|
234
243
|
requestId: RequestId;
|
|
235
|
-
|
|
244
|
+
appId: AppId;
|
|
236
245
|
trayId: TrayId;
|
|
237
246
|
} | {
|
|
238
247
|
type: "set-tray-menu";
|
|
239
248
|
requestId: RequestId;
|
|
240
|
-
|
|
249
|
+
appId: AppId;
|
|
241
250
|
trayId: TrayId;
|
|
242
251
|
menu: Menu;
|
|
243
252
|
} | {
|
|
244
253
|
type: "set-tray-icon";
|
|
245
254
|
requestId: RequestId;
|
|
246
|
-
|
|
255
|
+
appId: AppId;
|
|
247
256
|
trayId: TrayId;
|
|
248
257
|
icon: Icon;
|
|
249
258
|
} | {
|
|
250
259
|
type: "set-tray-tooltip";
|
|
251
260
|
requestId: RequestId;
|
|
252
|
-
|
|
261
|
+
appId: AppId;
|
|
253
262
|
trayId: TrayId;
|
|
254
263
|
tooltip: Tooltip;
|
|
255
|
-
} | {
|
|
256
|
-
type: "set-tray-title";
|
|
257
|
-
requestId: RequestId;
|
|
258
|
-
spaceId: SpaceId;
|
|
259
|
-
trayId: TrayId;
|
|
260
|
-
title: string;
|
|
261
264
|
} | {
|
|
262
265
|
type: "load-ext";
|
|
263
266
|
requestId: RequestId;
|
|
264
|
-
|
|
267
|
+
appId: AppId;
|
|
265
268
|
name: string;
|
|
266
269
|
path: string;
|
|
267
270
|
mountId?: string;
|
|
268
271
|
} | {
|
|
269
272
|
type: "ext-command";
|
|
270
273
|
requestId: RequestId;
|
|
271
|
-
|
|
274
|
+
appId: AppId;
|
|
272
275
|
trayId: TrayId;
|
|
273
276
|
ext: string;
|
|
274
277
|
data: unknown;
|
|
275
278
|
} | {
|
|
276
279
|
type: "unload-ext";
|
|
277
280
|
requestId: RequestId;
|
|
278
|
-
|
|
281
|
+
appId: AppId;
|
|
279
282
|
name: string;
|
|
280
283
|
} | {
|
|
281
284
|
type: "health";
|
|
@@ -287,22 +290,22 @@ type ServerFrame = {
|
|
|
287
290
|
brokerVersion: string;
|
|
288
291
|
sessionId: SessionId;
|
|
289
292
|
} | {
|
|
290
|
-
type: "
|
|
293
|
+
type: "app-created";
|
|
291
294
|
requestId: RequestId;
|
|
292
|
-
|
|
295
|
+
app: AppRef;
|
|
293
296
|
} | {
|
|
294
|
-
type: "default-
|
|
297
|
+
type: "default-app";
|
|
295
298
|
requestId: RequestId;
|
|
296
|
-
|
|
299
|
+
app: AppRef;
|
|
297
300
|
} | {
|
|
298
301
|
type: "tray-created";
|
|
299
302
|
requestId: RequestId;
|
|
300
|
-
|
|
303
|
+
appId: AppId;
|
|
301
304
|
trayId: TrayId;
|
|
302
305
|
} | {
|
|
303
306
|
type: "tray-bounds";
|
|
304
307
|
requestId: RequestId;
|
|
305
|
-
|
|
308
|
+
appId: AppId;
|
|
306
309
|
trayId: TrayId;
|
|
307
310
|
bounds: TrayBoundsResult;
|
|
308
311
|
} | {
|
|
@@ -313,15 +316,15 @@ type ServerFrame = {
|
|
|
313
316
|
requestId: RequestId;
|
|
314
317
|
events: ExtensionEnvelope[];
|
|
315
318
|
} | {
|
|
316
|
-
type: "
|
|
319
|
+
type: "runtime-host-health";
|
|
317
320
|
requestId: RequestId;
|
|
318
|
-
health:
|
|
321
|
+
health: RuntimeHostHealth;
|
|
319
322
|
} | {
|
|
320
323
|
type: "event";
|
|
321
324
|
event: TrayEvent;
|
|
322
325
|
} | {
|
|
323
326
|
type: "ext-event";
|
|
324
|
-
|
|
327
|
+
appId: AppId;
|
|
325
328
|
trayId: TrayId;
|
|
326
329
|
ext: string;
|
|
327
330
|
data: unknown;
|
|
@@ -339,5 +342,5 @@ interface ParseResult<T> {
|
|
|
339
342
|
declare const parseServerFrame: (line: string) => ParseResult<ServerFrame>;
|
|
340
343
|
declare const isServerFrame: (value: unknown) => value is ServerFrame;
|
|
341
344
|
//#endregion
|
|
342
|
-
export { BrokerEndpointIdentity, BrokerEndpointIdentityOptions, CALLER_LABEL_MAX_LENGTH, ClientFrame, ClientRequestFrame, DEFAULT_CALLER_LABEL,
|
|
345
|
+
export { AppId, AppIdentity, AppOptions, AppRef, BrokerEndpointIdentity, BrokerEndpointIdentityOptions, CALLER_LABEL_MAX_LENGTH, ClientFrame, ClientRequestFrame, DEFAULT_CALLER_LABEL, ExtensionEnvelope, ExtensionScope, Icon, IconCandidates, IconImage, IconText, Menu, MenuItem, MenuItemId, MouseButton, OPENTRAY_PROTOCOL_FAMILY, OPENTRAY_PROTOCOL_LINE, OPENTRAY_PROTOCOL_LINE_MAJOR, OPENTRAY_PROTOCOL_LINE_MINOR, OpenTrayProtocolLine, PROTOCOL_VERSION, ParseResult, ProtocolDistTag, ProtocolDistTagOptions, ProtocolLineReleaseChannel, Rect, RequestId, RuntimeHostHealth, RuntimeHostSessionHealth, ServerFrame, SessionId, SimpleIcon, Tooltip, TrayBoundsKind, TrayBoundsResult, TrayEvent, TrayId, TrayOptions, compareOpenTrayProtocolLine, createBrokerEndpointIdentity, formatBrokerEndpointName, formatBrokerProcessTitle, formatBrokerStateRoot, formatOpenTrayProtocolLine, formatProtocolDistTag, formatUnixSocketPath, formatWindowsPipeName, isOpenTrayProtocolLineCompatible, isProtocolLineReleaseChannel, isServerFrame, isSupportedProtocolVersion, parseProtocolDistTag, parseServerFrame, protocolLineReleaseChannels, sanitizeCallerLabel };
|
|
343
346
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";KAAY,SAAA;AAAA,KACA,SAAA;AAAA,KACA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -137,15 +137,15 @@ const isServerFrame = (value) => {
|
|
|
137
137
|
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
138
138
|
switch (value.type) {
|
|
139
139
|
case "ready": return typeof value.protocolVersion === "number" && typeof value.brokerVersion === "string" && typeof value.sessionId === "string";
|
|
140
|
-
case "
|
|
141
|
-
case "default-
|
|
142
|
-
case "tray-created": return typeof value.requestId === "string" && typeof value.
|
|
143
|
-
case "tray-bounds": return typeof value.requestId === "string" && typeof value.
|
|
140
|
+
case "app-created": return typeof value.requestId === "string" && isRecord(value.app);
|
|
141
|
+
case "default-app": return typeof value.requestId === "string" && isRecord(value.app);
|
|
142
|
+
case "tray-created": return typeof value.requestId === "string" && typeof value.appId === "string" && typeof value.trayId === "string";
|
|
143
|
+
case "tray-bounds": return typeof value.requestId === "string" && typeof value.appId === "string" && typeof value.trayId === "string" && isTrayBoundsResult(value.bounds);
|
|
144
144
|
case "ack": return typeof value.requestId === "string";
|
|
145
145
|
case "ext-command-result": return typeof value.requestId === "string" && Array.isArray(value.events) && value.events.every(isExtensionEnvelope);
|
|
146
|
-
case "
|
|
146
|
+
case "runtime-host-health": return typeof value.requestId === "string" && isRuntimeHostHealth(value.health);
|
|
147
147
|
case "event": return isTrayEvent(value.event);
|
|
148
|
-
case "ext-event": return typeof value.
|
|
148
|
+
case "ext-event": return typeof value.appId === "string" && typeof value.trayId === "string" && typeof value.ext === "string";
|
|
149
149
|
case "error": return (value.requestId === void 0 || typeof value.requestId === "string") && typeof value.code === "string" && typeof value.message === "string";
|
|
150
150
|
default: return false;
|
|
151
151
|
}
|
|
@@ -153,22 +153,22 @@ const isServerFrame = (value) => {
|
|
|
153
153
|
const isExtensionEnvelope = (value) => {
|
|
154
154
|
if (!isRecord(value) || !isRecord(value.scope)) return false;
|
|
155
155
|
const scope = value.scope;
|
|
156
|
-
return
|
|
156
|
+
return typeof scope.appId === "string" && (scope.trayId === void 0 || typeof scope.trayId === "string") && typeof scope.ext === "string" && "data" in value;
|
|
157
157
|
};
|
|
158
|
-
const
|
|
158
|
+
const isRuntimeHostHealth = (value) => {
|
|
159
159
|
if (!isRecord(value)) return false;
|
|
160
|
-
return typeof value.pid === "number" && typeof value.packageVersion === "string" && typeof value.protocolVersion === "number" && typeof value.endpoint === "string" && typeof value.sessionCount === "number" && Array.isArray(value.sessions) && value.sessions.every(
|
|
160
|
+
return typeof value.pid === "number" && typeof value.packageVersion === "string" && typeof value.protocolVersion === "number" && typeof value.endpoint === "string" && typeof value.appId === "string" && typeof value.appName === "string" && typeof value.callerLabel === "string" && typeof value.sessionCount === "number" && Array.isArray(value.sessions) && value.sessions.every(isRuntimeHostSessionHealth);
|
|
161
161
|
};
|
|
162
|
-
const
|
|
162
|
+
const isRuntimeHostSessionHealth = (value) => isRecord(value) && typeof value.sessionId === "number" && (value.internalSessionId === void 0 || typeof value.internalSessionId === "string") && typeof value.initialized === "boolean";
|
|
163
163
|
const isRect = (value) => isRecord(value) && typeof value.x === "number" && typeof value.y === "number" && typeof value.width === "number" && typeof value.height === "number";
|
|
164
164
|
const isTrayBoundsResult = (value) => isRecord(value) && (value.kind === "native" || value.kind === "inferred" || value.kind === "unavailable") && typeof value.source === "string" && (value.rect === null || isRect(value.rect));
|
|
165
165
|
const isTrayEvent = (value) => {
|
|
166
166
|
if (!isRecord(value) || typeof value.type !== "string") return false;
|
|
167
167
|
switch (value.type) {
|
|
168
|
-
case "ready": return typeof value.
|
|
169
|
-
case "menuClick": return typeof value.
|
|
168
|
+
case "ready": return typeof value.appId === "string";
|
|
169
|
+
case "menuClick": return typeof value.appId === "string" && typeof value.trayId === "string" && typeof value.itemId === "number";
|
|
170
170
|
case "trayClick":
|
|
171
|
-
case "trayDoubleClick": return typeof value.
|
|
171
|
+
case "trayDoubleClick": return typeof value.appId === "string" && typeof value.trayId === "string" && isMouseButton(value.button) && typeof value.x === "number" && typeof value.y === "number";
|
|
172
172
|
default: return false;
|
|
173
173
|
}
|
|
174
174
|
};
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type SessionId = string;\nexport type RequestId = string;\nexport type SpaceId = string;\nexport type TrayId = string;\nexport type MenuItemId = number;\n\n/** @deprecated Use `SessionId`. */\nexport type LeaseId = SessionId;\n/** @deprecated Use `SpaceId`. */\nexport type SurfaceId = SpaceId;\n\nexport const PROTOCOL_VERSION = 1;\nexport const OPENTRAY_PROTOCOL_FAMILY = \"opentray-protocol\";\nexport const OPENTRAY_PROTOCOL_LINE_MAJOR = 1;\nexport const OPENTRAY_PROTOCOL_LINE_MINOR = 1;\n\nexport const protocolLineReleaseChannels = [\"stable\", \"alpha\"] as const;\nexport type ProtocolLineReleaseChannel = (typeof protocolLineReleaseChannels)[number];\n\nexport interface OpenTrayProtocolLine {\n family: typeof OPENTRAY_PROTOCOL_FAMILY;\n major: number;\n minor: number;\n}\n\nexport interface ProtocolDistTagOptions {\n channel: ProtocolLineReleaseChannel;\n major?: number;\n minor?: number;\n}\n\nexport interface ProtocolDistTag {\n channel: ProtocolLineReleaseChannel;\n major: number;\n minor: number;\n}\n\nexport const OPENTRAY_PROTOCOL_LINE: OpenTrayProtocolLine = {\n family: OPENTRAY_PROTOCOL_FAMILY,\n major: OPENTRAY_PROTOCOL_LINE_MAJOR,\n minor: OPENTRAY_PROTOCOL_LINE_MINOR,\n};\n\n// Install-time protocol lines can advance by minor version while runtime authority stays numeric.\nexport const compareOpenTrayProtocolLine = (\n left: OpenTrayProtocolLine,\n right: OpenTrayProtocolLine,\n): number => {\n assertOpenTrayProtocolLine(left, \"left\");\n assertOpenTrayProtocolLine(right, \"right\");\n if (left.major !== right.major) {\n return left.major - right.major;\n }\n return left.minor - right.minor;\n};\n\nexport const isOpenTrayProtocolLineCompatible = (\n supported: OpenTrayProtocolLine,\n required: OpenTrayProtocolLine,\n): boolean => {\n assertOpenTrayProtocolLine(supported, \"supported\");\n assertOpenTrayProtocolLine(required, \"required\");\n return supported.major === required.major && supported.minor >= required.minor;\n};\n\nexport const formatOpenTrayProtocolLine = ({\n family,\n major,\n minor,\n}: OpenTrayProtocolLine = OPENTRAY_PROTOCOL_LINE): string => {\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return `${family}/${major}.${minor}`;\n};\n\nexport const isProtocolLineReleaseChannel = (value: string): value is ProtocolLineReleaseChannel =>\n protocolLineReleaseChannels.includes(value as ProtocolLineReleaseChannel);\n\nexport const formatProtocolDistTag = ({\n channel,\n major = OPENTRAY_PROTOCOL_LINE_MAJOR,\n minor = OPENTRAY_PROTOCOL_LINE_MINOR,\n}: ProtocolDistTagOptions): string => {\n assertProtocolLineReleaseChannel(channel);\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return `${channel}-${major}-${minor}`;\n};\n\nexport const parseProtocolDistTag = (tag: string): ProtocolDistTag => {\n const match = /^(?<channel>[a-z]+)-(?<major>\\d+)-(?<minor>\\d+)$/u.exec(tag);\n const groups = match?.groups;\n if (groups === undefined) {\n throw new Error(`invalid OpenTray protocol dist-tag: ${tag}`);\n }\n const channel = groups.channel;\n if (channel === undefined || !isProtocolLineReleaseChannel(channel)) {\n throw new Error(`unsupported OpenTray protocol dist-tag channel: ${channel ?? \"\"}`);\n }\n const major = Number(groups.major);\n const minor = Number(groups.minor);\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return { channel, major, minor };\n};\n\n/**\n * Neutral caller label used when no usable caller identity can be derived.\n * Keeps the broker honest instead of impersonating an unrelated application.\n */\nexport const DEFAULT_CALLER_LABEL = \"opentray\";\n\n/**\n * Maximum length of a sanitized caller label. Keeps socket paths, runtime\n * directory names, and process titles within platform limits.\n */\nexport const CALLER_LABEL_MAX_LENGTH = 48;\n\nconst callerLabelAllowedPattern = /[a-z0-9-]+/g;\n\nexport interface BrokerEndpointIdentity {\n packageVersion: string;\n protocolVersion: number;\n callerLabel: string;\n}\n\nexport interface BrokerEndpointIdentityOptions {\n packageVersion: string;\n protocolVersion?: number;\n callerLabel?: string;\n}\n\nexport const sanitizeCallerLabel = (value: string | undefined): string => {\n if (value !== undefined && typeof value !== \"string\") {\n throw new Error(`callerLabel must be a string: ${String(value)}`);\n }\n const raw = value ?? \"\";\n const lowered = raw.toLowerCase();\n const segments = lowered.match(callerLabelAllowedPattern);\n if (segments === null || segments.length === 0) {\n return DEFAULT_CALLER_LABEL;\n }\n const joined = segments.join(\"-\");\n const trimmed = joined.replace(/^-+|-+$/gu, \"\");\n if (trimmed.length === 0) {\n return DEFAULT_CALLER_LABEL;\n }\n return trimmed.slice(0, CALLER_LABEL_MAX_LENGTH);\n};\n\nexport const createBrokerEndpointIdentity = ({\n packageVersion,\n protocolVersion = PROTOCOL_VERSION,\n callerLabel,\n}: BrokerEndpointIdentityOptions): BrokerEndpointIdentity => {\n assertEndpointComponent(packageVersion, \"packageVersion\");\n if (!Number.isInteger(protocolVersion) || protocolVersion <= 0) {\n throw new Error(`protocolVersion must be a positive integer: ${protocolVersion}`);\n }\n\n return {\n packageVersion,\n protocolVersion,\n callerLabel: sanitizeCallerLabel(callerLabel),\n };\n};\n\nexport const isSupportedProtocolVersion = (protocolVersion: number): boolean =>\n protocolVersion === PROTOCOL_VERSION;\n\nexport const formatBrokerEndpointName = (identity: BrokerEndpointIdentity): string => {\n assertEndpointIdentity(identity);\n return `opentray-${identity.packageVersion}-p${identity.protocolVersion}-${identity.callerLabel}`;\n};\n\nexport const formatBrokerStateRoot = (homeDir: string, identity: BrokerEndpointIdentity): string => {\n assertEndpointIdentity(identity);\n if (homeDir.length === 0) {\n throw new Error(\"homeDir must not be empty\");\n }\n\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n return `${normalizedHome}/.opentray/${identity.packageVersion}/${identity.callerLabel}`;\n};\n\nexport const formatUnixSocketPath = (homeDir: string, identity: BrokerEndpointIdentity): string =>\n `${formatBrokerStateRoot(homeDir, identity)}/opentray-p${identity.protocolVersion}.sock`;\n\nexport const formatWindowsPipeName = (identity: BrokerEndpointIdentity): string =>\n `\\\\\\\\.\\\\pipe\\\\${formatBrokerEndpointName(identity)}`;\n\n/**\n * Human-readable process title for a broker pinned to a caller. Used by the SDK\n * spawn path so task managers show the owning application, not a generic name.\n */\nexport const formatBrokerProcessTitle = (identity: BrokerEndpointIdentity): string => {\n assertEndpointIdentity(identity);\n return `opentray · ${identity.callerLabel}`;\n};\n\nconst endpointComponentPattern = /^[0-9A-Za-z._+-]+$/u;\n\nconst assertOpenTrayProtocolLine = (line: OpenTrayProtocolLine, name: string): void => {\n assertProtocolLineVersion(line.major, `${name}.major`);\n assertProtocolLineVersion(line.minor, `${name}.minor`);\n};\n\nconst assertProtocolLineReleaseChannel = (channel: string): void => {\n if (!isProtocolLineReleaseChannel(channel)) {\n throw new Error(`unsupported OpenTray protocol release channel: ${channel}`);\n }\n};\n\nconst assertProtocolLineVersion = (value: number, name: string): void => {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(`protocol line ${name} must be a non-negative integer: ${value}`);\n }\n};\n\nconst assertEndpointIdentity = (identity: BrokerEndpointIdentity): void => {\n assertEndpointComponent(identity.packageVersion, \"packageVersion\");\n if (!Number.isInteger(identity.protocolVersion) || identity.protocolVersion <= 0) {\n throw new Error(`protocolVersion must be a positive integer: ${identity.protocolVersion}`);\n }\n};\n\nconst assertEndpointComponent = (value: string, name: string): void => {\n if (value.length === 0) {\n throw new Error(`${name} must not be empty`);\n }\n if (!endpointComponentPattern.test(value)) {\n throw new Error(`${name} contains invalid endpoint characters: ${value}`);\n }\n};\n\nexport interface SpaceOptions {\n id?: SpaceId;\n title?: string;\n icon?: Icon;\n default?: boolean;\n}\n\n/** @deprecated Use `SpaceOptions`. */\nexport type SurfaceOptions = SpaceOptions;\n\nexport interface SpaceRef {\n spaceId: SpaceId;\n}\n\n/** @deprecated Use `SpaceRef`. */\nexport type SurfaceRef = SpaceRef;\n\nexport interface TrayOptions {\n trayId?: TrayId;\n appId?: string;\n title?: string;\n tooltip?: Tooltip;\n icon?: Icon;\n menu?: Menu;\n}\n\nexport interface Tooltip {\n title: string;\n description: string;\n}\n\nexport interface Menu {\n items: MenuItem[];\n}\n\nexport type MenuItem =\n | {\n type: \"item\";\n id: MenuItemId;\n title: string;\n primaryEvent?: boolean;\n enabled?: boolean;\n shortcut?: string;\n }\n | {\n type: \"check\";\n id: MenuItemId;\n title: string;\n enabled?: boolean;\n checked?: boolean;\n }\n | {\n type: \"radio\";\n id: MenuItemId;\n title: string;\n enabled?: boolean;\n checked?: boolean;\n group: number;\n }\n | {\n type: \"separator\";\n }\n | {\n type: \"submenu\";\n title: string;\n enabled?: boolean;\n items: MenuItem[];\n };\n\nexport type Icon =\n | { type: \"rgba\"; data: Uint8Array | number[]; width: number; height: number }\n | { type: \"encoded\"; data: Uint8Array | number[] }\n | { type: \"file\"; path: string };\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type TrayBoundsKind = \"native\" | \"inferred\" | \"unavailable\";\n\nexport interface TrayBoundsResult {\n kind: TrayBoundsKind;\n source: string;\n rect: Rect | null;\n}\n\nexport type MouseButton = \"left\" | \"right\" | \"middle\";\n\nexport type TrayEvent =\n | { type: \"ready\"; spaceId: SpaceId }\n | { type: \"menuClick\"; spaceId: SpaceId; trayId: TrayId; itemId: MenuItemId }\n | { type: \"trayClick\"; spaceId: SpaceId; trayId: TrayId; button: MouseButton; x: number; y: number }\n | { type: \"trayDoubleClick\"; spaceId: SpaceId; trayId: TrayId; button: MouseButton; x: number; y: number };\n\nexport interface ExtensionScope {\n spaceId: SpaceId;\n trayId?: TrayId;\n ext: string;\n}\n\nexport interface ExtensionEnvelope<TData = unknown> {\n scope: ExtensionScope;\n data: TData;\n}\n\nexport interface DaemonSessionHealth {\n sessionId: number;\n internalLeaseId?: SessionId;\n initialized: boolean;\n}\n\nexport interface DaemonHealth {\n pid: number;\n packageVersion: string;\n protocolVersion: number;\n endpoint: string;\n sessionCount: number;\n sessions: DaemonSessionHealth[];\n}\n\nexport type ClientFrame =\n | { type: \"init\"; protocolVersion: number; clientVersion: string }\n | ClientRequestFrame\n | { type: \"exit\" };\n\nexport type ClientRequestFrame =\n | ({ type: \"create-space\"; requestId: RequestId } & SpaceOptions)\n | { type: \"resolve-default-space\"; requestId: RequestId }\n | { type: \"create-tray\"; requestId: RequestId; space: SpaceRef; tray: TrayOptions }\n | { type: \"destroy-tray\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId }\n | { type: \"get-tray-bounds\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId }\n | { type: \"set-tray-menu\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; menu: Menu }\n | { type: \"set-tray-icon\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; icon: Icon }\n | { type: \"set-tray-tooltip\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; tooltip: Tooltip }\n | { type: \"set-tray-title\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; title: string }\n | { type: \"load-ext\"; requestId: RequestId; spaceId: SpaceId; name: string; path: string; mountId?: string }\n | { type: \"ext-command\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; ext: string; data: unknown }\n | { type: \"unload-ext\"; requestId: RequestId; spaceId: SpaceId; name: string }\n | { type: \"health\"; requestId: RequestId };\n\nexport type ServerFrame =\n | { type: \"ready\"; protocolVersion: number; brokerVersion: string; sessionId: SessionId }\n | { type: \"space-created\"; requestId: RequestId; space: SpaceRef }\n | { type: \"default-space\"; requestId: RequestId; space: SpaceRef }\n | { type: \"tray-created\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId }\n | { type: \"tray-bounds\"; requestId: RequestId; spaceId: SpaceId; trayId: TrayId; bounds: TrayBoundsResult }\n | { type: \"ack\"; requestId: RequestId }\n | { type: \"ext-command-result\"; requestId: RequestId; events: ExtensionEnvelope[] }\n | { type: \"daemon-health\"; requestId: RequestId; health: DaemonHealth }\n | { type: \"event\"; event: TrayEvent }\n | { type: \"ext-event\"; spaceId: SpaceId; trayId: TrayId; ext: string; data: unknown }\n | { type: \"error\"; requestId?: RequestId; code: string; message: string };\n\nexport interface ParseResult<T> {\n ok: boolean;\n frame?: T;\n error?: string;\n}\n\nexport const parseServerFrame = (line: string): ParseResult<ServerFrame> => {\n try {\n const value: unknown = JSON.parse(line);\n if (!isServerFrame(value)) {\n return { ok: false, error: \"invalid server frame\" };\n }\n return { ok: true, frame: value };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : \"unknown parse error\",\n };\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport const isServerFrame = (value: unknown): value is ServerFrame => {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n\n switch (value.type) {\n case \"ready\":\n return (\n typeof value.protocolVersion === \"number\" &&\n typeof value.brokerVersion === \"string\" &&\n typeof value.sessionId === \"string\"\n );\n case \"space-created\":\n return typeof value.requestId === \"string\" && isRecord(value.space);\n case \"default-space\":\n return typeof value.requestId === \"string\" && isRecord(value.space);\n case \"tray-created\":\n return (\n typeof value.requestId === \"string\" &&\n typeof value.spaceId === \"string\" &&\n typeof value.trayId === \"string\"\n );\n case \"tray-bounds\":\n return (\n typeof value.requestId === \"string\" &&\n typeof value.spaceId === \"string\" &&\n typeof value.trayId === \"string\" &&\n isTrayBoundsResult(value.bounds)\n );\n case \"ack\":\n return typeof value.requestId === \"string\";\n case \"ext-command-result\":\n return typeof value.requestId === \"string\" && Array.isArray(value.events) && value.events.every(isExtensionEnvelope);\n case \"daemon-health\":\n return typeof value.requestId === \"string\" && isDaemonHealth(value.health);\n case \"event\":\n return isTrayEvent(value.event);\n case \"ext-event\":\n return (\n typeof value.spaceId === \"string\" &&\n typeof value.trayId === \"string\" &&\n typeof value.ext === \"string\"\n );\n case \"error\":\n return (\n (value.requestId === undefined || typeof value.requestId === \"string\") &&\n typeof value.code === \"string\" &&\n typeof value.message === \"string\"\n );\n default:\n return false;\n }\n};\n\nconst isExtensionEnvelope = (value: unknown): value is ExtensionEnvelope => {\n if (!isRecord(value) || !isRecord(value.scope)) {\n return false;\n }\n const scope = value.scope as Record<string, unknown>;\n return (\n (typeof scope.spaceId === \"string\" || typeof scope.surfaceId === \"string\") &&\n (scope.trayId === undefined || typeof scope.trayId === \"string\") &&\n typeof scope.ext === \"string\" &&\n \"data\" in value\n );\n};\n\nconst isDaemonHealth = (value: unknown): value is DaemonHealth => {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n typeof value.pid === \"number\" &&\n typeof value.packageVersion === \"string\" &&\n typeof value.protocolVersion === \"number\" &&\n typeof value.endpoint === \"string\" &&\n typeof value.sessionCount === \"number\" &&\n Array.isArray(value.sessions) &&\n value.sessions.every(isDaemonSessionHealth)\n );\n};\n\nconst isDaemonSessionHealth = (value: unknown): value is DaemonSessionHealth =>\n isRecord(value) &&\n typeof value.sessionId === \"number\" &&\n (value.internalLeaseId === undefined || typeof value.internalLeaseId === \"string\") &&\n typeof value.initialized === \"boolean\";\n\nconst isRect = (value: unknown): value is Rect =>\n isRecord(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\" &&\n typeof value.width === \"number\" &&\n typeof value.height === \"number\";\n\nconst isTrayBoundsResult = (value: unknown): value is TrayBoundsResult =>\n isRecord(value) &&\n (value.kind === \"native\" || value.kind === \"inferred\" || value.kind === \"unavailable\") &&\n typeof value.source === \"string\" &&\n (value.rect === null || isRect(value.rect));\n\nconst isTrayEvent = (value: unknown): value is TrayEvent => {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n\n switch (value.type) {\n case \"ready\":\n return typeof value.spaceId === \"string\";\n case \"menuClick\":\n return (\n typeof value.spaceId === \"string\" &&\n typeof value.trayId === \"string\" &&\n typeof value.itemId === \"number\"\n );\n case \"trayClick\":\n case \"trayDoubleClick\":\n return (\n typeof value.spaceId === \"string\" &&\n typeof value.trayId === \"string\" &&\n isMouseButton(value.button) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n );\n default:\n return false;\n }\n};\n\nconst isMouseButton = (value: unknown): value is MouseButton =>\n value === \"left\" || value === \"right\" || value === \"middle\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type SessionId = string;\nexport type RequestId = string;\nexport type AppId = string;\nexport type TrayId = string;\nexport type MenuItemId = number;\n\nexport const PROTOCOL_VERSION = 1;\nexport const OPENTRAY_PROTOCOL_FAMILY = \"opentray-protocol\";\nexport const OPENTRAY_PROTOCOL_LINE_MAJOR = 1;\nexport const OPENTRAY_PROTOCOL_LINE_MINOR = 1;\n\nexport const protocolLineReleaseChannels = [\"stable\", \"alpha\"] as const;\nexport type ProtocolLineReleaseChannel =\n (typeof protocolLineReleaseChannels)[number];\n\nexport interface OpenTrayProtocolLine {\n family: typeof OPENTRAY_PROTOCOL_FAMILY;\n major: number;\n minor: number;\n}\n\nexport interface ProtocolDistTagOptions {\n channel: ProtocolLineReleaseChannel;\n major?: number;\n minor?: number;\n}\n\nexport interface ProtocolDistTag {\n channel: ProtocolLineReleaseChannel;\n major: number;\n minor: number;\n}\n\nexport const OPENTRAY_PROTOCOL_LINE: OpenTrayProtocolLine = {\n family: OPENTRAY_PROTOCOL_FAMILY,\n major: OPENTRAY_PROTOCOL_LINE_MAJOR,\n minor: OPENTRAY_PROTOCOL_LINE_MINOR,\n};\n\n// Install-time protocol lines can advance by minor version while runtime authority stays numeric.\nexport const compareOpenTrayProtocolLine = (\n left: OpenTrayProtocolLine,\n right: OpenTrayProtocolLine\n): number => {\n assertOpenTrayProtocolLine(left, \"left\");\n assertOpenTrayProtocolLine(right, \"right\");\n if (left.major !== right.major) {\n return left.major - right.major;\n }\n return left.minor - right.minor;\n};\n\nexport const isOpenTrayProtocolLineCompatible = (\n supported: OpenTrayProtocolLine,\n required: OpenTrayProtocolLine\n): boolean => {\n assertOpenTrayProtocolLine(supported, \"supported\");\n assertOpenTrayProtocolLine(required, \"required\");\n return (\n supported.major === required.major && supported.minor >= required.minor\n );\n};\n\nexport const formatOpenTrayProtocolLine = ({\n family,\n major,\n minor,\n}: OpenTrayProtocolLine = OPENTRAY_PROTOCOL_LINE): string => {\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return `${family}/${major}.${minor}`;\n};\n\nexport const isProtocolLineReleaseChannel = (\n value: string\n): value is ProtocolLineReleaseChannel =>\n protocolLineReleaseChannels.includes(value as ProtocolLineReleaseChannel);\n\nexport const formatProtocolDistTag = ({\n channel,\n major = OPENTRAY_PROTOCOL_LINE_MAJOR,\n minor = OPENTRAY_PROTOCOL_LINE_MINOR,\n}: ProtocolDistTagOptions): string => {\n assertProtocolLineReleaseChannel(channel);\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return `${channel}-${major}-${minor}`;\n};\n\nexport const parseProtocolDistTag = (tag: string): ProtocolDistTag => {\n const match = /^(?<channel>[a-z]+)-(?<major>\\d+)-(?<minor>\\d+)$/u.exec(tag);\n const groups = match?.groups;\n if (groups === undefined) {\n throw new Error(`invalid OpenTray protocol dist-tag: ${tag}`);\n }\n const channel = groups.channel;\n if (channel === undefined || !isProtocolLineReleaseChannel(channel)) {\n throw new Error(\n `unsupported OpenTray protocol dist-tag channel: ${channel ?? \"\"}`\n );\n }\n const major = Number(groups.major);\n const minor = Number(groups.minor);\n assertProtocolLineVersion(major, \"major\");\n assertProtocolLineVersion(minor, \"minor\");\n return { channel, major, minor };\n};\n\n/**\n * Neutral caller label used when no usable caller identity can be derived.\n * Keeps the broker honest instead of impersonating an unrelated application.\n */\nexport const DEFAULT_CALLER_LABEL = \"opentray\";\n\n/**\n * Maximum length of a sanitized caller label. Keeps socket paths, runtime\n * directory names, and process titles within platform limits.\n */\nexport const CALLER_LABEL_MAX_LENGTH = 48;\n\nconst callerLabelAllowedPattern = /[a-z0-9-]+/g;\n\nexport interface BrokerEndpointIdentity {\n packageVersion: string;\n protocolVersion: number;\n callerLabel: string;\n}\n\nexport interface BrokerEndpointIdentityOptions {\n packageVersion: string;\n protocolVersion?: number;\n callerLabel?: string;\n}\n\nexport const sanitizeCallerLabel = (value: string | undefined): string => {\n if (value !== undefined && typeof value !== \"string\") {\n throw new Error(`callerLabel must be a string: ${String(value)}`);\n }\n const raw = value ?? \"\";\n const lowered = raw.toLowerCase();\n const segments = lowered.match(callerLabelAllowedPattern);\n if (segments === null || segments.length === 0) {\n return DEFAULT_CALLER_LABEL;\n }\n const joined = segments.join(\"-\");\n const trimmed = joined.replace(/^-+|-+$/gu, \"\");\n if (trimmed.length === 0) {\n return DEFAULT_CALLER_LABEL;\n }\n return trimmed.slice(0, CALLER_LABEL_MAX_LENGTH);\n};\n\nexport const createBrokerEndpointIdentity = ({\n packageVersion,\n protocolVersion = PROTOCOL_VERSION,\n callerLabel,\n}: BrokerEndpointIdentityOptions): BrokerEndpointIdentity => {\n assertEndpointComponent(packageVersion, \"packageVersion\");\n if (!Number.isInteger(protocolVersion) || protocolVersion <= 0) {\n throw new Error(\n `protocolVersion must be a positive integer: ${protocolVersion}`\n );\n }\n\n return {\n packageVersion,\n protocolVersion,\n callerLabel: sanitizeCallerLabel(callerLabel),\n };\n};\n\nexport const isSupportedProtocolVersion = (protocolVersion: number): boolean =>\n protocolVersion === PROTOCOL_VERSION;\n\nexport const formatBrokerEndpointName = (\n identity: BrokerEndpointIdentity\n): string => {\n assertEndpointIdentity(identity);\n return `opentray-${identity.packageVersion}-p${identity.protocolVersion}-${identity.callerLabel}`;\n};\n\nexport const formatBrokerStateRoot = (\n homeDir: string,\n identity: BrokerEndpointIdentity\n): string => {\n assertEndpointIdentity(identity);\n if (homeDir.length === 0) {\n throw new Error(\"homeDir must not be empty\");\n }\n\n const normalizedHome = homeDir.replace(/[\\\\/]+$/u, \"\");\n return `${normalizedHome}/.opentray/${identity.packageVersion}/${identity.callerLabel}`;\n};\n\nexport const formatUnixSocketPath = (\n homeDir: string,\n identity: BrokerEndpointIdentity\n): string =>\n `${formatBrokerStateRoot(homeDir, identity)}/opentray-p${\n identity.protocolVersion\n }.sock`;\n\nexport const formatWindowsPipeName = (\n identity: BrokerEndpointIdentity\n): string => `\\\\\\\\.\\\\pipe\\\\${formatBrokerEndpointName(identity)}`;\n\n/**\n * Human-readable process title for a broker pinned to a caller. Used by the SDK\n * spawn path so task managers show the owning application, not a generic name.\n */\nexport const formatBrokerProcessTitle = (\n identity: BrokerEndpointIdentity\n): string => {\n assertEndpointIdentity(identity);\n return `opentray · ${identity.callerLabel}`;\n};\n\nconst endpointComponentPattern = /^[0-9A-Za-z._+-]+$/u;\n\nconst assertOpenTrayProtocolLine = (\n line: OpenTrayProtocolLine,\n name: string\n): void => {\n assertProtocolLineVersion(line.major, `${name}.major`);\n assertProtocolLineVersion(line.minor, `${name}.minor`);\n};\n\nconst assertProtocolLineReleaseChannel = (channel: string): void => {\n if (!isProtocolLineReleaseChannel(channel)) {\n throw new Error(\n `unsupported OpenTray protocol release channel: ${channel}`\n );\n }\n};\n\nconst assertProtocolLineVersion = (value: number, name: string): void => {\n if (!Number.isInteger(value) || value < 0) {\n throw new Error(\n `protocol line ${name} must be a non-negative integer: ${value}`\n );\n }\n};\n\nconst assertEndpointIdentity = (identity: BrokerEndpointIdentity): void => {\n assertEndpointComponent(identity.packageVersion, \"packageVersion\");\n if (\n !Number.isInteger(identity.protocolVersion) ||\n identity.protocolVersion <= 0\n ) {\n throw new Error(\n `protocolVersion must be a positive integer: ${identity.protocolVersion}`\n );\n }\n};\n\nconst assertEndpointComponent = (value: string, name: string): void => {\n if (value.length === 0) {\n throw new Error(`${name} must not be empty`);\n }\n if (!endpointComponentPattern.test(value)) {\n throw new Error(`${name} contains invalid endpoint characters: ${value}`);\n }\n};\n\nexport interface AppOptions {\n id?: AppId;\n name?: string;\n icon?: Icon;\n default?: boolean;\n}\n\nexport interface AppRef {\n appId: AppId;\n}\n\nexport interface AppIdentity {\n appId: AppId;\n appName: string;\n}\n\nexport interface TrayOptions {\n id: string;\n tooltip?: Tooltip;\n icon?: Icon;\n menu?: Menu;\n}\n\nexport interface Tooltip {\n title: string;\n description: string;\n}\n\nexport interface Menu {\n items: MenuItem[];\n}\n\nexport type MenuItem =\n | {\n type: \"item\";\n id: MenuItemId;\n title: string;\n primaryEvent?: boolean;\n enabled?: boolean;\n shortcut?: string;\n }\n | {\n type: \"check\";\n id: MenuItemId;\n title: string;\n enabled?: boolean;\n checked?: boolean;\n }\n | {\n type: \"radio\";\n id: MenuItemId;\n title: string;\n enabled?: boolean;\n checked?: boolean;\n group: number;\n }\n | {\n type: \"separator\";\n }\n | {\n type: \"submenu\";\n title: string;\n enabled?: boolean;\n items: MenuItem[];\n };\n\nexport type IconImage =\n | { type: \"rgba\"; data: Uint8Array | number[]; width: number; height: number }\n | { type: \"encoded\"; data: Uint8Array | number[] }\n | { type: \"file\"; path: string };\n\nexport type SimpleIcon = IconImage & { text?: string };\n\nexport type IconText = IconImage & { text: string };\n\nexport interface IconCandidates {\n \"icon-only\"?: IconImage;\n \"text-only\"?: string;\n \"icon-text\"?: IconText;\n}\n\nexport type Icon = IconCandidates & Partial<SimpleIcon>;\n\nexport interface Rect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport type TrayBoundsKind = \"native\" | \"inferred\" | \"unavailable\";\n\nexport interface TrayBoundsResult {\n kind: TrayBoundsKind;\n source: string;\n rect: Rect | null;\n}\n\nexport type MouseButton = \"left\" | \"right\" | \"middle\";\n\nexport type TrayEvent =\n | { type: \"ready\"; appId: AppId }\n | { type: \"menuClick\"; appId: AppId; trayId: TrayId; itemId: MenuItemId }\n | {\n type: \"trayClick\";\n appId: AppId;\n trayId: TrayId;\n button: MouseButton;\n x: number;\n y: number;\n }\n | {\n type: \"trayDoubleClick\";\n appId: AppId;\n trayId: TrayId;\n button: MouseButton;\n x: number;\n y: number;\n };\n\nexport interface ExtensionScope {\n appId: AppId;\n trayId?: TrayId;\n ext: string;\n}\n\nexport interface ExtensionEnvelope<TData = unknown> {\n scope: ExtensionScope;\n data: TData;\n}\n\nexport interface RuntimeHostSessionHealth {\n sessionId: number;\n internalSessionId?: SessionId;\n initialized: boolean;\n}\n\nexport interface RuntimeHostHealth {\n pid: number;\n packageVersion: string;\n protocolVersion: number;\n endpoint: string;\n appId: AppId;\n appName: string;\n callerLabel: string;\n sessionCount: number;\n sessions: RuntimeHostSessionHealth[];\n}\n\nexport type ClientFrame =\n | { type: \"init\"; protocolVersion: number; clientVersion: string }\n | ClientRequestFrame\n | { type: \"exit\" };\n\nexport type ClientRequestFrame =\n | ({ type: \"create-app\"; requestId: RequestId } & AppOptions)\n | { type: \"resolve-default-app\"; requestId: RequestId }\n | {\n type: \"create-tray\";\n requestId: RequestId;\n app: AppRef;\n tray: TrayOptions;\n }\n | { type: \"destroy-tray\"; requestId: RequestId; appId: AppId; trayId: TrayId }\n | {\n type: \"get-tray-bounds\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n }\n | {\n type: \"set-tray-menu\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n menu: Menu;\n }\n | {\n type: \"set-tray-icon\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n icon: Icon;\n }\n | {\n type: \"set-tray-tooltip\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n tooltip: Tooltip;\n }\n | {\n type: \"load-ext\";\n requestId: RequestId;\n appId: AppId;\n name: string;\n path: string;\n mountId?: string;\n }\n | {\n type: \"ext-command\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n ext: string;\n data: unknown;\n }\n | { type: \"unload-ext\"; requestId: RequestId; appId: AppId; name: string }\n | { type: \"health\"; requestId: RequestId };\n\nexport type ServerFrame =\n | {\n type: \"ready\";\n protocolVersion: number;\n brokerVersion: string;\n sessionId: SessionId;\n }\n | { type: \"app-created\"; requestId: RequestId; app: AppRef }\n | { type: \"default-app\"; requestId: RequestId; app: AppRef }\n | { type: \"tray-created\"; requestId: RequestId; appId: AppId; trayId: TrayId }\n | {\n type: \"tray-bounds\";\n requestId: RequestId;\n appId: AppId;\n trayId: TrayId;\n bounds: TrayBoundsResult;\n }\n | { type: \"ack\"; requestId: RequestId }\n | {\n type: \"ext-command-result\";\n requestId: RequestId;\n events: ExtensionEnvelope[];\n }\n | {\n type: \"runtime-host-health\";\n requestId: RequestId;\n health: RuntimeHostHealth;\n }\n | { type: \"event\"; event: TrayEvent }\n | {\n type: \"ext-event\";\n appId: AppId;\n trayId: TrayId;\n ext: string;\n data: unknown;\n }\n | { type: \"error\"; requestId?: RequestId; code: string; message: string };\n\nexport interface ParseResult<T> {\n ok: boolean;\n frame?: T;\n error?: string;\n}\n\nexport const parseServerFrame = (line: string): ParseResult<ServerFrame> => {\n try {\n const value: unknown = JSON.parse(line);\n if (!isServerFrame(value)) {\n return { ok: false, error: \"invalid server frame\" };\n }\n return { ok: true, frame: value };\n } catch (error) {\n return {\n ok: false,\n error: error instanceof Error ? error.message : \"unknown parse error\",\n };\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport const isServerFrame = (value: unknown): value is ServerFrame => {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n\n switch (value.type) {\n case \"ready\":\n return (\n typeof value.protocolVersion === \"number\" &&\n typeof value.brokerVersion === \"string\" &&\n typeof value.sessionId === \"string\"\n );\n case \"app-created\":\n return typeof value.requestId === \"string\" && isRecord(value.app);\n case \"default-app\":\n return typeof value.requestId === \"string\" && isRecord(value.app);\n case \"tray-created\":\n return (\n typeof value.requestId === \"string\" &&\n typeof value.appId === \"string\" &&\n typeof value.trayId === \"string\"\n );\n case \"tray-bounds\":\n return (\n typeof value.requestId === \"string\" &&\n typeof value.appId === \"string\" &&\n typeof value.trayId === \"string\" &&\n isTrayBoundsResult(value.bounds)\n );\n case \"ack\":\n return typeof value.requestId === \"string\";\n case \"ext-command-result\":\n return (\n typeof value.requestId === \"string\" &&\n Array.isArray(value.events) &&\n value.events.every(isExtensionEnvelope)\n );\n case \"runtime-host-health\":\n return (\n typeof value.requestId === \"string\" && isRuntimeHostHealth(value.health)\n );\n case \"event\":\n return isTrayEvent(value.event);\n case \"ext-event\":\n return (\n typeof value.appId === \"string\" &&\n typeof value.trayId === \"string\" &&\n typeof value.ext === \"string\"\n );\n case \"error\":\n return (\n (value.requestId === undefined ||\n typeof value.requestId === \"string\") &&\n typeof value.code === \"string\" &&\n typeof value.message === \"string\"\n );\n default:\n return false;\n }\n};\n\nconst isExtensionEnvelope = (value: unknown): value is ExtensionEnvelope => {\n if (!isRecord(value) || !isRecord(value.scope)) {\n return false;\n }\n const scope = value.scope as Record<string, unknown>;\n return (\n typeof scope.appId === \"string\" &&\n (scope.trayId === undefined || typeof scope.trayId === \"string\") &&\n typeof scope.ext === \"string\" &&\n \"data\" in value\n );\n};\n\nconst isRuntimeHostHealth = (value: unknown): value is RuntimeHostHealth => {\n if (!isRecord(value)) {\n return false;\n }\n\n return (\n typeof value.pid === \"number\" &&\n typeof value.packageVersion === \"string\" &&\n typeof value.protocolVersion === \"number\" &&\n typeof value.endpoint === \"string\" &&\n typeof value.appId === \"string\" &&\n typeof value.appName === \"string\" &&\n typeof value.callerLabel === \"string\" &&\n typeof value.sessionCount === \"number\" &&\n Array.isArray(value.sessions) &&\n value.sessions.every(isRuntimeHostSessionHealth)\n );\n};\n\nconst isRuntimeHostSessionHealth = (\n value: unknown\n): value is RuntimeHostSessionHealth =>\n isRecord(value) &&\n typeof value.sessionId === \"number\" &&\n (value.internalSessionId === undefined ||\n typeof value.internalSessionId === \"string\") &&\n typeof value.initialized === \"boolean\";\n\nconst isRect = (value: unknown): value is Rect =>\n isRecord(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\" &&\n typeof value.width === \"number\" &&\n typeof value.height === \"number\";\n\nconst isTrayBoundsResult = (value: unknown): value is TrayBoundsResult =>\n isRecord(value) &&\n (value.kind === \"native\" ||\n value.kind === \"inferred\" ||\n value.kind === \"unavailable\") &&\n typeof value.source === \"string\" &&\n (value.rect === null || isRect(value.rect));\n\nconst isTrayEvent = (value: unknown): value is TrayEvent => {\n if (!isRecord(value) || typeof value.type !== \"string\") {\n return false;\n }\n\n switch (value.type) {\n case \"ready\":\n return typeof value.appId === \"string\";\n case \"menuClick\":\n return (\n typeof value.appId === \"string\" &&\n typeof value.trayId === \"string\" &&\n typeof value.itemId === \"number\"\n );\n case \"trayClick\":\n case \"trayDoubleClick\":\n return (\n typeof value.appId === \"string\" &&\n typeof value.trayId === \"string\" &&\n isMouseButton(value.button) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n );\n default:\n return false;\n }\n};\n\nconst isMouseButton = (value: unknown): value is MouseButton =>\n value === \"left\" || value === \"right\" || value === \"middle\";\n"],"mappings":";AAMA,MAAa,mBAAmB;AAChC,MAAa,2BAA2B;AACxC,MAAa,+BAA+B;AAC5C,MAAa,+BAA+B;AAE5C,MAAa,8BAA8B,CAAC,UAAU,OAAO;AAsB7D,MAAa,yBAA+C;CAC1D,QAAQ;CACR,OAAA;CACA,OAAA;AACF;AAGA,MAAa,+BACX,MACA,UACW;CACX,2BAA2B,MAAM,MAAM;CACvC,2BAA2B,OAAO,OAAO;CACzC,IAAI,KAAK,UAAU,MAAM,OACvB,OAAO,KAAK,QAAQ,MAAM;CAE5B,OAAO,KAAK,QAAQ,MAAM;AAC5B;AAEA,MAAa,oCACX,WACA,aACY;CACZ,2BAA2B,WAAW,WAAW;CACjD,2BAA2B,UAAU,UAAU;CAC/C,OACE,UAAU,UAAU,SAAS,SAAS,UAAU,SAAS,SAAS;AAEtE;AAEA,MAAa,8BAA8B,EACzC,QACA,OACA,UACwB,2BAAmC;CAC3D,0BAA0B,OAAO,OAAO;CACxC,0BAA0B,OAAO,OAAO;CACxC,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG;AAC/B;AAEA,MAAa,gCACX,UAEA,4BAA4B,SAAS,KAAmC;AAE1E,MAAa,yBAAyB,EACpC,SACA,QAAA,GACA,QAAA,QACoC;CACpC,iCAAiC,OAAO;CACxC,0BAA0B,OAAO,OAAO;CACxC,0BAA0B,OAAO,OAAO;CACxC,OAAO,GAAG,QAAQ,GAAG,MAAM,GAAG;AAChC;AAEA,MAAa,wBAAwB,QAAiC;CAEpE,MAAM,SADQ,oDAAoD,KAAK,GACpD,GAAG;CACtB,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,MAAM,uCAAuC,KAAK;CAE9D,MAAM,UAAU,OAAO;CACvB,IAAI,YAAY,KAAA,KAAa,CAAC,6BAA6B,OAAO,GAChE,MAAM,IAAI,MACR,mDAAmD,WAAW,IAChE;CAEF,MAAM,QAAQ,OAAO,OAAO,KAAK;CACjC,MAAM,QAAQ,OAAO,OAAO,KAAK;CACjC,0BAA0B,OAAO,OAAO;CACxC,0BAA0B,OAAO,OAAO;CACxC,OAAO;EAAE;EAAS;EAAO;CAAM;AACjC;;;;;AAMA,MAAa,uBAAuB;;;;;AAMpC,MAAa,0BAA0B;AAEvC,MAAM,4BAA4B;AAclC,MAAa,uBAAuB,UAAsC;CACxE,IAAI,UAAU,KAAA,KAAa,OAAO,UAAU,UAC1C,MAAM,IAAI,MAAM,iCAAiC,OAAO,KAAK,GAAG;CAIlE,MAAM,YAFM,SAAS,IACD,YACG,EAAE,MAAM,yBAAyB;CACxD,IAAI,aAAa,QAAQ,SAAS,WAAW,GAC3C,OAAO;CAGT,MAAM,UADS,SAAS,KAAK,GACR,EAAE,QAAQ,aAAa,EAAE;CAC9C,IAAI,QAAQ,WAAW,GACrB,OAAO;CAET,OAAO,QAAQ,MAAM,GAAA,EAA0B;AACjD;AAEA,MAAa,gCAAgC,EAC3C,gBACA,kBAAA,GACA,kBAC2D;CAC3D,wBAAwB,gBAAgB,gBAAgB;CACxD,IAAI,CAAC,OAAO,UAAU,eAAe,KAAK,mBAAmB,GAC3D,MAAM,IAAI,MACR,+CAA+C,iBACjD;CAGF,OAAO;EACL;EACA;EACA,aAAa,oBAAoB,WAAW;CAC9C;AACF;AAEA,MAAa,8BAA8B,oBACzC,oBAAA;AAEF,MAAa,4BACX,aACW;CACX,uBAAuB,QAAQ;CAC/B,OAAO,YAAY,SAAS,eAAe,IAAI,SAAS,gBAAgB,GAAG,SAAS;AACtF;AAEA,MAAa,yBACX,SACA,aACW;CACX,uBAAuB,QAAQ;CAC/B,IAAI,QAAQ,WAAW,GACrB,MAAM,IAAI,MAAM,2BAA2B;CAI7C,OAAO,GADgB,QAAQ,QAAQ,YAAY,EAC5B,EAAE,aAAa,SAAS,eAAe,GAAG,SAAS;AAC5E;AAEA,MAAa,wBACX,SACA,aAEA,GAAG,sBAAsB,SAAS,QAAQ,EAAE,aAC1C,SAAS,gBACV;AAEH,MAAa,yBACX,aACW,gBAAgB,yBAAyB,QAAQ;;;;;AAM9D,MAAa,4BACX,aACW;CACX,uBAAuB,QAAQ;CAC/B,OAAO,cAAc,SAAS;AAChC;AAEA,MAAM,2BAA2B;AAEjC,MAAM,8BACJ,MACA,SACS;CACT,0BAA0B,KAAK,OAAO,GAAG,KAAK,OAAO;CACrD,0BAA0B,KAAK,OAAO,GAAG,KAAK,OAAO;AACvD;AAEA,MAAM,oCAAoC,YAA0B;CAClE,IAAI,CAAC,6BAA6B,OAAO,GACvC,MAAM,IAAI,MACR,kDAAkD,SACpD;AAEJ;AAEA,MAAM,6BAA6B,OAAe,SAAuB;CACvE,IAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GACtC,MAAM,IAAI,MACR,iBAAiB,KAAK,mCAAmC,OAC3D;AAEJ;AAEA,MAAM,0BAA0B,aAA2C;CACzE,wBAAwB,SAAS,gBAAgB,gBAAgB;CACjE,IACE,CAAC,OAAO,UAAU,SAAS,eAAe,KAC1C,SAAS,mBAAmB,GAE5B,MAAM,IAAI,MACR,+CAA+C,SAAS,iBAC1D;AAEJ;AAEA,MAAM,2BAA2B,OAAe,SAAuB;CACrE,IAAI,MAAM,WAAW,GACnB,MAAM,IAAI,MAAM,GAAG,KAAK,mBAAmB;CAE7C,IAAI,CAAC,yBAAyB,KAAK,KAAK,GACtC,MAAM,IAAI,MAAM,GAAG,KAAK,yCAAyC,OAAO;AAE5E;AAgQA,MAAa,oBAAoB,SAA2C;CAC1E,IAAI;EACF,MAAM,QAAiB,KAAK,MAAM,IAAI;EACtC,IAAI,CAAC,cAAc,KAAK,GACtB,OAAO;GAAE,IAAI;GAAO,OAAO;EAAuB;EAEpD,OAAO;GAAE,IAAI;GAAM,OAAO;EAAM;CAClC,SAAS,OAAO;EACd,OAAO;GACL,IAAI;GACJ,OAAO,iBAAiB,QAAQ,MAAM,UAAU;EAClD;CACF;AACF;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAa,iBAAiB,UAAyC;CACrE,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,UAC5C,OAAO;CAGT,QAAQ,MAAM,MAAd;EACE,KAAK,SACH,OACE,OAAO,MAAM,oBAAoB,YACjC,OAAO,MAAM,kBAAkB,YAC/B,OAAO,MAAM,cAAc;EAE/B,KAAK,eACH,OAAO,OAAO,MAAM,cAAc,YAAY,SAAS,MAAM,GAAG;EAClE,KAAK,eACH,OAAO,OAAO,MAAM,cAAc,YAAY,SAAS,MAAM,GAAG;EAClE,KAAK,gBACH,OACE,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW;EAE5B,KAAK,eACH,OACE,OAAO,MAAM,cAAc,YAC3B,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,mBAAmB,MAAM,MAAM;EAEnC,KAAK,OACH,OAAO,OAAO,MAAM,cAAc;EACpC,KAAK,sBACH,OACE,OAAO,MAAM,cAAc,YAC3B,MAAM,QAAQ,MAAM,MAAM,KAC1B,MAAM,OAAO,MAAM,mBAAmB;EAE1C,KAAK,uBACH,OACE,OAAO,MAAM,cAAc,YAAY,oBAAoB,MAAM,MAAM;EAE3E,KAAK,SACH,OAAO,YAAY,MAAM,KAAK;EAChC,KAAK,aACH,OACE,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,QAAQ;EAEzB,KAAK,SACH,QACG,MAAM,cAAc,KAAA,KACnB,OAAO,MAAM,cAAc,aAC7B,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,YAAY;EAE7B,SACE,OAAO;CACX;AACF;AAEA,MAAM,uBAAuB,UAA+C;CAC1E,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS,MAAM,KAAK,GAC3C,OAAO;CAET,MAAM,QAAQ,MAAM;CACpB,OACE,OAAO,MAAM,UAAU,aACtB,MAAM,WAAW,KAAA,KAAa,OAAO,MAAM,WAAW,aACvD,OAAO,MAAM,QAAQ,YACrB,UAAU;AAEd;AAEA,MAAM,uBAAuB,UAA+C;CAC1E,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO;CAGT,OACE,OAAO,MAAM,QAAQ,YACrB,OAAO,MAAM,mBAAmB,YAChC,OAAO,MAAM,oBAAoB,YACjC,OAAO,MAAM,aAAa,YAC1B,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,YAAY,YACzB,OAAO,MAAM,gBAAgB,YAC7B,OAAO,MAAM,iBAAiB,YAC9B,MAAM,QAAQ,MAAM,QAAQ,KAC5B,MAAM,SAAS,MAAM,0BAA0B;AAEnD;AAEA,MAAM,8BACJ,UAEA,SAAS,KAAK,KACd,OAAO,MAAM,cAAc,aAC1B,MAAM,sBAAsB,KAAA,KAC3B,OAAO,MAAM,sBAAsB,aACrC,OAAO,MAAM,gBAAgB;AAE/B,MAAM,UAAU,UACd,SAAS,KAAK,KACd,OAAO,MAAM,MAAM,YACnB,OAAO,MAAM,MAAM,YACnB,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW;AAE1B,MAAM,sBAAsB,UAC1B,SAAS,KAAK,MACb,MAAM,SAAS,YACd,MAAM,SAAS,cACf,MAAM,SAAS,kBACjB,OAAO,MAAM,WAAW,aACvB,MAAM,SAAS,QAAQ,OAAO,MAAM,IAAI;AAE3C,MAAM,eAAe,UAAuC;CAC1D,IAAI,CAAC,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS,UAC5C,OAAO;CAGT,QAAQ,MAAM,MAAd;EACE,KAAK,SACH,OAAO,OAAO,MAAM,UAAU;EAChC,KAAK,aACH,OACE,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,OAAO,MAAM,WAAW;EAE5B,KAAK;EACL,KAAK,mBACH,OACE,OAAO,MAAM,UAAU,YACvB,OAAO,MAAM,WAAW,YACxB,cAAc,MAAM,MAAM,KAC1B,OAAO,MAAM,MAAM,YACnB,OAAO,MAAM,MAAM;EAEvB,SACE,OAAO;CACX;AACF;AAEA,MAAM,iBAAiB,UACrB,UAAU,UAAU,UAAU,WAAW,UAAU"}
|