glimpse-sdk 0.2.0 → 0.5.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.
@@ -60,21 +60,6 @@ export class Image extends WidgetBase {
60
60
  return { type: "image", data: payload };
61
61
  }
62
62
  }
63
- export class IconWidget extends WidgetBase {
64
- icon;
65
- options;
66
- constructor(icon, options = {}) {
67
- super(options);
68
- this.icon = icon;
69
- this.options = options;
70
- }
71
- toProtocol() {
72
- const payload = this.withCommon({ icon: this.icon.toProtocol() });
73
- if (this.options.pixel_size !== undefined)
74
- payload.pixel_size = this.options.pixel_size;
75
- return { type: "icon", data: payload };
76
- }
77
- }
78
63
  export class Progress extends WidgetBase {
79
64
  options;
80
65
  constructor(options) {
@@ -93,23 +78,121 @@ export class Progress extends WidgetBase {
93
78
  return { type: "progress", data: payload };
94
79
  }
95
80
  }
81
+ export class LevelBar extends WidgetBase {
82
+ options;
83
+ constructor(options) {
84
+ super(options);
85
+ this.options = options;
86
+ }
87
+ toProtocol() {
88
+ return {
89
+ type: "level_bar",
90
+ data: this.withCommon({
91
+ value: this.options.value,
92
+ min: this.options.min ?? 0,
93
+ max: this.options.max ?? 1,
94
+ mode: this.options.mode ?? "continuous",
95
+ }),
96
+ };
97
+ }
98
+ }
99
+ export class Picture extends WidgetBase {
100
+ options;
101
+ constructor(options) {
102
+ super(options);
103
+ this.options = options;
104
+ }
105
+ toProtocol() {
106
+ const payload = this.withCommon({ path: this.options.path });
107
+ if (this.options.content_fit !== undefined)
108
+ payload.content_fit = this.options.content_fit;
109
+ return { type: "picture", data: payload };
110
+ }
111
+ }
96
112
  export class Button extends WidgetBase {
97
113
  options;
98
114
  constructor(options = {}) {
99
- super(options);
115
+ const { variant: _variant, ...common } = options;
116
+ super(common);
100
117
  this.options = options;
101
118
  }
102
119
  toProtocol() {
103
- const payload = this.withCommon({});
120
+ const { variant, ...common } = this.options;
121
+ const payload = applyCommonProps({}, common);
104
122
  if (this.options.label !== undefined)
105
123
  payload.label = this.options.label;
106
124
  if (this.options.icon !== undefined)
107
- payload.icon = this.options.icon.toProtocol();
108
- if (this.options.child !== undefined)
109
- payload.child = this.options.child.toProtocol();
125
+ payload.icon = this.options.icon;
126
+ if (this.options.enabled !== undefined)
127
+ payload.enabled = this.options.enabled;
128
+ if (variant !== undefined)
129
+ payload.variant = variant;
110
130
  return { type: "button", data: payload };
111
131
  }
112
132
  }
133
+ export class LinkButton extends WidgetBase {
134
+ options;
135
+ constructor(options) {
136
+ super(options);
137
+ this.options = options;
138
+ }
139
+ toProtocol() {
140
+ const payload = this.withCommon({ uri: this.options.uri });
141
+ if (this.options.label !== undefined)
142
+ payload.label = this.options.label;
143
+ return { type: "link_button", data: payload };
144
+ }
145
+ }
146
+ export class Expander extends WidgetBase {
147
+ options;
148
+ constructor(options) {
149
+ super(options);
150
+ this.options = options;
151
+ }
152
+ toProtocol() {
153
+ return {
154
+ type: "expander",
155
+ data: this.withCommon({
156
+ label: this.options.label,
157
+ expanded: this.options.expanded ?? false,
158
+ child: this.options.child.toProtocol(),
159
+ }),
160
+ };
161
+ }
162
+ }
163
+ export class TreeExpander extends WidgetBase {
164
+ options;
165
+ constructor(options) {
166
+ super(options);
167
+ this.options = options;
168
+ }
169
+ toProtocol() {
170
+ return {
171
+ type: "tree_expander",
172
+ data: this.withCommon({
173
+ child: this.options.child.toProtocol(),
174
+ hide_expander: this.options.hide_expander ?? false,
175
+ indent_for_depth: this.options.indent_for_depth ?? false,
176
+ indent_for_icon: this.options.indent_for_icon ?? false,
177
+ }),
178
+ };
179
+ }
180
+ }
181
+ export class MenuButton extends WidgetBase {
182
+ options;
183
+ constructor(options) {
184
+ super(options);
185
+ this.options = options;
186
+ }
187
+ toProtocol() {
188
+ const payload = this.withCommon({ popover: this.options.popover.toProtocol() });
189
+ if (this.options.label !== undefined)
190
+ payload.label = this.options.label;
191
+ if (this.options.icon !== undefined)
192
+ payload.icon = this.options.icon;
193
+ return { type: "menu_button", data: payload };
194
+ }
195
+ }
113
196
  export class Switch extends WidgetBase {
114
197
  options;
115
198
  constructor(options = {}) {
@@ -123,7 +206,20 @@ export class Switch extends WidgetBase {
123
206
  return { type: "switch", data: payload };
124
207
  }
125
208
  }
126
- export class Scale extends WidgetBase {
209
+ export class ToggleButton extends WidgetBase {
210
+ options;
211
+ constructor(options = {}) {
212
+ super(options);
213
+ this.options = options;
214
+ }
215
+ toProtocol() {
216
+ const payload = this.withCommon({ active: this.options.active ?? false });
217
+ if (this.options.label !== undefined)
218
+ payload.label = this.options.label;
219
+ return { type: "toggle_button", data: payload };
220
+ }
221
+ }
222
+ export class Slider extends WidgetBase {
127
223
  options;
128
224
  constructor(options = {}) {
129
225
  super(options);
@@ -140,7 +236,7 @@ export class Scale extends WidgetBase {
140
236
  payload.orientation = this.options.orientation;
141
237
  if (this.options.draw_value !== undefined)
142
238
  payload.draw_value = this.options.draw_value;
143
- return { type: "scale", data: payload };
239
+ return { type: "slider", data: payload };
144
240
  }
145
241
  }
146
242
  export class Checkbox extends WidgetBase {
@@ -156,7 +252,7 @@ export class Checkbox extends WidgetBase {
156
252
  return { type: "checkbox", data: payload };
157
253
  }
158
254
  }
159
- export class DropdownItem {
255
+ export class SelectOption {
160
256
  id;
161
257
  label;
162
258
  constructor(id, label) {
@@ -167,7 +263,7 @@ export class DropdownItem {
167
263
  return { id: this.id, label: this.label };
168
264
  }
169
265
  }
170
- export class Dropdown extends WidgetBase {
266
+ export class Select extends WidgetBase {
171
267
  options;
172
268
  constructor(options = {}) {
173
269
  super(options);
@@ -179,7 +275,7 @@ export class Dropdown extends WidgetBase {
179
275
  });
180
276
  if (this.options.selected !== undefined)
181
277
  payload.selected = this.options.selected;
182
- return { type: "dropdown", data: payload };
278
+ return { type: "select", data: payload };
183
279
  }
184
280
  }
185
281
  export class Separator extends WidgetBase {
@@ -205,6 +301,37 @@ export class Scroll extends WidgetBase {
205
301
  return { type: "scroll", data: this.withCommon({ child: this.child.toProtocol() }) };
206
302
  }
207
303
  }
304
+ export class Overlay extends WidgetBase {
305
+ options;
306
+ constructor(options) {
307
+ super(options);
308
+ this.options = options;
309
+ }
310
+ toProtocol() {
311
+ return {
312
+ type: "overlay",
313
+ data: this.withCommon({
314
+ child: this.options.child.toProtocol(),
315
+ overlays: (this.options.overlays ?? []).map((overlay) => overlay.toProtocol()),
316
+ }),
317
+ };
318
+ }
319
+ }
320
+ export class ListBox extends WidgetBase {
321
+ options;
322
+ constructor(options = {}) {
323
+ super(options);
324
+ this.options = options;
325
+ }
326
+ toProtocol() {
327
+ return {
328
+ type: "list_box",
329
+ data: this.withCommon({
330
+ children: (this.options.children ?? []).map((child) => child.toProtocol()),
331
+ }),
332
+ };
333
+ }
334
+ }
208
335
  export class GridChild {
209
336
  row;
210
337
  column;
@@ -276,20 +403,6 @@ export class Card extends WidgetBase {
276
403
  };
277
404
  }
278
405
  }
279
- export class Header {
280
- title;
281
- subtitle;
282
- constructor(title, subtitle = "") {
283
- this.title = title;
284
- this.subtitle = subtitle;
285
- }
286
- toProtocol() {
287
- const payload = { title: this.title };
288
- if (this.subtitle !== "")
289
- payload.subtitle = this.subtitle;
290
- return payload;
291
- }
292
- }
293
406
  export class Section extends WidgetBase {
294
407
  options;
295
408
  constructor(options) {
@@ -297,80 +410,19 @@ export class Section extends WidgetBase {
297
410
  this.options = options;
298
411
  }
299
412
  toProtocol() {
300
- const header = this.options.header ??
301
- (this.options.title === undefined
302
- ? undefined
303
- : new Header(this.options.title, this.options.subtitle ?? ""));
304
- const body = this.options.body ?? this.options.children ?? [];
305
- return {
306
- type: "section",
307
- data: this.withCommon({
308
- ...(header === undefined ? {} : { header: header.toProtocol() }),
309
- body: body.map((child) => child.toProtocol()),
310
- }),
413
+ const payload = {
414
+ title: this.options.title ?? "",
415
+ children: (this.options.children ?? []).map((child) => child.toProtocol()),
311
416
  };
312
- }
313
- }
314
- export class Collapsible extends WidgetBase {
315
- options;
316
- constructor(options) {
317
- super(options);
318
- this.options = options;
319
- }
320
- toProtocol() {
321
- const header = this.options.header ??
322
- (this.options.title === undefined
323
- ? undefined
324
- : new Header(this.options.title, this.options.subtitle ?? ""));
325
- const body = this.options.body ?? this.options.children ?? [];
417
+ if (this.options.subtitle !== undefined && this.options.subtitle !== "") {
418
+ payload.subtitle = this.options.subtitle;
419
+ }
326
420
  return {
327
- type: "collapsible",
328
- data: this.withCommon({
329
- ...(header === undefined ? {} : { header: header.toProtocol() }),
330
- expanded: this.options.expanded ?? false,
331
- body: body.map((child) => child.toProtocol()),
332
- }),
421
+ type: "section",
422
+ data: this.withCommon(payload),
333
423
  };
334
424
  }
335
425
  }
336
- export class Item extends WidgetBase {
337
- options;
338
- constructor(options = {}) {
339
- super(options);
340
- this.options = options;
341
- }
342
- toProtocol() {
343
- const payload = this.withCommon({
344
- label: this.options.label ?? "",
345
- clickable: this.options.clickable ?? false,
346
- menu: (this.options.menu ?? []).map((item) => item.toProtocol()),
347
- });
348
- if (this.options.left !== undefined)
349
- payload.left = this.options.left.toProtocol();
350
- if (this.options.right !== undefined)
351
- payload.right = this.options.right.toProtocol();
352
- return { type: "item", data: payload };
353
- }
354
- }
355
- export class CollapsibleItem extends WidgetBase {
356
- options;
357
- constructor(options = {}) {
358
- super(options);
359
- this.options = options;
360
- }
361
- toProtocol() {
362
- const payload = this.withCommon({
363
- label: this.options.label ?? "",
364
- expanded: this.options.expanded ?? false,
365
- body: (this.options.body ?? this.options.children ?? []).map((child) => child.toProtocol()),
366
- });
367
- if (this.options.left !== undefined)
368
- payload.left = this.options.left.toProtocol();
369
- if (this.options.right !== undefined)
370
- payload.right = this.options.right.toProtocol();
371
- return { type: "collapsible_item", data: payload };
372
- }
373
- }
374
426
  export class Meter extends WidgetBase {
375
427
  options;
376
428
  constructor(options) {
@@ -409,52 +461,6 @@ export class Copyable extends WidgetBase {
409
461
  };
410
462
  }
411
463
  }
412
- export class ToastAction {
413
- id;
414
- label;
415
- constructor(id, label) {
416
- this.id = id;
417
- this.label = label;
418
- }
419
- toProtocol() {
420
- return { id: this.id, label: this.label };
421
- }
422
- }
423
- export class Toast extends WidgetBase {
424
- options;
425
- constructor(options) {
426
- super(options);
427
- this.options = options;
428
- }
429
- toProtocol() {
430
- const payload = this.withCommon({
431
- title: this.options.title,
432
- message: this.options.message ?? "",
433
- });
434
- if (this.options.icon !== undefined)
435
- payload.icon = this.options.icon.toProtocol();
436
- if (this.options.action !== undefined)
437
- payload.action = this.options.action.toProtocol();
438
- return { type: "toast", data: payload };
439
- }
440
- }
441
- export class ActionRow extends WidgetBase {
442
- options;
443
- constructor(options) {
444
- super(options);
445
- this.options = options;
446
- }
447
- toProtocol() {
448
- const payload = this.withCommon({
449
- title: this.options.title,
450
- subtitle: this.options.subtitle ?? "",
451
- meta: this.options.meta ?? "",
452
- });
453
- if (this.options.icon !== undefined)
454
- payload.icon = this.options.icon.toProtocol();
455
- return { type: "action_row", data: payload };
456
- }
457
- }
458
464
  export class Row extends WidgetBase {
459
465
  options;
460
466
  constructor(options = {}) {
@@ -487,78 +493,88 @@ export class Column extends WidgetBase {
487
493
  };
488
494
  }
489
495
  }
490
- export class ActionMenuItem {
496
+ export class Spinner extends WidgetBase {
491
497
  options;
492
- constructor(options) {
498
+ constructor(options = {}) {
499
+ super(options);
493
500
  this.options = options;
494
501
  }
495
502
  toProtocol() {
496
- const payload = {
497
- id: this.options.id,
498
- label: this.options.label,
503
+ return {
504
+ type: "spinner",
505
+ data: this.withCommon({ spinning: this.options.spinning ?? true }),
499
506
  };
500
- if (this.options.icon !== undefined)
501
- payload.icon = this.options.icon.toProtocol();
502
- if (this.options.visible !== undefined)
503
- payload.visible = this.options.visible;
504
- if (this.options.checked !== undefined)
505
- payload.checked = this.options.checked;
506
- if (this.options.selectable !== undefined)
507
- payload.selectable = this.options.selectable;
508
- return payload;
509
507
  }
510
508
  }
511
- export class ActionMenu extends WidgetBase {
509
+ export class PropertyList extends WidgetBase {
512
510
  options;
513
511
  constructor(options = {}) {
514
512
  super(options);
515
513
  this.options = options;
516
514
  }
517
515
  toProtocol() {
518
- const payload = this.withCommon({
519
- items: (this.options.items ?? []).map((item) => item.toProtocol()),
520
- });
521
- if (this.options.header !== undefined)
522
- payload.header = this.options.header;
523
- return { type: "action_menu", data: payload };
516
+ const payload = {
517
+ rows: Object.entries(this.options.rows ?? {}).map(([key, value]) => ({ key, value })),
518
+ };
519
+ if (this.options.title !== undefined && this.options.title !== "") {
520
+ payload.title = this.options.title;
521
+ }
522
+ return {
523
+ type: "property_list",
524
+ data: this.withCommon(payload),
525
+ };
524
526
  }
525
527
  }
526
- export class Spinner extends WidgetBase {
528
+ export class Item extends WidgetBase {
527
529
  options;
528
- constructor(options = {}) {
530
+ constructor(options) {
529
531
  super(options);
530
532
  this.options = options;
531
533
  }
532
534
  toProtocol() {
535
+ const payload = {
536
+ label: this.options.label,
537
+ };
538
+ if (this.options.icon !== undefined && this.options.icon !== "") {
539
+ payload.icon = this.options.icon;
540
+ }
541
+ if (this.options.sublabel !== undefined && this.options.sublabel !== "") {
542
+ payload.sublabel = this.options.sublabel;
543
+ }
544
+ if (this.options.right !== undefined) {
545
+ payload.right = this.options.right.toProtocol();
546
+ }
533
547
  return {
534
- type: "spinner",
535
- data: this.withCommon({ spinning: this.options.spinning ?? true }),
548
+ type: "item",
549
+ data: this.withCommon(payload),
536
550
  };
537
551
  }
538
552
  }
539
- export class DetailGridItem {
540
- key;
541
- value;
542
- constructor(key, value) {
543
- this.key = key;
544
- this.value = value;
545
- }
546
- toProtocol() {
547
- return { key: this.key, value: this.value };
548
- }
549
- }
550
- export class DetailGrid extends WidgetBase {
553
+ export class ActionItem extends WidgetBase {
551
554
  options;
552
- constructor(options = {}) {
555
+ constructor(options) {
553
556
  super(options);
554
557
  this.options = options;
555
558
  }
556
559
  toProtocol() {
560
+ const payload = {
561
+ label: this.options.label,
562
+ };
563
+ if (this.options.icon !== undefined && this.options.icon !== "") {
564
+ payload.icon = this.options.icon;
565
+ }
566
+ if (this.options.sublabel !== undefined && this.options.sublabel !== "") {
567
+ payload.sublabel = this.options.sublabel;
568
+ }
569
+ if (this.options.right !== undefined) {
570
+ payload.right = this.options.right.toProtocol();
571
+ }
572
+ if (this.options.enabled !== undefined) {
573
+ payload.enabled = this.options.enabled;
574
+ }
557
575
  return {
558
- type: "detail_grid",
559
- data: this.withCommon({
560
- rows: (this.options.rows ?? []).map((row) => row.toProtocol()),
561
- }),
576
+ type: "action_item",
577
+ data: this.withCommon(payload),
562
578
  };
563
579
  }
564
580
  }
@@ -601,6 +617,44 @@ export class StatusDot extends WidgetBase {
601
617
  return { type: "status", data: this.withCommon({}) };
602
618
  }
603
619
  }
620
+ export class PagerItem extends WidgetBase {
621
+ options;
622
+ constructor(options = {}) {
623
+ super(options);
624
+ this.options = options;
625
+ }
626
+ toData() {
627
+ return this.withCommon({
628
+ appearance: this.options.appearance ?? "dots",
629
+ label: this.options.label ?? "",
630
+ active: this.options.active ?? false,
631
+ inactive: this.options.inactive ?? false,
632
+ occupied: this.options.occupied ?? false,
633
+ urgent: this.options.urgent ?? false,
634
+ });
635
+ }
636
+ toProtocol() {
637
+ return {
638
+ type: "pager_item",
639
+ data: this.toData(),
640
+ };
641
+ }
642
+ }
643
+ export class PagerStrip extends WidgetBase {
644
+ options;
645
+ constructor(options = {}) {
646
+ super(options);
647
+ this.options = options;
648
+ }
649
+ toProtocol() {
650
+ return {
651
+ type: "pager_strip",
652
+ data: this.withCommon({
653
+ items: (this.options.items ?? []).map((item) => item.toData()),
654
+ }),
655
+ };
656
+ }
657
+ }
604
658
  export class Box extends WidgetBase {
605
659
  options;
606
660
  constructor(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "glimpse-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.5.0",
4
4
  "description": "Typed async framework for building Glimpse exec applets",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
package/src/app.ts CHANGED
@@ -23,21 +23,22 @@ interface OutgoingMessage {
23
23
  line: string;
24
24
  }
25
25
 
26
- export class RenderResult {
27
- constructor(
28
- public readonly options: {
29
- status?: StatusItem[];
30
- tree?: TreeNode | null;
31
- } = {},
32
- ) {}
26
+ interface ShowNotificationArgs {
27
+ summary: string;
28
+ body?: string;
29
+ urgency?: string;
30
+ }
33
31
 
34
- get status(): StatusItem[] {
35
- return this.options.status ?? [];
36
- }
32
+ interface OpenUriArgs {
33
+ uri: string;
34
+ }
37
35
 
38
- get tree(): TreeNode | null {
39
- return this.options.tree ?? null;
40
- }
36
+ interface CopyToClipboardArgs {
37
+ text: string;
38
+ }
39
+
40
+ interface DismissNotificationArgs {
41
+ id: number;
41
42
  }
42
43
 
43
44
  export abstract class Applet<State extends object> {
@@ -63,8 +64,12 @@ export abstract class Applet<State extends object> {
63
64
 
64
65
  protected async onCallback(_event: CallbackEvent): Promise<void> {}
65
66
 
66
- protected async render(): Promise<RenderResult> {
67
- return new RenderResult();
67
+ protected async status(_state: State): Promise<StatusItem[]> {
68
+ return [];
69
+ }
70
+
71
+ protected async popover(_state: State): Promise<TreeNode | null> {
72
+ return null;
68
73
  }
69
74
 
70
75
  async setState(patch: Partial<State>): Promise<void> {
@@ -96,6 +101,26 @@ export abstract class Applet<State extends object> {
96
101
  return this.popoverOpen;
97
102
  }
98
103
 
104
+ protected showNotification(args: ShowNotificationArgs): void {
105
+ this.emitAction("show_notification", args);
106
+ }
107
+
108
+ protected openUri(args: OpenUriArgs): void {
109
+ this.emitAction("open_uri", args);
110
+ }
111
+
112
+ protected copyToClipboard(args: CopyToClipboardArgs): void {
113
+ this.emitAction("copy_to_clipboard", args);
114
+ }
115
+
116
+ protected dismissNotification(args: DismissNotificationArgs): void {
117
+ this.emitAction("dismiss_notification", args);
118
+ }
119
+
120
+ protected closePopover(): void {
121
+ this.emitAction("close_popover", {});
122
+ }
123
+
99
124
  async run(): Promise<void> {
100
125
  process.stdout.on("error", (err: NodeJS.ErrnoException) => {
101
126
  if (err.code === "EPIPE") {
@@ -188,16 +213,16 @@ export abstract class Applet<State extends object> {
188
213
  }
189
214
 
190
215
  private async flushRender(): Promise<void> {
191
- const rendered = await this.render();
192
- const status = rendered.status.map((item) => item.toProtocol());
193
- const tree = { root: rendered.tree?.toProtocol() ?? null };
194
-
216
+ const statusItems = await this.status(this.state);
217
+ const status = statusItems.map((item) => item.toProtocol());
195
218
  if (!deepEqual(status, this.lastStatus)) {
196
219
  this.lastStatus = status;
197
220
  this.emit("status", { items: status });
198
221
  }
199
- const publishPopover = this.popoverOpen || this.lastTree === null || tree.root === null;
200
- if (publishPopover && !deepEqual(tree, this.lastTree)) {
222
+
223
+ const widget = await this.popover(this.state);
224
+ const tree = { root: widget?.toProtocol() ?? null };
225
+ if (!deepEqual(tree, this.lastTree)) {
201
226
  this.lastTree = tree;
202
227
  this.emit("popover", tree);
203
228
  }
@@ -215,6 +240,10 @@ export abstract class Applet<State extends object> {
215
240
  }
216
241
  }
217
242
  }
243
+
244
+ private emitAction(type: string, args: object): void {
245
+ this.emit("action", { type, arguments: args });
246
+ }
218
247
  }
219
248
 
220
249
  function parseLine(line: string): { command: string; data: unknown } | null {