@glasshome/widget-sdk 1.7.0 → 1.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.
@@ -32,6 +32,14 @@ var e = "https://glasshome.app/docs/widget-sdk/config", t = [
32
32
  docsUrl: e,
33
33
  sourcePattern: "widgetFields\\.areaId\\b"
34
34
  },
35
+ {
36
+ id: "area-by-field-name",
37
+ since: "1.8.0",
38
+ removeIn: "2.0.0",
39
+ replacement: "field.area()",
40
+ docsUrl: e,
41
+ sourcePattern: "\\bareaId\\s*:\\s*z\\.string\\("
42
+ },
35
43
  {
36
44
  id: "raw-zod-config",
37
45
  since: "1.4.0",
@@ -71,6 +71,17 @@ declare function entity(domain: string, o?: EntitiesOpts): z.ZodDefault<z.ZodArr
71
71
  declare function area(o?: {
72
72
  title?: string;
73
73
  }): z.ZodOptional<z.ZodString>;
74
+ /**
75
+ * Icon name (e.g. "mdi:lightbulb"). The host renders its icon picker; the
76
+ * widget only declares that this field is an icon.
77
+ */
78
+ declare function icon(o: {
79
+ title?: string;
80
+ default: string;
81
+ }): z.ZodDefault<z.ZodString>;
82
+ declare function icon(o?: {
83
+ title?: string;
84
+ }): z.ZodOptional<z.ZodString>;
74
85
  declare function stringList(o: {
75
86
  title: string;
76
87
  description?: string;
@@ -89,6 +100,7 @@ export declare const field: {
89
100
  entities: typeof entities;
90
101
  entity: typeof entity;
91
102
  area: typeof area;
103
+ icon: typeof icon;
92
104
  stringList: typeof stringList;
93
105
  group: typeof group;
94
106
  };
@@ -4,7 +4,7 @@ import { z } from "zod";
4
4
  *
5
5
  * @deprecated since 1.4.0, removed in 2.0.0. Use the config API instead:
6
6
  * `defineConfig` + `field.*` (`field.title()`, `field.entities()`, `field.entity()`,
7
- * `field.area()`). See https://glasshome.app/docs/widget-sdk/config
7
+ * `field.area()`, `field.icon()`). See https://glasshome.app/docs/widget-sdk/config
8
8
  */
9
9
  export declare const widgetFields: {
10
10
  /**
package/dist/index.js CHANGED
@@ -1246,16 +1246,23 @@ function Xt(e) {
1246
1246
  });
1247
1247
  }
1248
1248
  function Zt(e) {
1249
+ let t = l.string().meta({
1250
+ formType: "icon-picker",
1251
+ title: e?.title ?? "Icon"
1252
+ });
1253
+ return e?.default === void 0 ? t.optional() : t.default(e.default);
1254
+ }
1255
+ function Qt(e) {
1249
1256
  return l.array(l.string()).default([]).meta($({
1250
1257
  title: e.title,
1251
1258
  description: e.description
1252
1259
  }));
1253
1260
  }
1254
- function Qt(e, t) {
1261
+ function $t(e, t) {
1255
1262
  let n = l.object(e);
1256
1263
  return n.default(Se(n)).meta({ title: t.title });
1257
1264
  }
1258
- var $t = {
1265
+ var en = {
1259
1266
  title: Ut,
1260
1267
  text: Wt,
1261
1268
  number: Gt,
@@ -1264,15 +1271,16 @@ var $t = {
1264
1271
  entities: Jt,
1265
1272
  entity: Yt,
1266
1273
  area: Xt,
1267
- stringList: Zt,
1268
- group: Qt
1274
+ icon: Zt,
1275
+ stringList: Qt,
1276
+ group: $t
1269
1277
  };
1270
- function en(e) {
1278
+ function tn(e) {
1271
1279
  return l.object(e);
1272
1280
  }
1273
1281
  //#endregion
1274
1282
  //#region src/shared/spline.ts
1275
- function tn(e) {
1283
+ function nn(e) {
1276
1284
  if (e.length < 2) return "";
1277
1285
  let t = e[0], n = e[1];
1278
1286
  if (!t || !n) return "";
@@ -1299,14 +1307,14 @@ function tn(e) {
1299
1307
  }
1300
1308
  //#endregion
1301
1309
  //#region src/theme.ts
1302
- function nn() {
1310
+ function rn() {
1303
1311
  return document.documentElement.classList.contains("dark");
1304
1312
  }
1305
1313
  //#endregion
1306
1314
  //#region src/version.ts
1307
- var rn = {
1315
+ var an = {
1308
1316
  name: "@glasshome/widget-sdk",
1309
- version: "1.7.0",
1317
+ version: "1.8.0",
1310
1318
  description: "SDK for building GlassHome dashboard widgets with SolidJS",
1311
1319
  keywords: [
1312
1320
  "glasshome",
@@ -1394,4 +1402,4 @@ var rn = {
1394
1402
  engines: { node: ">=18" }
1395
1403
  }.version;
1396
1404
  //#endregion
1397
- export { rn as SDK_VERSION, Vt as ToneSchema, U as Widget, Pe as WidgetContent, e as WidgetCtx, Ct as WidgetDialog, Re as WidgetIcon, o as WidgetManifestSchema, Me as WidgetSliderFill, Be as WidgetStatus, Ue as WidgetTitle, tt as WidgetValue, ee as byDomain, We as calculateLightGroup, Ge as calculateSensorGroup, Xe as countActiveEntities, en as defineConfig, Ce as defineWidget, i as deprecations, $t as field, a as formatDeprecation, s as formatSchemaError, Ye as getEntityAttribute, u as getForecasts, d as getStream, f as getWebRtcClientConfig, p as hassMediaUrl, n as injectTokens, nn as isDark, Je as isEntityActive, tn as monotoneCubicPath, m as sendWebRtcCandidate, h as startWebRtcSession, g as state, Bt as svgColors, _ as trackEntityHistory, v as untrackEntityHistory, y as useArea, b as useCamera, te as useConnection, x as useCurrency, ne as useEntities, S as useEntity, re as useEntityHistory, ie as useEntityStatistics, C as useForecast, ae as useHassConfig, wt as useIntersectionPause, oe as useLocale, Tt as useReducedMotion, Te as useService, ce as useStore, le as useTemperatureUnit, Oe as useToggle, De as useTurnOff, Ee as useTurnOn, pe as useUnitSystem, t as useWidgetContext, Et as useWidgetDialog, Dt as useWidgetEntityGroup, Lt as useWidgetGestures, Ht as widgetFields, c as z };
1405
+ export { an as SDK_VERSION, Vt as ToneSchema, U as Widget, Pe as WidgetContent, e as WidgetCtx, Ct as WidgetDialog, Re as WidgetIcon, o as WidgetManifestSchema, Me as WidgetSliderFill, Be as WidgetStatus, Ue as WidgetTitle, tt as WidgetValue, ee as byDomain, We as calculateLightGroup, Ge as calculateSensorGroup, Xe as countActiveEntities, tn as defineConfig, Ce as defineWidget, i as deprecations, en as field, a as formatDeprecation, s as formatSchemaError, Ye as getEntityAttribute, u as getForecasts, d as getStream, f as getWebRtcClientConfig, p as hassMediaUrl, n as injectTokens, rn as isDark, Je as isEntityActive, nn as monotoneCubicPath, m as sendWebRtcCandidate, h as startWebRtcSession, g as state, Bt as svgColors, _ as trackEntityHistory, v as untrackEntityHistory, y as useArea, b as useCamera, te as useConnection, x as useCurrency, ne as useEntities, S as useEntity, re as useEntityHistory, ie as useEntityStatistics, C as useForecast, ae as useHassConfig, wt as useIntersectionPause, oe as useLocale, Tt as useReducedMotion, Te as useService, ce as useStore, le as useTemperatureUnit, Oe as useToggle, De as useTurnOff, Ee as useTurnOn, pe as useUnitSystem, t as useWidgetContext, Et as useWidgetDialog, Dt as useWidgetEntityGroup, Lt as useWidgetGestures, Ht as widgetFields, c as z };
@@ -3,6 +3,14 @@ export interface GlasshomeWidgetOptions {
3
3
  /** Entry file for the widget (default: "src/index.tsx") */
4
4
  entry?: string;
5
5
  }
6
+ /**
7
+ * Most previews anyone will look at, and the ceiling the hub renders to.
8
+ *
9
+ * Declaring more than this used to mean the extras were quietly not rendered:
10
+ * the gallery looked complete and was not. Failing the build instead makes the
11
+ * author decide which states matter rather than discovering the loss never.
12
+ */
13
+ export declare const MAX_EXAMPLES = 20;
6
14
  /**
7
15
  * Configuration-drift guard (D-11). Imports the built bundle, derives the JSON
8
16
  * Schema from its configSchema, and compares against the recorded
@@ -10,7 +10,28 @@ function v(e) {
10
10
  let t = JSON.stringify(e, Object.keys(e).sort());
11
11
  return r("sha256").update(t).digest("hex").slice(0, 16);
12
12
  }
13
- async function y(e) {
13
+ var y = 20;
14
+ async function b(e, t) {
15
+ if (e === void 0) return;
16
+ let n;
17
+ try {
18
+ ({z: n} = await import("zod"));
19
+ } catch {
20
+ return;
21
+ }
22
+ let r = n.number().int().positive(), i = n.array(n.object({
23
+ config: n.record(n.string(), n.unknown()),
24
+ size: n.object({
25
+ w: r,
26
+ h: r
27
+ }),
28
+ label: n.string().optional()
29
+ })).max(20).safeParse(e);
30
+ if (i.success) return;
31
+ let a = i.error.issues.map((e) => ` examples${e.path.length ? `[${e.path.join("][")}]` : ""}: ${e.message}`).join("\n");
32
+ throw Error(`[widget-sdk] Invalid \`examples\` for "${t}":\n${a}\nEach example needs a \`config\` object and a \`size\` of whole positive tiles, and at most 20 are rendered.`);
33
+ }
34
+ async function x(e) {
14
35
  if (!i(e.outFile)) return;
15
36
  let t;
16
37
  try {
@@ -18,7 +39,7 @@ async function y(e) {
18
39
  } catch {
19
40
  return;
20
41
  }
21
- if (!t?.configSchema) return;
42
+ if (await b(t?.manifest?.examples, t?.manifest?.name ?? e.widgetName), !t?.configSchema) return;
22
43
  let n;
23
44
  try {
24
45
  let { z: e } = await import("zod");
@@ -47,17 +68,17 @@ async function y(e) {
47
68
  configVersion: a
48
69
  })}\n`);
49
70
  }
50
- var b = "virtual:glasshome-widget", x = "\0virtual:glasshome-widget", S = "/@glasshome/preview";
51
- function C(e) {
71
+ var S = "virtual:glasshome-widget", C = "\0virtual:glasshome-widget", w = "/@glasshome/preview";
72
+ function T(e) {
52
73
  return h === "\\" ? e.split(h).join("/") : e;
53
74
  }
54
- function w() {
75
+ function E() {
55
76
  return m(f(g(import.meta.url)), "../../preview");
56
77
  }
57
- function T(e) {
78
+ function D(e) {
58
79
  return t(e);
59
80
  }
60
- function E() {
81
+ function O() {
61
82
  return {
62
83
  name: "glasshome-widget:sync-layer-guard",
63
84
  apply: "build",
@@ -67,15 +88,15 @@ function E() {
67
88
  }
68
89
  };
69
90
  }
70
- var D = "node_modules/.cache/glasshome-widgets";
71
- function O(e) {
91
+ var k = "node_modules/.cache/glasshome-widgets";
92
+ function A(e) {
72
93
  try {
73
94
  return d(p(process.cwd(), "package.json")).resolve(e);
74
95
  } catch {
75
96
  return null;
76
97
  }
77
98
  }
78
- var k = [
99
+ var j = [
79
100
  "--radius",
80
101
  "--background",
81
102
  "--foreground",
@@ -85,21 +106,21 @@ var k = [
85
106
  "--muted",
86
107
  "--accent"
87
108
  ];
88
- function A(e, t) {
109
+ function M(e, t) {
89
110
  let n = p(e, `${t}.css`);
90
111
  if (!i(n)) return;
91
112
  let r = o(n, "utf-8");
92
113
  for (let e of r.matchAll(/:host[^{]*\{([^}]*)\}/g)) {
93
- let n = e[1] ?? "", r = k.find((e) => n.includes(`${e}:`));
114
+ let n = e[1] ?? "", r = j.find((e) => n.includes(`${e}:`));
94
115
  if (r) throw Error(`[glasshome-widget] "${t}.css" defines theme variable "${r}" on :host. Theme variables must inherit from the document so widgets follow the household theme — never re-scope them into the widget's shadow root.`);
95
116
  }
96
117
  }
97
- function j(e, t, n) {
98
- let r = m(e, D);
118
+ function N(e, t, n) {
119
+ let r = m(e, k);
99
120
  a(r, { recursive: !0 });
100
- let o = O("@glasshome/ui/styles");
121
+ let o = A("@glasshome/ui/styles");
101
122
  if (!o) throw Error("[widget-sdk] @glasshome/ui is required to build widgets (it provides the theme every GlassHome widget ships with). Add it to your widget's dependencies.");
102
- let s = m(f(o), "../.."), c = O("@glasshome/widget-sdk/tailwind-sources"), l = c ? f(c) : null, d = [
123
+ let s = m(f(o), "../.."), c = A("@glasshome/widget-sdk/tailwind-sources"), l = c ? f(c) : null, d = [
103
124
  p(s, "src"),
104
125
  p(s, "dist"),
105
126
  ...l ? [p(l, "src"), p(l, "dist")] : [],
@@ -109,7 +130,7 @@ function j(e, t, n) {
109
130
  "@import \"tailwindcss\" source(none);",
110
131
  "@import \"tw-animate-css\";",
111
132
  "@import \"@glasshome/ui/styles/theme\";",
112
- ...d.map((e) => `@source "${C(e)}";`),
133
+ ...d.map((e) => `@source "${T(e)}";`),
113
134
  "@custom-variant dark {",
114
135
  " &:is(.dark *) { @slot }",
115
136
  " :host(.dark) & { @slot }",
@@ -118,17 +139,17 @@ function j(e, t, n) {
118
139
  ].join("\n"));
119
140
  let g = m(r, `${t}.entry.ts`);
120
141
  return u(g, [
121
- `import "${C(h)}";`,
122
- `export { default } from "${C(n)}";`,
142
+ `import "${T(h)}";`,
143
+ `export { default } from "${T(n)}";`,
123
144
  ""
124
145
  ].join("\n")), g;
125
146
  }
126
- function M() {
147
+ function P() {
127
148
  let e = _();
128
149
  for (let t of e) t.apply ??= "build";
129
150
  return e;
130
151
  }
131
- function N(e) {
152
+ function F(e) {
132
153
  let t = [];
133
154
  if (!i(e)) return t;
134
155
  for (let n of s(e)) {
@@ -142,7 +163,7 @@ function N(e) {
142
163
  }
143
164
  return t;
144
165
  }
145
- function P(t, r) {
166
+ function I(t, r) {
146
167
  let c = [];
147
168
  if (i(t)) for (let a of s(t)) {
148
169
  if (!l(p(t, a)).isDirectory()) continue;
@@ -165,8 +186,8 @@ function P(t, r) {
165
186
  };
166
187
  i(r) || a(r, { recursive: !0 }), u(p(r, "registry.json"), JSON.stringify(d, null, 2)), console.log(`[registry] Generated registry.json with ${c.length} widget(s)`);
167
188
  }
168
- async function F(e) {
169
- let { build: t } = await import("vite"), n = process.cwd(), r = m(n, e?.srcDir ?? "src"), o = m(n, e?.outDir ?? "dist"), s = N(r);
189
+ async function L(e) {
190
+ let { build: t } = await import("vite"), n = process.cwd(), r = m(n, e?.srcDir ?? "src"), o = m(n, e?.outDir ?? "dist"), s = F(r);
170
191
  if (s.length === 0) {
171
192
  console.warn("[glasshome-widgets] No widgets found in", r);
172
193
  return;
@@ -180,33 +201,33 @@ async function F(e) {
180
201
  configFile: !1,
181
202
  root: n,
182
203
  plugins: [
183
- ...M(),
204
+ ...P(),
184
205
  ...e?.plugins ?? [],
185
- E()
206
+ O()
186
207
  ],
187
208
  ...e?.viteConfig,
188
209
  build: {
189
210
  lib: {
190
- entry: j(n, r.name, r.entry),
211
+ entry: N(n, r.name, r.entry),
191
212
  formats: ["es"],
192
213
  fileName: r.name,
193
214
  cssFileName: r.name
194
215
  },
195
- rollupOptions: { external: T },
216
+ rollupOptions: { external: D },
196
217
  outDir: o,
197
218
  emptyOutDir: !1,
198
219
  copyPublicDir: !1,
199
220
  ...e?.viteConfig?.build
200
221
  },
201
222
  logLevel: "warn"
202
- }), A(o, r.name), await y({
223
+ }), M(o, r.name), await x({
203
224
  outFile: p(o, `${r.name}.js`),
204
225
  hashFile: p(f(r.entry), ".schema-hash"),
205
226
  widgetName: r.name
206
227
  });
207
- P(r, o);
228
+ I(r, o);
208
229
  }
209
- function I(e) {
230
+ function R(e) {
210
231
  let t = e?.entry ?? "src/index.tsx", n = {
211
232
  name: "glasshome-widget:build",
212
233
  apply: "build",
@@ -214,12 +235,12 @@ function I(e) {
214
235
  let e = process.cwd();
215
236
  return { build: {
216
237
  lib: {
217
- entry: j(e, "index", m(e, t)),
238
+ entry: N(e, "index", m(e, t)),
218
239
  formats: ["es"],
219
240
  fileName: "index",
220
241
  cssFileName: "index"
221
242
  },
222
- rollupOptions: { external: T }
243
+ rollupOptions: { external: D }
223
244
  } };
224
245
  }
225
246
  }, r = {
@@ -227,7 +248,7 @@ function I(e) {
227
248
  apply: "serve",
228
249
  enforce: "pre",
229
250
  config() {
230
- let e = w(), t = process.cwd();
251
+ let e = E(), t = process.cwd();
231
252
  return { server: { fs: { allow: [
232
253
  e,
233
254
  t,
@@ -235,7 +256,7 @@ function I(e) {
235
256
  ] } } };
236
257
  },
237
258
  configureServer(e) {
238
- let t = p(w(), "preview.html");
259
+ let t = p(E(), "preview.html");
239
260
  e.middlewares.use(async (n, r, i) => {
240
261
  if (n.url === "/" || n.url === "/index.html") try {
241
262
  let i = o(t, "utf-8");
@@ -247,17 +268,17 @@ function I(e) {
247
268
  });
248
269
  },
249
270
  resolveId(e) {
250
- if (e === S) return C(p(w(), "host.tsx"));
251
- if (e === b) return x;
271
+ if (e === w) return T(p(E(), "host.tsx"));
272
+ if (e === S) return C;
252
273
  },
253
274
  load(e) {
254
- if (e === x) return `export { default } from "${C(m(process.cwd(), t))}";`;
275
+ if (e === C) return `export { default } from "${T(m(process.cwd(), t))}";`;
255
276
  }
256
277
  }, i = {
257
278
  name: "glasshome-widget:schema",
258
279
  apply: "build",
259
280
  async closeBundle() {
260
- A(m(process.cwd(), "dist"), "index"), await y({
281
+ M(m(process.cwd(), "dist"), "index"), await x({
261
282
  outFile: m(process.cwd(), "dist", "index.js"),
262
283
  hashFile: m(process.cwd(), ".schema-hash"),
263
284
  widgetName: "index",
@@ -266,14 +287,14 @@ function I(e) {
266
287
  }
267
288
  };
268
289
  return [
269
- ...M(),
290
+ ...P(),
270
291
  n,
271
- E(),
292
+ O(),
272
293
  i,
273
294
  r
274
295
  ];
275
296
  }
276
- function L(e) {
297
+ function z(e) {
277
298
  let t = e?.srcDir ?? "src", n = e?.outDir ?? "dist", r = [];
278
299
  return [{
279
300
  name: "glasshome-widgets:build",
@@ -292,7 +313,7 @@ function L(e) {
292
313
  if (e === "\0glasshome-noop") return "export {}";
293
314
  },
294
315
  async closeBundle() {
295
- await F({
316
+ await L({
296
317
  srcDir: t,
297
318
  outDir: n,
298
319
  plugins: r
@@ -301,4 +322,4 @@ function L(e) {
301
322
  }];
302
323
  }
303
324
  //#endregion
304
- export { F as buildWidgets, N as discoverWidgets, P as generateRegistry, I as glasshomeWidget, L as glasshomeWidgets, T as isWidgetExternal, y as runSchemaGuard };
325
+ export { y as MAX_EXAMPLES, L as buildWidgets, F as discoverWidgets, I as generateRegistry, R as glasshomeWidget, z as glasshomeWidgets, D as isWidgetExternal, x as runSchemaGuard };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/widget-sdk",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "SDK for building GlassHome dashboard widgets with SolidJS",
5
5
  "keywords": [
6
6
  "glasshome",