glimpse-sdk 0.5.0 → 0.7.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,467 @@
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";
5
+ add(event: "click" | "toggle" | "change", id: string, handler: InlineHandler): void {
6
+ this.handlers.set(`${event}:${id}`, handler);
7
+ }
8
+ }
9
+
10
+ export type BadgeKind = "default" | "success" | "warning" | "error" | "accent";
11
+ export type StatusDotStatus = "neutral" | "success" | "warning" | "error" | "accent";
7
12
  export type PagerAppearance = "dots" | "numbers";
8
- export type ContentFit = "fill" | "contain" | "cover" | "scale_down";
9
- export type LevelBarMode = "continuous" | "discrete";
13
+ export type PopoverSize = "small" | "medium" | "large" | "wide";
10
14
 
11
- export interface WidgetNode {
12
- toProtocol(): Record<string, unknown>;
15
+ export interface ProtocolNode {
16
+ type: string;
17
+ data: Record<string, unknown>;
13
18
  }
14
19
 
15
- export interface CommonProps {
16
- id?: string;
20
+ export interface CommonOptions {
17
21
  visible?: boolean;
18
- hexpand?: boolean;
19
- vexpand?: boolean;
20
- halign?: Align;
21
- valign?: Align;
22
22
  tooltip?: string;
23
- variant?: Variant;
23
+ css_classes?: string[];
24
+ styles?: Record<string, string>;
24
25
  }
25
26
 
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
- }
27
+ export abstract class WidgetBase {
28
+ protected constructor(protected readonly common: CommonOptions = {}) {}
29
+ abstract readonly type: string;
30
+ abstract data(): Record<string, unknown>;
31
+ bindHandlers(_registry: InlineHandlerRegistry, _path: number[]): void {}
40
32
 
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);
33
+ toProtocol(): ProtocolNode {
34
+ return { type: this.type, data: this.data() };
46
35
  }
47
36
 
48
- abstract toProtocol(): Record<string, unknown>;
37
+ protected commonData(): Record<string, unknown> {
38
+ const out: Record<string, unknown> = {};
39
+ if (this.common.visible !== undefined) out.visible = this.common.visible;
40
+ if (this.common.tooltip !== undefined) out.tooltip = this.common.tooltip;
41
+ if (this.common.css_classes?.length) out.css_classes = this.common.css_classes;
42
+ if (this.common.styles && Object.keys(this.common.styles).length > 0) out.styles = this.common.styles;
43
+ return out;
44
+ }
49
45
  }
50
46
 
47
+ const child = (node?: TreeNode): ProtocolNode | undefined => node?.toProtocol();
48
+ const children = (nodes: TreeNode[]): ProtocolNode[] => nodes.map((node) => node.toProtocol());
49
+
51
50
  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);
51
+ readonly type = "label";
52
+ private readonly options: CommonOptions & { label: string; xalign?: number; wrap?: boolean };
53
+ constructor(options: string | CommonOptions & { label: string; xalign?: number; wrap?: boolean }) {
54
+ const normalized = typeof options === "string" ? { label: options } : options;
55
+ super(normalized);
56
+ this.options = normalized;
61
57
  }
62
-
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 };
58
+ data(): Record<string, unknown> {
59
+ const out: Record<string, unknown> = { ...this.commonData(), label: this.options.label };
60
+ if (this.options.xalign !== undefined) out.xalign = this.options.xalign;
61
+ if (this.options.wrap !== undefined) out.wrap = this.options.wrap;
62
+ return out;
69
63
  }
70
64
  }
71
65
 
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
- }
79
-
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
- }
66
+ export class Header extends WidgetBase {
67
+ readonly type = "header";
68
+ constructor(private readonly options: CommonOptions & { label: string }) { super(options); }
69
+ data(): Record<string, unknown> { return { ...this.commonData(), label: this.options.label }; }
85
70
  }
86
71
 
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);
72
+ export class Hero extends WidgetBase {
73
+ readonly type = "hero";
74
+ 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); }
75
+ data(): Record<string, unknown> {
76
+ const out: Record<string, unknown> = { ...this.commonData(), title: this.options.title, subtitle: this.options.subtitle ?? "" };
77
+ for (const key of ["id", "icon", "icon_size", "toggle", "toggle_sensitive", "separator"] as const) {
78
+ if (this.options[key] !== undefined) out[key] = this.options[key];
79
+ }
80
+ const trailing = child(this.options.trailing);
81
+ if (trailing) out.trailing = trailing;
82
+ return out;
97
83
  }
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 };
84
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
85
+ if (this.options.id && this.options.on_toggle) registry.add("toggle", this.options.id, this.options.on_toggle);
86
+ this.options.trailing?.bindHandlers(registry, [...path, 0]);
107
87
  }
108
88
  }
109
89
 
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
- }
121
-
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
- };
132
- }
90
+ export class Badge extends WidgetBase {
91
+ readonly type = "badge";
92
+ constructor(private readonly options: CommonOptions & { label: string; kind?: BadgeKind }) { super(options); }
93
+ data(): Record<string, unknown> { return { ...this.commonData(), label: this.options.label, kind: this.options.kind ?? "default" }; }
133
94
  }
134
95
 
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 };
146
- }
96
+ export class StatusDot extends WidgetBase {
97
+ readonly type = "status_dot";
98
+ constructor(private readonly options: CommonOptions & { status?: StatusDotStatus } = {}) { super(options); }
99
+ data(): Record<string, unknown> { return { ...this.commonData(), status: this.options.status ?? "neutral" }; }
147
100
  }
148
101
 
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
- };
156
-
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;
102
+ export class PanelIndicator extends WidgetBase {
103
+ readonly type = "panel_indicator";
104
+ 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); }
105
+ data(): Record<string, unknown> {
106
+ const out: Record<string, unknown> = { ...this.commonData(), active: this.options.active ?? false, checked: this.options.checked ?? false, needs_attention: this.options.needs_attention ?? false };
107
+ for (const key of ["id", "icon", "label"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
108
+ const extra = child(this.options.extra);
109
+ if (extra) out.extra = extra;
110
+ return out;
166
111
  }
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 };
112
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
113
+ if (this.options.id && this.options.on_click) registry.add("click", this.options.id, this.options.on_click);
114
+ this.options.extra?.bindHandlers(registry, [...path, 0]);
176
115
  }
177
116
  }
178
117
 
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 };
118
+ export class EmptyState extends WidgetBase {
119
+ readonly type = "empty_state";
120
+ constructor(private readonly options: CommonOptions & { title: string; subtitle?: string }) { super(options); }
121
+ data(): Record<string, unknown> {
122
+ const out: Record<string, unknown> = { ...this.commonData(), title: this.options.title };
123
+ if (this.options.subtitle !== undefined) out.subtitle = this.options.subtitle;
124
+ return out;
193
125
  }
194
126
  }
195
127
 
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
- };
216
- }
128
+ export class Spinner extends WidgetBase {
129
+ readonly type = "spinner";
130
+ constructor(private readonly options: CommonOptions & { spinning?: boolean } = {}) { super(options); }
131
+ data(): Record<string, unknown> { return { ...this.commonData(), spinning: this.options.spinning ?? true }; }
217
132
  }
218
133
 
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
- }),
134
+ export class Meter extends WidgetBase {
135
+ readonly type = "meter";
136
+ 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); }
137
+ data(): Record<string, unknown> {
138
+ const out: Record<string, unknown> = {
139
+ ...this.commonData(),
140
+ label: this.options.label ?? "",
141
+ value: this.options.value ?? 0,
142
+ min: this.options.min ?? 0,
143
+ max: this.options.max ?? 1,
144
+ step: this.options.step ?? 0.01,
145
+ interactive: this.options.interactive ?? this.options.on_change !== undefined,
240
146
  };
147
+ for (const key of ["id", "icon", "text"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
148
+ return out;
241
149
  }
242
- }
243
-
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 };
150
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void {
151
+ if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
260
152
  }
261
153
  }
262
154
 
263
- export class Switch extends WidgetBase {
264
- constructor(
265
- private readonly options: CommonProps & {
266
- label?: string;
267
- active?: boolean;
268
- } = {},
269
- ) {
270
- super(options);
271
- }
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 };
277
- }
155
+ export class Separator extends WidgetBase {
156
+ readonly type = "separator";
157
+ constructor(common: CommonOptions = {}) { super(common); }
158
+ data(): Record<string, unknown> { return this.commonData(); }
278
159
  }
279
160
 
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 };
161
+ export class Scroll extends WidgetBase {
162
+ readonly type = "scroll";
163
+ constructor(private readonly options: CommonOptions & { child: TreeNode }) { super(options); }
164
+ data(): Record<string, unknown> { return { ...this.commonData(), child: this.options.child.toProtocol() }; }
165
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
166
+ this.options.child.bindHandlers(registry, [...path, 0]);
294
167
  }
295
168
  }
296
169
 
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
- }
310
-
311
- toProtocol(): Record<string, unknown> {
312
- const payload = this.withCommon({
313
- min: this.options.min ?? 0,
314
- 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 };
170
+ abstract class ChildrenWidget extends WidgetBase {
171
+ constructor(common: CommonOptions & { children?: TreeNode[] } = {}) { super(common); this.kids = common.children ?? []; }
172
+ protected readonly kids: TreeNode[];
173
+ data(): Record<string, unknown> { return { ...this.commonData(), children: children(this.kids) }; }
174
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
175
+ this.kids.forEach((kid, index) => kid.bindHandlers(registry, [...path, index]));
321
176
  }
322
177
  }
323
178
 
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
- }
179
+ export class Row extends ChildrenWidget { readonly type = "row"; }
180
+ export class Column extends ChildrenWidget { readonly type = "column"; }
181
+ export class BoxedList extends ChildrenWidget { readonly type = "boxed_list"; }
182
+ export class ButtonRow extends ChildrenWidget { readonly type = "button_row"; }
333
183
 
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 };
184
+ export class CircleBox extends WidgetBase {
185
+ readonly type = "circle_box";
186
+ constructor(private readonly options: CommonOptions & { color: string }) { super(options); }
187
+ data(): Record<string, unknown> {
188
+ const out = this.commonData();
189
+ out.color = this.options.color;
190
+ return out;
338
191
  }
339
192
  }
340
193
 
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
- }
194
+ export class Container extends ChildrenWidget {
195
+ readonly type = "container";
350
196
  }
351
197
 
352
- export class Select extends WidgetBase {
353
- constructor(
354
- private readonly options: CommonProps & {
355
- items?: SelectOption[];
356
- selected?: number;
357
- } = {},
358
- ) {
359
- super(options);
198
+ export class PopoverShell extends ChildrenWidget {
199
+ readonly type = "popover_shell";
200
+ constructor(private readonly options: CommonOptions & { children?: TreeNode[]; size?: PopoverSize; footer?: TreeNode[]; footer_visible?: boolean } = {}) { super(options); }
201
+ data(): Record<string, unknown> {
202
+ const out: Record<string, unknown> = { ...super.data(), size: this.options.size ?? "medium" };
203
+ if ((this.options.footer ?? []).length > 0) out.footer = children(this.options.footer ?? []);
204
+ if (this.options.footer_visible) out.footer_visible = this.options.footer_visible;
205
+ return out;
360
206
  }
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 };
207
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
208
+ super.bindHandlers(registry, path);
209
+ (this.options.footer ?? []).forEach((kid, index) => kid.bindHandlers(registry, [...path, this.kids.length + index]));
368
210
  }
369
211
  }
370
212
 
371
- export class Separator extends WidgetBase {
372
- constructor(private readonly options: CommonProps & { orientation?: Orientation } = {}) {
373
- super(options);
213
+ export class Tile extends WidgetBase {
214
+ readonly type: string = "tile";
215
+ constructor(protected readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; right?: TreeNode; activatable?: boolean; on_click?: InlineHandler }) { super(options); }
216
+ data(): Record<string, unknown> {
217
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary, activatable: this.options.activatable ?? false };
218
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
219
+ const left = child(this.options.left);
220
+ const right = child(this.options.right);
221
+ if (left) out.left = left;
222
+ if (right) out.right = right;
223
+ return out;
374
224
  }
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 };
225
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
226
+ if (this.options.id && this.options.on_click) registry.add("click", this.options.id, this.options.on_click);
227
+ this.options.left?.bindHandlers(registry, [...path, 0]);
228
+ this.options.right?.bindHandlers(registry, [...path, 1]);
380
229
  }
381
230
  }
382
231
 
383
- export class Scroll extends WidgetBase {
384
- constructor(
385
- private readonly child: TreeNode,
386
- options: CommonProps = {},
387
- ) {
388
- super(options);
232
+ export class SegmentedTile extends Tile {
233
+ readonly type = "segmented_tile";
234
+ constructor(protected readonly options: ConstructorParameters<typeof Tile>[0] & { child?: TreeNode; expanded?: boolean; on_toggle?: InlineHandler }) { super(options); }
235
+ data(): Record<string, unknown> {
236
+ const out = super.data();
237
+ const nested = child(this.options.child);
238
+ if (nested) out.child = nested;
239
+ out.expanded = this.options.expanded ?? false;
240
+ return out;
389
241
  }
390
-
391
- toProtocol(): Record<string, unknown> {
392
- return { type: "scroll", data: this.withCommon({ child: this.child.toProtocol() }) };
242
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
243
+ super.bindHandlers(registry, path);
244
+ if (this.options.id && this.options.on_toggle) registry.add("toggle", this.options.id, this.options.on_toggle);
245
+ this.options.child?.bindHandlers(registry, [...path, 2]);
393
246
  }
394
247
  }
395
248
 
396
- export class Overlay extends WidgetBase {
397
- constructor(
398
- private readonly options: CommonProps & {
399
- child: TreeNode;
400
- overlays?: TreeNode[];
401
- },
402
- ) {
403
- super(options);
249
+ export class SwitchTile extends WidgetBase {
250
+ readonly type = "switch_tile";
251
+ constructor(private readonly options: CommonOptions & { id: string; primary: string; secondary?: string; left_icon?: string; left?: TreeNode; active?: boolean; on_toggle?: InlineHandler }) { super(options); }
252
+ data(): Record<string, unknown> {
253
+ const out: Record<string, unknown> = { ...this.commonData(), id: this.options.id, primary: this.options.primary, active: this.options.active ?? false };
254
+ for (const key of ["secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
255
+ const left = child(this.options.left);
256
+ if (left) out.left = left;
257
+ return out;
404
258
  }
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
- };
259
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
260
+ if (this.options.on_toggle) registry.add("toggle", this.options.id, this.options.on_toggle);
261
+ this.options.left?.bindHandlers(registry, [...path, 0]);
414
262
  }
415
263
  }
416
264
 
417
- export class ListBox extends WidgetBase {
418
- constructor(
419
- private readonly options: CommonProps & {
420
- children?: TreeNode[];
421
- } = {},
422
- ) {
423
- super(options);
265
+ export class ExpanderTile extends WidgetBase {
266
+ readonly type = "expander_tile";
267
+ 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); }
268
+ data(): Record<string, unknown> {
269
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary, expanded: this.options.expanded ?? false };
270
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
271
+ const left = child(this.options.left);
272
+ const nested = child(this.options.child);
273
+ if (left) out.left = left;
274
+ if (nested) out.child = nested;
275
+ return out;
424
276
  }
425
-
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
- };
433
- }
434
- }
435
-
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
- };
277
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
278
+ if (this.options.id && this.options.on_toggle) registry.add("toggle", this.options.id, this.options.on_toggle);
279
+ this.options.left?.bindHandlers(registry, [...path, 0]);
280
+ this.options.child?.bindHandlers(registry, [...path, 1]);
453
281
  }
454
282
  }
455
283
 
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);
284
+ export class SliderTile extends WidgetBase {
285
+ readonly type = "slider_tile";
286
+ 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); }
287
+ data(): Record<string, unknown> {
288
+ 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 };
289
+ for (const key of ["label", "left_icon", "snap_step"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
290
+ const left = child(this.options.left);
291
+ if (left) out.left = left;
292
+ return out;
465
293
  }
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
- };
294
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
295
+ if (this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
296
+ this.options.left?.bindHandlers(registry, [...path, 0]);
476
297
  }
477
298
  }
478
299
 
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);
300
+ export class ChoiceTile extends WidgetBase {
301
+ readonly type = "choice_tile";
302
+ constructor(private readonly options: CommonOptions & { primary: string; id?: string; secondary?: string; left_icon?: string; left?: TreeNode; selected?: boolean; on_click?: InlineHandler }) { super(options); }
303
+ data(): Record<string, unknown> {
304
+ const out: Record<string, unknown> = { ...this.commonData(), primary: this.options.primary, selected: this.options.selected ?? false };
305
+ for (const key of ["id", "secondary", "left_icon"] as const) if (this.options[key] !== undefined) out[key] = this.options[key];
306
+ const left = child(this.options.left);
307
+ if (left) out.left = left;
308
+ return out;
488
309
  }
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 };
310
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
311
+ if (this.options.id && this.options.on_click) registry.add("click", this.options.id, this.options.on_click);
312
+ this.options.left?.bindHandlers(registry, [...path, 0]);
497
313
  }
498
314
  }
499
315
 
500
- export class Card extends WidgetBase {
501
- constructor(
502
- private readonly options: CommonProps & {
503
- children?: TreeNode[];
504
- } = {},
505
- ) {
506
- super(options);
507
- }
508
-
316
+ export class Choice {
317
+ constructor(public readonly options: { id: string; primary: string; secondary?: string; icon?: string }) {}
509
318
  toProtocol(): Record<string, unknown> {
510
- return {
511
- type: "card",
512
- data: this.withCommon({
513
- children: (this.options.children ?? []).map((child) => child.toProtocol()),
514
- }),
515
- };
319
+ const out: Record<string, unknown> = { id: this.options.id, primary: this.options.primary };
320
+ if (this.options.secondary !== undefined) out.secondary = this.options.secondary;
321
+ if (this.options.icon !== undefined) out.icon = this.options.icon;
322
+ return out;
516
323
  }
517
324
  }
518
325
 
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);
326
+ export class ChoiceList extends WidgetBase {
327
+ readonly type = "choice_list";
328
+ constructor(private readonly options: CommonOptions & { id: string; choices?: Choice[]; active?: string; on_change?: InlineHandler }) { super(options); }
329
+ data(): Record<string, unknown> {
330
+ const out: Record<string, unknown> = { ...this.commonData(), id: this.options.id, choices: (this.options.choices ?? []).map((choice) => choice.toProtocol()) };
331
+ if (this.options.active !== undefined) out.active = this.options.active;
332
+ return out;
528
333
  }
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;
537
- }
538
- return {
539
- type: "section",
540
- data: this.withCommon(payload),
541
- };
334
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void {
335
+ if (this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
542
336
  }
543
337
  }
544
338
 
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
-
561
- 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 };
573
- }
339
+ export class KeyValueGrid extends WidgetBase {
340
+ readonly type = "key_value_grid";
341
+ constructor(private readonly options: CommonOptions & { rows?: { key: string; value: string }[] } = {}) { super(options); }
342
+ data(): Record<string, unknown> { return { ...this.commonData(), rows: this.options.rows ?? [] }; }
574
343
  }
575
344
 
576
- export class Copyable extends WidgetBase {
577
- constructor(
578
- private readonly options: CommonProps & {
579
- label?: string;
580
- value: string;
581
- },
582
- ) {
583
- super(options);
584
- }
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
- };
594
- }
345
+ export class PagerItem extends WidgetBase {
346
+ readonly type = "pager_item";
347
+ constructor(private readonly options: CommonOptions & { id: number; label?: string; appearance?: PagerAppearance; active?: boolean; inactive?: boolean; occupied?: boolean; urgent?: boolean; on_click?: InlineHandler }) { super(options); }
348
+ 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 }; }
349
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void { if (this.options.on_click) registry.add("click", String(this.options.id), this.options.on_click); }
595
350
  }
596
351
 
597
- export class Row extends WidgetBase {
598
- constructor(
599
- private readonly options: CommonProps & {
600
- spacing?: number;
601
- children?: TreeNode[];
602
- } = {},
603
- ) {
604
- super(options);
352
+ export class PagerStrip extends WidgetBase {
353
+ readonly type = "pager_strip";
354
+ constructor(private readonly options: CommonOptions & { id?: string; items?: PagerItem[]; placeholder?: boolean; on_change?: InlineHandler } = {}) { super(options); }
355
+ data(): Record<string, unknown> {
356
+ const out: Record<string, unknown> = { ...this.commonData(), placeholder: this.options.placeholder ?? false, items: (this.options.items ?? []).map((item) => item.data()) };
357
+ if (this.options.id !== undefined) out.id = this.options.id;
358
+ return out;
605
359
  }
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
- };
360
+ bindHandlers(registry: InlineHandlerRegistry, path: number[]): void {
361
+ if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change);
362
+ (this.options.items ?? []).forEach((item, index) => item.bindHandlers(registry, [...path, index]));
615
363
  }
616
364
  }
617
365
 
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
- }
366
+ abstract class ActiveIndicator extends WidgetBase {
367
+ constructor(protected readonly options: CommonOptions & { active?: boolean } = {}) { super(options); }
368
+ data(): Record<string, unknown> { return { ...this.commonData(), active: this.options.active ?? false }; }
637
369
  }
638
370
 
639
- export class Spinner extends WidgetBase {
640
- constructor(
641
- private readonly options: CommonProps & {
642
- spinning?: boolean;
643
- } = {},
644
- ) {
645
- super(options);
646
- }
371
+ export class CameraIndicator extends ActiveIndicator { readonly type = "camera_indicator"; }
372
+ export class MicIndicator extends ActiveIndicator { readonly type = "mic_indicator"; }
373
+ export class MutedIndicator extends ActiveIndicator { readonly type = "muted_indicator"; }
374
+ export class LocationIndicator extends ActiveIndicator { readonly type = "location_indicator"; }
647
375
 
648
- toProtocol(): Record<string, unknown> {
649
- return {
650
- type: "spinner",
651
- data: this.withCommon({ spinning: this.options.spinning ?? true }),
652
- };
376
+ export class ScreenCastIndicator extends ActiveIndicator {
377
+ readonly type = "screencast_indicator";
378
+ constructor(protected readonly options: CommonOptions & { active?: boolean; timer_text?: string } = {}) { super(options); }
379
+ data(): Record<string, unknown> {
380
+ const out = super.data();
381
+ if (this.options.timer_text !== undefined) out.timer_text = this.options.timer_text;
382
+ return out;
653
383
  }
654
384
  }
655
385
 
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
- };
386
+ export class Calendar extends WidgetBase {
387
+ readonly type = "calendar";
388
+ constructor(private readonly options: CommonOptions & { selected_date: string; id?: string; event_days?: string[]; on_change?: InlineHandler }) { super(options); }
389
+ data(): Record<string, unknown> {
390
+ const out: Record<string, unknown> = { ...this.commonData(), selected_date: this.options.selected_date, event_days: this.options.event_days ?? [] };
391
+ if (this.options.id !== undefined) out.id = this.options.id;
392
+ return out;
679
393
  }
394
+ bindHandlers(registry: InlineHandlerRegistry, _path: number[]): void { if (this.options.id && this.options.on_change) registry.add("change", this.options.id, this.options.on_change); }
680
395
  }
681
396
 
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
- }
693
-
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
- };
711
- }
397
+ export class BatteryHero extends WidgetBase {
398
+ readonly type = "battery_hero";
399
+ constructor(private readonly options: CommonOptions & { icon: string; percentage: string; fraction: number; state: string }) { super(options); }
400
+ data(): Record<string, unknown> { return { ...this.commonData(), icon: this.options.icon, percentage: this.options.percentage, fraction: this.options.fraction, state: this.options.state }; }
712
401
  }
713
402
 
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
- };
747
- }
403
+ export class DateHero extends WidgetBase {
404
+ readonly type = "date_hero";
405
+ constructor(private readonly options: CommonOptions & { weekday: string; date: string }) { super(options); }
406
+ data(): Record<string, unknown> { return { ...this.commonData(), weekday: this.options.weekday, date: this.options.date }; }
748
407
  }
749
408
 
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
-
409
+ export class EventItem {
410
+ constructor(public readonly options: { id: string; title: string; start: string; end?: string; location?: string; all_day?: boolean }) {}
760
411
  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
- };
412
+ 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 };
413
+ if (this.options.location !== undefined) out.location = this.options.location;
414
+ return out;
768
415
  }
769
416
  }
770
417
 
771
- export class Badge extends WidgetBase {
772
- constructor(
773
- private readonly options: CommonProps & {
774
- label: string;
775
- },
776
- ) {
777
- super(options);
778
- }
779
-
780
- toProtocol(): Record<string, unknown> {
781
- return {
782
- type: "badge",
783
- data: this.withCommon({
784
- label: this.options.label,
785
- }),
786
- };
787
- }
418
+ export class Events extends WidgetBase {
419
+ readonly type = "events";
420
+ constructor(private readonly options: CommonOptions & { date: string; events?: EventItem[]; loading?: boolean }) { super(options); }
421
+ data(): Record<string, unknown> { return { ...this.commonData(), date: this.options.date, loading: this.options.loading ?? false, events: (this.options.events ?? []).map((event) => event.toProtocol()) }; }
788
422
  }
789
423
 
790
- export class StatusDot extends WidgetBase {
791
- constructor(options: CommonProps = {}) {
792
- super(options);
793
- }
794
-
795
- toProtocol(): Record<string, unknown> {
796
- return { type: "status", data: this.withCommon({}) };
797
- }
424
+ export class WeatherForecastItem {
425
+ constructor(public readonly options: { day_name: string; icon: string; condition: string; temperatures: string; is_today?: boolean }) {}
426
+ 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
427
  }
799
428
 
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
- }
429
+ export class WeatherForecastList extends WidgetBase {
430
+ readonly type = "weather_forecast_list";
431
+ constructor(private readonly options: CommonOptions & { items?: WeatherForecastItem[] } = {}) { super(options); }
432
+ data(): Record<string, unknown> { return { ...this.commonData(), items: (this.options.items ?? []).map((item) => item.toProtocol()) }; }
433
+ }
824
434
 
825
- toProtocol(): Record<string, unknown> {
826
- return {
827
- type: "pager_item",
828
- data: this.toData(),
829
- };
830
- }
435
+ export class WeatherHourlyItem {
436
+ constructor(public readonly options: { time: string; icon: string; temperature: string }) {}
437
+ toProtocol(): Record<string, unknown> { return { time: this.options.time, icon: this.options.icon, temperature: this.options.temperature }; }
831
438
  }
832
439
 
833
- export class PagerStrip extends WidgetBase {
834
- constructor(
835
- private readonly options: CommonProps & {
836
- items?: PagerItem[];
837
- } = {},
838
- ) {
839
- super(options);
840
- }
440
+ export class WeatherHourlyStrip extends WidgetBase {
441
+ readonly type = "weather_hourly_strip";
442
+ constructor(private readonly options: CommonOptions & { items?: WeatherHourlyItem[] } = {}) { super(options); }
443
+ data(): Record<string, unknown> { return { ...this.commonData(), items: (this.options.items ?? []).map((item) => item.toProtocol()) }; }
444
+ }
841
445
 
446
+ export class WorldClockRow {
447
+ constructor(public readonly options: { name: string; timezone: string; time: string; offset: string; day_label?: string }) {}
842
448
  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
- };
449
+ const out: Record<string, unknown> = { name: this.options.name, timezone: this.options.timezone, time: this.options.time, offset: this.options.offset };
450
+ if (this.options.day_label !== undefined) out.day_label = this.options.day_label;
451
+ return out;
849
452
  }
850
453
  }
851
454
 
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
- }
455
+ export class WorldClock extends WidgetBase {
456
+ readonly type = "world_clock";
457
+ constructor(private readonly options: CommonOptions & { rows?: WorldClockRow[] } = {}) { super(options); }
458
+ data(): Record<string, unknown> { return { ...this.commonData(), rows: (this.options.rows ?? []).map((row) => row.toProtocol()) }; }
881
459
  }
882
460
 
883
461
  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;
462
+ | Row | Column | Container | CircleBox | BoxedList | PopoverShell
463
+ | Label | Header | Hero | Badge | StatusDot | PanelIndicator | EmptyState | Spinner | Meter | Separator | Scroll
464
+ | Tile | SegmentedTile | ButtonRow | SwitchTile | ExpanderTile | SliderTile | ChoiceTile | ChoiceList | KeyValueGrid
465
+ | PagerItem | PagerStrip
466
+ | CameraIndicator | MicIndicator | MutedIndicator | ScreenCastIndicator | LocationIndicator
467
+ | Calendar | BatteryHero | DateHero | Events | WeatherForecastList | WeatherHourlyStrip | WorldClock;