glimpse-sdk 0.5.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/src/widgets.ts CHANGED
@@ -1,920 +1,491 @@
1
- import { Icon } from "./protocol.js";
1
+ export type InlineHandler = (event: unknown) => void | Promise<void>;
2
+ export class InlineHandlerRegistry {
3
+ readonly handlers = new Map<string, InlineHandler>();
2
4
 
3
- export type Align = "fill" | "start" | "end" | "center" | "baseline";
4
- export type Orientation = "horizontal" | "vertical";
5
- export type Variant = "normal" | "muted" | "accent" | "success" | "warning" | "danger";
6
- export type ButtonVariant = "primary" | "secondary" | "compact" | "flat" | "danger";
7
- export type PagerAppearance = "dots" | "numbers";
8
- export type ContentFit = "fill" | "contain" | "cover" | "scale_down";
9
- export type LevelBarMode = "continuous" | "discrete";
10
-
11
- export interface WidgetNode {
12
- toProtocol(): Record<string, unknown>;
13
- }
14
-
15
- export interface CommonProps {
16
- id?: string;
17
- visible?: boolean;
18
- hexpand?: boolean;
19
- vexpand?: boolean;
20
- halign?: Align;
21
- valign?: Align;
22
- tooltip?: string;
23
- variant?: Variant;
24
- }
25
-
26
- function applyCommonProps(
27
- payload: Record<string, unknown>,
28
- props: CommonProps,
29
- ): Record<string, unknown> {
30
- if (props.id !== undefined) payload.id = props.id;
31
- if (props.visible !== undefined) payload.visible = props.visible;
32
- if (props.hexpand !== undefined) payload.hexpand = props.hexpand;
33
- if (props.vexpand !== undefined) payload.vexpand = props.vexpand;
34
- if (props.halign !== undefined) payload.halign = props.halign;
35
- if (props.valign !== undefined) payload.valign = props.valign;
36
- if (props.tooltip !== undefined) payload.tooltip = props.tooltip;
37
- if (props.variant !== undefined) payload.variant = props.variant;
38
- return payload;
39
- }
40
-
41
- abstract class WidgetBase implements WidgetNode {
42
- protected constructor(protected readonly common: CommonProps = {}) {}
43
-
44
- protected withCommon(payload: Record<string, unknown>): Record<string, unknown> {
45
- return applyCommonProps(payload, this.common);
5
+ generatedId(event: "click" | "toggle" | "change", path: number[]): string {
6
+ const suffix = path.length === 0 ? "root" : path.join(".");
7
+ return `__glimpse:${event}:${suffix}`;
46
8
  }
47
9
 
48
- abstract toProtocol(): Record<string, unknown>;
49
- }
50
-
51
- export class Label extends WidgetBase {
52
- constructor(
53
- public readonly text: string,
54
- private readonly options: CommonProps & {
55
- wrap?: boolean;
56
- xalign?: number;
57
- selectable?: boolean;
58
- } = {},
59
- ) {
60
- super(options);
10
+ targetId(event: "click" | "toggle" | "change", id: string | undefined, path: number[]): string {
11
+ return id ?? this.generatedId(event, path);
61
12
  }
62
13
 
63
- toProtocol(): Record<string, unknown> {
64
- const payload = this.withCommon({ text: this.text });
65
- if (this.options.wrap !== undefined) payload.wrap = this.options.wrap;
66
- if (this.options.xalign !== undefined) payload.xalign = this.options.xalign;
67
- if (this.options.selectable !== undefined) payload.selectable = this.options.selectable;
68
- return { type: "label", data: payload };
14
+ add(event: "click" | "toggle" | "change", id: string, handler: InlineHandler): void {
15
+ this.handlers.set(`${event}:${id}`, handler);
69
16
  }
70
17
  }
71
18
 
72
- export class Image extends WidgetBase {
73
- constructor(
74
- public readonly icon: Icon,
75
- private readonly options: CommonProps & { pixel_size?: number } = {},
76
- ) {
77
- super(options);
78
- }
19
+ export type BadgeKind = "default" | "success" | "warning" | "error" | "accent";
20
+ export type StatusDotStatus = "neutral" | "success" | "warning" | "error" | "accent";
21
+ export type PagerAppearance = "dots" | "numbers";
22
+ export type PopoverSize = "small" | "medium" | "large" | "wide";
79
23
 
80
- toProtocol(): Record<string, unknown> {
81
- const payload = this.withCommon({ icon: this.icon.toProtocol() });
82
- if (this.options.pixel_size !== undefined) payload.pixel_size = this.options.pixel_size;
83
- return { type: "image", data: payload };
84
- }
24
+ export interface ProtocolNode {
25
+ type: string;
26
+ data: Record<string, unknown>;
85
27
  }
86
28
 
87
- export class Progress extends WidgetBase {
88
- constructor(
89
- private readonly options: CommonProps & {
90
- value: number;
91
- max?: number;
92
- show_text?: boolean;
93
- text?: string;
94
- },
95
- ) {
96
- super(options);
97
- }
98
-
99
- toProtocol(): Record<string, unknown> {
100
- const payload = this.withCommon({
101
- value: this.options.value,
102
- max: this.options.max ?? 1,
103
- });
104
- if (this.options.show_text !== undefined) payload.show_text = this.options.show_text;
105
- if (this.options.text !== undefined) payload.text = this.options.text;
106
- return { type: "progress", data: payload };
107
- }
29
+ export interface CommonOptions {
30
+ visible?: boolean;
31
+ tooltip?: string;
32
+ css_classes?: string[];
33
+ styles?: Record<string, string>;
108
34
  }
109
35
 
110
- export class LevelBar extends WidgetBase {
111
- constructor(
112
- private readonly options: CommonProps & {
113
- value: number;
114
- min?: number;
115
- max?: number;
116
- mode?: LevelBarMode;
117
- },
118
- ) {
119
- super(options);
120
- }
36
+ export abstract class WidgetBase {
37
+ protected constructor(protected readonly common: CommonOptions = {}) {}
38
+ abstract readonly type: string;
39
+ abstract data(): Record<string, unknown>;
40
+ bindHandlers(_registry: InlineHandlerRegistry, _path: number[]): void {}
121
41
 
122
- toProtocol(): Record<string, unknown> {
123
- return {
124
- type: "level_bar",
125
- data: this.withCommon({
126
- value: this.options.value,
127
- min: this.options.min ?? 0,
128
- max: this.options.max ?? 1,
129
- mode: this.options.mode ?? "continuous",
130
- }),
131
- };
42
+ toProtocol(): ProtocolNode {
43
+ return { type: this.type, data: this.data() };
132
44
  }
133
- }
134
45
 
135
- export class Picture extends WidgetBase {
136
- constructor(
137
- public readonly options: CommonProps & { path: string; content_fit?: ContentFit },
138
- ) {
139
- super(options);
140
- }
141
-
142
- toProtocol(): Record<string, unknown> {
143
- const payload = this.withCommon({ path: this.options.path });
144
- if (this.options.content_fit !== undefined) payload.content_fit = this.options.content_fit;
145
- return { type: "picture", data: payload };
46
+ protected commonData(): Record<string, unknown> {
47
+ const out: Record<string, unknown> = {};
48
+ if (this.common.visible !== undefined) out.visible = this.common.visible;
49
+ if (this.common.tooltip !== undefined) out.tooltip = this.common.tooltip;
50
+ if (this.common.css_classes?.length) out.css_classes = this.common.css_classes;
51
+ if (this.common.styles && Object.keys(this.common.styles).length > 0) out.styles = this.common.styles;
52
+ return out;
146
53
  }
147
54
  }
148
55
 
149
- export class Button extends WidgetBase {
150
- private readonly options: Omit<CommonProps, "variant"> & {
151
- label?: string;
152
- icon?: string;
153
- enabled?: boolean;
154
- variant?: ButtonVariant;
155
- };
56
+ const child = (node?: TreeNode): ProtocolNode | undefined => node?.toProtocol();
57
+ const children = (nodes: TreeNode[]): ProtocolNode[] => nodes.map((node) => node.toProtocol());
156
58
 
157
- constructor(options: Omit<CommonProps, "variant"> & {
158
- label?: string;
159
- icon?: string;
160
- enabled?: boolean;
161
- variant?: ButtonVariant;
162
- } = {}) {
163
- const { variant: _variant, ...common } = options;
164
- super(common);
165
- this.options = options;
59
+ export class Label extends WidgetBase {
60
+ readonly type = "label";
61
+ private readonly options: CommonOptions & { label: string; xalign?: number; wrap?: boolean };
62
+ constructor(options: string | CommonOptions & { label: string; xalign?: number; wrap?: boolean }) {
63
+ const normalized = typeof options === "string" ? { label: options } : options;
64
+ super(normalized);
65
+ this.options = normalized;
166
66
  }
167
-
168
- toProtocol(): Record<string, unknown> {
169
- const { variant, ...common } = this.options;
170
- const payload = applyCommonProps({}, common);
171
- if (this.options.label !== undefined) payload.label = this.options.label;
172
- if (this.options.icon !== undefined) payload.icon = this.options.icon;
173
- if (this.options.enabled !== undefined) payload.enabled = this.options.enabled;
174
- if (variant !== undefined) payload.variant = variant;
175
- return { type: "button", data: payload };
67
+ data(): Record<string, unknown> {
68
+ const out: Record<string, unknown> = { ...this.commonData(), label: this.options.label };
69
+ if (this.options.xalign !== undefined) out.xalign = this.options.xalign;
70
+ if (this.options.wrap !== undefined) out.wrap = this.options.wrap;
71
+ return out;
176
72
  }
177
73
  }
178
74
 
179
- export class LinkButton extends WidgetBase {
180
- constructor(
181
- private readonly options: CommonProps & {
182
- uri: string;
183
- label?: string;
184
- },
185
- ) {
186
- super(options);
187
- }
188
-
189
- toProtocol(): Record<string, unknown> {
190
- const payload = this.withCommon({ uri: this.options.uri });
191
- if (this.options.label !== undefined) payload.label = this.options.label;
192
- return { type: "link_button", data: payload };
193
- }
75
+ export class Header extends WidgetBase {
76
+ readonly type = "header";
77
+ constructor(private readonly options: CommonOptions & { label: string }) { super(options); }
78
+ data(): Record<string, unknown> { return { ...this.commonData(), label: this.options.label }; }
194
79
  }
195
80
 
196
- export class Expander extends WidgetBase {
197
- constructor(
198
- private readonly options: CommonProps & {
199
- label: string;
200
- child: TreeNode;
201
- expanded?: boolean;
202
- },
203
- ) {
204
- super(options);
205
- }
206
-
207
- toProtocol(): Record<string, unknown> {
208
- return {
209
- type: "expander",
210
- data: this.withCommon({
211
- label: this.options.label,
212
- expanded: this.options.expanded ?? false,
213
- child: this.options.child.toProtocol(),
214
- }),
215
- };
81
+ export class Hero extends WidgetBase {
82
+ readonly type = "hero";
83
+ constructor(private readonly options: CommonOptions & { title: string; subtitle?: string; id?: string; icon?: string; icon_size?: number; toggle?: boolean; toggle_sensitive?: boolean; separator?: boolean; trailing?: TreeNode; on_toggle?: InlineHandler }) { super(options); }
84
+ data(): Record<string, unknown> {
85
+ const out: Record<string, unknown> = { ...this.commonData(), title: this.options.title, subtitle: this.options.subtitle ?? "" };
86
+ for (const key of ["id", "icon", "icon_size", "toggle", "toggle_sensitive", "separator"] as const) {
87
+ if (this.options[key] !== undefined) out[key] = this.options[key];
88
+ }
89
+ const trailing = child(this.options.trailing);
90
+ if (trailing) out.trailing = trailing;
91
+ return out;
92
+ }
93
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
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
+ }
98
+ this.options.trailing?.bindHandlers(registry, [...path, 0]);
216
99
  }
217
100
  }
218
101
 
219
- export class TreeExpander extends WidgetBase {
220
- constructor(
221
- private readonly options: CommonProps & {
222
- child: TreeNode;
223
- hide_expander?: boolean;
224
- indent_for_depth?: boolean;
225
- indent_for_icon?: boolean;
226
- },
227
- ) {
228
- super(options);
229
- }
230
-
231
- toProtocol(): Record<string, unknown> {
232
- return {
233
- type: "tree_expander",
234
- data: this.withCommon({
235
- child: this.options.child.toProtocol(),
236
- hide_expander: this.options.hide_expander ?? false,
237
- indent_for_depth: this.options.indent_for_depth ?? false,
238
- indent_for_icon: this.options.indent_for_icon ?? false,
239
- }),
240
- };
241
- }
102
+ export class Badge extends WidgetBase {
103
+ readonly type = "badge";
104
+ constructor(private readonly options: CommonOptions & { label: string; kind?: BadgeKind }) { super(options); }
105
+ data(): Record<string, unknown> { return { ...this.commonData(), label: this.options.label, kind: this.options.kind ?? "default" }; }
242
106
  }
243
107
 
244
- export class MenuButton extends WidgetBase {
245
- constructor(
246
- private readonly options: CommonProps & {
247
- label?: string;
248
- icon?: string;
249
- popover: TreeNode;
250
- },
251
- ) {
252
- super(options);
253
- }
254
-
255
- toProtocol(): Record<string, unknown> {
256
- const payload = this.withCommon({ popover: this.options.popover.toProtocol() });
257
- if (this.options.label !== undefined) payload.label = this.options.label;
258
- if (this.options.icon !== undefined) payload.icon = this.options.icon;
259
- return { type: "menu_button", data: payload };
260
- }
108
+ export class StatusDot extends WidgetBase {
109
+ readonly type = "status_dot";
110
+ constructor(private readonly options: CommonOptions & { status?: StatusDotStatus } = {}) { super(options); }
111
+ data(): Record<string, unknown> { return { ...this.commonData(), status: this.options.status ?? "neutral" }; }
261
112
  }
262
113
 
263
- export class Switch extends WidgetBase {
264
- constructor(
265
- private readonly options: CommonProps & {
266
- label?: string;
267
- active?: boolean;
268
- } = {},
269
- ) {
270
- super(options);
114
+ export class PanelIndicator extends WidgetBase {
115
+ readonly type = "panel_indicator";
116
+ constructor(private readonly options: CommonOptions & { id?: string; icon?: string; label?: string; active?: boolean; checked?: boolean; needs_attention?: boolean; extra?: TreeNode; on_click?: InlineHandler }) { super(options); }
117
+ data(): Record<string, unknown> {
118
+ const out: Record<string, unknown> = { ...this.commonData(), active: this.options.active ?? false, checked: this.options.checked ?? false, needs_attention: this.options.needs_attention ?? false };
119
+ for (const key of ["id", "icon", "label"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
120
+ const extra = child(this.options.extra);
121
+ if (extra) out.extra = extra;
122
+ return out;
271
123
  }
272
-
273
- toProtocol(): Record<string, unknown> {
274
- const payload = this.withCommon({ active: this.options.active ?? false });
275
- if (this.options.label !== undefined) payload.label = this.options.label;
276
- return { type: "switch", data: payload };
124
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
125
+ if (this.options.id && this.options.on_click) registry.add("click", this.options.id, this.options.on_click);
126
+ this.options.extra?.bindHandlers(registry, [...path, 0]);
277
127
  }
278
128
  }
279
129
 
280
- export class ToggleButton extends WidgetBase {
281
- constructor(
282
- private readonly options: CommonProps & {
283
- label?: string;
284
- active?: boolean;
285
- } = {},
286
- ) {
287
- super(options);
288
- }
289
-
290
- toProtocol(): Record<string, unknown> {
291
- const payload = this.withCommon({ active: this.options.active ?? false });
292
- if (this.options.label !== undefined) payload.label = this.options.label;
293
- return { type: "toggle_button", data: payload };
130
+ export class EmptyState extends WidgetBase {
131
+ readonly type = "empty_state";
132
+ constructor(private readonly options: CommonOptions & { title: string; subtitle?: string }) { super(options); }
133
+ data(): Record<string, unknown> {
134
+ const out: Record<string, unknown> = { ...this.commonData(), title: this.options.title };
135
+ if (this.options.subtitle !== undefined) out.subtitle = this.options.subtitle;
136
+ return out;
294
137
  }
295
138
  }
296
139
 
297
- export class Slider extends WidgetBase {
298
- constructor(
299
- private readonly options: CommonProps & {
300
- min?: number;
301
- max?: number;
302
- step?: number;
303
- value?: number;
304
- orientation?: Orientation;
305
- draw_value?: boolean;
306
- } = {},
307
- ) {
308
- super(options);
309
- }
140
+ export class Spinner extends WidgetBase {
141
+ readonly type = "spinner";
142
+ constructor(private readonly options: CommonOptions & { spinning?: boolean } = {}) { super(options); }
143
+ data(): Record<string, unknown> { return { ...this.commonData(), spinning: this.options.spinning ?? true }; }
144
+ }
310
145
 
311
- toProtocol(): Record<string, unknown> {
312
- const payload = this.withCommon({
146
+ export class Meter extends WidgetBase {
147
+ readonly type = "meter";
148
+ constructor(private readonly options: CommonOptions & { id?: string; icon?: string; label?: string; value?: number; min?: number; max?: number; step?: number; text?: string; interactive?: boolean; on_change?: InlineHandler } = {}) { super(options); }
149
+ data(): Record<string, unknown> {
150
+ const out: Record<string, unknown> = {
151
+ ...this.commonData(),
152
+ label: this.options.label ?? "",
153
+ value: this.options.value ?? 0,
313
154
  min: this.options.min ?? 0,
314
155
  max: this.options.max ?? 1,
315
- step: this.options.step ?? 0.1,
316
- value: this.options.value ?? 0,
317
- });
318
- if (this.options.orientation !== undefined) payload.orientation = this.options.orientation;
319
- if (this.options.draw_value !== undefined) payload.draw_value = this.options.draw_value;
320
- return { type: "slider", data: payload };
321
- }
322
- }
323
-
324
- export class Checkbox extends WidgetBase {
325
- constructor(
326
- private readonly options: CommonProps & {
327
- label?: string;
328
- active?: boolean;
329
- } = {},
330
- ) {
331
- super(options);
332
- }
333
-
334
- toProtocol(): Record<string, unknown> {
335
- const payload = this.withCommon({ active: this.options.active ?? false });
336
- if (this.options.label !== undefined) payload.label = this.options.label;
337
- return { type: "checkbox", data: payload };
338
- }
339
- }
340
-
341
- export class SelectOption {
342
- constructor(
343
- public readonly id: string,
344
- public readonly label: string,
345
- ) {}
346
-
347
- toProtocol(): Record<string, unknown> {
348
- return { id: this.id, label: this.label };
349
- }
350
- }
351
-
352
- export class Select extends WidgetBase {
353
- constructor(
354
- private readonly options: CommonProps & {
355
- items?: SelectOption[];
356
- selected?: number;
357
- } = {},
358
- ) {
359
- super(options);
156
+ step: this.options.step ?? 0.01,
157
+ interactive: this.options.interactive ?? this.options.on_change !== undefined,
158
+ };
159
+ for (const key of ["id", "icon", "text"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
160
+ return out;
360
161
  }
361
-
362
- toProtocol(): Record<string, unknown> {
363
- const payload = this.withCommon({
364
- items: (this.options.items ?? []).map((item) => item.toProtocol()),
365
- });
366
- if (this.options.selected !== undefined) payload.selected = this.options.selected;
367
- return { type: "select", data: payload };
162
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void {
163
+ if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
368
164
  }
369
165
  }
370
166
 
371
167
  export class Separator extends WidgetBase {
372
- constructor(private readonly options: CommonProps & { orientation?: Orientation } = {}) {
373
- super(options);
374
- }
375
-
376
- toProtocol(): Record<string, unknown> {
377
- const payload = this.withCommon({});
378
- if (this.options.orientation !== undefined) payload.orientation = this.options.orientation;
379
- return { type: "separator", data: payload };
380
- }
168
+ readonly type = "separator";
169
+ constructor(common: CommonOptions = {}) { super(common); }
170
+ data(): Record<string, unknown> { return this.commonData(); }
381
171
  }
382
172
 
383
173
  export class Scroll extends WidgetBase {
384
- constructor(
385
- private readonly child: TreeNode,
386
- options: CommonProps = {},
387
- ) {
388
- super(options);
389
- }
390
-
391
- toProtocol(): Record<string, unknown> {
392
- return { type: "scroll", data: this.withCommon({ child: this.child.toProtocol() }) };
174
+ readonly type = "scroll";
175
+ constructor(private readonly options: CommonOptions & { child: TreeNode }) { super(options); }
176
+ data(): Record<string, unknown> { return { ...this.commonData(), child: this.options.child.toProtocol() }; }
177
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
178
+ this.options.child.bindHandlers(registry, [...path, 0]);
393
179
  }
394
180
  }
395
181
 
396
- export class Overlay extends WidgetBase {
397
- constructor(
398
- private readonly options: CommonProps & {
399
- child: TreeNode;
400
- overlays?: TreeNode[];
401
- },
402
- ) {
403
- super(options);
404
- }
405
-
406
- toProtocol(): Record<string, unknown> {
407
- return {
408
- type: "overlay",
409
- data: this.withCommon({
410
- child: this.options.child.toProtocol(),
411
- overlays: (this.options.overlays ?? []).map((overlay) => overlay.toProtocol()),
412
- }),
413
- };
182
+ abstract class ChildrenWidget extends WidgetBase {
183
+ constructor(common: CommonOptions & { children?: TreeNode[] } = {}) { super(common); this.kids = common.children ?? []; }
184
+ protected readonly kids: TreeNode[];
185
+ data(): Record<string, unknown> { return { ...this.commonData(), children: children(this.kids) }; }
186
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
187
+ this.kids.forEach((kid, index) => kid.bindHandlers(registry, [...path, index]));
414
188
  }
415
189
  }
416
190
 
417
- export class ListBox extends WidgetBase {
418
- constructor(
419
- private readonly options: CommonProps & {
420
- children?: TreeNode[];
421
- } = {},
422
- ) {
423
- super(options);
424
- }
191
+ export class Row extends ChildrenWidget { readonly type = "row"; }
192
+ export class Column extends ChildrenWidget { readonly type = "column"; }
193
+ export class BoxedList extends ChildrenWidget { readonly type = "boxed_list"; }
194
+ export class ButtonRow extends ChildrenWidget { readonly type = "button_row"; }
425
195
 
426
- toProtocol(): Record<string, unknown> {
427
- return {
428
- type: "list_box",
429
- data: this.withCommon({
430
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
431
- }),
432
- };
196
+ export class CircleBox extends WidgetBase {
197
+ readonly type = "circle_box";
198
+ constructor(private readonly options: CommonOptions & { color: string }) { super(options); }
199
+ data(): Record<string, unknown> {
200
+ const out = this.commonData();
201
+ out.color = this.options.color;
202
+ return out;
433
203
  }
434
204
  }
435
205
 
436
- export class GridChild {
437
- constructor(
438
- public readonly row: number,
439
- public readonly column: number,
440
- public readonly child: TreeNode,
441
- public readonly width: number = 1,
442
- public readonly height: number = 1,
443
- ) {}
444
-
445
- toProtocol(): Record<string, unknown> {
446
- return {
447
- row: this.row,
448
- column: this.column,
449
- width: this.width,
450
- height: this.height,
451
- child: this.child.toProtocol(),
452
- };
453
- }
206
+ export class Container extends ChildrenWidget {
207
+ readonly type = "container";
454
208
  }
455
209
 
456
- export class Grid extends WidgetBase {
457
- constructor(
458
- private readonly options: CommonProps & {
459
- children?: GridChild[];
460
- row_spacing?: number;
461
- column_spacing?: number;
462
- } = {},
463
- ) {
464
- super(options);
210
+ export class PopoverShell extends ChildrenWidget {
211
+ readonly type = "popover_shell";
212
+ constructor(private readonly options: CommonOptions & { children?: TreeNode[]; size?: PopoverSize; footer?: TreeNode[]; footer_visible?: boolean } = {}) { super(options); }
213
+ data(): Record<string, unknown> {
214
+ const out: Record<string, unknown> = { ...super.data(), size: this.options.size ?? "medium" };
215
+ if ((this.options.footer ?? []).length > 0) out.footer = children(this.options.footer ?? []);
216
+ if (this.options.footer_visible) out.footer_visible = this.options.footer_visible;
217
+ return out;
465
218
  }
466
-
467
- toProtocol(): Record<string, unknown> {
468
- return {
469
- type: "grid",
470
- data: this.withCommon({
471
- row_spacing: this.options.row_spacing ?? 0,
472
- column_spacing: this.options.column_spacing ?? 0,
473
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
474
- }),
475
- };
219
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
220
+ super.bindHandlers(registry, path);
221
+ (this.options.footer ?? []).forEach((kid, index) => kid.bindHandlers(registry, [...path, this.kids.length + index]));
476
222
  }
477
223
  }
478
224
 
479
- export class Hero extends WidgetBase {
480
- constructor(
481
- private readonly options: CommonProps & {
482
- title: string;
483
- subtitle: string;
484
- icon?: Icon;
485
- },
486
- ) {
487
- super(options);
225
+ export class Tile extends WidgetBase {
226
+ readonly type: string = "tile";
227
+ constructor(protected readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; right?: TreeNode; on_click?: InlineHandler }) { super(options); }
228
+ data(): Record<string, unknown> {
229
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary };
230
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
231
+ const left = child(this.options.left);
232
+ const right = child(this.options.right);
233
+ if (left) out.left = left;
234
+ if (right) out.right = right;
235
+ return out;
488
236
  }
489
-
490
- toProtocol(): Record<string, unknown> {
491
- const payload = this.withCommon({
492
- title: this.options.title,
493
- subtitle: this.options.subtitle,
494
- });
495
- if (this.options.icon !== undefined) payload.icon = this.options.icon.toProtocol();
496
- return { type: "hero", data: payload };
237
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
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
+ }
242
+ this.options.left?.bindHandlers(registry, [...path, 0]);
243
+ this.options.right?.bindHandlers(registry, [...path, 1]);
244
+ }
245
+ }
246
+
247
+ export class SegmentedTile extends Tile {
248
+ readonly type = "segmented_tile";
249
+ constructor(protected readonly options: ConstructorParameters<typeof Tile>[0] & { child?: TreeNode; expanded?: boolean; on_toggle?: InlineHandler }) { super(options); }
250
+ data(): Record<string, unknown> {
251
+ const out = super.data();
252
+ const nested = child(this.options.child);
253
+ if (nested) out.child = nested;
254
+ out.expanded = this.options.expanded ?? false;
255
+ return out;
256
+ }
257
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
258
+ super.bindHandlers(registry, path);
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
+ }
263
+ this.options.child?.bindHandlers(registry, [...path, 2]);
264
+ }
265
+ }
266
+
267
+ export class SwitchTile extends WidgetBase {
268
+ readonly type = "switch_tile";
269
+ constructor(private readonly options: CommonOptions & { id: string; primary: string; secondary?: string; left_icon?: string; left?: TreeNode; active?: boolean; on_toggle?: InlineHandler }) { super(options); }
270
+ data(): Record<string, unknown> {
271
+ const out: Record<string, unknown> = { ...this.commonData(), id: this.options.id, primary: this.options.primary, active: this.options.active ?? false };
272
+ for (const key of ["secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
273
+ const left = child(this.options.left);
274
+ if (left) out.left = left;
275
+ return out;
276
+ }
277
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
278
+ if (this.options.on_toggle) registry.add("toggle", this.options.id, this.options.on_toggle);
279
+ this.options.left?.bindHandlers(registry, [...path, 0]);
280
+ }
281
+ }
282
+
283
+ export class ExpanderTile extends WidgetBase {
284
+ readonly type = "expander_tile";
285
+ constructor(private readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; child?: TreeNode; expanded?: boolean; on_toggle?: InlineHandler }) { super(options); }
286
+ data(): Record<string, unknown> {
287
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary, expanded: this.options.expanded ?? false };
288
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
289
+ const left = child(this.options.left);
290
+ const nested = child(this.options.child);
291
+ if (left) out.left = left;
292
+ if (nested) out.child = nested;
293
+ return out;
294
+ }
295
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
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
+ }
300
+ this.options.left?.bindHandlers(registry, [...path, 0]);
301
+ this.options.child?.bindHandlers(registry, [...path, 1]);
497
302
  }
498
303
  }
499
304
 
500
- export class Card extends WidgetBase {
501
- constructor(
502
- private readonly options: CommonProps & {
503
- children?: TreeNode[];
504
- } = {},
505
- ) {
506
- super(options);
305
+ export class SliderTile extends WidgetBase {
306
+ readonly type = "slider_tile";
307
+ constructor(private readonly options: CommonOptions & { id: string; label?: string; left_icon?: string; left?: TreeNode; value?: number; min?: number; max?: number; step?: number; page?: number; digits?: number; snap_step?: number; on_change?: InlineHandler }) { super(options); }
308
+ data(): Record<string, unknown> {
309
+ const out: Record<string, unknown> = { ...this.commonData(), id: this.options.id, value: this.options.value ?? 0, min: this.options.min ?? 0, max: this.options.max ?? 1, step: this.options.step ?? 0.01, page: this.options.page ?? 0.1, digits: this.options.digits ?? 0 };
310
+ for (const key of ["label", "left_icon", "snap_step"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
311
+ const left = child(this.options.left);
312
+ if (left) out.left = left;
313
+ return out;
507
314
  }
508
-
509
- toProtocol(): Record<string, unknown> {
510
- return {
511
- type: "card",
512
- data: this.withCommon({
513
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
514
- }),
515
- };
315
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
316
+ if (this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
317
+ this.options.left?.bindHandlers(registry, [...path, 0]);
516
318
  }
517
319
  }
518
320
 
519
- export class Section extends WidgetBase {
520
- constructor(
521
- private readonly options: CommonProps & {
522
- title?: string;
523
- subtitle?: string;
524
- children?: TreeNode[];
525
- },
526
- ) {
527
- super(options);
321
+ export class ChoiceTile extends WidgetBase {
322
+ readonly type = "choice_tile";
323
+ constructor(private readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; selected?: boolean; on_click?: InlineHandler }) { super(options); }
324
+ data(): Record<string, unknown> {
325
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary, selected: this.options.selected ?? false };
326
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
327
+ const left = child(this.options.left);
328
+ if (left) out.left = left;
329
+ return out;
528
330
  }
529
-
530
- toProtocol(): Record<string, unknown> {
531
- const payload: Record<string, unknown> = {
532
- title: this.options.title ?? "",
533
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
534
- };
535
- if (this.options.subtitle !== undefined && this.options.subtitle !== "") {
536
- payload.subtitle = this.options.subtitle;
331
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
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);
537
335
  }
538
- return {
539
- type: "section",
540
- data: this.withCommon(payload),
541
- };
336
+ this.options.left?.bindHandlers(registry, [...path, 0]);
542
337
  }
543
338
  }
544
339
 
545
- export class Meter extends WidgetBase {
546
- constructor(
547
- private readonly options: CommonProps & {
548
- icon?: Icon;
549
- label?: string;
550
- value: number;
551
- min?: number;
552
- max?: number;
553
- step?: number;
554
- text?: string;
555
- interactive?: boolean;
556
- },
557
- ) {
558
- super(options);
559
- }
560
-
340
+ export class Choice {
341
+ constructor(public readonly options: { id: string; primary: string; secondary?: string; icon?: string }) {}
561
342
  toProtocol(): Record<string, unknown> {
562
- const payload = this.withCommon({
563
- label: this.options.label ?? "",
564
- value: this.options.value,
565
- min: this.options.min ?? 0,
566
- max: this.options.max ?? 1,
567
- step: this.options.step ?? 0.01,
568
- interactive: this.options.interactive ?? false,
569
- });
570
- if (this.options.icon !== undefined) payload.icon = this.options.icon.toProtocol();
571
- if (this.options.text !== undefined) payload.text = this.options.text;
572
- return { type: "meter", data: payload };
343
+ const out: Record<string, unknown> = { id: this.options.id, primary: this.options.primary };
344
+ if (this.options.secondary !== undefined) out.secondary = this.options.secondary;
345
+ if (this.options.icon !== undefined) out.icon = this.options.icon;
346
+ return out;
573
347
  }
574
348
  }
575
349
 
576
- export class Copyable extends WidgetBase {
577
- constructor(
578
- private readonly options: CommonProps & {
579
- label?: string;
580
- value: string;
581
- },
582
- ) {
583
- super(options);
350
+ export class ChoiceList extends WidgetBase {
351
+ readonly type = "choice_list";
352
+ constructor(private readonly options: CommonOptions & { id: string; choices?: Choice[]; active?: string; on_change?: InlineHandler }) { super(options); }
353
+ data(): Record<string, unknown> {
354
+ const out: Record<string, unknown> = { ...this.commonData(), id: this.options.id, choices: (this.options.choices ?? []).map((choice) => choice.toProtocol()) };
355
+ if (this.options.active !== undefined) out.active = this.options.active;
356
+ return out;
584
357
  }
585
-
586
- toProtocol(): Record<string, unknown> {
587
- return {
588
- type: "copyable",
589
- data: this.withCommon({
590
- label: this.options.label ?? "",
591
- value: this.options.value,
592
- }),
593
- };
358
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void {
359
+ if (this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
594
360
  }
595
361
  }
596
362
 
597
- export class Row extends WidgetBase {
598
- constructor(
599
- private readonly options: CommonProps & {
600
- spacing?: number;
601
- children?: TreeNode[];
602
- } = {},
603
- ) {
604
- super(options);
605
- }
606
-
607
- toProtocol(): Record<string, unknown> {
608
- return {
609
- type: "row",
610
- data: this.withCommon({
611
- spacing: this.options.spacing ?? 0,
612
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
613
- }),
614
- };
615
- }
363
+ export class KeyValueGrid extends WidgetBase {
364
+ readonly type = "key_value_grid";
365
+ constructor(private readonly options: CommonOptions & { rows?: { key: string; value: string }[] } = {}) { super(options); }
366
+ data(): Record<string, unknown> { return { ...this.commonData(), rows: this.options.rows ?? [] }; }
616
367
  }
617
368
 
618
- export class Column extends WidgetBase {
619
- constructor(
620
- private readonly options: CommonProps & {
621
- spacing?: number;
622
- children?: TreeNode[];
623
- } = {},
624
- ) {
625
- super(options);
626
- }
627
-
628
- toProtocol(): Record<string, unknown> {
629
- return {
630
- type: "column",
631
- data: this.withCommon({
632
- spacing: this.options.spacing ?? 0,
633
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
634
- }),
635
- };
636
- }
369
+ export class PagerItem extends WidgetBase {
370
+ readonly type = "pager_item";
371
+ constructor(private readonly options: CommonOptions & { id: number; label?: string; appearance?: PagerAppearance; active?: boolean; inactive?: boolean; occupied?: boolean; urgent?: boolean; on_click?: InlineHandler }) { super(options); }
372
+ data(): Record<string, unknown> { return { ...this.commonData(), id: this.options.id, label: this.options.label ?? "", appearance: this.options.appearance ?? "dots", active: this.options.active ?? false, inactive: this.options.inactive ?? false, occupied: this.options.occupied ?? false, urgent: this.options.urgent ?? false }; }
373
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void { if (this.options.on_click) registry.add("click", String(this.options.id), this.options.on_click); }
637
374
  }
638
375
 
639
- export class Spinner extends WidgetBase {
640
- constructor(
641
- private readonly options: CommonProps & {
642
- spinning?: boolean;
643
- } = {},
644
- ) {
645
- super(options);
376
+ export class PagerStrip extends WidgetBase {
377
+ readonly type = "pager_strip";
378
+ constructor(private readonly options: CommonOptions & { id?: string; items?: PagerItem[]; placeholder?: boolean; on_change?: InlineHandler } = {}) { super(options); }
379
+ data(): Record<string, unknown> {
380
+ const out: Record<string, unknown> = { ...this.commonData(), placeholder: this.options.placeholder ?? false, items: (this.options.items ?? []).map((item) => item.data()) };
381
+ if (this.options.id !== undefined) out.id = this.options.id;
382
+ return out;
646
383
  }
647
-
648
- toProtocol(): Record<string, unknown> {
649
- return {
650
- type: "spinner",
651
- data: this.withCommon({ spinning: this.options.spinning ?? true }),
652
- };
384
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
385
+ if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
386
+ (this.options.items ?? []).forEach((item, index) => item.bindHandlers(registry, [...path, index]));
653
387
  }
654
388
  }
655
389
 
656
- export type Properties = Record<string, string>;
657
-
658
- export class PropertyList extends WidgetBase {
659
- constructor(
660
- private readonly options: CommonProps & {
661
- title?: string;
662
- rows?: Properties;
663
- } = {},
664
- ) {
665
- super(options);
666
- }
667
-
668
- toProtocol(): Record<string, unknown> {
669
- const payload: Record<string, unknown> = {
670
- rows: Object.entries(this.options.rows ?? {}).map(([key, value]) => ({ key, value })),
671
- };
672
- if (this.options.title !== undefined && this.options.title !== "") {
673
- payload.title = this.options.title;
674
- }
675
- return {
676
- type: "property_list",
677
- data: this.withCommon(payload),
678
- };
679
- }
390
+ abstract class ActiveIndicator extends WidgetBase {
391
+ constructor(protected readonly options: CommonOptions & { active?: boolean } = {}) { super(options); }
392
+ data(): Record<string, unknown> { return { ...this.commonData(), active: this.options.active ?? false }; }
680
393
  }
681
394
 
682
- export class Item extends WidgetBase {
683
- constructor(
684
- private readonly options: CommonProps & {
685
- label: string;
686
- sublabel?: string;
687
- icon?: string;
688
- right?: TreeNode;
689
- },
690
- ) {
691
- super(options);
692
- }
395
+ export class CameraIndicator extends ActiveIndicator { readonly type = "camera_indicator"; }
396
+ export class MicIndicator extends ActiveIndicator { readonly type = "mic_indicator"; }
397
+ export class MutedIndicator extends ActiveIndicator { readonly type = "muted_indicator"; }
398
+ export class LocationIndicator extends ActiveIndicator { readonly type = "location_indicator"; }
693
399
 
694
- toProtocol(): Record<string, unknown> {
695
- const payload: Record<string, unknown> = {
696
- label: this.options.label,
697
- };
698
- if (this.options.icon !== undefined && this.options.icon !== "") {
699
- payload.icon = this.options.icon;
700
- }
701
- if (this.options.sublabel !== undefined && this.options.sublabel !== "") {
702
- payload.sublabel = this.options.sublabel;
703
- }
704
- if (this.options.right !== undefined) {
705
- payload.right = this.options.right.toProtocol();
706
- }
707
- return {
708
- type: "item",
709
- data: this.withCommon(payload),
710
- };
400
+ export class ScreenCastIndicator extends ActiveIndicator {
401
+ readonly type = "screencast_indicator";
402
+ constructor(protected readonly options: CommonOptions & { active?: boolean; timer_text?: string } = {}) { super(options); }
403
+ data(): Record<string, unknown> {
404
+ const out = super.data();
405
+ if (this.options.timer_text !== undefined) out.timer_text = this.options.timer_text;
406
+ return out;
711
407
  }
712
408
  }
713
409
 
714
- export class ActionItem extends WidgetBase {
715
- constructor(
716
- private readonly options: CommonProps & {
717
- label: string;
718
- sublabel?: string;
719
- icon?: string;
720
- right?: TreeNode;
721
- enabled?: boolean;
722
- },
723
- ) {
724
- super(options);
725
- }
726
-
727
- toProtocol(): Record<string, unknown> {
728
- const payload: Record<string, unknown> = {
729
- label: this.options.label,
730
- };
731
- if (this.options.icon !== undefined && this.options.icon !== "") {
732
- payload.icon = this.options.icon;
733
- }
734
- if (this.options.sublabel !== undefined && this.options.sublabel !== "") {
735
- payload.sublabel = this.options.sublabel;
736
- }
737
- if (this.options.right !== undefined) {
738
- payload.right = this.options.right.toProtocol();
739
- }
740
- if (this.options.enabled !== undefined) {
741
- payload.enabled = this.options.enabled;
742
- }
743
- return {
744
- type: "action_item",
745
- data: this.withCommon(payload),
746
- };
410
+ export class Calendar extends WidgetBase {
411
+ readonly type = "calendar";
412
+ constructor(private readonly options: CommonOptions & { selected_date: string; id?: string; event_days?: string[]; on_change?: InlineHandler }) { super(options); }
413
+ data(): Record<string, unknown> {
414
+ const out: Record<string, unknown> = { ...this.commonData(), selected_date: this.options.selected_date, event_days: this.options.event_days ?? [] };
415
+ if (this.options.id !== undefined) out.id = this.options.id;
416
+ return out;
747
417
  }
418
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void { if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change); }
748
419
  }
749
420
 
750
- export class EmptyState extends WidgetBase {
751
- constructor(
752
- private readonly options: CommonProps & {
753
- title: string;
754
- subtitle?: string;
755
- },
756
- ) {
757
- super(options);
758
- }
759
-
760
- toProtocol(): Record<string, unknown> {
761
- return {
762
- type: "empty_state",
763
- data: this.withCommon({
764
- title: this.options.title,
765
- subtitle: this.options.subtitle ?? "",
766
- }),
767
- };
768
- }
421
+ export class BatteryHero extends WidgetBase {
422
+ readonly type = "battery_hero";
423
+ constructor(private readonly options: CommonOptions & { icon: string; percentage: string; fraction: number; state: string }) { super(options); }
424
+ data(): Record<string, unknown> { return { ...this.commonData(), icon: this.options.icon, percentage: this.options.percentage, fraction: this.options.fraction, state: this.options.state }; }
769
425
  }
770
426
 
771
- export class Badge extends WidgetBase {
772
- constructor(
773
- private readonly options: CommonProps & {
774
- label: string;
775
- },
776
- ) {
777
- super(options);
778
- }
427
+ export class DateHero extends WidgetBase {
428
+ readonly type = "date_hero";
429
+ constructor(private readonly options: CommonOptions & { weekday: string; date: string }) { super(options); }
430
+ data(): Record<string, unknown> { return { ...this.commonData(), weekday: this.options.weekday, date: this.options.date }; }
431
+ }
779
432
 
433
+ export class EventItem {
434
+ constructor(public readonly options: { id: string; title: string; start: string; end?: string; location?: string; all_day?: boolean }) {}
780
435
  toProtocol(): Record<string, unknown> {
781
- return {
782
- type: "badge",
783
- data: this.withCommon({
784
- label: this.options.label,
785
- }),
786
- };
436
+ const out: Record<string, unknown> = { id: this.options.id, title: this.options.title, start: this.options.start, end: this.options.end ?? "", all_day: this.options.all_day ?? false };
437
+ if (this.options.location !== undefined) out.location = this.options.location;
438
+ return out;
787
439
  }
788
440
  }
789
441
 
790
- export class StatusDot extends WidgetBase {
791
- constructor(options: CommonProps = {}) {
792
- super(options);
793
- }
442
+ export class Events extends WidgetBase {
443
+ readonly type = "events";
444
+ constructor(private readonly options: CommonOptions & { date: string; events?: EventItem[]; loading?: boolean }) { super(options); }
445
+ data(): Record<string, unknown> { return { ...this.commonData(), date: this.options.date, loading: this.options.loading ?? false, events: (this.options.events ?? []).map((event) => event.toProtocol()) }; }
446
+ }
794
447
 
795
- toProtocol(): Record<string, unknown> {
796
- return { type: "status", data: this.withCommon({}) };
797
- }
448
+ export class WeatherForecastItem {
449
+ constructor(public readonly options: { day_name: string; icon: string; condition: string; temperatures: string; is_today?: boolean }) {}
450
+ toProtocol(): Record<string, unknown> { return { day_name: this.options.day_name, icon: this.options.icon, condition: this.options.condition, temperatures: this.options.temperatures, is_today: this.options.is_today ?? false }; }
798
451
  }
799
452
 
800
- export class PagerItem extends WidgetBase {
801
- constructor(
802
- private readonly options: CommonProps & {
803
- appearance?: PagerAppearance;
804
- label?: string;
805
- active?: boolean;
806
- inactive?: boolean;
807
- occupied?: boolean;
808
- urgent?: boolean;
809
- } = {},
810
- ) {
811
- super(options);
812
- }
813
-
814
- toData(): Record<string, unknown> {
815
- return this.withCommon({
816
- appearance: this.options.appearance ?? "dots",
817
- label: this.options.label ?? "",
818
- active: this.options.active ?? false,
819
- inactive: this.options.inactive ?? false,
820
- occupied: this.options.occupied ?? false,
821
- urgent: this.options.urgent ?? false,
822
- });
823
- }
453
+ export class WeatherForecastList extends WidgetBase {
454
+ readonly type = "weather_forecast_list";
455
+ constructor(private readonly options: CommonOptions & { items?: WeatherForecastItem[] } = {}) { super(options); }
456
+ data(): Record<string, unknown> { return { ...this.commonData(), items: (this.options.items ?? []).map((item) => item.toProtocol()) }; }
457
+ }
824
458
 
825
- toProtocol(): Record<string, unknown> {
826
- return {
827
- type: "pager_item",
828
- data: this.toData(),
829
- };
830
- }
459
+ export class WeatherHourlyItem {
460
+ constructor(public readonly options: { time: string; icon: string; temperature: string }) {}
461
+ toProtocol(): Record<string, unknown> { return { time: this.options.time, icon: this.options.icon, temperature: this.options.temperature }; }
831
462
  }
832
463
 
833
- export class PagerStrip extends WidgetBase {
834
- constructor(
835
- private readonly options: CommonProps & {
836
- items?: PagerItem[];
837
- } = {},
838
- ) {
839
- super(options);
840
- }
464
+ export class WeatherHourlyStrip extends WidgetBase {
465
+ readonly type = "weather_hourly_strip";
466
+ constructor(private readonly options: CommonOptions & { items?: WeatherHourlyItem[] } = {}) { super(options); }
467
+ data(): Record<string, unknown> { return { ...this.commonData(), items: (this.options.items ?? []).map((item) => item.toProtocol()) }; }
468
+ }
841
469
 
470
+ export class WorldClockRow {
471
+ constructor(public readonly options: { name: string; timezone: string; time: string; offset: string; day_label?: string }) {}
842
472
  toProtocol(): Record<string, unknown> {
843
- return {
844
- type: "pager_strip",
845
- data: this.withCommon({
846
- items: (this.options.items ?? []).map((item) => item.toData()),
847
- }),
848
- };
473
+ const out: Record<string, unknown> = { name: this.options.name, timezone: this.options.timezone, time: this.options.time, offset: this.options.offset };
474
+ if (this.options.day_label !== undefined) out.day_label = this.options.day_label;
475
+ return out;
849
476
  }
850
477
  }
851
478
 
852
- export class Box extends WidgetBase {
853
- constructor(
854
- private readonly options: CommonProps & {
855
- orientation?: Orientation;
856
- spacing?: number;
857
- children?: TreeNode[];
858
- } = {},
859
- ) {
860
- super(options);
861
- }
862
-
863
- static vertical(children: TreeNode[], spacing = 0, options: CommonProps = {}): Box {
864
- return new Box({ ...options, orientation: "vertical", spacing, children });
865
- }
866
-
867
- static horizontal(children: TreeNode[], spacing = 0, options: CommonProps = {}): Box {
868
- return new Box({ ...options, orientation: "horizontal", spacing, children });
869
- }
870
-
871
- toProtocol(): Record<string, unknown> {
872
- return {
873
- type: "box",
874
- data: this.withCommon({
875
- orientation: this.options.orientation ?? "vertical",
876
- spacing: this.options.spacing ?? 0,
877
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
878
- }),
879
- };
880
- }
479
+ export class WorldClock extends WidgetBase {
480
+ readonly type = "world_clock";
481
+ constructor(private readonly options: CommonOptions & { rows?: WorldClockRow[] } = {}) { super(options); }
482
+ data(): Record<string, unknown> { return { ...this.commonData(), rows: (this.options.rows ?? []).map((row) => row.toProtocol()) }; }
881
483
  }
882
484
 
883
485
  export type TreeNode =
884
- | Hero
885
- | Card
886
- | Section
887
- | Meter
888
- | Copyable
889
- | PropertyList
890
- | Item
891
- | ActionItem
892
- | EmptyState
893
- | Badge
894
- | StatusDot
895
- | PagerItem
896
- | PagerStrip
897
- | Box
898
- | Row
899
- | Column
900
- | Grid
901
- | Scroll
902
- | Overlay
903
- | ListBox
904
- | LevelBar
905
- | TreeExpander
906
- | MenuButton
907
- | Progress
908
- | Separator
909
- | Spinner
910
- | Label
911
- | Image
912
- | Picture
913
- | Button
914
- | LinkButton
915
- | Expander
916
- | Switch
917
- | ToggleButton
918
- | Slider
919
- | Select
920
- | Checkbox;
486
+ | Row | Column | Container | CircleBox | BoxedList | PopoverShell
487
+ | Label | Header | Hero | Badge | StatusDot | PanelIndicator | EmptyState | Spinner | Meter | Separator | Scroll
488
+ | Tile | SegmentedTile | ButtonRow | SwitchTile | ExpanderTile | SliderTile | ChoiceTile | ChoiceList | KeyValueGrid
489
+ | PagerItem | PagerStrip
490
+ | CameraIndicator | MicIndicator | MutedIndicator | ScreenCastIndicator | LocationIndicator
491
+ | Calendar | BatteryHero | DateHero | Events | WeatherForecastList | WeatherHourlyStrip | WorldClock;