@glasshome/widget-sdk 0.1.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/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/create-entity.d.ts +22 -0
- package/dist/define-widget.d.ts +10 -0
- package/dist/framework/backgrounds/Glow.d.ts +24 -0
- package/dist/framework/backgrounds/WidgetSliderFill.d.ts +33 -0
- package/dist/framework/components/WidgetContent.d.ts +29 -0
- package/dist/framework/components/WidgetEmptyState.d.ts +34 -0
- package/dist/framework/components/WidgetIcon.d.ts +48 -0
- package/dist/framework/components/WidgetMetrics.d.ts +47 -0
- package/dist/framework/components/WidgetStatus.d.ts +26 -0
- package/dist/framework/components/WidgetSubtitle.d.ts +28 -0
- package/dist/framework/components/WidgetTitle.d.ts +29 -0
- package/dist/framework/components/WidgetValue.d.ts +31 -0
- package/dist/framework/core/Widget.d.ts +88 -0
- package/dist/framework/design-system/index.d.ts +8 -0
- package/dist/framework/design-system/spacing.d.ts +40 -0
- package/dist/framework/design-system/typography.d.ts +40 -0
- package/dist/framework/design-system/z-index.d.ts +23 -0
- package/dist/framework/dialogs/WidgetDialog.d.ts +47 -0
- package/dist/framework/dialogs/index.d.ts +4 -0
- package/dist/framework/gestures/cursors.d.ts +17 -0
- package/dist/framework/gestures/use-widget-gestures.d.ts +53 -0
- package/dist/framework/hooks/index.d.ts +15 -0
- package/dist/framework/hooks/use-debug-data.d.ts +46 -0
- package/dist/framework/hooks/use-widget-config.d.ts +48 -0
- package/dist/framework/hooks/use-widget-context.d.ts +46 -0
- package/dist/framework/hooks/use-widget-dialog.d.ts +45 -0
- package/dist/framework/hooks/use-widget-entity-group.d.ts +67 -0
- package/dist/framework/hooks/use-widget-entity.d.ts +53 -0
- package/dist/framework/hooks/use-widget-form.d.ts +79 -0
- package/dist/framework/hooks/use-widget-responsive.d.ts +41 -0
- package/dist/framework/index.d.ts +55 -0
- package/dist/framework/layout/WidgetStack.d.ts +28 -0
- package/dist/framework/theming/adaptive-color.d.ts +28 -0
- package/dist/framework/theming/colors.d.ts +59 -0
- package/dist/framework/theming/index.d.ts +8 -0
- package/dist/framework/types.d.ts +335 -0
- package/dist/framework/utils/cn.d.ts +28 -0
- package/dist/framework/utils/empty-state.d.ts +49 -0
- package/dist/framework/utils/entity-aggregation.d.ts +73 -0
- package/dist/framework/utils/entity-state.d.ts +103 -0
- package/dist/framework/utils/format-value.d.ts +22 -0
- package/dist/framework/utils/index.d.ts +12 -0
- package/dist/framework/utils/interpret-value.d.ts +17 -0
- package/dist/framework/variants/built-in-variants.d.ts +39 -0
- package/dist/framework/variants/index.d.ts +7 -0
- package/dist/framework/variants/variant-utils.d.ts +105 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1955 -0
- package/dist/theme.d.ts +21 -0
- package/dist/types.d.ts +51 -0
- package/dist/version.d.ts +5 -0
- package/dist/vite/index.d.ts +56 -0
- package/dist/vite/index.js +184 -0
- package/package.json +76 -0
- package/preview/host.tsx +89 -0
- package/preview/preview.html +49 -0
- package/tailwind-sources.css +1 -0
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Theme utilities — plain DOM reads.
|
|
3
|
+
* Framework-agnostic: no SolidJS imports.
|
|
4
|
+
*
|
|
5
|
+
* These functions read CSS custom properties and class state from the
|
|
6
|
+
* document root. They are synchronous and work in any browser environment.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Read a CSS custom property (theme token) from the document root.
|
|
10
|
+
* Automatically prepends "--" if not already present.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* getThemeToken("color-primary") // reads --color-primary
|
|
14
|
+
* getThemeToken("--color-primary") // also works
|
|
15
|
+
*/
|
|
16
|
+
export declare function getThemeToken(name: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Check whether the current theme is dark mode.
|
|
19
|
+
* Reads the presence of the "dark" class on <html>.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isDark(): boolean;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Grid size constraint — number of columns (w) and rows (h).
|
|
3
|
+
*/
|
|
4
|
+
export interface GridSize {
|
|
5
|
+
w: number;
|
|
6
|
+
h: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Widget manifest — declarative metadata about a widget.
|
|
10
|
+
* Framework-agnostic: no SolidJS imports.
|
|
11
|
+
*/
|
|
12
|
+
export interface WidgetManifest {
|
|
13
|
+
tag: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
minSize: GridSize;
|
|
17
|
+
maxSize: GridSize;
|
|
18
|
+
sdkVersion: string;
|
|
19
|
+
icon?: string;
|
|
20
|
+
schema?: object;
|
|
21
|
+
defaultConfig?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Runtime context provided to widgets by the host.
|
|
25
|
+
* Framework-agnostic: plain objects and functions.
|
|
26
|
+
*/
|
|
27
|
+
export interface WidgetContext {
|
|
28
|
+
config: Record<string, unknown>;
|
|
29
|
+
theme: {
|
|
30
|
+
getToken: (name: string) => string;
|
|
31
|
+
isDark: () => boolean;
|
|
32
|
+
};
|
|
33
|
+
dimensions: {
|
|
34
|
+
width: number;
|
|
35
|
+
height: number;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A complete widget definition combining manifest metadata with a component.
|
|
40
|
+
* The component return type is `any` so the type contract does not force SolidJS
|
|
41
|
+
* (internally the runtime knows it is SolidJS, but widget authors code against
|
|
42
|
+
* a stable contract that does not leak framework internals).
|
|
43
|
+
*
|
|
44
|
+
* @template C - Widget configuration type
|
|
45
|
+
*/
|
|
46
|
+
export interface WidgetDefinition<C = Record<string, unknown>> {
|
|
47
|
+
manifest: WidgetManifest;
|
|
48
|
+
component: (props: {
|
|
49
|
+
config: C;
|
|
50
|
+
}) => any;
|
|
51
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { InlineConfig, Plugin } from "vite";
|
|
2
|
+
export interface GlasshomeWidgetOptions {
|
|
3
|
+
/** Entry file for the widget (default: "src/index.tsx") */
|
|
4
|
+
entry?: string;
|
|
5
|
+
}
|
|
6
|
+
/** Packages provided by the host import map — widgets must not bundle these. */
|
|
7
|
+
export declare function isWidgetExternal(id: string): boolean;
|
|
8
|
+
interface DiscoveredWidget {
|
|
9
|
+
name: string;
|
|
10
|
+
entry: string;
|
|
11
|
+
}
|
|
12
|
+
/** Scan srcDir for subdirectories containing index.tsx + manifest.json. */
|
|
13
|
+
export declare function discoverWidgets(srcDir: string): DiscoveredWidget[];
|
|
14
|
+
/** Generate registry.json from widget manifests. */
|
|
15
|
+
export declare function generateRegistry(srcDir: string, outDir: string): void;
|
|
16
|
+
export interface BuildWidgetsOptions {
|
|
17
|
+
/** Directory containing widget subdirectories (default: "src") */
|
|
18
|
+
srcDir?: string;
|
|
19
|
+
/** Output directory for built bundles and registry (default: "dist") */
|
|
20
|
+
outDir?: string;
|
|
21
|
+
/** Additional Vite plugins to apply to each widget build (e.g. solid()) */
|
|
22
|
+
plugins?: Plugin[];
|
|
23
|
+
/** Extra Vite config merged into each widget build */
|
|
24
|
+
viteConfig?: InlineConfig;
|
|
25
|
+
/** Build only these widget names (subdirectory names). Skips full clean. */
|
|
26
|
+
only?: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Build each widget as a separate Vite invocation so shared code is inlined
|
|
30
|
+
* into each bundle (no chunk splitting).
|
|
31
|
+
*/
|
|
32
|
+
export declare function buildWidgets(options?: BuildWidgetsOptions): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Vite plugin for GlassHome widget development and building.
|
|
35
|
+
*
|
|
36
|
+
* Returns an array of plugins:
|
|
37
|
+
* - Build mode: configures library build with proper externals
|
|
38
|
+
* - Dev mode: serves a preview host with dark mode toggle and manifest display
|
|
39
|
+
*/
|
|
40
|
+
export declare function glasshomeWidget(options?: GlasshomeWidgetOptions): Plugin[];
|
|
41
|
+
export interface GlasshomeWidgetsOptions {
|
|
42
|
+
/** Directory containing widget subdirectories (default: "src") */
|
|
43
|
+
srcDir?: string;
|
|
44
|
+
/** Output directory for built bundles and registry (default: "dist") */
|
|
45
|
+
outDir?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Vite plugin for multi-widget projects.
|
|
49
|
+
*
|
|
50
|
+
* In build mode, delegates to `buildWidgets()` which runs a separate Vite
|
|
51
|
+
* build per widget so shared code is inlined (no chunk splitting).
|
|
52
|
+
* The plugin's `config()` returns a minimal build config to suppress Vite's
|
|
53
|
+
* default build, and `closeBundle()` runs the actual per-widget builds.
|
|
54
|
+
*/
|
|
55
|
+
export declare function glasshomeWidgets(options?: GlasshomeWidgetsOptions): Plugin[];
|
|
56
|
+
export {};
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { existsSync as a, rmSync as x, mkdirSync as w, readdirSync as v, statSync as b, readFileSync as D, writeFileSync as S } from "node:fs";
|
|
2
|
+
import { resolve as l, join as g, sep as h, dirname as W } from "node:path";
|
|
3
|
+
import { fileURLToPath as I } from "node:url";
|
|
4
|
+
const j = "virtual:glasshome-widget", p = "\0virtual:glasshome-widget", E = "/@glasshome/preview";
|
|
5
|
+
function y(e) {
|
|
6
|
+
return h === "\\" ? e.split(h).join("/") : e;
|
|
7
|
+
}
|
|
8
|
+
function f() {
|
|
9
|
+
const e = I(import.meta.url);
|
|
10
|
+
return l(W(e), "../../preview");
|
|
11
|
+
}
|
|
12
|
+
function P(e) {
|
|
13
|
+
return e === "solid-js" || e.startsWith("solid-js/") || e === "@glasshome/widget-sdk" || e.startsWith("@glasshome/widget-sdk/") || e === "@glasshome/ui" || e.startsWith("@glasshome/ui/") || e === "@glasshome/sync-layer" || e.startsWith("@glasshome/sync-layer/");
|
|
14
|
+
}
|
|
15
|
+
function _(e) {
|
|
16
|
+
const i = [];
|
|
17
|
+
if (!a(e)) return i;
|
|
18
|
+
for (const r of v(e)) {
|
|
19
|
+
const n = l(e, r);
|
|
20
|
+
if (!b(n).isDirectory()) continue;
|
|
21
|
+
const t = l(n, "index.tsx"), s = l(n, "manifest.json");
|
|
22
|
+
a(t) && a(s) && i.push({ name: r, entry: t });
|
|
23
|
+
}
|
|
24
|
+
return i;
|
|
25
|
+
}
|
|
26
|
+
function O(e, i) {
|
|
27
|
+
const r = [];
|
|
28
|
+
if (a(e))
|
|
29
|
+
for (const t of v(e)) {
|
|
30
|
+
if (!b(g(e, t)).isDirectory()) continue;
|
|
31
|
+
const s = g(e, t, "manifest.json");
|
|
32
|
+
try {
|
|
33
|
+
const o = JSON.parse(D(s, "utf-8"));
|
|
34
|
+
r.push({ ...o, bundleUrl: `./${t}.js` });
|
|
35
|
+
} catch {
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const n = {
|
|
39
|
+
version: 1,
|
|
40
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
41
|
+
baseUrl: "./",
|
|
42
|
+
widgets: r
|
|
43
|
+
};
|
|
44
|
+
a(i) || w(i, { recursive: !0 }), S(g(i, "registry.json"), JSON.stringify(n, null, 2)), console.log(`[registry] Generated registry.json with ${r.length} widget(s)`);
|
|
45
|
+
}
|
|
46
|
+
async function R(e) {
|
|
47
|
+
const { build: i } = await import("vite"), r = process.cwd(), n = l(r, e?.srcDir ?? "src"), t = l(r, e?.outDir ?? "dist");
|
|
48
|
+
let s = _(n);
|
|
49
|
+
if (s.length === 0) {
|
|
50
|
+
console.warn("[glasshome-widgets] No widgets found in", n);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (e?.only) {
|
|
54
|
+
const o = new Set(e.only);
|
|
55
|
+
s = s.filter((u) => o.has(u.name));
|
|
56
|
+
}
|
|
57
|
+
e?.only || a(t) && x(t, { recursive: !0 }), a(t) || w(t, { recursive: !0 });
|
|
58
|
+
for (const o of s)
|
|
59
|
+
await i({
|
|
60
|
+
configFile: !1,
|
|
61
|
+
root: r,
|
|
62
|
+
plugins: e?.plugins ?? [],
|
|
63
|
+
...e?.viteConfig,
|
|
64
|
+
build: {
|
|
65
|
+
lib: {
|
|
66
|
+
entry: o.entry,
|
|
67
|
+
formats: ["es"],
|
|
68
|
+
fileName: o.name
|
|
69
|
+
},
|
|
70
|
+
rollupOptions: {
|
|
71
|
+
external: P
|
|
72
|
+
},
|
|
73
|
+
outDir: t,
|
|
74
|
+
emptyOutDir: !1,
|
|
75
|
+
copyPublicDir: !1,
|
|
76
|
+
...e?.viteConfig?.build
|
|
77
|
+
},
|
|
78
|
+
logLevel: "warn"
|
|
79
|
+
});
|
|
80
|
+
O(n, t);
|
|
81
|
+
}
|
|
82
|
+
function L(e) {
|
|
83
|
+
const i = e?.entry ?? "src/index.tsx";
|
|
84
|
+
return [{
|
|
85
|
+
name: "glasshome-widget:build",
|
|
86
|
+
apply: "build",
|
|
87
|
+
config() {
|
|
88
|
+
return {
|
|
89
|
+
build: {
|
|
90
|
+
lib: {
|
|
91
|
+
entry: i,
|
|
92
|
+
formats: ["es"],
|
|
93
|
+
fileName: "index"
|
|
94
|
+
},
|
|
95
|
+
rollupOptions: {
|
|
96
|
+
external: P
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}, {
|
|
102
|
+
name: "glasshome-widget:dev",
|
|
103
|
+
apply: "serve",
|
|
104
|
+
enforce: "pre",
|
|
105
|
+
config() {
|
|
106
|
+
const t = f(), s = process.cwd(), o = l(s, "..", "..");
|
|
107
|
+
return {
|
|
108
|
+
server: {
|
|
109
|
+
fs: {
|
|
110
|
+
allow: [t, s, o]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
configureServer(t) {
|
|
116
|
+
const s = f(), o = g(s, "preview.html");
|
|
117
|
+
t.middlewares.use(async (u, d, m) => {
|
|
118
|
+
if (u.url === "/" || u.url === "/index.html")
|
|
119
|
+
try {
|
|
120
|
+
let c = D(o, "utf-8");
|
|
121
|
+
c = await t.transformIndexHtml(u.url, c), d.setHeader("Content-Type", "text/html"), d.statusCode = 200, d.end(c);
|
|
122
|
+
} catch (c) {
|
|
123
|
+
m(c);
|
|
124
|
+
}
|
|
125
|
+
else
|
|
126
|
+
m();
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
resolveId(t) {
|
|
130
|
+
if (t === E) {
|
|
131
|
+
const s = f();
|
|
132
|
+
return y(g(s, "host.tsx"));
|
|
133
|
+
}
|
|
134
|
+
if (t === j)
|
|
135
|
+
return p;
|
|
136
|
+
},
|
|
137
|
+
load(t) {
|
|
138
|
+
if (t === p)
|
|
139
|
+
return `export { default } from "${y(l(process.cwd(), i))}";`;
|
|
140
|
+
}
|
|
141
|
+
}];
|
|
142
|
+
}
|
|
143
|
+
function k(e) {
|
|
144
|
+
const i = e?.srcDir ?? "src", r = e?.outDir ?? "dist";
|
|
145
|
+
let n = [];
|
|
146
|
+
return [{
|
|
147
|
+
name: "glasshome-widgets:build",
|
|
148
|
+
apply: "build",
|
|
149
|
+
config(s) {
|
|
150
|
+
return n = (s.plugins ?? []).flat().filter(Boolean).filter(
|
|
151
|
+
(o) => !o.name?.startsWith("glasshome-widgets:")
|
|
152
|
+
), {
|
|
153
|
+
build: {
|
|
154
|
+
rollupOptions: {
|
|
155
|
+
input: { __glasshome_noop: "\0glasshome-noop" }
|
|
156
|
+
},
|
|
157
|
+
outDir: r,
|
|
158
|
+
copyPublicDir: !1
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
resolveId(s) {
|
|
163
|
+
if (s === "\0glasshome-noop") return s;
|
|
164
|
+
},
|
|
165
|
+
load(s) {
|
|
166
|
+
if (s === "\0glasshome-noop") return "export {}";
|
|
167
|
+
},
|
|
168
|
+
async closeBundle() {
|
|
169
|
+
await R({
|
|
170
|
+
srcDir: i,
|
|
171
|
+
outDir: r,
|
|
172
|
+
plugins: n
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}];
|
|
176
|
+
}
|
|
177
|
+
export {
|
|
178
|
+
R as buildWidgets,
|
|
179
|
+
_ as discoverWidgets,
|
|
180
|
+
O as generateRegistry,
|
|
181
|
+
L as glasshomeWidget,
|
|
182
|
+
k as glasshomeWidgets,
|
|
183
|
+
P as isWidgetExternal
|
|
184
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@glasshome/widget-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"glasshome",
|
|
7
|
+
"widget",
|
|
8
|
+
"sdk",
|
|
9
|
+
"solidjs",
|
|
10
|
+
"vite",
|
|
11
|
+
"home-assistant",
|
|
12
|
+
"dashboard",
|
|
13
|
+
"smart-home"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"main": "./dist/index.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"@glasshome/source": "./src/index.ts",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./vite": {
|
|
26
|
+
"@glasshome/source": "./src/vite/index.ts",
|
|
27
|
+
"types": "./dist/vite/index.d.ts",
|
|
28
|
+
"import": "./dist/vite/index.js"
|
|
29
|
+
},
|
|
30
|
+
"./tailwind-sources": "./tailwind-sources.css",
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"preview",
|
|
36
|
+
"tailwind-sources.css"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "vite build && tsc",
|
|
40
|
+
"dev": "vite build --watch"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"solid-js": "^1.9.11"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modular-forms/solid": "^0.25.1",
|
|
47
|
+
"@solid-primitives/resize-observer": "^2.1.5",
|
|
48
|
+
"clsx": "^2.1.1",
|
|
49
|
+
"tailwind-merge": "^3.5.0",
|
|
50
|
+
"zod": "^4.1.13"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/bun": "latest",
|
|
54
|
+
"@types/node": "^25.2.2",
|
|
55
|
+
"typescript": "^5",
|
|
56
|
+
"vite": "^7.3.1",
|
|
57
|
+
"vite-plugin-externalize-deps": "^0.8.0",
|
|
58
|
+
"vite-plugin-solid": "^2.11.10"
|
|
59
|
+
},
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/glasshome/widget-sdk.git"
|
|
63
|
+
},
|
|
64
|
+
"homepage": "https://github.com/glasshome/widget-sdk#readme",
|
|
65
|
+
"bugs": {
|
|
66
|
+
"url": "https://github.com/glasshome/widget-sdk/issues"
|
|
67
|
+
},
|
|
68
|
+
"author": "GlassHome Labs",
|
|
69
|
+
"license": "MIT",
|
|
70
|
+
"publishConfig": {
|
|
71
|
+
"access": "public"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=18"
|
|
75
|
+
}
|
|
76
|
+
}
|
package/preview/host.tsx
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import widget from "virtual:glasshome-widget";
|
|
2
|
+
import { createSignal } from "solid-js";
|
|
3
|
+
import { render } from "solid-js/web";
|
|
4
|
+
|
|
5
|
+
function PreviewHost() {
|
|
6
|
+
const [isDark, setIsDark] = createSignal(false);
|
|
7
|
+
const [config, setConfig] = createSignal(widget.manifest.defaultConfig || {});
|
|
8
|
+
|
|
9
|
+
const toggleDark = () => {
|
|
10
|
+
const next = !isDark();
|
|
11
|
+
setIsDark(next);
|
|
12
|
+
document.documentElement.classList.toggle("dark", next);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const WidgetComponent = widget.component;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
style={{
|
|
20
|
+
"max-width": "600px",
|
|
21
|
+
margin: "0 auto",
|
|
22
|
+
padding: "24px",
|
|
23
|
+
}}
|
|
24
|
+
>
|
|
25
|
+
<header
|
|
26
|
+
style={{
|
|
27
|
+
display: "flex",
|
|
28
|
+
"align-items": "center",
|
|
29
|
+
"justify-content": "space-between",
|
|
30
|
+
"margin-bottom": "24px",
|
|
31
|
+
"padding-bottom": "16px",
|
|
32
|
+
"border-bottom": "1px solid var(--color-border)",
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<div>
|
|
36
|
+
<h1 style={{ "font-size": "1.5rem", "font-weight": "bold" }}>{widget.manifest.name}</h1>
|
|
37
|
+
<p
|
|
38
|
+
style={{
|
|
39
|
+
"font-size": "0.875rem",
|
|
40
|
+
opacity: 0.7,
|
|
41
|
+
"margin-top": "4px",
|
|
42
|
+
}}
|
|
43
|
+
>
|
|
44
|
+
<{widget.manifest.tag}> | {widget.manifest.type} | {widget.manifest.size}
|
|
45
|
+
</p>
|
|
46
|
+
</div>
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={toggleDark}
|
|
50
|
+
style={{
|
|
51
|
+
padding: "8px 16px",
|
|
52
|
+
"border-radius": "6px",
|
|
53
|
+
border: "1px solid var(--color-border)",
|
|
54
|
+
background: "transparent",
|
|
55
|
+
color: "var(--color-foreground)",
|
|
56
|
+
cursor: "pointer",
|
|
57
|
+
"font-size": "0.875rem",
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{isDark() ? "Light Mode" : "Dark Mode"}
|
|
61
|
+
</button>
|
|
62
|
+
</header>
|
|
63
|
+
|
|
64
|
+
<section
|
|
65
|
+
style={{
|
|
66
|
+
padding: "24px",
|
|
67
|
+
border: "1px solid var(--color-border)",
|
|
68
|
+
"border-radius": "8px",
|
|
69
|
+
background: "var(--color-background)",
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<WidgetComponent config={config()} />
|
|
73
|
+
</section>
|
|
74
|
+
|
|
75
|
+
<footer
|
|
76
|
+
style={{
|
|
77
|
+
"margin-top": "16px",
|
|
78
|
+
"font-size": "0.75rem",
|
|
79
|
+
opacity: 0.5,
|
|
80
|
+
"text-align": "center",
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
GlassHome Widget Preview | SDK {widget.manifest.sdkVersion}
|
|
84
|
+
</footer>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
render(() => <PreviewHost />, document.getElementById("root")!);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Widget Preview</title>
|
|
7
|
+
<style>
|
|
8
|
+
*,
|
|
9
|
+
*::before,
|
|
10
|
+
*::after {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
:root {
|
|
17
|
+
--color-primary: #3b82f6;
|
|
18
|
+
--color-background: #ffffff;
|
|
19
|
+
--color-foreground: #0f172a;
|
|
20
|
+
--color-border: #e2e8f0;
|
|
21
|
+
--color-success: #22c55e;
|
|
22
|
+
--color-error: #ef4444;
|
|
23
|
+
color-scheme: light;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dark {
|
|
27
|
+
--color-primary: #60a5fa;
|
|
28
|
+
--color-background: #0f172a;
|
|
29
|
+
--color-foreground: #f1f5f9;
|
|
30
|
+
--color-border: #334155;
|
|
31
|
+
--color-success: #4ade80;
|
|
32
|
+
--color-error: #f87171;
|
|
33
|
+
color-scheme: dark;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
body {
|
|
37
|
+
font-family: system-ui, -apple-system, sans-serif;
|
|
38
|
+
background: var(--color-background);
|
|
39
|
+
color: var(--color-foreground);
|
|
40
|
+
min-height: 100vh;
|
|
41
|
+
transition: background 0.2s, color 0.2s;
|
|
42
|
+
}
|
|
43
|
+
</style>
|
|
44
|
+
</head>
|
|
45
|
+
<body>
|
|
46
|
+
<div id="root"></div>
|
|
47
|
+
<script type="module" src="/@glasshome/preview"></script>
|
|
48
|
+
</body>
|
|
49
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@source "./src";
|