@opentray/spec 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,3 +9,11 @@ Shared TypeScript protocol and contract package for OpenTray.
9
9
  - Keep protocol types reusable by the `opentray` package and official extensions.
10
10
 
11
11
  This package must stay platform-neutral and must not import native implementation packages.
12
+
13
+ ## Example
14
+
15
+ Run a protocol parser example that shows successful server-frame parsing and malformed-frame rejection:
16
+
17
+ ```bash
18
+ pnpm --filter @opentray/spec example:parse
19
+ ```
@@ -0,0 +1,190 @@
1
+ //#region src/index.d.ts
2
+ type LeaseId = string;
3
+ type SurfaceId = string;
4
+ type TrayId = string;
5
+ type MenuItemId = number;
6
+ interface SurfaceOptions {
7
+ appId: string;
8
+ title?: string;
9
+ icon?: Icon;
10
+ default?: boolean;
11
+ }
12
+ interface SurfaceRef {
13
+ surfaceId: SurfaceId;
14
+ appId: string;
15
+ }
16
+ interface TrayOptions {
17
+ trayId?: TrayId;
18
+ appId?: string;
19
+ title?: string;
20
+ tooltip?: Tooltip;
21
+ icon: Icon;
22
+ menu?: Menu;
23
+ }
24
+ interface Tooltip {
25
+ title: string;
26
+ description: string;
27
+ }
28
+ interface Menu {
29
+ items: MenuItem[];
30
+ }
31
+ type MenuItem = {
32
+ type: "item";
33
+ id: MenuItemId;
34
+ title: string;
35
+ enabled?: boolean;
36
+ shortcut?: string;
37
+ } | {
38
+ type: "check";
39
+ id: MenuItemId;
40
+ title: string;
41
+ enabled?: boolean;
42
+ checked?: boolean;
43
+ } | {
44
+ type: "radio";
45
+ id: MenuItemId;
46
+ title: string;
47
+ enabled?: boolean;
48
+ checked?: boolean;
49
+ group: number;
50
+ } | {
51
+ type: "separator";
52
+ } | {
53
+ type: "submenu";
54
+ title: string;
55
+ enabled?: boolean;
56
+ items: MenuItem[];
57
+ };
58
+ type Icon = {
59
+ type: "rgba";
60
+ data: Uint8Array | number[];
61
+ width: number;
62
+ height: number;
63
+ } | {
64
+ type: "encoded";
65
+ data: Uint8Array | number[];
66
+ } | {
67
+ type: "file";
68
+ path: string;
69
+ };
70
+ interface Rect {
71
+ x: number;
72
+ y: number;
73
+ width: number;
74
+ height: number;
75
+ }
76
+ type MouseButton = "left" | "right" | "middle";
77
+ type TrayEvent = {
78
+ type: "ready";
79
+ surfaceId: SurfaceId;
80
+ } | {
81
+ type: "menuClick";
82
+ surfaceId: SurfaceId;
83
+ trayId: TrayId;
84
+ itemId: MenuItemId;
85
+ } | {
86
+ type: "trayClick";
87
+ surfaceId: SurfaceId;
88
+ button: MouseButton;
89
+ x: number;
90
+ y: number;
91
+ } | {
92
+ type: "trayDoubleClick";
93
+ surfaceId: SurfaceId;
94
+ button: MouseButton;
95
+ x: number;
96
+ y: number;
97
+ };
98
+ interface ExtensionScope {
99
+ surfaceId: SurfaceId;
100
+ trayId?: TrayId;
101
+ ext: string;
102
+ }
103
+ interface ExtensionEnvelope<TData = unknown> {
104
+ scope: ExtensionScope;
105
+ data: TData;
106
+ }
107
+ type ClientFrame = {
108
+ type: "init";
109
+ version: number;
110
+ } | ({
111
+ type: "create-surface";
112
+ } & SurfaceOptions) | {
113
+ type: "resolve-default-surface";
114
+ } | {
115
+ type: "create-tray";
116
+ surface: SurfaceRef;
117
+ tray: TrayOptions;
118
+ } | {
119
+ type: "destroy-tray";
120
+ surfaceId: SurfaceId;
121
+ trayId: TrayId;
122
+ } | {
123
+ type: "set-tray-menu";
124
+ surfaceId: SurfaceId;
125
+ trayId: TrayId;
126
+ menu: Menu;
127
+ } | {
128
+ type: "set-tray-icon";
129
+ surfaceId: SurfaceId;
130
+ trayId: TrayId;
131
+ icon: Icon;
132
+ } | {
133
+ type: "set-tray-tooltip";
134
+ surfaceId: SurfaceId;
135
+ trayId: TrayId;
136
+ tooltip: Tooltip;
137
+ } | {
138
+ type: "load-ext";
139
+ surfaceId: SurfaceId;
140
+ name: string;
141
+ path: string;
142
+ } | {
143
+ type: "ext-command";
144
+ surfaceId: SurfaceId;
145
+ trayId: TrayId;
146
+ ext: string;
147
+ data: unknown;
148
+ } | {
149
+ type: "unload-ext";
150
+ surfaceId: SurfaceId;
151
+ name: string;
152
+ } | {
153
+ type: "exit";
154
+ };
155
+ type ServerFrame = {
156
+ type: "ready";
157
+ version: number;
158
+ } | {
159
+ type: "surface-created";
160
+ surface: SurfaceRef;
161
+ } | {
162
+ type: "default-surface";
163
+ surface: SurfaceRef;
164
+ } | {
165
+ type: "tray-created";
166
+ surfaceId: SurfaceId;
167
+ trayId: TrayId;
168
+ } | {
169
+ type: "event";
170
+ event: TrayEvent;
171
+ } | {
172
+ type: "ext-event";
173
+ surfaceId: SurfaceId;
174
+ trayId: TrayId;
175
+ ext: string;
176
+ data: unknown;
177
+ } | {
178
+ type: "error";
179
+ message: string;
180
+ };
181
+ interface ParseResult<T> {
182
+ ok: boolean;
183
+ frame?: T;
184
+ error?: string;
185
+ }
186
+ declare const parseServerFrame: (line: string) => ParseResult<ServerFrame>;
187
+ declare const isServerFrame: (value: unknown) => value is ServerFrame;
188
+ //#endregion
189
+ export { ClientFrame, ExtensionEnvelope, ExtensionScope, Icon, LeaseId, Menu, MenuItem, MenuItemId, MouseButton, ParseResult, Rect, ServerFrame, SurfaceId, SurfaceOptions, SurfaceRef, Tooltip, TrayEvent, TrayId, TrayOptions, isServerFrame, parseServerFrame };
190
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,25 @@
1
+ //#region src/index.ts
2
+ const parseServerFrame = (line) => {
3
+ try {
4
+ const value = JSON.parse(line);
5
+ if (!isServerFrame(value)) return {
6
+ ok: false,
7
+ error: "invalid server frame"
8
+ };
9
+ return {
10
+ ok: true,
11
+ frame: value
12
+ };
13
+ } catch (error) {
14
+ return {
15
+ ok: false,
16
+ error: error instanceof Error ? error.message : "unknown parse error"
17
+ };
18
+ }
19
+ };
20
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
21
+ const isServerFrame = (value) => isRecord(value) && typeof value.type === "string";
22
+ //#endregion
23
+ export { isServerFrame, parseServerFrame };
24
+
25
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["export type LeaseId = string;\nexport type SurfaceId = string;\nexport type TrayId = string;\nexport type MenuItemId = number;\n\nexport interface SurfaceOptions {\n appId: string;\n title?: string;\n icon?: Icon;\n default?: boolean;\n}\n\nexport interface SurfaceRef {\n surfaceId: SurfaceId;\n appId: string;\n}\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 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 MouseButton = \"left\" | \"right\" | \"middle\";\n\nexport type TrayEvent =\n | { type: \"ready\"; surfaceId: SurfaceId }\n | { type: \"menuClick\"; surfaceId: SurfaceId; trayId: TrayId; itemId: MenuItemId }\n | { type: \"trayClick\"; surfaceId: SurfaceId; button: MouseButton; x: number; y: number }\n | { type: \"trayDoubleClick\"; surfaceId: SurfaceId; button: MouseButton; x: number; y: number };\n\nexport interface ExtensionScope {\n surfaceId: SurfaceId;\n trayId?: TrayId;\n ext: string;\n}\n\nexport interface ExtensionEnvelope<TData = unknown> {\n scope: ExtensionScope;\n data: TData;\n}\n\nexport type ClientFrame =\n | { type: \"init\"; version: number }\n | ({ type: \"create-surface\" } & SurfaceOptions)\n | { type: \"resolve-default-surface\" }\n | { type: \"create-tray\"; surface: SurfaceRef; tray: TrayOptions }\n | { type: \"destroy-tray\"; surfaceId: SurfaceId; trayId: TrayId }\n | { type: \"set-tray-menu\"; surfaceId: SurfaceId; trayId: TrayId; menu: Menu }\n | { type: \"set-tray-icon\"; surfaceId: SurfaceId; trayId: TrayId; icon: Icon }\n | { type: \"set-tray-tooltip\"; surfaceId: SurfaceId; trayId: TrayId; tooltip: Tooltip }\n | { type: \"load-ext\"; surfaceId: SurfaceId; name: string; path: string }\n | { type: \"ext-command\"; surfaceId: SurfaceId; trayId: TrayId; ext: string; data: unknown }\n | { type: \"unload-ext\"; surfaceId: SurfaceId; name: string }\n | { type: \"exit\" };\n\nexport type ServerFrame =\n | { type: \"ready\"; version: number }\n | { type: \"surface-created\"; surface: SurfaceRef }\n | { type: \"default-surface\"; surface: SurfaceRef }\n | { type: \"tray-created\"; surfaceId: SurfaceId; trayId: TrayId }\n | { type: \"event\"; event: TrayEvent }\n | { type: \"ext-event\"; surfaceId: SurfaceId; trayId: TrayId; ext: string; data: unknown }\n | { type: \"error\"; 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 isRecord(value) && typeof value.type === \"string\";\n"],"mappings":";AAgIA,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,UAC5B,SAAS,KAAK,KAAK,OAAO,MAAM,SAAS"}
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "@opentray/spec",
3
- "version": "0.0.0",
3
+ "version": "0.1.0",
4
4
  "description": "Shared TypeScript protocol and contract types for OpenTray.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/jixoai/opentray"
10
+ },
7
11
  "exports": {
8
12
  ".": {
9
- "types": "./dist/index.d.ts",
10
- "import": "./dist/index.js"
13
+ "types": "./dist/index.d.mts",
14
+ "import": "./dist/index.mjs"
11
15
  },
12
16
  "./package.json": "./package.json"
13
17
  },
@@ -15,5 +19,16 @@
15
19
  "dist",
16
20
  "README.md"
17
21
  ],
18
- "sideEffects": false
22
+ "sideEffects": false,
23
+ "devDependencies": {
24
+ "tsdown": "^0.22.1",
25
+ "typescript": "^6.0.3",
26
+ "vitest": "^4.1.7"
27
+ },
28
+ "scripts": {
29
+ "build": "tsdown src/index.ts --format esm --dts",
30
+ "example:parse": "bun run examples/parse-frame.ts",
31
+ "test": "vitest run",
32
+ "typecheck": "tsc --noEmit"
33
+ }
19
34
  }