@glasshome/widget-sdk 1.8.0 → 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,59 @@
1
+ //#region src/vite/introspect.ts
2
+ function e(e) {
3
+ return typeof e?.safeParse == "function";
4
+ }
5
+ function t(t, n) {
6
+ if (!e(t) || !Array.isArray(n)) return [];
7
+ let r = [];
8
+ for (let [e, i] of n.entries()) {
9
+ let n = i?.config;
10
+ if (typeof n != "object" || !n) continue;
11
+ let a = t.safeParse(n);
12
+ a.success || r.push({
13
+ index: e,
14
+ label: i.label,
15
+ problems: (a.error?.issues ?? []).map((e) => {
16
+ let t = e.path.map(String).join(".");
17
+ return t ? `${t}: ${e.message}` : e.message;
18
+ })
19
+ });
20
+ }
21
+ return r;
22
+ }
23
+ async function n() {
24
+ let { Window: e } = await import("happy-dom"), t = new e({ url: "http://localhost" });
25
+ for (let e of [
26
+ "window",
27
+ "document",
28
+ "navigator",
29
+ "location",
30
+ "customElements",
31
+ "HTMLElement",
32
+ "Element",
33
+ "Node",
34
+ "CSS",
35
+ "getComputedStyle",
36
+ "requestAnimationFrame",
37
+ "cancelAnimationFrame",
38
+ "matchMedia"
39
+ ]) globalThis[e] === void 0 && (globalThis[e] = t[e]);
40
+ }
41
+ async function r() {
42
+ let e = process.argv[2];
43
+ e || (process.stderr.write("introspect: no bundle path given\n"), process.exit(2)), await n();
44
+ let r = (await import(e)).default, i = null;
45
+ if (r?.configSchema) {
46
+ let { z: e } = await import("zod");
47
+ i = e.toJSONSchema(r.configSchema, { unrepresentable: "any" });
48
+ }
49
+ let a = {
50
+ manifest: r?.manifest ?? null,
51
+ jsonSchema: i,
52
+ exampleConfigIssues: t(r?.configSchema, r?.manifest?.examples)
53
+ };
54
+ process.stdout.write(JSON.stringify(a));
55
+ }
56
+ r().catch((e) => {
57
+ process.stderr.write(`${e instanceof Error ? e.message : String(e)}\n`), process.exit(1);
58
+ });
59
+ //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glasshome/widget-sdk",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "SDK for building GlassHome dashboard widgets with SolidJS",
5
5
  "keywords": [
6
6
  "glasshome",
@@ -57,17 +57,19 @@
57
57
  "typecheck:test": "tsc --noEmit -p tsconfig.test.json"
58
58
  },
59
59
  "peerDependencies": {
60
- "@glasshome/sync-layer": "0.4.0",
60
+ "@glasshome/sync-layer": "0.5.0",
61
+ "@glasshome/ui": "1.3.0",
61
62
  "solid-js": "^1.9.11"
62
63
  },
63
64
  "dependencies": {
64
65
  "@glasshome/widget-contract": "0.2.0",
65
66
  "@modular-forms/solid": "^0.25.1",
67
+ "@tailwindcss/vite": "^4.1.13",
66
68
  "clsx": "^2.1.1",
69
+ "happy-dom": "^20.0.0",
67
70
  "tailwind-merge": "^3.5.0",
68
- "zod": "^4.1.13",
69
- "@tailwindcss/vite": "^4.1.13",
70
- "tailwindcss": "^4.1.13"
71
+ "tailwindcss": "^4.1.13",
72
+ "zod": "^4.1.13"
71
73
  },
72
74
  "devDependencies": {
73
75
  "@types/bun": "latest",
@@ -1,33 +0,0 @@
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.glasshome-widget {\n background: 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 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 };