glimpse-sdk 0.7.0 → 0.8.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 +33 -31
- package/dist/src/protocol.d.ts +8 -0
- package/dist/src/protocol.js +14 -0
- package/dist/src/widgets.d.ts +2 -2
- package/dist/src/widgets.js +23 -6
- package/package.json +1 -1
- package/src/protocol.ts +16 -0
- package/src/widgets.ts +31 -7
package/README.md
CHANGED
|
@@ -15,19 +15,19 @@ npm install glimpse-sdk
|
|
|
15
15
|
Create and live-run a TypeScript applet project with the Glimpse tooling:
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
|
-
glimpse-
|
|
18
|
+
glimpse-shell applets new counter --lang typescript
|
|
19
19
|
cd counter
|
|
20
|
-
glimpse-
|
|
20
|
+
glimpse-shell applets dev
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Read `docs/custom-applets/tooling.md` for project layout, `applet.toml`, dev applets, linking, and diagnostics.
|
|
23
|
+
Read `docs/custom-applets/tooling.md` for project layout, `applet.toml`, dev applets, local linking, distribution, and diagnostics.
|
|
24
24
|
|
|
25
25
|
## Goals
|
|
26
26
|
|
|
27
27
|
- typed protocol models
|
|
28
28
|
- typed widget builders
|
|
29
29
|
- async runtime
|
|
30
|
-
- explicit typed handler registration
|
|
30
|
+
- widget-local callbacks, with explicit typed handler registration available when needed
|
|
31
31
|
- separate `status(state)` and `popover(state)` methods; state mutation via `await this.setState(...)`
|
|
32
32
|
|
|
33
33
|
## Example
|
|
@@ -35,12 +35,11 @@ Read `docs/custom-applets/tooling.md` for project layout, `applet.toml`, dev app
|
|
|
35
35
|
```ts
|
|
36
36
|
import {
|
|
37
37
|
Applet,
|
|
38
|
-
|
|
39
|
-
Button,
|
|
38
|
+
Column,
|
|
40
39
|
Hero,
|
|
41
|
-
Icon,
|
|
42
|
-
StatusItem,
|
|
43
40
|
Label,
|
|
41
|
+
StatusItem,
|
|
42
|
+
Tile,
|
|
44
43
|
type TreeNode,
|
|
45
44
|
} from "glimpse-sdk";
|
|
46
45
|
|
|
@@ -50,42 +49,43 @@ interface DeployState {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
class DeployApplet extends Applet<DeployState> {
|
|
53
|
-
protected initialState(): DeployState {
|
|
54
|
-
return { version: "2026.04.07", status: "Ready" };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
52
|
constructor() {
|
|
58
53
|
super();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
protected initialState(): DeployState {
|
|
57
|
+
return { version: "2026.04.07", status: "Ready" };
|
|
62
58
|
}
|
|
63
59
|
|
|
64
60
|
protected async status(state: DeployState): Promise<StatusItem[]> {
|
|
65
61
|
return [
|
|
66
62
|
new StatusItem({
|
|
67
63
|
id: "deploy",
|
|
68
|
-
icon:
|
|
64
|
+
icon: "software-update-available-symbolic",
|
|
69
65
|
label: state.status,
|
|
70
66
|
}),
|
|
71
67
|
];
|
|
72
68
|
}
|
|
73
69
|
|
|
74
70
|
protected async popover(state: DeployState): Promise<TreeNode | null> {
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
71
|
+
return new Column({
|
|
72
|
+
children: [
|
|
73
|
+
new Hero({
|
|
74
|
+
icon: "software-update-available-symbolic",
|
|
75
|
+
title: "Deploy",
|
|
76
|
+
subtitle: state.version,
|
|
77
|
+
}),
|
|
78
|
+
new Label("Version"),
|
|
79
|
+
new Tile({
|
|
80
|
+
id: "deploy_now",
|
|
81
|
+
primary: "Deploy now",
|
|
82
|
+
left_icon: "media-playback-start-symbolic",
|
|
83
|
+
on_click: async () => {
|
|
84
|
+
await this.setState({ status: "Deploying" });
|
|
85
|
+
},
|
|
86
|
+
}),
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -94,7 +94,9 @@ await new DeployApplet().run();
|
|
|
94
94
|
|
|
95
95
|
## Handler Registration
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
Prefer widget-local callbacks for controls rendered in `popover(state)`.
|
|
98
|
+
Explicit registration helpers are still available when the handler should live
|
|
99
|
+
outside the widget tree:
|
|
98
100
|
|
|
99
101
|
- `this.onClick(id, handler)`
|
|
100
102
|
- `this.onScroll(id, handler)`
|
package/dist/src/protocol.d.ts
CHANGED
|
@@ -4,12 +4,20 @@ export declare class StatusItem {
|
|
|
4
4
|
icon?: string;
|
|
5
5
|
label?: string;
|
|
6
6
|
tooltip?: string;
|
|
7
|
+
cssClasses?: string[];
|
|
7
8
|
};
|
|
8
9
|
constructor(options?: {
|
|
9
10
|
id?: string;
|
|
10
11
|
icon?: string;
|
|
11
12
|
label?: string;
|
|
12
13
|
tooltip?: string;
|
|
14
|
+
cssClasses?: string[];
|
|
13
15
|
});
|
|
16
|
+
/**
|
|
17
|
+
* Return a copy with `cssClass` appended to `cssClasses`. Immutable-update
|
|
18
|
+
* form that mirrors the Rust SDK's `css_class()` builder so SDK-hopping
|
|
19
|
+
* users see equivalent ergonomics across languages.
|
|
20
|
+
*/
|
|
21
|
+
withCssClass(cssClass: string): StatusItem;
|
|
14
22
|
toProtocol(): Record<string, unknown>;
|
|
15
23
|
}
|
package/dist/src/protocol.js
CHANGED
|
@@ -3,6 +3,17 @@ export class StatusItem {
|
|
|
3
3
|
constructor(options = {}) {
|
|
4
4
|
this.options = options;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Return a copy with `cssClass` appended to `cssClasses`. Immutable-update
|
|
8
|
+
* form that mirrors the Rust SDK's `css_class()` builder so SDK-hopping
|
|
9
|
+
* users see equivalent ergonomics across languages.
|
|
10
|
+
*/
|
|
11
|
+
withCssClass(cssClass) {
|
|
12
|
+
return new StatusItem({
|
|
13
|
+
...this.options,
|
|
14
|
+
cssClasses: [...(this.options.cssClasses ?? []), cssClass],
|
|
15
|
+
});
|
|
16
|
+
}
|
|
6
17
|
toProtocol() {
|
|
7
18
|
const payload = {};
|
|
8
19
|
if (this.options.id !== undefined) {
|
|
@@ -17,6 +28,9 @@ export class StatusItem {
|
|
|
17
28
|
if (this.options.tooltip !== undefined) {
|
|
18
29
|
payload.tooltip = this.options.tooltip;
|
|
19
30
|
}
|
|
31
|
+
if (this.options.cssClasses && this.options.cssClasses.length > 0) {
|
|
32
|
+
payload.css_classes = [...this.options.cssClasses];
|
|
33
|
+
}
|
|
20
34
|
return payload;
|
|
21
35
|
}
|
|
22
36
|
}
|
package/dist/src/widgets.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export type InlineHandler = (event: unknown) => void | Promise<void>;
|
|
2
2
|
export declare class InlineHandlerRegistry {
|
|
3
3
|
readonly handlers: Map<string, InlineHandler>;
|
|
4
|
+
generatedId(event: "click" | "toggle" | "change", path: number[]): string;
|
|
5
|
+
targetId(event: "click" | "toggle" | "change", id: string | undefined, path: number[]): string;
|
|
4
6
|
add(event: "click" | "toggle" | "change", id: string, handler: InlineHandler): void;
|
|
5
7
|
}
|
|
6
8
|
export type BadgeKind = "default" | "success" | "warning" | "error" | "accent";
|
|
@@ -195,7 +197,6 @@ export declare class Tile extends WidgetBase {
|
|
|
195
197
|
left_icon?: string;
|
|
196
198
|
left?: TreeNode;
|
|
197
199
|
right?: TreeNode;
|
|
198
|
-
activatable?: boolean;
|
|
199
200
|
on_click?: InlineHandler;
|
|
200
201
|
};
|
|
201
202
|
readonly type: string;
|
|
@@ -206,7 +207,6 @@ export declare class Tile extends WidgetBase {
|
|
|
206
207
|
left_icon?: string;
|
|
207
208
|
left?: TreeNode;
|
|
208
209
|
right?: TreeNode;
|
|
209
|
-
activatable?: boolean;
|
|
210
210
|
on_click?: InlineHandler;
|
|
211
211
|
});
|
|
212
212
|
data(): Record<string, unknown>;
|
package/dist/src/widgets.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export class InlineHandlerRegistry {
|
|
2
2
|
handlers = new Map();
|
|
3
|
+
generatedId(event, path) {
|
|
4
|
+
const suffix = path.length === 0 ? "root" : path.join(".");
|
|
5
|
+
return `__glimpse:${event}:${suffix}`;
|
|
6
|
+
}
|
|
7
|
+
targetId(event, id, path) {
|
|
8
|
+
return id ?? this.generatedId(event, path);
|
|
9
|
+
}
|
|
3
10
|
add(event, id, handler) {
|
|
4
11
|
this.handlers.set(`${event}:${id}`, handler);
|
|
5
12
|
}
|
|
@@ -73,8 +80,10 @@ export class Hero extends WidgetBase {
|
|
|
73
80
|
return out;
|
|
74
81
|
}
|
|
75
82
|
bindHandlers(registry, path) {
|
|
76
|
-
if (this.options.
|
|
83
|
+
if (this.options.on_toggle) {
|
|
84
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
77
85
|
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
86
|
+
}
|
|
78
87
|
this.options.trailing?.bindHandlers(registry, [...path, 0]);
|
|
79
88
|
}
|
|
80
89
|
}
|
|
@@ -250,7 +259,7 @@ export class Tile extends WidgetBase {
|
|
|
250
259
|
this.options = options;
|
|
251
260
|
}
|
|
252
261
|
data() {
|
|
253
|
-
const out = { ...this.commonData(), primary: this.options.primary
|
|
262
|
+
const out = { ...this.commonData(), primary: this.options.primary };
|
|
254
263
|
for (const key of ["id", "secondary", "left_icon"])
|
|
255
264
|
if (this.options[key] !== undefined)
|
|
256
265
|
out[key] = this.options[key];
|
|
@@ -263,8 +272,10 @@ export class Tile extends WidgetBase {
|
|
|
263
272
|
return out;
|
|
264
273
|
}
|
|
265
274
|
bindHandlers(registry, path) {
|
|
266
|
-
if (this.options.
|
|
275
|
+
if (this.options.on_click) {
|
|
276
|
+
this.options.id = registry.targetId("click", this.options.id, path);
|
|
267
277
|
registry.add("click", this.options.id, this.options.on_click);
|
|
278
|
+
}
|
|
268
279
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
269
280
|
this.options.right?.bindHandlers(registry, [...path, 1]);
|
|
270
281
|
}
|
|
@@ -286,8 +297,10 @@ export class SegmentedTile extends Tile {
|
|
|
286
297
|
}
|
|
287
298
|
bindHandlers(registry, path) {
|
|
288
299
|
super.bindHandlers(registry, path);
|
|
289
|
-
if (this.options.
|
|
300
|
+
if (this.options.on_toggle) {
|
|
301
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
290
302
|
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
303
|
+
}
|
|
291
304
|
this.options.child?.bindHandlers(registry, [...path, 2]);
|
|
292
305
|
}
|
|
293
306
|
}
|
|
@@ -335,8 +348,10 @@ export class ExpanderTile extends WidgetBase {
|
|
|
335
348
|
return out;
|
|
336
349
|
}
|
|
337
350
|
bindHandlers(registry, path) {
|
|
338
|
-
if (this.options.
|
|
351
|
+
if (this.options.on_toggle) {
|
|
352
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
339
353
|
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
354
|
+
}
|
|
340
355
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
341
356
|
this.options.child?.bindHandlers(registry, [...path, 1]);
|
|
342
357
|
}
|
|
@@ -382,8 +397,10 @@ export class ChoiceTile extends WidgetBase {
|
|
|
382
397
|
return out;
|
|
383
398
|
}
|
|
384
399
|
bindHandlers(registry, path) {
|
|
385
|
-
if (this.options.
|
|
400
|
+
if (this.options.on_click) {
|
|
401
|
+
this.options.id = registry.targetId("click", this.options.id, path);
|
|
386
402
|
registry.add("click", this.options.id, this.options.on_click);
|
|
403
|
+
}
|
|
387
404
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
388
405
|
}
|
|
389
406
|
}
|
package/package.json
CHANGED
package/src/protocol.ts
CHANGED
|
@@ -5,9 +5,22 @@ export class StatusItem {
|
|
|
5
5
|
icon?: string;
|
|
6
6
|
label?: string;
|
|
7
7
|
tooltip?: string;
|
|
8
|
+
cssClasses?: string[];
|
|
8
9
|
} = {},
|
|
9
10
|
) {}
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Return a copy with `cssClass` appended to `cssClasses`. Immutable-update
|
|
14
|
+
* form that mirrors the Rust SDK's `css_class()` builder so SDK-hopping
|
|
15
|
+
* users see equivalent ergonomics across languages.
|
|
16
|
+
*/
|
|
17
|
+
withCssClass(cssClass: string): StatusItem {
|
|
18
|
+
return new StatusItem({
|
|
19
|
+
...this.options,
|
|
20
|
+
cssClasses: [...(this.options.cssClasses ?? []), cssClass],
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
toProtocol(): Record<string, unknown> {
|
|
12
25
|
const payload: Record<string, unknown> = {};
|
|
13
26
|
if (this.options.id !== undefined) {
|
|
@@ -22,6 +35,9 @@ export class StatusItem {
|
|
|
22
35
|
if (this.options.tooltip !== undefined) {
|
|
23
36
|
payload.tooltip = this.options.tooltip;
|
|
24
37
|
}
|
|
38
|
+
if (this.options.cssClasses && this.options.cssClasses.length > 0) {
|
|
39
|
+
payload.css_classes = [...this.options.cssClasses];
|
|
40
|
+
}
|
|
25
41
|
return payload;
|
|
26
42
|
}
|
|
27
43
|
}
|
package/src/widgets.ts
CHANGED
|
@@ -2,6 +2,15 @@ export type InlineHandler = (event: unknown) => void | Promise<void>;
|
|
|
2
2
|
export class InlineHandlerRegistry {
|
|
3
3
|
readonly handlers = new Map<string, InlineHandler>();
|
|
4
4
|
|
|
5
|
+
generatedId(event: "click" | "toggle" | "change", path: number[]): string {
|
|
6
|
+
const suffix = path.length === 0 ? "root" : path.join(".");
|
|
7
|
+
return `__glimpse:${event}:${suffix}`;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
targetId(event: "click" | "toggle" | "change", id: string | undefined, path: number[]): string {
|
|
11
|
+
return id ?? this.generatedId(event, path);
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
add(event: "click" | "toggle" | "change", id: string, handler: InlineHandler): void {
|
|
6
15
|
this.handlers.set(`${event}:${id}`, handler);
|
|
7
16
|
}
|
|
@@ -82,7 +91,10 @@ export class Hero extends WidgetBase {
|
|
|
82
91
|
return out;
|
|
83
92
|
}
|
|
84
93
|
bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
|
|
85
|
-
if (this.options.
|
|
94
|
+
if (this.options.on_toggle) {
|
|
95
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
96
|
+
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
97
|
+
}
|
|
86
98
|
this.options.trailing?.bindHandlers(registry, [...path, 0]);
|
|
87
99
|
}
|
|
88
100
|
}
|
|
@@ -212,9 +224,9 @@ export class PopoverShell extends ChildrenWidget {
|
|
|
212
224
|
|
|
213
225
|
export class Tile extends WidgetBase {
|
|
214
226
|
readonly type: string = "tile";
|
|
215
|
-
constructor(protected readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; right?: TreeNode;
|
|
227
|
+
constructor(protected readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; right?: TreeNode; on_click?: InlineHandler }) { super(options); }
|
|
216
228
|
data(): Record<string, unknown> {
|
|
217
|
-
const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary
|
|
229
|
+
const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary };
|
|
218
230
|
for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
|
|
219
231
|
const left = child(this.options.left);
|
|
220
232
|
const right = child(this.options.right);
|
|
@@ -223,7 +235,10 @@ export class Tile extends WidgetBase {
|
|
|
223
235
|
return out;
|
|
224
236
|
}
|
|
225
237
|
bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
|
|
226
|
-
if (this.options.
|
|
238
|
+
if (this.options.on_click) {
|
|
239
|
+
this.options.id = registry.targetId("click", this.options.id, path);
|
|
240
|
+
registry.add("click", this.options.id, this.options.on_click);
|
|
241
|
+
}
|
|
227
242
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
228
243
|
this.options.right?.bindHandlers(registry, [...path, 1]);
|
|
229
244
|
}
|
|
@@ -241,7 +256,10 @@ export class SegmentedTile extends Tile {
|
|
|
241
256
|
}
|
|
242
257
|
bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
|
|
243
258
|
super.bindHandlers(registry, path);
|
|
244
|
-
if (this.options.
|
|
259
|
+
if (this.options.on_toggle) {
|
|
260
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
261
|
+
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
262
|
+
}
|
|
245
263
|
this.options.child?.bindHandlers(registry, [...path, 2]);
|
|
246
264
|
}
|
|
247
265
|
}
|
|
@@ -275,7 +293,10 @@ export class ExpanderTile extends WidgetBase {
|
|
|
275
293
|
return out;
|
|
276
294
|
}
|
|
277
295
|
bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
|
|
278
|
-
if (this.options.
|
|
296
|
+
if (this.options.on_toggle) {
|
|
297
|
+
this.options.id = registry.targetId("toggle", this.options.id, path);
|
|
298
|
+
registry.add("toggle", this.options.id, this.options.on_toggle);
|
|
299
|
+
}
|
|
279
300
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
280
301
|
this.options.child?.bindHandlers(registry, [...path, 1]);
|
|
281
302
|
}
|
|
@@ -308,7 +329,10 @@ export class ChoiceTile extends WidgetBase {
|
|
|
308
329
|
return out;
|
|
309
330
|
}
|
|
310
331
|
bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
|
|
311
|
-
if (this.options.
|
|
332
|
+
if (this.options.on_click) {
|
|
333
|
+
this.options.id = registry.targetId("click", this.options.id, path);
|
|
334
|
+
registry.add("click", this.options.id, this.options.on_click);
|
|
335
|
+
}
|
|
312
336
|
this.options.left?.bindHandlers(registry, [...path, 0]);
|
|
313
337
|
}
|
|
314
338
|
}
|