ais6-app 0.1.0 → 0.2.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.
@@ -0,0 +1,34 @@
1
+ {
2
+ "id": "__APP_ID__",
3
+ "version": "0.1.0",
4
+ "protocol": 1,
5
+ "kit": ">=1 <2",
6
+ "windows": [
7
+ {
8
+ "window": "__WINDOW__",
9
+ "app": "__WINDOW__",
10
+ "entry": "index.html",
11
+ "titleKey": "app_title",
12
+ "windowClass": "extracted-app-window",
13
+ "size": { "width": "520px", "height": "420px" },
14
+ "capabilities": ["state", "files", "appearance", "snapshot"],
15
+ "menus": [
16
+ {
17
+ "id": "file",
18
+ "labelKey": "menu_file",
19
+ "items": [
20
+ { "type": "item", "action": "__APP_ID__-export", "labelKey": "export_text", "conditionId": "__APP_ID__-export" },
21
+ { "type": "item", "action": "__APP_ID__-clear", "labelKey": "clear", "conditionId": "__APP_ID__-clear" },
22
+ { "type": "item", "action": "close-active-window", "labelKey": "close", "shortcutId": "close-window", "conditionId": "close-active-window" }
23
+ ]
24
+ },
25
+ { "id": "window", "hostMenu": "window" }
26
+ ]
27
+ }
28
+ ],
29
+ "intents": [],
30
+ "strings": {
31
+ "en": { "app_title": "__NAME__", "menu_file": "File", "export_text": "Export as Text", "clear": "Clear", "close": "Close" },
32
+ "zh": { "app_title": "__NAME_ZH__", "menu_file": "文件", "export_text": "导出为文本", "clear": "清空", "close": "关闭" }
33
+ }
34
+ }
@@ -0,0 +1,431 @@
1
+ /* AI System 6 app kit v1 — what an app draws inside its own pane.
2
+ *
3
+ * Generated from the desk's styles, never the other way round: the desk sends
4
+ * the resolved value of every --kit-* token in kit-tokens.json (one
5
+ * getComputedStyle on the window's pane per appearance change) and sets them
6
+ * on this document's root, where they win over the fallbacks below. The
7
+ * fallbacks are what an app looks like on its own page, outside any desk.
8
+ *
9
+ * Rules (plan section 5): token-only — no appearance selector anywhere in this
10
+ * file; an era difference reaches the kit as a token value or it is not in the
11
+ * kit. The window frame, menus, alerts and the file picker are the desk's and
12
+ * are not here. Removing or renaming a class or token bumps the kit major.
13
+ */
14
+
15
+ :root {
16
+ --kit-ink: #000000;
17
+ --kit-paper: #ffffff;
18
+ --kit-window-bg: #ffffff;
19
+ --kit-shade-dark: #808080;
20
+ --kit-ui-font: Chicago, Charcoal, system-ui, sans-serif;
21
+ --kit-text-font: Geneva, Verdana, sans-serif;
22
+ --kit-control-size: 13px;
23
+ --kit-control-min-height: 26px;
24
+ --kit-control-line: 1px;
25
+ --kit-control-inset: inset 1px 1px 0 #777777, inset -1px -1px 0 #ffffff;
26
+ --kit-hit-target: 28px;
27
+ --kit-disabled-opacity: 0.56;
28
+ --kit-focus-outline: 1px dotted #000000;
29
+ --kit-focus-offset: 2px;
30
+ --kit-btn-bg: #ffffff;
31
+ --kit-btn-fg: #000000;
32
+ --kit-btn-border-color: #000000;
33
+ --kit-btn-radius: 8px;
34
+ --kit-btn-shadow: none;
35
+ --kit-btn-active-bg: #000000;
36
+ --kit-btn-active-fg: #ffffff;
37
+ --kit-btn-default-bg: #ffffff;
38
+ --kit-btn-default-fg: #000000;
39
+ --kit-btn-default-shadow: 0 0 0 1px #ffffff, 0 0 0 3px #000000;
40
+ --kit-field-bg: #ffffff;
41
+ --kit-field-border: 1px solid #000000;
42
+ --kit-field-radius: 0;
43
+ --kit-field-shadow: none;
44
+ --kit-field-focus-ring: 0 0 0 2px #808080;
45
+ --kit-select-radius: 0;
46
+ --kit-selection-bg: #000000;
47
+ --kit-selection-fg: #ffffff;
48
+ /* Added for One More (kit 1, additive). */
49
+ --kit-separator: #000000;
50
+ --kit-shade: #c0c0c0;
51
+ --kit-surface-primary: #ffffff;
52
+ --kit-surface-secondary: #ffffff;
53
+ --kit-surface-radius: 0px;
54
+ --kit-container-radius: 0px;
55
+ --kit-inset-radius: 0px;
56
+ --kit-item-radius: 0px;
57
+ --kit-mono-font: Monaco, Menlo, monospace;
58
+ --kit-system-border: 1px solid #000000;
59
+ --kit-system-shadow: 2px 2px 0 #000000;
60
+ --kit-selection-edge: #000000;
61
+ --kit-btn-hover-bg: #ffffff;
62
+ --kit-btn-hover-fg: #000000;
63
+ --kit-text-size: 12px;
64
+ --kit-scrollbar-width: 16px;
65
+ --kit-scrollbar-track: #ffffff;
66
+ --kit-scrollbar-thumb: #c0c0c0;
67
+ --kit-leading: 16px;
68
+ --kit-status-bg: #ffffff;
69
+ --kit-status-fg: #000000;
70
+ --kit-status-font: Geneva, Verdana, sans-serif;
71
+ --kit-status-font-size: 12px;
72
+ --kit-status-min-height: 24px;
73
+ --kit-status-pad-top: 3px;
74
+ --kit-status-pad-bottom: 5px;
75
+ --kit-status-pad-x: 8px;
76
+ --kit-status-border-color: #000000;
77
+ --kit-status-border-width: 1px;
78
+ --kit-status-shadow: none;
79
+ --kit-pane-bg: #ffffff;
80
+ --kit-font-variation: normal;
81
+ --kit-font-synthesis: weight style small-caps;
82
+ --kit-status-text-size: 12px;
83
+ --kit-status-image: none;
84
+ --kit-pane-border-top: 0px none rgb(0, 0, 0);
85
+ --kit-tab-end-radius: 0px;
86
+ --kit-color-scheme: normal;
87
+ --kit-btn-image: none;
88
+ --kit-btn-border-width: 1px;
89
+ --kit-btn-border-style: solid;
90
+ --kit-btn-font: Chicago, Charcoal, system-ui, sans-serif;
91
+ --kit-btn-font-size: 13px;
92
+ --kit-btn-weight: 400;
93
+ --kit-btn-line-height: normal;
94
+ --kit-btn-pad-y: 3px;
95
+ --kit-btn-pad-x: 14px;
96
+ --kit-btn-min-height: 26px;
97
+ --kit-btn-min-width: 64px;
98
+ --kit-btn-text-shadow: none;
99
+ --kit-btn-default-image: none;
100
+ --kit-btn-default-border-width: 1px;
101
+ --kit-btn-default-border-color: #000000;
102
+ --kit-btn-default-text-shadow: none;
103
+ --kit-hint-fg: #000000;
104
+ --kit-hint-font: Geneva, Verdana, sans-serif;
105
+ --kit-hint-font-size: 12px;
106
+ --kit-hint-weight: 400;
107
+ --kit-hint-line-height: normal;
108
+ --kit-hint-margin-top: 6px;
109
+ --kit-tab-bg: #c0c0c0;
110
+ --kit-tab-image: none;
111
+ --kit-tab-fg: #000000;
112
+ --kit-tab-border-width: 1px;
113
+ --kit-tab-border-style: solid;
114
+ --kit-tab-border-color: #000000;
115
+ --kit-tab-radius: 0px;
116
+ --kit-tab-shadow: none;
117
+ --kit-tab-font: Chicago, Charcoal, system-ui, sans-serif;
118
+ --kit-tab-font-size: 12px;
119
+ --kit-tab-weight: 400;
120
+ --kit-tab-pad-y: 4px;
121
+ --kit-tab-pad-x: 10px;
122
+ --kit-tab-min-height: 28px;
123
+ --kit-tab-active-bg: #ffffff;
124
+ --kit-tab-active-image: none;
125
+ --kit-tab-active-fg: #000000;
126
+ --kit-tab-active-shadow: none;
127
+ --kit-tab-active-weight: 400;
128
+ }
129
+
130
+ /* Standalone dark fallbacks; a hosted page receives resolved tokens instead. */
131
+ :root[data-color-mode="dark"] {
132
+ --kit-ink: #f2f2f2;
133
+ --kit-paper: #1e1e1e;
134
+ --kit-window-bg: #1e1e1e;
135
+ --kit-btn-bg: #2b2b2b;
136
+ --kit-btn-fg: #f2f2f2;
137
+ --kit-btn-border-color: #f2f2f2;
138
+ --kit-btn-default-bg: #2b2b2b;
139
+ --kit-btn-default-fg: #f2f2f2;
140
+ --kit-btn-default-shadow: 0 0 0 1px #1e1e1e, 0 0 0 3px #f2f2f2;
141
+ --kit-btn-active-bg: #f2f2f2;
142
+ --kit-btn-active-fg: #1e1e1e;
143
+ --kit-field-bg: #1e1e1e;
144
+ --kit-field-border: 1px solid #f2f2f2;
145
+ --kit-focus-outline: 1px dotted #f2f2f2;
146
+ --kit-selection-bg: #f2f2f2;
147
+ --kit-selection-fg: #1e1e1e;
148
+ color-scheme: dark;
149
+ }
150
+
151
+ .kit-pane {
152
+ background: var(--kit-window-bg);
153
+ color: var(--kit-ink);
154
+ font-family: var(--kit-ui-font);
155
+ font-size: var(--kit-control-size);
156
+ color-scheme: var(--kit-color-scheme);
157
+ font-synthesis: var(--kit-font-synthesis);
158
+ font-variation-settings: var(--kit-font-variation);
159
+ line-height: var(--kit-leading);
160
+ }
161
+
162
+ .kit-pane ::selection {
163
+ background: var(--kit-selection-bg);
164
+ color: var(--kit-selection-fg);
165
+ }
166
+
167
+ /* Push buttons and the default-button ring. The specimen tokens are the
168
+ desk's own button as it resolves in the window's appearance; the older
169
+ tokens are their fallback for a desk that sends none. */
170
+ .kit-button {
171
+ box-sizing: border-box;
172
+ display: inline-flex;
173
+ align-items: center;
174
+ justify-content: center;
175
+ gap: 4px;
176
+ min-height: var(--kit-btn-min-height, var(--kit-control-min-height));
177
+ min-width: var(--kit-btn-min-width, 64px);
178
+ padding: var(--kit-btn-pad-y, 3px) var(--kit-btn-pad-x, 14px);
179
+ border-width: var(--kit-btn-border-width, var(--kit-control-line));
180
+ border-style: var(--kit-btn-border-style, solid);
181
+ border-color: var(--kit-btn-border-color);
182
+ border-radius: var(--kit-btn-radius);
183
+ background-color: var(--kit-btn-bg);
184
+ background-image: var(--kit-btn-image, none);
185
+ box-shadow: var(--kit-btn-shadow);
186
+ color: var(--kit-btn-fg);
187
+ font: inherit;
188
+ font-family: var(--kit-btn-font, var(--kit-ui-font));
189
+ font-size: var(--kit-btn-font-size, var(--kit-control-size));
190
+ font-weight: var(--kit-btn-weight, 400);
191
+ line-height: var(--kit-btn-line-height, 1.2);
192
+ text-shadow: var(--kit-btn-text-shadow, none);
193
+ white-space: nowrap;
194
+ cursor: default;
195
+ user-select: none;
196
+ }
197
+
198
+ .kit-button.is-default {
199
+ border-width: var(--kit-btn-default-border-width, var(--kit-control-line));
200
+ border-color: var(--kit-btn-default-border-color, var(--kit-btn-border-color));
201
+ background-color: var(--kit-btn-default-bg);
202
+ background-image: var(--kit-btn-default-image, none);
203
+ box-shadow: var(--kit-btn-default-shadow);
204
+ color: var(--kit-btn-default-fg);
205
+ text-shadow: var(--kit-btn-default-text-shadow, none);
206
+ }
207
+
208
+ .kit-button:hover:not(:disabled):not(.is-default) {
209
+ background-color: var(--kit-btn-hover-bg);
210
+ color: var(--kit-btn-hover-fg);
211
+ }
212
+
213
+ .kit-button:active:not(:disabled),
214
+ .kit-button.is-pressed {
215
+ background-color: var(--kit-btn-active-bg);
216
+ background-image: none;
217
+ color: var(--kit-btn-active-fg);
218
+ }
219
+
220
+ /* Text fields and text areas. */
221
+ .kit-field {
222
+ box-sizing: border-box;
223
+ min-height: var(--kit-control-min-height);
224
+ padding: 4px 7px;
225
+ border: var(--kit-field-border);
226
+ border-radius: var(--kit-field-radius);
227
+ background: var(--kit-field-bg);
228
+ box-shadow: var(--kit-field-shadow);
229
+ color: var(--kit-ink);
230
+ font-family: var(--kit-text-font);
231
+ font-size: var(--kit-control-size);
232
+ line-height: 1.35;
233
+ }
234
+
235
+ textarea.kit-field {
236
+ line-height: 1.55;
237
+ }
238
+
239
+ .kit-field:focus {
240
+ outline: none;
241
+ box-shadow: var(--kit-field-focus-ring);
242
+ }
243
+
244
+ /* The closed-set select harness: a native select under the desk's arrow. */
245
+ .kit-select-wrap {
246
+ position: relative;
247
+ display: inline-block;
248
+ max-width: 100%;
249
+ }
250
+
251
+ .kit-select-wrap > select {
252
+ box-sizing: border-box;
253
+ min-height: var(--kit-control-min-height);
254
+ padding: 4px 28px 4px 7px;
255
+ border: var(--kit-field-border);
256
+ border-radius: var(--kit-select-radius);
257
+ background: var(--kit-paper);
258
+ box-shadow: var(--kit-control-inset);
259
+ color: var(--kit-ink);
260
+ font-family: var(--kit-ui-font);
261
+ font-size: var(--kit-control-size);
262
+ appearance: none;
263
+ -webkit-appearance: none;
264
+ }
265
+
266
+ .kit-select-wrap::after {
267
+ content: "";
268
+ position: absolute;
269
+ right: 9px;
270
+ top: 50%;
271
+ width: 0;
272
+ height: 0;
273
+ border-left: 5px solid transparent;
274
+ border-right: 5px solid transparent;
275
+ border-top: 6px solid var(--kit-ink);
276
+ transform: translateY(-3px);
277
+ pointer-events: none;
278
+ }
279
+
280
+ /* Focus ring and disabled state, shared by every kit control. */
281
+ .kit-button:focus-visible,
282
+ .kit-select-wrap > select:focus-visible {
283
+ outline: var(--kit-focus-outline);
284
+ outline-offset: var(--kit-focus-offset);
285
+ }
286
+
287
+ .kit-button:disabled,
288
+ .kit-field:disabled,
289
+ .kit-select-wrap > select:disabled {
290
+ opacity: var(--kit-disabled-opacity);
291
+ }
292
+
293
+ /* Touch: controls keep the desk's hit target. */
294
+ :root[data-touch="true"] .kit-button,
295
+ :root[data-touch="true"] .kit-select-wrap > select {
296
+ min-height: var(--kit-hit-target);
297
+ }
298
+
299
+ /* Help text under a control. */
300
+ .kit-hint {
301
+ margin: var(--kit-hint-margin-top) 0 0;
302
+ color: var(--kit-hint-fg, inherit);
303
+ font-family: var(--kit-hint-font, var(--kit-text-font));
304
+ font-size: var(--kit-hint-font-size, var(--kit-text-size));
305
+ font-weight: var(--kit-hint-weight, 400);
306
+ line-height: var(--kit-hint-line-height, 1.35);
307
+ }
308
+
309
+ /* A button drawn as a link: no frame, the text underlined. */
310
+ .kit-link-button {
311
+ padding: 0;
312
+ border: 0;
313
+ background: none;
314
+ color: inherit;
315
+ font: inherit;
316
+ text-decoration: underline;
317
+ cursor: pointer;
318
+ }
319
+
320
+ /* Tabs (TDI): a strip of tabs over one panel. */
321
+ .kit-tabs {
322
+ display: flex;
323
+ gap: 3px;
324
+ border-bottom: var(--kit-system-border);
325
+ }
326
+
327
+ .kit-tab {
328
+ box-sizing: border-box;
329
+ min-height: var(--kit-tab-min-height);
330
+ padding: var(--kit-tab-pad-y) var(--kit-tab-pad-x);
331
+ border-width: var(--kit-tab-border-width);
332
+ border-style: var(--kit-tab-border-style);
333
+ border-color: var(--kit-tab-border-color);
334
+ border-bottom: none;
335
+ border-radius: var(--kit-tab-radius) var(--kit-tab-radius) 0 0;
336
+ background-color: var(--kit-tab-bg);
337
+ background-image: var(--kit-tab-image, none);
338
+ box-shadow: var(--kit-tab-shadow, none);
339
+ color: var(--kit-tab-fg);
340
+ font: inherit;
341
+ font-family: var(--kit-tab-font);
342
+ font-size: var(--kit-tab-font-size);
343
+ font-weight: var(--kit-tab-weight);
344
+ cursor: default;
345
+ }
346
+
347
+ .kit-tab:first-child,
348
+ .kit-tab:last-child {
349
+ border-radius: var(--kit-tab-end-radius);
350
+ }
351
+
352
+ .kit-tab.is-active,
353
+ .kit-tab[aria-selected="true"] {
354
+ background-color: var(--kit-tab-active-bg);
355
+ background-image: var(--kit-tab-active-image, none);
356
+ box-shadow: var(--kit-tab-active-shadow, none);
357
+ color: var(--kit-tab-active-fg);
358
+ font-weight: var(--kit-tab-active-weight);
359
+ }
360
+
361
+ /* List rows: a selectable row in a list the app owns. */
362
+ .kit-list {
363
+ margin: 0;
364
+ padding: 0;
365
+ list-style: none;
366
+ border: var(--kit-system-border);
367
+ border-radius: var(--kit-inset-radius);
368
+ background: var(--kit-paper);
369
+ }
370
+
371
+ .kit-list-row {
372
+ padding: 3px 8px;
373
+ border-radius: var(--kit-item-radius);
374
+ color: var(--kit-ink);
375
+ }
376
+
377
+ .kit-list-row.is-selected,
378
+ .kit-list-row[aria-selected="true"] {
379
+ background: var(--kit-selection-bg);
380
+ color: var(--kit-selection-fg);
381
+ }
382
+
383
+ /* An in-pane sheet: paper on the pane, the appearance's edge and shadow. */
384
+ .kit-sheet {
385
+ border: var(--kit-system-border);
386
+ border-radius: var(--kit-container-radius);
387
+ background: var(--kit-surface-primary);
388
+ box-shadow: var(--kit-system-shadow);
389
+ color: var(--kit-ink);
390
+ }
391
+
392
+ /* Scroll bars of a scrolling region the app owns. */
393
+ .kit-scroll {
394
+ overflow: auto;
395
+ scrollbar-color: var(--kit-scrollbar-thumb) var(--kit-scrollbar-track);
396
+ }
397
+
398
+ .kit-scroll::-webkit-scrollbar {
399
+ width: var(--kit-scrollbar-width);
400
+ height: var(--kit-scrollbar-width);
401
+ background: var(--kit-scrollbar-track);
402
+ }
403
+
404
+ .kit-scroll::-webkit-scrollbar-thumb {
405
+ border: 1px solid var(--kit-ink);
406
+ background: var(--kit-scrollbar-thumb);
407
+ }
408
+
409
+ .kit-tab:focus-visible,
410
+ .kit-link-button:focus-visible {
411
+ outline: var(--kit-focus-outline);
412
+ outline-offset: var(--kit-focus-offset);
413
+ }
414
+
415
+ /* A window's status bar, as the desk draws its own. */
416
+ .kit-status-bar {
417
+ box-sizing: border-box;
418
+ min-height: var(--kit-status-min-height);
419
+ padding: var(--kit-status-pad-top) var(--kit-status-pad-x) var(--kit-status-pad-bottom);
420
+ border-bottom: var(--kit-status-border-width) solid var(--kit-status-border-color);
421
+ background-color: var(--kit-status-bg);
422
+ background-image: var(--kit-status-image);
423
+ box-shadow: var(--kit-status-shadow);
424
+ color: var(--kit-status-fg);
425
+ font-family: var(--kit-status-font);
426
+ font-size: var(--kit-status-font-size);
427
+ }
428
+
429
+ .kit-status-bar > span {
430
+ font-size: var(--kit-status-text-size);
431
+ }
@@ -0,0 +1,89 @@
1
+ // The app's one connection to its host. Inside the desk the kit talks to the
2
+ // desk over a MessagePort; on the app's own page (npm run dev, or dist/ opened
3
+ // on its own) the kit's standalone adapter answers state and files itself.
4
+ // Everything the app needs from outside its frame goes through here.
5
+
6
+ import type { Language } from "../strings/index.ts";
7
+ import type { Appearance, BridgeClient, MenuState, SuspendCause } from "./types.ts";
8
+
9
+ /** The window's app and name in app.manifest.json: the desk checks the hello against them. */
10
+ export const APP_ID = "__WINDOW__";
11
+ export const WINDOW_NAME = "__WINDOW__";
12
+ /** Must equal the manifest's and package.json's version, or the desk refuses the connection. */
13
+ export const APP_VERSION = "0.1.0";
14
+
15
+ export type { BridgeClient } from "./types.ts";
16
+
17
+ /** Connects through the kit the page loaded before this module. */
18
+ export async function connect(): Promise<BridgeClient> {
19
+ const kit = window.AIS6Bridge;
20
+ if (!kit) throw new Error("ais6-bridge.js did not load");
21
+ return kit.connect({ appId: APP_ID, window: WINDOW_NAME, version: APP_VERSION });
22
+ }
23
+
24
+ export async function readState(client: BridgeClient, key: string): Promise<unknown> {
25
+ try {
26
+ return (await client.call("state.get", { key })).value;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ export async function writeState(client: BridgeClient, key: string, value: unknown): Promise<boolean> {
33
+ try {
34
+ await client.call("state.set", { key, value });
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+
41
+ /** Hands a file to the host to save (the desk's Save dialog, or a download on the app's own page). */
42
+ export async function exportFile(client: BridgeClient, name: string, mime: string, blob: Blob): Promise<boolean> {
43
+ try {
44
+ const bytes = new Uint8Array(await blob.arrayBuffer());
45
+ await client.call("files.export", { name, mime, bytes });
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ export function pushMenuState(client: BridgeClient, state: MenuState): void {
53
+ client.menuState(state);
54
+ }
55
+
56
+ export function onMenuCommand(client: BridgeClient, run: (action: string) => boolean): void {
57
+ client.handle("menu.command", ({ action }) => ({ handled: run(action) }));
58
+ }
59
+
60
+ /** Suspend, resume and dispose; each handler runs before the reply goes back, so save in suspend. */
61
+ export function onLifecycle(
62
+ client: BridgeClient,
63
+ handlers: { suspend: (cause: SuspendCause) => void | Promise<void>; resume: () => void; dispose: () => void },
64
+ ): void {
65
+ client.handle("lifecycle.suspend", async ({ cause }) => {
66
+ await handlers.suspend(cause ?? "away");
67
+ return {};
68
+ });
69
+ client.handle("lifecycle.resume", () => {
70
+ handlers.resume();
71
+ return {};
72
+ });
73
+ client.handle("lifecycle.dispose", () => {
74
+ handlers.dispose();
75
+ return {};
76
+ });
77
+ }
78
+
79
+ export function onAppearance(client: BridgeClient, listener: (appearance: Appearance) => void): () => void {
80
+ return client.on("appearance.changed", listener);
81
+ }
82
+
83
+ export function onLanguage(client: BridgeClient, listener: (language: Language) => void): () => void {
84
+ return client.on("language.changed", ({ language }) => listener(language));
85
+ }
86
+
87
+ export function setTitle(client: BridgeClient, title: string): void {
88
+ client.setTitle(title);
89
+ }
@@ -0,0 +1,100 @@
1
+ // The part of bridge contract v1 this app uses. The kit (ais6-bridge.js) is a
2
+ // classic script that installs `window.AIS6Bridge`; these types describe it.
3
+
4
+ import type { Language } from "../strings/index.ts";
5
+
6
+ export type ColorMode = "light" | "dark";
7
+
8
+ export interface Appearance {
9
+ readonly id: string;
10
+ readonly colorMode: ColorMode;
11
+ readonly oneBit?: boolean;
12
+ readonly touch: boolean;
13
+ readonly reducedMotion: boolean;
14
+ readonly tokens: Readonly<Record<string, string>>;
15
+ readonly kitUrl: string;
16
+ }
17
+
18
+ export interface Welcome {
19
+ readonly protocol: number;
20
+ readonly appId: string;
21
+ readonly window: string;
22
+ readonly version: string;
23
+ readonly language: Language;
24
+ readonly appearance: Appearance;
25
+ readonly capabilities: readonly string[];
26
+ readonly readOnly: boolean;
27
+ readonly standalone?: boolean;
28
+ }
29
+
30
+ /** `menu.state`: what the desk shows enabled and checked in this window's menus. */
31
+ export interface MenuState {
32
+ readonly available: Readonly<Record<string, boolean>>;
33
+ readonly checked: Readonly<Record<string, boolean>>;
34
+ }
35
+
36
+ export interface MenuCommand {
37
+ readonly action: string;
38
+ readonly arg?: unknown;
39
+ }
40
+
41
+ export type SuspendCause = "close" | "away";
42
+
43
+ /** The request methods this app calls, with their params and results. */
44
+ export interface BridgeCalls {
45
+ "state.get": { params: { key: string }; result: { value: unknown } };
46
+ "state.set": { params: { key: string; value: unknown }; result: Record<string, never> };
47
+ "files.export": { params: { name: string; mime: string; bytes: Uint8Array }; result: Record<string, never> };
48
+ "service.request": { params: { name: string; params: unknown; requestId?: string }; result: unknown };
49
+ "service.cancel": { params: { requestId: string }; result: { cancelled: boolean } };
50
+ }
51
+
52
+ /** The requests the desk sends this app, and what the app answers. */
53
+ export interface BridgeHandlers {
54
+ "menu.command": { params: MenuCommand; result: { handled: boolean } };
55
+ "lifecycle.suspend": { params: { cause?: SuspendCause }; result: Record<string, never> };
56
+ "lifecycle.resume": { params: Record<string, never>; result: Record<string, never> };
57
+ "lifecycle.dispose": { params: Record<string, never>; result: Record<string, never> };
58
+ }
59
+
60
+ /** The events the desk sends this app. */
61
+ export interface BridgeEvents {
62
+ "appearance.changed": Appearance;
63
+ "language.changed": { language: Language };
64
+ }
65
+
66
+ /** The client object `AIS6Bridge.connect()` resolves to (hosted or standalone). */
67
+ export interface BridgeClient {
68
+ readonly standalone: boolean;
69
+ readonly welcome: Welcome;
70
+ call<M extends keyof BridgeCalls>(method: M, params: BridgeCalls[M]["params"]): Promise<BridgeCalls[M]["result"]>;
71
+ on<E extends keyof BridgeEvents>(method: E, listener: (params: BridgeEvents[E]) => void): () => void;
72
+ handle<M extends keyof BridgeHandlers>(
73
+ method: M,
74
+ handler: (
75
+ params: BridgeHandlers[M]["params"],
76
+ ) => BridgeHandlers[M]["result"] | Promise<BridgeHandlers[M]["result"]>,
77
+ ): void;
78
+ ready(): boolean;
79
+ menuState(state: MenuState): boolean;
80
+ setTitle(title: string): boolean;
81
+ enableSnapshots(config: { producer: "canvas" | "dom"; target?: Element | null }): BridgeClient;
82
+ }
83
+
84
+ export interface ConnectOptions {
85
+ readonly appId: string;
86
+ readonly window: string;
87
+ readonly version: string;
88
+ }
89
+
90
+ /** `window.AIS6Bridge`, installed by ais6-bridge.js. */
91
+ export interface Ais6Bridge {
92
+ readonly protocol: number;
93
+ connect(options: ConnectOptions): Promise<BridgeClient>;
94
+ }
95
+
96
+ declare global {
97
+ interface Window {
98
+ AIS6Bridge?: Ais6Bridge;
99
+ }
100
+ }