@glasshome/widget-sdk 1.8.1 → 1.9.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.
@@ -0,0 +1,33 @@
1
+ import { createContext as e, useContext as t } from "solid-js";
2
+ //#region src/framework/hooks/use-widget-context.ts
3
+ var n = e();
4
+ function r() {
5
+ let e = t(n);
6
+ if (!e) throw Error("useWidgetContext must be used within a Widget component");
7
+ return e;
8
+ }
9
+ //#endregion
10
+ //#region src/framework/theming/tokens.css?raw
11
+ var i = "/* Authoritative SDK CSS contract. Injected once on first Widget mount\n * via framework/theming/tokens.ts.\n *\n * All widget-internal visual scale is driven from CSS custom properties +\n * container queries on the `.glasshome-widget` shell (`container-type: size`).\n * No JS classifier, no size tier enum.\n */\n\n:root {\n --tone-success: oklch(0.70 0.18 145);\n --tone-warning: oklch(0.80 0.17 75);\n --tone-danger: oklch(0.64 0.22 27);\n --tone-info: oklch(0.66 0.20 245);\n --tone-neutral: oklch(0.65 0.02 250);\n --tone-accent: oklch(0.74 0.18 60);\n\n --widget-grad-strength: 1;\n --widget-glow-default: 0.4;\n --widget-border-highlight: oklch(0.98 0 0 / 0.4);\n}\n\n.dark {\n --tone-success: oklch(0.74 0.18 145);\n --tone-warning: oklch(0.82 0.17 75);\n --tone-danger: oklch(0.68 0.22 27);\n --tone-info: oklch(0.70 0.20 245);\n --tone-neutral: oklch(0.69 0.02 250);\n --tone-accent: oklch(0.78 0.18 60);\n\n --widget-grad-strength: 1.1;\n --widget-glow-default: 0.5;\n --widget-border-highlight: oklch(1 0 0 / 0.08);\n}\n\n@property --widget-color {\n syntax: \"<color>\";\n inherits: true;\n initial-value: oklch(0.65 0.02 250);\n}\n\n/* === Widget shell ===\n * Fluid scale tokens — tied to the shell's own container box (cqb=height, cqi=width).\n * Icon/pad/gap track HEIGHT (cqb) not the shortest side: two widgets of equal\n * height match regardless of width (a 2×2 and a 4×2 get the same icon). cqmin\n * starved portrait tiles by pinning the icon to the narrow width. Tune\n * multipliers / clamps to adjust density. */\n.glasshome-widget {\n --widget-color: var(--tone-neutral);\n --widget-glow-strength: var(--widget-glow-default);\n\n --widget-icon-box: clamp(44px, 25cqb, 58px);\n --widget-icon-glyph: clamp(24px, 14cqb, 33px);\n --widget-pad: clamp(8px, 3.5cqb, 22px);\n --widget-gap: clamp(6px, 2cqb, 14px);\n --widget-title-size: clamp(12px, 2cqi, 18px);\n --widget-subtitle: clamp(12px, 2.5cqi, 20px);\n --widget-status-size: clamp(14px, 5cqi, 36px);\n --widget-value-size: clamp(18px, 7cqi, 48px);\n --widget-badge-size: clamp(11px, 1.6cqi, 14px);\n}\n\n/* Shell material. Two channels, both host/variant-driven:\n * - `--widget-backdrop`: the live backdrop filter (the shared glass recipe is\n * blur + saturate, matching ui's CARD_BLUR). Default none — a variant opts\n * in (classic-glass), and a performant-blur host disables it via override\n * sheet when it paints a precomputed frost instead.\n * - `--glass-frost`: one full background layer (image + position / size +\n * repeat) composited at the BOTTOM, under the accent gradient — the same\n * frost slot ui's `.glass` formula ends its background list with. Unset →\n * none. A performant-blur host sets it per shell with the viewport-aligned\n * slice of its pre-blurred bitmap. */\n.glasshome-widget {\n background:\n var(--widget-gradient,\n linear-gradient(135deg,\n color-mix(in oklch, var(--widget-color) calc(22% * var(--widget-grad-strength)), transparent),\n color-mix(in oklch, var(--widget-color-to, var(--widget-color)) calc(11% * var(--widget-grad-strength)), transparent)\n )\n ),\n var(--glass-frost, none);\n backdrop-filter: var(--widget-backdrop, none);\n -webkit-backdrop-filter: var(--widget-backdrop, none);\n box-shadow: inset 0 1px 0 var(--widget-border-highlight);\n}\n\n/* === Slot: content layout === */\n/* Default: column. Flip to row only on wide-and-short widgets (matches the\n * old contentLayout heuristic: aspect > 1 AND height < 150px). */\n.glasshome-widget-content {\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n width: 100%;\n height: 100%;\n padding: var(--widget-pad);\n gap: var(--widget-gap);\n overflow: hidden;\n}\n@container widget (min-aspect-ratio: 1) and (max-height: 149px) {\n .glasshome-widget-content {\n flex-direction: row;\n align-items: center;\n justify-content: flex-start;\n }\n}\n\n/* === Compact 1×1 ===\n * Small in BOTH axes → stack the tile: icon on top, label + status centered\n * under it. Icon stays moderate (not a hero) so the text keeps room. Wide\n * tiles (2×1) fail the width test and keep the row layout above. Placed after\n * the row-flip so it wins. */\n@container widget (max-width: 189px) and (max-height: 189px) {\n .glasshome-widget-content {\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n gap: clamp(4px, 2.5cqmin, 8px);\n }\n .glasshome-widget-content > * { align-items: center; text-align: center; }\n .glasshome-widget-icon {\n --widget-icon-box: clamp(36px, 30cqmin, 56px);\n --widget-icon-glyph: clamp(20px, 17cqmin, 32px);\n }\n .glasshome-widget-title { font-size: clamp(11px, 6cqmin, 14px); max-width: 100%; }\n .glasshome-widget-status,\n .glasshome-widget-value { font-size: clamp(13px, 7cqmin, 17px); }\n .glasshome-widget-content [class*=\"ml-auto\"] { margin-left: 0 !important; }\n}\n.glasshome-widget-content [class*=\"ml-auto\"] {\n position: relative;\n z-index: 30;\n}\n\n/* === Slot: icon === */\n.glasshome-widget-icon {\n width: var(--widget-icon-box);\n height: var(--widget-icon-box);\n border-radius: var(--radius-sm, 12px);\n background: var(--widget-icon-color, var(--widget-color));\n box-shadow: 0 0 calc(var(--widget-glow-strength) * 30px)\n color-mix(in oklch, var(--widget-icon-color, var(--widget-color)) 50%, transparent);\n color: oklch(from var(--widget-icon-color, var(--widget-color)) calc(0.52 + sign(0.5 - l) * 0.43) calc(c * 0.2) h);\n}\n.glasshome-widget-icon-glyph {\n font-size: var(--widget-icon-glyph);\n line-height: 1;\n}\n.glasshome-widget-icon-stack {\n position: absolute;\n width: 102%;\n height: 102%;\n border-radius: var(--radius-sm, 12px);\n}\n.glasshome-widget-icon-stack-2 { bottom: -14%; right: -10%; opacity: 0.45; z-index: -2; }\n.glasshome-widget-icon-stack-3 { bottom: -28%; right: -20%; opacity: 0.20; z-index: -3; }\n\n/* === Slot: text === */\n.glasshome-widget-title {\n font-size: var(--widget-title-size);\n line-height: 1.2;\n color: color-mix(in oklch, var(--color-foreground, currentColor) 60%, transparent);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.glasshome-widget-subtitle {\n font-size: var(--widget-subtitle);\n color: color-mix(in oklch, var(--color-foreground, currentColor) 60%, transparent);\n}\n.glasshome-widget-status {\n font-size: var(--widget-status-size);\n font-weight: 700;\n color: var(--color-foreground, currentColor);\n line-height: 1.1;\n}\n.glasshome-widget-value {\n font-size: var(--widget-value-size);\n font-weight: 700;\n color: var(--color-foreground, currentColor);\n line-height: 1.05;\n}\n.glasshome-widget-badge {\n font-size: var(--widget-badge-size);\n font-weight: 500;\n padding: 0.125rem 0.5rem;\n border-radius: 9999px;\n background: color-mix(in oklch, var(--color-foreground, currentColor) 20%, transparent);\n color: var(--color-foreground, currentColor);\n}\n\n.glasshome-widget-loading {\n background: color-mix(in oklch, var(--widget-color) 20%, transparent);\n}\n\n/* === Slider fill ===\n * Value flows in via --widget-fill-value (0-100). Vertical orientation auto-\n * detected via container query. */\n.glasshome-widget-slider-fill {\n --widget-fill-value: 0;\n background: color-mix(in oklch, var(--widget-icon-color, var(--widget-color)) 30%, transparent);\n box-shadow: 0 0 calc(var(--widget-glow-strength) * 40px)\n color-mix(in oklch, var(--widget-icon-color, var(--widget-color)) 40%, transparent);\n clip-path: inset(0 calc((100 - var(--widget-fill-value)) * 1%) 0 0);\n}\n@container widget (max-aspect-ratio: 1) {\n .glasshome-widget-slider-fill {\n clip-path: inset(calc((100 - var(--widget-fill-value)) * 1%) 0 0 0);\n }\n}\n", a = !1, o, s = /* @__PURE__ */ new WeakSet();
12
+ function c() {
13
+ return o || (o = new CSSStyleSheet(), o.replaceSync(i.replace(/:root\b/g, ":host").replace(/^\.dark\b/gm, ":host(.dark)"))), o;
14
+ }
15
+ function l(e) {
16
+ if (typeof ShadowRoot < "u" && e instanceof ShadowRoot) {
17
+ if (s.has(e)) return;
18
+ let t = c();
19
+ e.adoptedStyleSheets.includes(t) || (e.adoptedStyleSheets = [...e.adoptedStyleSheets, t]), s.add(e);
20
+ return;
21
+ }
22
+ if (a) return;
23
+ let t = e ?? (typeof document > "u" ? void 0 : document);
24
+ if (!t) return;
25
+ if (t.head.querySelector?.("style[data-glasshome-tokens]")) {
26
+ a = !0;
27
+ return;
28
+ }
29
+ let n = t.createElement("style");
30
+ n.setAttribute("data-glasshome-tokens", ""), n.textContent = i, t.head.appendChild(n), a = !0;
31
+ }
32
+ //#endregion
33
+ export { n, r, l as t };
package/dist/ui.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Host UI primitives, re-exported so the SDK is the single gated door.
3
+ *
4
+ * The host serves `@glasshome/ui/solid` through its import map with no compat
5
+ * check of its own, so a widget importing it directly is exposed to ui drift
6
+ * across the Hub auto-update boundary. Routed through here, the same
7
+ * primitives sit behind `sdkVersion` — the one contract `checkSdkCompat`
8
+ * enforces (same model as the sync-layer hooks).
9
+ *
10
+ * The set is exactly what shipped widgets use; extending it is an additive
11
+ * SDK minor. Direct `@glasshome/ui` imports keep working but are deprecated
12
+ * (`direct-ui-import` in the registry), removed in 2.0.0.
13
+ *
14
+ * `@glasshome/ui/solid` is a vite external of this package: these re-exports
15
+ * resolve to the host-provided singleton at runtime, never a bundled copy.
16
+ */
17
+ export { Badge, Button, type Color, ColorSlider, ColorWheel, Input, Label, parseColor, ResponsiveDialog, ResponsiveDialogContent, ResponsiveDialogDescription, ResponsiveDialogHeader, ResponsiveDialogTitle, SchemaForm, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Slider, Switch, } from "@glasshome/ui/solid";
@@ -11,18 +11,6 @@ export interface GlasshomeWidgetOptions {
11
11
  * author decide which states matter rather than discovering the loss never.
12
12
  */
13
13
  export declare const MAX_EXAMPLES = 20;
14
- /**
15
- * Configuration-drift guard (D-11). Reads the built bundle, derives the JSON
16
- * Schema from its configSchema, and compares against the recorded
17
- * `.schema-hash`. A shape change without a configVersion bump fails the build:
18
- * tsc cannot catch this class of break because the old persisted config still
19
- * parses, only its meaning shifts. Legacy plain-hash records (written by
20
- * pre-1.7 builds) cannot prove a missing bump, so they only warn, then upgrade
21
- * to the JSON record format.
22
- *
23
- * When `manifestPath` is given (single-widget projects), the generated JSON
24
- * Schema is also written into `manifest.json` as `schema`.
25
- */
26
14
  export declare function runSchemaGuard(args: {
27
15
  outFile: string;
28
16
  hashFile: string;
@@ -37,6 +25,14 @@ export declare function runSchemaGuard(args: {
37
25
  * must be bundled, because leaving it external is a runtime 404.
38
26
  */
39
27
  export declare function isWidgetExternal(id: string): boolean;
28
+ /**
29
+ * True when a widget source file imports `@glasshome/ui` directly. The regex is
30
+ * the registry's `sourcePattern` for `direct-ui-import`, so the build warning and
31
+ * the widget-cli lint can never disagree on what counts as a direct import.
32
+ * node_modules is excluded: dependencies and the SDK's own generated entries in
33
+ * BUILD_CACHE_DIR (the theme `@import`) are the SDK's business, not the widget's.
34
+ */
35
+ export declare function isDirectUiImportSource(id: string, code: string): boolean;
40
36
  interface DiscoveredWidget {
41
37
  name: string;
42
38
  entry: string;
@@ -1,18 +1,19 @@
1
- import { formatSchemaError as e, isHostProvidedModule as t, widgetManifestSchema as n } from "@glasshome/widget-contract";
2
- import { spawnSync as r } from "node:child_process";
3
- import { createHash as i } from "node:crypto";
4
- import { existsSync as a, mkdirSync as o, readFileSync as s, readdirSync as c, rmSync as l, statSync as u, writeFileSync as d } from "node:fs";
5
- import { createRequire as f } from "node:module";
6
- import { dirname as p, join as m, resolve as h, sep as g } from "node:path";
7
- import { fileURLToPath as _ } from "node:url";
8
- import v from "@tailwindcss/vite";
1
+ import { deprecations as e, formatDeprecation as t } from "../deprecations.js";
2
+ import { formatSchemaError as n, isHostProvidedModule as r, widgetManifestSchema as i } from "@glasshome/widget-contract";
3
+ import { spawnSync as a } from "node:child_process";
4
+ import { createHash as o } from "node:crypto";
5
+ import { existsSync as s, mkdirSync as c, readFileSync as l, readdirSync as u, rmSync as d, statSync as f, writeFileSync as p } from "node:fs";
6
+ import { createRequire as m } from "node:module";
7
+ import { dirname as h, join as g, relative as _, resolve as v, sep as y } from "node:path";
8
+ import { fileURLToPath as b } from "node:url";
9
+ import x from "@tailwindcss/vite";
9
10
  //#region src/vite/index.ts
10
- function y(e) {
11
+ function S(e) {
11
12
  let t = JSON.stringify(e, Object.keys(e).sort());
12
- return i("sha256").update(t).digest("hex").slice(0, 16);
13
+ return o("sha256").update(t).digest("hex").slice(0, 16);
13
14
  }
14
- var b = 20;
15
- async function x(e, t) {
15
+ var C = 20;
16
+ async function w(e, t) {
16
17
  if (e === void 0) return;
17
18
  let n;
18
19
  try {
@@ -32,13 +33,21 @@ async function x(e, t) {
32
33
  let a = i.error.issues.map((e) => ` examples${e.path.length ? `[${e.path.join("][")}]` : ""}: ${e.message}`).join("\n");
33
34
  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.`);
34
35
  }
35
- function S(e) {
36
- let t = p(_(import.meta.url)), n = [m(t, "introspect.js"), m(t, "introspect.ts")].find((e) => a(e));
36
+ function T(e, t) {
37
+ if (!e?.length) return;
38
+ let n = e.map((e) => {
39
+ let t = e.label ? `"${e.label}"` : `#${e.index}`;
40
+ return [` examples[${e.index}] ${t}:`, ...e.problems.map((e) => ` ${e}`)].join("\n");
41
+ }).join("\n");
42
+ throw Error(`[widget-sdk] Example config rejected by "${t}"'s own configSchema:\n${n}\nEach example's \`config\` must be a config the widget would actually accept, or its preview renders empty.`);
43
+ }
44
+ function E(e) {
45
+ let t = h(b(import.meta.url)), n = [g(t, "introspect.js"), g(t, "introspect.ts")].find((e) => s(e));
37
46
  if (!n) return {
38
47
  ok: !1,
39
48
  reason: `introspect not found next to ${t}`
40
49
  };
41
- let i = r(process.execPath, [
50
+ let r = a(process.execPath, [
42
51
  "--conditions",
43
52
  "browser",
44
53
  n,
@@ -47,18 +56,18 @@ function S(e) {
47
56
  encoding: "utf-8",
48
57
  timeout: 3e4
49
58
  });
50
- if (i.error) return {
59
+ if (r.error) return {
51
60
  ok: !1,
52
- reason: i.error.message
61
+ reason: r.error.message
53
62
  };
54
- if (i.status !== 0) return {
63
+ if (r.status !== 0) return {
55
64
  ok: !1,
56
- reason: (i.stderr || "").trim().split("\n")[0] || `exit ${i.status}`
65
+ reason: (r.stderr || "").trim().split("\n")[0] || `exit ${r.status}`
57
66
  };
58
67
  try {
59
68
  return {
60
69
  ok: !0,
61
- value: JSON.parse(i.stdout)
70
+ value: JSON.parse(r.stdout)
62
71
  };
63
72
  } catch {
64
73
  return {
@@ -67,49 +76,60 @@ function S(e) {
67
76
  };
68
77
  }
69
78
  }
70
- async function C(e) {
71
- if (!a(e.outFile)) return;
72
- let t = S(e.outFile);
73
- if (!t.ok) {
74
- console.warn(`[widget-sdk] Could not read "${e.widgetName}" to check it: ${t.reason}\n Its examples and config schema were NOT validated.`);
75
- return;
76
- }
77
- let n = t.value;
78
- await x(n.manifest?.examples, n.manifest?.name ?? e.widgetName);
79
- let r = n.jsonSchema;
80
- if (!r) return;
81
- if (e.manifestPath && a(e.manifestPath)) {
82
- let t = JSON.parse(s(e.manifestPath, "utf-8"));
83
- t.schema = r, d(e.manifestPath, JSON.stringify(t, null, 2));
84
- }
85
- let i = y(r), o = n.manifest?.configVersion ?? null, c = n.manifest?.name ?? e.widgetName;
86
- if (a(e.hashFile)) {
87
- let t = s(e.hashFile, "utf-8").trim(), n = null;
79
+ var D = [
80
+ "schema",
81
+ "defaultConfig",
82
+ "examples",
83
+ "configVersion"
84
+ ];
85
+ function O(e, t, n) {
86
+ let r = l(e, "utf-8"), i = JSON.parse(r), a = {
87
+ ...t.manifest ?? {},
88
+ schema: n
89
+ };
90
+ for (let e of D) a[e] !== void 0 && (i[e] = a[e]);
91
+ let o = `${JSON.stringify(i, null, 2)}\n`;
92
+ o !== r && p(e, o);
93
+ }
94
+ async function k(e) {
95
+ if (!s(e.outFile)) return;
96
+ let t = E(e.outFile);
97
+ if (!t.ok) throw Error(`[widget-sdk] Could not read "${e.widgetName}" to check it: ${t.reason}\n Its examples and config schema could not be validated, so the build cannot continue.
98
+ This usually means the bundle throws while being imported.`);
99
+ let n = t.value, r = n.manifest?.name ?? e.widgetName;
100
+ await w(n.manifest?.examples, r), T(n.exampleConfigIssues, r);
101
+ let i = n.jsonSchema;
102
+ if (!i) return;
103
+ e.manifestPath && s(e.manifestPath) && O(e.manifestPath, n, i);
104
+ let a = S(i), o = n.manifest?.configVersion ?? null, c = n.manifest?.name ?? e.widgetName;
105
+ if (s(e.hashFile)) {
106
+ let t = l(e.hashFile, "utf-8").trim(), n = null;
88
107
  if (t.startsWith("{")) try {
89
108
  n = JSON.parse(t);
90
109
  } catch {
91
110
  n = null;
92
111
  }
93
112
  if (n) {
94
- if (n.hash !== i && n.configVersion === o) throw Error(`[widget-sdk] Config schema shape changed for "${c}" without a configVersion bump. Bump configVersion in defineWidget (currently ${o ?? "unset"}) or revert the schema change, then rebuild. Recorded in ${e.hashFile}`);
95
- } else t !== i && console.warn(`[widget-sdk] Schema shape changed for "${c}" — verify configVersion was bumped`);
113
+ if (n.hash !== a && n.configVersion === o) throw Error(`[widget-sdk] Config schema shape changed for "${c}" without a configVersion bump. Bump configVersion in defineWidget (currently ${o ?? "unset"}) or revert the schema change, then rebuild. Recorded in ${e.hashFile}`);
114
+ } else t !== a && console.warn(`[widget-sdk] Schema shape changed for "${c}" — verify configVersion was bumped`);
96
115
  }
97
- d(e.hashFile, `${JSON.stringify({
98
- hash: i,
116
+ p(e.hashFile, `${JSON.stringify({
117
+ hash: a,
99
118
  configVersion: o
100
119
  })}\n`);
101
120
  }
102
- var w = "virtual:glasshome-widget", T = "\0virtual:glasshome-widget", E = "/@glasshome/preview";
103
- function D(e) {
104
- return g === "\\" ? e.split(g).join("/") : e;
121
+ var A = "virtual:glasshome-widget", j = "\0virtual:glasshome-widget", M = "/@glasshome/preview";
122
+ function N(e) {
123
+ return y === "\\" ? e.split(y).join("/") : e;
105
124
  }
106
- function O() {
107
- return h(p(_(import.meta.url)), "../../preview");
125
+ function P() {
126
+ let e = b(import.meta.url);
127
+ return v(h(e), "../../preview");
108
128
  }
109
- function k(e) {
110
- return t(e);
129
+ function F(e) {
130
+ return r(e);
111
131
  }
112
- function A() {
132
+ function I() {
113
133
  return {
114
134
  name: "glasshome-widget:sync-layer-guard",
115
135
  apply: "build",
@@ -119,15 +139,38 @@ function A() {
119
139
  }
120
140
  };
121
141
  }
122
- var j = "node_modules/.cache/glasshome-widgets";
123
- function M(e) {
142
+ var L = /\.(ts|tsx|js|jsx)$/;
143
+ function R(t, n) {
144
+ let r = N(t).split("?")[0] ?? "";
145
+ if (r.includes("/node_modules/") || !L.test(r)) return !1;
146
+ let i = e.find((e) => e.id === "direct-ui-import");
147
+ return i?.sourcePattern ? new RegExp(i.sourcePattern).test(n) : !1;
148
+ }
149
+ function z() {
150
+ let n = /* @__PURE__ */ new Set();
151
+ return {
152
+ name: "glasshome-widget:ui-import-guard",
153
+ apply: "build",
154
+ enforce: "pre",
155
+ transform(e, t) {
156
+ return R(t, e) && n.add(N(_(process.cwd(), t.split("?")[0] ?? t))), null;
157
+ },
158
+ buildEnd() {
159
+ if (n.size === 0) return;
160
+ let r = e.find((e) => e.id === "direct-ui-import"), i = r ? t(r) : "[@glasshome/widget-sdk] Direct @glasshome/ui imports are deprecated; import the same export from \"@glasshome/widget-sdk\".", a = [...n].map((e) => ` ${e}`).join("\n");
161
+ console.warn(`${i}\n Direct @glasshome/ui import(s) in:\n${a}`);
162
+ }
163
+ };
164
+ }
165
+ var B = "node_modules/.cache/glasshome-widgets";
166
+ function V(e) {
124
167
  try {
125
- return f(m(process.cwd(), "package.json")).resolve(e);
168
+ return m(g(process.cwd(), "package.json")).resolve(e);
126
169
  } catch {
127
170
  return null;
128
171
  }
129
172
  }
130
- var N = [
173
+ var H = [
131
174
  "--radius",
132
175
  "--background",
133
176
  "--foreground",
@@ -137,128 +180,130 @@ var N = [
137
180
  "--muted",
138
181
  "--accent"
139
182
  ];
140
- function P(e, t) {
141
- let n = m(e, `${t}.css`);
142
- if (!a(n)) return;
143
- let r = s(n, "utf-8");
183
+ function U(e, t) {
184
+ let n = g(e, `${t}.css`);
185
+ if (!s(n)) return;
186
+ let r = l(n, "utf-8");
144
187
  for (let e of r.matchAll(/:host[^{]*\{([^}]*)\}/g)) {
145
- let n = e[1] ?? "", r = N.find((e) => n.includes(`${e}:`));
188
+ let n = e[1] ?? "", r = H.find((e) => n.includes(`${e}:`));
146
189
  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.`);
147
190
  }
148
191
  }
149
- function F(e, t, n) {
150
- let r = h(e, j);
151
- o(r, { recursive: !0 });
152
- let i = M("@glasshome/ui/styles");
192
+ function W(e, t, n) {
193
+ let r = v(e, B);
194
+ c(r, { recursive: !0 });
195
+ let i = V("@glasshome/ui/styles");
153
196
  if (!i) 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.");
154
- let s = h(p(i), "../.."), c = M("@glasshome/widget-sdk/tailwind-sources"), l = c ? p(c) : null, u = [
155
- m(s, "src"),
156
- m(s, "dist"),
157
- ...l ? [m(l, "src"), m(l, "dist")] : [],
158
- p(n)
159
- ].filter((e) => a(e)), f = h(r, `${t}.tailwind.css`);
160
- d(f, [
197
+ let a = v(h(i), "../.."), o = V("@glasshome/widget-sdk/tailwind-sources"), l = o ? h(o) : null, u = [
198
+ g(a, "src"),
199
+ g(a, "dist"),
200
+ ...l ? [g(l, "src"), g(l, "dist")] : [],
201
+ h(n)
202
+ ].filter((e) => s(e)), d = v(r, `${t}.tailwind.css`);
203
+ p(d, [
161
204
  "@import \"tailwindcss\" source(none);",
162
205
  "@import \"tw-animate-css\";",
163
206
  "@import \"@glasshome/ui/styles/theme\";",
164
- ...u.map((e) => `@source "${D(e)}";`),
207
+ ...u.map((e) => `@source "${N(e)}";`),
165
208
  "@custom-variant dark {",
166
209
  " &:is(.dark *) { @slot }",
167
210
  " :host(.dark) & { @slot }",
168
211
  "}",
169
212
  ""
170
213
  ].join("\n"));
171
- let g = h(r, `${t}.entry.ts`);
172
- return d(g, [
173
- `import "${D(f)}";`,
174
- `export { default } from "${D(n)}";`,
214
+ let f = v(r, `${t}.entry.ts`);
215
+ return p(f, [
216
+ `import "${N(d)}";`,
217
+ `export { default } from "${N(n)}";`,
175
218
  ""
176
- ].join("\n")), g;
219
+ ].join("\n")), f;
177
220
  }
178
- function I() {
179
- let e = v();
221
+ function G() {
222
+ let e = x();
180
223
  for (let t of e) t.apply ??= "build";
181
224
  return e;
182
225
  }
183
- function L(e) {
226
+ function K(e) {
184
227
  let t = [];
185
- if (!a(e)) return t;
186
- for (let n of c(e)) {
187
- let r = h(e, n);
188
- if (!u(r).isDirectory()) continue;
189
- let i = h(r, "index.tsx"), o = h(r, "manifest.json");
190
- a(i) && a(o) && t.push({
228
+ if (!s(e)) return t;
229
+ for (let n of u(e)) {
230
+ let r = v(e, n);
231
+ if (!f(r).isDirectory()) continue;
232
+ let i = v(r, "index.tsx"), a = v(r, "manifest.json");
233
+ s(i) && s(a) && t.push({
191
234
  name: n,
192
235
  entry: i
193
236
  });
194
237
  }
195
238
  return t;
196
239
  }
197
- function R(t, r) {
198
- let i = [];
199
- if (a(t)) for (let o of c(t)) {
200
- if (!u(m(t, o)).isDirectory()) continue;
201
- let c = m(t, o, "manifest.json");
202
- if (!a(c)) continue;
203
- let l = n.safeParse(JSON.parse(s(c, "utf-8")));
204
- if (!l.success) throw Error(`[registry] Invalid manifest for "${o}": ${e(l.error)}`);
205
- let d = a(m(r, `${o}.css`));
206
- i.push({
207
- ...l.data,
208
- bundleUrl: `./${o}.js`,
209
- ...d ? { cssUrl: `./${o}.css` } : {}
240
+ function q(e, t) {
241
+ let r = [];
242
+ if (s(e)) for (let a of u(e)) {
243
+ if (!f(g(e, a)).isDirectory()) continue;
244
+ let o = g(e, a, "manifest.json");
245
+ if (!s(o)) continue;
246
+ let c = i.safeParse(JSON.parse(l(o, "utf-8")));
247
+ if (!c.success) throw Error(`[registry] Invalid manifest for "${a}": ${n(c.error)}`);
248
+ let u = s(g(t, `${a}.css`));
249
+ r.push({
250
+ ...c.data,
251
+ bundleUrl: `./${a}.js`,
252
+ ...u ? { cssUrl: `./${a}.css` } : {}
210
253
  });
211
254
  }
212
- let l = {
255
+ let a = {
213
256
  version: 1,
214
257
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
215
258
  baseUrl: "./",
216
- widgets: i
259
+ widgets: r
217
260
  };
218
- a(r) || o(r, { recursive: !0 }), d(m(r, "registry.json"), JSON.stringify(l, null, 2)), console.log(`[registry] Generated registry.json with ${i.length} widget(s)`);
261
+ s(t) || c(t, { recursive: !0 }), p(g(t, "registry.json"), JSON.stringify(a, null, 2)), console.log(`[registry] Generated registry.json with ${r.length} widget(s)`);
219
262
  }
220
- async function z(e) {
221
- let { build: t } = await import("vite"), n = process.cwd(), r = h(n, e?.srcDir ?? "src"), i = h(n, e?.outDir ?? "dist"), s = L(r);
222
- if (s.length === 0) {
263
+ async function J(e) {
264
+ let { build: t } = await import("vite"), n = process.cwd(), r = v(n, e?.srcDir ?? "src"), i = v(n, e?.outDir ?? "dist"), a = K(r);
265
+ if (a.length === 0) {
223
266
  console.warn("[glasshome-widgets] No widgets found in", r);
224
267
  return;
225
268
  }
226
269
  if (e?.only) {
227
270
  let t = new Set(e.only);
228
- s = s.filter((e) => t.has(e.name));
271
+ a = a.filter((e) => t.has(e.name));
229
272
  }
230
- e?.only || a(i) && l(i, { recursive: !0 }), a(i) || o(i, { recursive: !0 });
231
- for (let r of s) await t({
273
+ e?.only || s(i) && d(i, { recursive: !0 }), s(i) || c(i, { recursive: !0 });
274
+ for (let r of a) await t({
232
275
  configFile: !1,
233
276
  root: n,
234
277
  plugins: [
235
- ...I(),
278
+ ...G(),
236
279
  ...e?.plugins ?? [],
237
- A()
280
+ I(),
281
+ z()
238
282
  ],
239
283
  ...e?.viteConfig,
240
284
  build: {
241
285
  lib: {
242
- entry: F(n, r.name, r.entry),
286
+ entry: W(n, r.name, r.entry),
243
287
  formats: ["es"],
244
288
  fileName: r.name,
245
289
  cssFileName: r.name
246
290
  },
247
- rollupOptions: { external: k },
291
+ rollupOptions: { external: F },
248
292
  outDir: i,
249
293
  emptyOutDir: !1,
250
294
  copyPublicDir: !1,
251
295
  ...e?.viteConfig?.build
252
296
  },
253
297
  logLevel: "warn"
254
- }), P(i, r.name), await C({
255
- outFile: m(i, `${r.name}.js`),
256
- hashFile: m(p(r.entry), ".schema-hash"),
257
- widgetName: r.name
298
+ }), U(i, r.name), await k({
299
+ outFile: g(i, `${r.name}.js`),
300
+ hashFile: g(h(r.entry), ".schema-hash"),
301
+ widgetName: r.name,
302
+ manifestPath: g(h(r.entry), "manifest.json")
258
303
  });
259
- R(r, i);
304
+ q(r, i);
260
305
  }
261
- function B(e) {
306
+ function Y(e) {
262
307
  let t = e?.entry ?? "src/index.tsx", n = {
263
308
  name: "glasshome-widget:build",
264
309
  apply: "build",
@@ -266,12 +311,12 @@ function B(e) {
266
311
  let e = process.cwd();
267
312
  return { build: {
268
313
  lib: {
269
- entry: F(e, "index", h(e, t)),
314
+ entry: W(e, "index", v(e, t)),
270
315
  formats: ["es"],
271
316
  fileName: "index",
272
317
  cssFileName: "index"
273
318
  },
274
- rollupOptions: { external: k }
319
+ rollupOptions: { external: F }
275
320
  } };
276
321
  }
277
322
  }, r = {
@@ -279,19 +324,19 @@ function B(e) {
279
324
  apply: "serve",
280
325
  enforce: "pre",
281
326
  config() {
282
- let e = O(), t = process.cwd();
327
+ let e = P(), t = process.cwd();
283
328
  return { server: { fs: { allow: [
284
329
  e,
285
330
  t,
286
- h(t, "..", "..")
331
+ v(t, "..", "..")
287
332
  ] } } };
288
333
  },
289
334
  configureServer(e) {
290
- let t = m(O(), "preview.html");
291
- e.middlewares.use(async (n, r, i) => {
292
- if (n.url === "/" || n.url === "/index.html") try {
293
- let i = s(t, "utf-8");
294
- i = await e.transformIndexHtml(n.url, i), r.setHeader("Content-Type", "text/html"), r.statusCode = 200, r.end(i);
335
+ let t = P(), n = g(t, "preview.html");
336
+ e.middlewares.use(async (t, r, i) => {
337
+ if (t.url === "/" || t.url === "/index.html") try {
338
+ let i = l(n, "utf-8");
339
+ i = await e.transformIndexHtml(t.url, i), r.setHeader("Content-Type", "text/html"), r.statusCode = 200, r.end(i);
295
340
  } catch (e) {
296
341
  i(e);
297
342
  }
@@ -299,33 +344,37 @@ function B(e) {
299
344
  });
300
345
  },
301
346
  resolveId(e) {
302
- if (e === E) return D(m(O(), "host.tsx"));
303
- if (e === w) return T;
347
+ if (e === M) {
348
+ let e = P();
349
+ return N(g(e, "host.tsx"));
350
+ }
351
+ if (e === A) return j;
304
352
  },
305
353
  load(e) {
306
- if (e === T) return `export { default } from "${D(h(process.cwd(), t))}";`;
354
+ if (e === j) return `export { default } from "${N(v(process.cwd(), t))}";`;
307
355
  }
308
356
  }, i = {
309
357
  name: "glasshome-widget:schema",
310
358
  apply: "build",
311
359
  async closeBundle() {
312
- P(h(process.cwd(), "dist"), "index"), await C({
313
- outFile: h(process.cwd(), "dist", "index.js"),
314
- hashFile: h(process.cwd(), ".schema-hash"),
360
+ U(v(process.cwd(), "dist"), "index"), await k({
361
+ outFile: v(process.cwd(), "dist", "index.js"),
362
+ hashFile: v(process.cwd(), ".schema-hash"),
315
363
  widgetName: "index",
316
- manifestPath: h(process.cwd(), "manifest.json")
364
+ manifestPath: v(process.cwd(), "manifest.json")
317
365
  });
318
366
  }
319
367
  };
320
368
  return [
321
- ...I(),
369
+ ...G(),
322
370
  n,
323
- A(),
371
+ I(),
372
+ z(),
324
373
  i,
325
374
  r
326
375
  ];
327
376
  }
328
- function V(e) {
377
+ function X(e) {
329
378
  let t = e?.srcDir ?? "src", n = e?.outDir ?? "dist", r = [];
330
379
  return [{
331
380
  name: "glasshome-widgets:build",
@@ -344,7 +393,7 @@ function V(e) {
344
393
  if (e === "\0glasshome-noop") return "export {}";
345
394
  },
346
395
  async closeBundle() {
347
- await z({
396
+ await J({
348
397
  srcDir: t,
349
398
  outDir: n,
350
399
  plugins: r
@@ -353,4 +402,4 @@ function V(e) {
353
402
  }];
354
403
  }
355
404
  //#endregion
356
- export { b as MAX_EXAMPLES, z as buildWidgets, L as discoverWidgets, R as generateRegistry, B as glasshomeWidget, V as glasshomeWidgets, k as isWidgetExternal, C as runSchemaGuard };
405
+ export { C as MAX_EXAMPLES, J as buildWidgets, K as discoverWidgets, q as generateRegistry, Y as glasshomeWidget, X as glasshomeWidgets, R as isDirectUiImportSource, F as isWidgetExternal, k as runSchemaGuard };
@@ -14,14 +14,8 @@
14
14
  * Usage: bun --conditions browser introspect.js <bundle.js>
15
15
  * Prints: { manifest, jsonSchema } on stdout, or exits non-zero with a reason.
16
16
  */
17
- interface Introspection {
18
- manifest: {
19
- name?: string;
20
- configVersion?: number;
21
- examples?: unknown;
22
- } | null;
23
- /** JSON Schema derived from the widget's configSchema, when it has one. */
24
- jsonSchema: object | null;
17
+ export interface ExampleConfigIssue {
18
+ index: number;
19
+ label?: string;
20
+ problems: string[];
25
21
  }
26
- declare function installDom(): Promise<void>;
27
- declare function main(): Promise<void>;