@glasshome/widget-sdk 1.18.0 → 1.19.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/dist/framework/index.d.ts +2 -2
- package/dist/framework/utils/index.d.ts +1 -1
- package/dist/index.js +49 -40
- package/guide/widgets.md +74 -0
- package/package.json +33 -28
- package/preview/preview.html +8 -3
|
@@ -38,9 +38,9 @@ export { isPanelControllable, PanelEntityRow } from "./panel/panel-entity";
|
|
|
38
38
|
export { buildDebugData, WidgetDebugTab, type WidgetDebugData, type WidgetDebugEntity, WidgetDebugView, WidgetDialog, type SheetTile, type WidgetDialogProps, type WidgetDialogTab, } from "./dialogs";
|
|
39
39
|
export { type AggregationPreset, type ReactiveWidgetContext, type ServiceCallFn, type UseWidgetEntityGroupOptions, type UseWidgetEntityGroupResult, type Daylight, type DaylightPhase, useDaylight, useIntersectionPause, useReducedMotion, useWidgetContext, useWidgetDashboard, useWidgetViewer, useWidgetDialog, useWidgetDimensions, useWidgetEntityGroup, WidgetCtx, type WidgetDashboard, type WidgetViewer, type WidgetDialogReturn, type WidgetDimensions, type WidgetDimensionsAccessor, WidgetSizeCtx, } from "./hooks";
|
|
40
40
|
export { type GestureHandlers, useWidgetGestures } from "./gestures/use-widget-gestures";
|
|
41
|
-
export { injectTokens, type SvgColorKey, svgColors, type Tone, ToneSchema
|
|
41
|
+
export { injectTokens, type SvgColorKey, svgColors, type Tone, ToneSchema } from "./theming";
|
|
42
42
|
export { widgetFields } from "./fields";
|
|
43
43
|
export { type ConfigShape, defineConfig, field, type Field, type ImagePresetSource, type Infer, } from "./config";
|
|
44
44
|
export { imageSrc } from "./image-src";
|
|
45
45
|
export { calculateLightGroup, calculateSensorGroup, type CopyState, copyText, countActiveEntities, getEntityAttribute, isEntityActive, type LightGroupResult, type SensorGroupResult, type SensorGroupType, useCopyText, } from "./utils";
|
|
46
|
-
export type { EntityView, WidgetStyles, WidgetVariantConfig
|
|
46
|
+
export type { EntityView, WidgetStyles, WidgetVariantConfig } from "./types";
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { copyText, type CopyState, useCopyText } from "./clipboard";
|
|
9
9
|
export { calculateLightGroup, calculateSensorGroup, type LightGroupResult, type SensorGroupResult, type SensorGroupType, } from "./entity-aggregation";
|
|
10
|
-
export { countActiveEntities, getEntityAttribute, isEntityActive
|
|
10
|
+
export { countActiveEntities, getEntityAttribute, isEntityActive } from "./entity-state";
|
|
11
11
|
export { formatValue } from "./format-value";
|
|
12
12
|
export { interpretValue } from "./interpret-value";
|
package/dist/index.js
CHANGED
|
@@ -468,7 +468,7 @@ function kn(e) {
|
|
|
468
468
|
return Dn();
|
|
469
469
|
}
|
|
470
470
|
}), r), F(r, () => e.icon), P((t) => {
|
|
471
|
-
var r = X("glasshome-widget-icon glass glass-tint", "relative flex shrink-0 items-center justify-center
|
|
471
|
+
var r = X("glasshome-widget-icon glass glass-tint", "pointer-events-none relative flex shrink-0 items-center justify-center transition-all", e.dimmed && "opacity-50", e.class), i = e.color ? { "--widget-icon-color": e.color } : void 0;
|
|
472
472
|
return r !== t.e && M(n, t.e = r), t.t = z(n, i, t.t), t;
|
|
473
473
|
}, {
|
|
474
474
|
e: void 0,
|
|
@@ -706,7 +706,7 @@ function Qn(e) {
|
|
|
706
706
|
}
|
|
707
707
|
//#endregion
|
|
708
708
|
//#region src/framework/theming/tokens.css?raw
|
|
709
|
-
var $n = "/* 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-glow-default: 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-glow-default: 0.5;\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 * One scale: --widget-unit is 1% of the shell's short side. Each size is a floor,\n * a line through the designed 2x2 and 4x4 sizes, and past 4x4 the share it has\n * at 4x4. A widget sizes its own text with these tokens and scales with every tile. */\n.glasshome-widget {\n --widget-color: var(--tone-neutral);\n --widget-glow-strength: var(--widget-glow-default);\n --widget-radius: var(--radius-xl, 12px);\n\n --widget-unit: 1cqmin;\n --widget-text-value: max(22px, calc(8.5px + 13.4 * var(--widget-unit)), calc(16 * var(--widget-unit)));\n --widget-text-name: max(14px, calc(8.9px + 4.37 * var(--widget-unit)), calc(7.07 * var(--widget-unit)));\n --widget-text-meta: max(11px, calc(8px + 2.44 * var(--widget-unit)), calc(4.88 * var(--widget-unit)));\n --widget-text-sub: max(12px, calc(10.1px + 1.22 * var(--widget-unit)), calc(4.3 * var(--widget-unit)));\n --widget-text-caption: max(11px, calc(3.6 * var(--widget-unit)));\n --widget-control-h: max(36px, calc(30px + 3.84 * var(--widget-unit)));\n --widget-grid-pad: max(10px, calc(1.2px + 5.64 * var(--widget-unit)), calc(6 * var(--widget-unit)));\n --widget-grid-gap: max(6px, calc(2.5px + 2.23 * var(--widget-unit)));\n --widget-icon-box: max(44px, calc(31.3px + 8.14 * var(--widget-unit)), calc(17.7 * var(--widget-unit)));\n --widget-icon-glyph: max(24px, calc(16px + 5.12 * var(--widget-unit)), calc(10 * var(--widget-unit)));\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: ui's .glass, worn through the class Widget.tsx carries.\n * Unlayered and class-specific, so these beat the formula's :where() defaults\n * (wash-2 is re-declared there and cannot be set from a utility). The tone\n * stays the formula's transparent default until Widget.tsx sets one inline:\n * a widget without a tone is the plain card, never a neutral-grey wash. */\n.glasshome-widget {\n --widget-wash: 22%;\n --widget-wash-2: 11%;\n --glass-wash: var(--widget-wash);\n --glass-wash-2: var(--widget-wash-2);\n --glass-wash-angle: 135deg;\n --glass-base: color-mix(in srgb, var(--card) var(--material-clarity, 60%), transparent);\n --glass-rim: 0.3;\n --glass-lift: 0.45;\n --glass-shade: 0.05;\n backdrop-filter: var(--widget-backdrop, none);\n -webkit-backdrop-filter: var(--widget-backdrop, none);\n}\n/* A fill widget keeps a neutral card; the fill carries the tone, so a full fill is the toned card. */\n.glasshome-widget:has(.glasshome-widget-slider-fill) {\n --glass-wash: 0%;\n --glass-wash-2: 0%;\n}\n.dark .glasshome-widget {\n --glass-shade: 0;\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 border-radius: var(--widget-radius);\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 * ui's tinted icon pill (ICON_PILL_TINT): the disc is .glass, so the material shapes it. */\n.glasshome-widget-icon {\n width: var(--widget-icon-box);\n height: var(--widget-icon-box);\n border-radius: var(--radius-sm, 12px);\n --glass-tone: var(--widget-icon-color, var(--widget-color));\n --glass-rim: 0.4;\n --glass-wash: 18%;\n}\n.glasshome-widget-icon-glyph {\n font-size: var(--widget-icon-glyph);\n line-height: 1;\n}\n.glasshome-widget-icon-stack {\n --stack-step: calc(var(--widget-icon-box) * 0.12);\n position: absolute;\n /* The pill's border would otherwise shrink each plate and widen the first step. */\n inset: -1px;\n width: auto;\n height: auto;\n border-radius: var(--radius-sm, 12px);\n}\n.glasshome-widget-icon-stack-2 { translate: var(--stack-step) var(--stack-step); opacity: 0.45; z-index: -2; }\n.glasshome-widget-icon-stack-3 { translate: calc(2 * var(--stack-step)) calc(2 * var(--stack-step)); opacity: 0.2; 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: var(--color-muted-foreground, currentColor);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.glasshome-widget-subtitle {\n font-size: var(--widget-subtitle);\n color: var(--color-muted-foreground, currentColor);\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: linear-gradient(\n 135deg,\n color-mix(in srgb, var(--widget-color) calc(var(--widget-wash) * var(--material-tint, 1)), transparent),\n color-mix(\n in srgb,\n var(--widget-color-to, var(--widget-color)) calc(var(--widget-wash-2) * var(--material-tint, 1)),\n transparent\n )\n );\n /* Inset: the clip-path cuts any outer shadow. */\n box-shadow: inset 0 0 calc(var(--material-glow, 0px) * 1.2)\n oklch(from var(--widget-color) l c h / 0.55);\n clip-path: inset(0 calc((100 - var(--widget-fill-value)) * 1%) 0 0 round var(--widget-radius));\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 round var(--widget-radius));\n }\n}\n\n/* === Anatomy: Widget.Content with a Widget.Head lays its parts out as a tile === */\n.glasshome-widget-grid {\n position: relative;\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr) auto;\n gap: var(--widget-grid-gap);\n height: 100%;\n padding: var(--widget-grid-pad);\n overflow: hidden;\n isolation: isolate;\n color: var(--foreground);\n}\n\n/* In-flow content spans the tile (the max-content first column would collapse it); absolute parts keep the padding box. */\n.glasshome-widget-grid > :where(:not(.glasshome-widget-backdrop, .glasshome-widget-layer, .glasshome-widget-glyph)) {\n grid-column: 1 / -1;\n}\n.glasshome-widget-layer {\n position: absolute;\n inset: 0;\n z-index: -1;\n overflow: hidden;\n}\n.glasshome-widget-head {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n}\n.glasshome-widget-head-text {\n min-height: var(--widget-icon-box);\n justify-content: center;\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n flex: 1;\n}\n.glasshome-widget-head-aside {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 6px;\n max-height: 26px;\n overflow: hidden;\n flex-shrink: 1;\n}\n.glasshome-widget-eyebrow {\n font-size: var(--widget-text-meta);\n font-weight: 500;\n line-height: 1.25;\n color: var(--muted-foreground);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.glasshome-widget-name {\n font-size: var(--widget-text-name);\n font-weight: 650;\n line-height: 1.2;\n letter-spacing: -0.01em;\n color: var(--foreground);\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n overflow: hidden;\n overflow-wrap: break-word;\n}\n\n.glasshome-widget-hero {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--widget-grid-gap);\n min-height: 0;\n min-width: 0;\n}\n.glasshome-widget-reading {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n z-index: 1;\n}\n.glasshome-widget-reading-value {\n font-size: var(--widget-text-value);\n font-weight: 650;\n line-height: 1;\n letter-spacing: -0.03em;\n font-variant-numeric: tabular-nums;\n color: var(--foreground);\n white-space: nowrap;\n}\n/* Alternating names restart the rise on each change without rebuilding the text. */\n.glasshome-widget-reading-value[data-turn=\"a\"] {\n animation: tile-value-in-a var(--duration-morph) var(--ease-morph);\n}\n.glasshome-widget-reading-value[data-turn=\"b\"] {\n animation: tile-value-in-b var(--duration-morph) var(--ease-morph);\n}\n@keyframes tile-value-in-a {\n from {\n opacity: 0;\n transform: translateY(0.25em);\n }\n}\n@keyframes tile-value-in-b {\n from {\n opacity: 0;\n transform: translateY(0.25em);\n }\n}\n.glasshome-widget-unit {\n font-size: 0.5em;\n font-weight: 550;\n letter-spacing: 0;\n margin-left: 0.08em;\n color: var(--muted-foreground);\n}\n.glasshome-widget-sub {\n font-size: var(--widget-text-sub);\n font-weight: 500;\n color: var(--muted-foreground);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Placed on the tile, not the hero, so a control row never moves or shrinks it. */\n.glasshome-widget-art {\n position: absolute;\n right: var(--widget-grid-pad);\n bottom: var(--widget-grid-pad);\n height: min(calc(100% - 2 * var(--widget-grid-pad) - var(--widget-icon-box) - var(--widget-grid-gap)), 48cqi);\n display: flex;\n align-items: flex-end;\n justify-content: flex-end;\n pointer-events: none;\n}\n/* Beside art a long word (\"Unlocked\") shrinks to end where the art starts, at about 50cqi; short values keep full size. */\n@container widget (min-width: 180px) and (min-height: 130px) {\n .glasshome-widget-hero[data-art] .glasshome-widget-reading-value {\n font-size: max(18px, min(var(--widget-text-value), calc(50cqi / (var(--value-len, 1) * 0.56))));\n }\n}\n.glasshome-widget-backdrop > img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n/* Art keeps its shape and clears a state word at its largest (15cqi, about 3.4em); each art sets --art-aspect. */\n.glasshome-widget-grid .glasshome-widget-art > * {\n height: min(100%, calc((52cqi - 2 * var(--widget-grid-pad) - var(--widget-grid-gap)) / var(--art-aspect, 1)));\n flex-shrink: 0;\n}\n.glasshome-widget-grid .glasshome-widget-art > img {\n width: auto;\n height: auto;\n max-height: 100%;\n max-width: calc(52cqi - 2 * var(--widget-grid-pad) - var(--widget-grid-gap));\n}\n\n.glasshome-widget-controls {\n display: flex;\n gap: 8px;\n min-width: 0;\n}\n\n.glasshome-widget-glyph {\n display: none;\n position: absolute;\n right: -8%;\n bottom: -22%;\n font-size: min(130cqb, 80cqi);\n line-height: 1;\n color: var(--widget-color);\n opacity: 0.16;\n pointer-events: none;\n z-index: -1;\n}\n\n/* Controls sit on the bottom row beside the value, or under it on a narrow tile; the art lifts above them. */\n.glasshome-widget-head {\n grid-column: 1 / -1;\n}\n.glasshome-widget-hero {\n grid-row: 3;\n grid-column: 1 / -1;\n}\n.glasshome-widget-grid:has(> .glasshome-widget-controls) > .glasshome-widget-hero {\n grid-column: 1;\n}\n.glasshome-widget-controls {\n grid-row: 3;\n grid-column: 2;\n align-self: end;\n position: relative;\n z-index: 1;\n}\n.glasshome-widget-grid:has(> .glasshome-widget-hero[data-empty]) > .glasshome-widget-controls {\n grid-column: 1 / -1;\n}\n@container widget (max-width: 519px) {\n .glasshome-widget-grid:has(> .glasshome-widget-controls) {\n grid-template-rows: auto minmax(0, 1fr) auto auto;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) > .glasshome-widget-hero {\n grid-column: 1 / -1;\n }\n .glasshome-widget-controls {\n grid-row: 4;\n grid-column: 1 / -1;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) .glasshome-widget-art {\n bottom: calc(var(--widget-grid-pad) + var(--widget-control-h) + var(--widget-grid-gap));\n height: min(\n calc(\n 100% -\n 2 *\n var(--widget-grid-pad) -\n var(--widget-icon-box) -\n 2 *\n var(--widget-grid-gap) -\n var(--widget-control-h)\n ),\n 48cqi\n );\n }\n}\n@container widget (max-height: 229px) {\n .glasshome-widget-controls {\n display: none;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) {\n grid-template-rows: auto minmax(0, 1fr) auto;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) .glasshome-widget-art {\n bottom: var(--widget-grid-pad);\n height: min(calc(100% - 2 * var(--widget-grid-pad) - var(--widget-icon-box) - var(--widget-grid-gap)), 48cqi);\n }\n}\n@container widget (max-width: 179px) {\n .glasshome-widget-art {\n display: none;\n }\n}\n@container widget (max-height: 169px) {\n .glasshome-widget-head-aside {\n display: none;\n }\n}\n@container widget ((max-height: 129px) or (max-width: 139px)) {\n .glasshome-widget-head-text {\n min-height: 0;\n }\n .glasshome-widget-name {\n -webkit-line-clamp: 1;\n }\n .glasshome-widget-grid {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n gap: 2px;\n }\n .glasshome-widget-head-icon,\n .glasshome-widget-art,\n .glasshome-widget-eyebrow,\n .glasshome-widget-sub {\n display: none;\n }\n .glasshome-widget-glyph {\n display: block;\n }\n .glasshome-widget-reading-value {\n font-size: clamp(18px, min(34cqb, 20cqi), 30px);\n }\n .glasshome-widget-name {\n font-size: clamp(12px, min(18cqb, 11cqi), 15px);\n font-weight: 550;\n color: var(--muted-foreground);\n }\n}\n@container widget (max-height: 129px) and (min-width: 260px) {\n .glasshome-widget-grid {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--widget-grid-gap);\n }\n .glasshome-widget-name {\n font-size: clamp(14px, 22cqb, 18px);\n font-weight: 650;\n color: var(--foreground);\n }\n .glasshome-widget-eyebrow {\n display: block;\n }\n}\n\n.glasshome-widget-backdrop {\n position: absolute;\n inset: 0;\n z-index: -2;\n overflow: hidden;\n}\n.glasshome-widget-backdrop > img.area-night {\n opacity: 0;\n transition: opacity 700ms ease;\n}\n.glasshome-widget-backdrop > img.area-night[data-visible] {\n opacity: 1;\n}\n.glasshome-widget-backdrop > .glasshome-widget-backdrop-light {\n position: absolute;\n inset: 0;\n background: radial-gradient(90% 80% at 25% 30%, oklch(0.8 0.14 70), transparent 70%);\n mix-blend-mode: soft-light;\n opacity: var(--backdrop-light, 0);\n transition: opacity 500ms ease;\n}\n.glasshome-widget-backdrop::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background: linear-gradient(\n to bottom,\n color-mix(in oklch, var(--card) 82%, transparent) 0%,\n color-mix(in oklch, var(--card) 40%, transparent) 22%,\n transparent 42%,\n transparent 62%,\n color-mix(in oklch, var(--card) 45%, transparent) 82%,\n color-mix(in oklch, var(--card) 80%, transparent) 100%\n );\n}\n\n@container widget (max-width: 239px) {\n .glasshome-widget-choice [data-optional] {\n display: none;\n }\n}\n@container widget (max-width: 259px) {\n .glasshome-widget-head-aside {\n display: none;\n }\n}\n@container widget (max-width: 139px) {\n .glasshome-widget-controls {\n display: none;\n }\n}\n\n.glasshome-widget-controls {\n justify-content: flex-start;\n align-items: center;\n}\n.glasshome-widget-control.glasshome-widget-control {\n width: var(--widget-control-h);\n height: var(--widget-control-h);\n}\n.glasshome-widget-choice .glasshome-widget-control.glasshome-widget-control {\n width: calc(var(--widget-control-h) - 8px);\n height: calc(var(--widget-control-h) - 8px);\n}\n@container widget (max-width: 179px) and (min-height: 130px) {\n .glasshome-widget-grid:not(:has(> .glasshome-widget-backdrop)) .glasshome-widget-head-text {\n min-height: 0;\n }\n .glasshome-widget-grid:not(:has(> .glasshome-widget-backdrop)) .glasshome-widget-head {\n flex-direction: column;\n gap: 6px;\n }\n}\n.glasshome-widget-unit[data-degree] {\n vertical-align: top;\n margin-left: 0.02em;\n color: var(--foreground);\n}\n.glasshome-widget-control-wide.glasshome-widget-control-wide {\n height: var(--widget-control-h);\n gap: 6px;\n padding: 0 14px;\n}\n@container widget (max-width: 179px) {\n .glasshome-widget-grid:has(> .glasshome-widget-backdrop) .glasshome-widget-head-icon {\n display: none;\n }\n}\n.glasshome-widget-grid:has(> .glasshome-widget-backdrop) :is(.glasshome-widget-reading-value, .glasshome-widget-sub, .glasshome-widget-name, .glasshome-widget-eyebrow) {\n text-shadow: 0 1px 8px color-mix(in oklch, var(--card) 75%, transparent);\n}\n.glasshome-widget-grid:has(> .glasshome-widget-backdrop) :is(.glasshome-widget-sub, .glasshome-widget-eyebrow) {\n color: color-mix(in oklch, var(--foreground) 82%, transparent);\n}\n/* Control glyphs and words keep their share of a control as it grows past 4x4. */\n.glasshome-widget-grid :is(.glasshome-widget-control, .glasshome-widget-control-wide) :is(iconify-icon, svg) {\n font-size: calc(var(--widget-control-h) * 0.43);\n width: 1em;\n height: 1em;\n}\n:is(.glasshome-widget-controls, .glasshome-widget-control-wide) {\n font-size: max(13px, calc(var(--widget-control-h) * 0.3));\n}\n/* A big tile with no art of its own carries its icon large and faint, so the glass is not an empty box. */\n@container widget (min-width: 300px) and (min-height: 260px) {\n .glasshome-widget-grid:not(:has(> .glasshome-widget-backdrop), :has(> .glasshome-widget-hero[data-art], > .glasshome-widget-art, > .glasshome-widget-layer))\n > .glasshome-widget-glyph {\n display: block;\n right: -3%;\n bottom: -12%;\n font-size: min(78cqb, 52cqi);\n opacity: 0.1;\n }\n}\n/* A one-shot action confirms on its control and on the glyph, the one thing a compact tile shows. */\n.glasshome-widget-grid > .glasshome-widget-glyph {\n transition: opacity var(--duration-state) var(--ease-morph);\n}\n.glasshome-widget[data-confirmed] .glasshome-widget-glyph {\n opacity: 0.5;\n}\n.glasshome-widget-grid [data-confirmed]:is(.glasshome-widget-control, .glasshome-widget-control-wide) {\n --glass-base: color-mix(in srgb, var(--widget-color) 35%, transparent);\n transition: background-color var(--duration-state) var(--ease-morph);\n}\n\n/* === Sheet: what a held tile opens (framework/panel) ===\n * Rendered in a popover or dialog outside the widget's shadow root, so every rule\n * the sheet needs lives here, never in a widget's own utilities. */\n.glasshome-sheet {\n display: flex;\n flex-direction: column;\n gap: 18px;\n color: var(--foreground);\n}\n.glasshome-sheet [data-slot=\"swatch-picker\"] {\n max-width: 360px;\n}\n.glasshome-panel-section {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.glasshome-panel-label {\n margin: 0;\n font-size: 13px;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n.glasshome-panel-rows {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n}\n.glasshome-panel-row-wrap {\n position: relative;\n min-width: 0;\n}\n.glasshome-panel-row {\n --widget-wash: 22%;\n --widget-wash-2: 11%;\n --widget-radius: var(--radius-lg, 16px);\n --widget-icon-box: 34px;\n --widget-icon-glyph: 19px;\n position: relative;\n width: 100%;\n height: 56px;\n justify-content: flex-start;\n gap: 10px;\n padding: 0 12px 0 10px;\n overflow: hidden;\n text-align: left;\n touch-action: pan-y;\n}\n.glasshome-panel-row-static {\n cursor: default;\n scale: none;\n}\n.glasshome-panel-row-fill {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.glasshome-panel-row-icon {\n position: relative;\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n.glasshome-panel-row-icon-off {\n color: var(--muted-foreground);\n}\n.glasshome-panel-row-text {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.glasshome-panel-row-name,\n.glasshome-panel-row-state,\n.glasshome-panel-fact-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* A reading may take a second line rather than lose its end (\"3 Moderate\"). */\n.glasshome-panel-fact-value {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n.glasshome-panel-row-name,\n.glasshome-panel-fact-value {\n font-size: 14px;\n font-weight: 500;\n color: var(--foreground);\n}\n.glasshome-panel-row-state,\n.glasshome-panel-fact-label {\n font-size: 12px;\n font-weight: 400;\n color: var(--muted-foreground);\n}\n.glasshome-panel-row-trailing {\n position: absolute;\n top: 50%;\n right: 10px;\n translate: 0 -50%;\n}\n.glasshome-panel-facts {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));\n row-gap: 10px;\n padding: 10px 4px 10px 0;\n overflow: hidden;\n border-radius: var(--radius-lg, 16px);\n --glass-base: color-mix(in srgb, var(--card) var(--material-clarity, 60%), transparent);\n backdrop-filter: blur(calc(var(--material-blur, 24px) * 0.6)) saturate(1.6);\n -webkit-backdrop-filter: blur(calc(var(--material-blur, 24px) * 0.6)) saturate(1.6);\n}\n/* Without blur a see-through strip is just see-through: it takes the card fill. */\n.reduce-blur .glasshome-panel-facts {\n --glass-base: var(--card);\n}\n.glasshome-panel-fact {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n padding: 0 12px;\n box-shadow: -1px 0 0 color-mix(in oklch, var(--foreground) 10%, transparent);\n}\n.glasshome-panel-fact-icon {\n flex-shrink: 0;\n font-size: 18px;\n color: var(--muted-foreground);\n}\n\n/* Inset: the grid cell clips anything drawn outside the tile. */\n.glasshome-widget:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: -2px;\n}\n\n/* === Hold flood: the dock's hold fill (ui HOLD_MOTION), grown from the finger === */\n.glasshome-widget-hold-flood {\n position: absolute;\n translate: -50% -50%;\n border-radius: 9999px;\n background: color-mix(in oklch, var(--primary) 45%, transparent);\n pointer-events: none;\n scale: 0;\n opacity: 0;\n transition:\n scale var(--duration-state, 200ms) var(--ease-contract, ease-in),\n opacity var(--duration-state, 200ms) var(--ease-contract, ease-in);\n}\n.glasshome-widget-hold-flood[data-hold] {\n scale: 1;\n opacity: 1;\n transition-duration: var(--duration-morph, 400ms);\n transition-timing-function: linear;\n}\n.glasshome-widget-hold-flood[data-fired] {\n scale: 1;\n opacity: 0;\n}\n/* A row of ui buttons in a sheet (scenes, one-tap actions). */\n.glasshome-sheet-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n/* The sheet wears its tile: the tile's tone washed into the glass, its picture as a band behind\n the head that fades into the glass, so the tile reads as having grown open. */\n.glasshome-sheet-surface {\n --glass-wash: 20%;\n --glass-wash-2: 7%;\n --widget-icon-box: 48px;\n --widget-icon-glyph: 26px;\n}\n.glasshome-sheet-band {\n position: absolute;\n inset: 0 0 auto;\n height: 150px;\n overflow: hidden;\n pointer-events: none;\n mask-image: linear-gradient(180deg, #000 30%, transparent);\n}\n.glasshome-sheet-band > img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n opacity: 0.55;\n}\n.glasshome-sheet-surface > [data-slot$=\"-header\"],\n.glasshome-sheet-surface > [data-slot$=\"-body\"] {\n position: relative;\n}\n.glasshome-sheet-icon {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n", er = !1, tr, nr = /* @__PURE__ */ new WeakSet();
|
|
709
|
+
var $n = "/* 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.7 0.18 145);\n --tone-warning: oklch(0.8 0.17 75);\n --tone-danger: oklch(0.64 0.22 27);\n --tone-info: oklch(0.66 0.2 245);\n --tone-neutral: oklch(0.65 0.02 250);\n --tone-accent: oklch(0.74 0.18 60);\n\n --widget-glow-default: 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.7 0.2 245);\n --tone-neutral: oklch(0.69 0.02 250);\n --tone-accent: oklch(0.78 0.18 60);\n\n --widget-glow-default: 0.5;\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 * One scale: --widget-unit is 1% of the shell's short side. Each size is a floor,\n * a line through the designed 2x2 and 4x4 sizes, and past 4x4 the share it has\n * at 4x4. A widget sizes its own text with these tokens and scales with every tile. */\n.glasshome-widget {\n --widget-color: var(--tone-neutral);\n --widget-glow-strength: var(--widget-glow-default);\n --widget-radius: var(--radius-xl, 12px);\n\n --widget-unit: 1cqmin;\n --widget-text-value: max(\n 22px,\n calc(8.5px + 13.4 * var(--widget-unit)),\n calc(16 * var(--widget-unit))\n );\n --widget-text-name: max(\n 14px,\n calc(8.9px + 4.37 * var(--widget-unit)),\n calc(7.07 * var(--widget-unit))\n );\n --widget-text-meta: max(\n 11px,\n calc(8px + 2.44 * var(--widget-unit)),\n calc(4.88 * var(--widget-unit))\n );\n --widget-text-sub: max(\n 12px,\n calc(10.1px + 1.22 * var(--widget-unit)),\n calc(4.3 * var(--widget-unit))\n );\n --widget-text-caption: max(11px, calc(3.6 * var(--widget-unit)));\n --widget-control-h: max(36px, calc(30px + 3.84 * var(--widget-unit)));\n --widget-grid-pad: max(\n 10px,\n calc(1.2px + 5.64 * var(--widget-unit)),\n calc(6 * var(--widget-unit))\n );\n --widget-grid-gap: max(6px, calc(2.5px + 2.23 * var(--widget-unit)));\n --widget-icon-box: max(\n 44px,\n calc(31.3px + 8.14 * var(--widget-unit)),\n calc(17.7 * var(--widget-unit))\n );\n --widget-icon-glyph: max(\n 24px,\n calc(16px + 5.12 * var(--widget-unit)),\n calc(10 * var(--widget-unit))\n );\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: ui's .glass, worn through the class Widget.tsx carries.\n * Unlayered and class-specific, so these beat the formula's :where() defaults\n * (wash-2 is re-declared there and cannot be set from a utility). The tone\n * stays the formula's transparent default until Widget.tsx sets one inline:\n * a widget without a tone is the plain card, never a neutral-grey wash. */\n.glasshome-widget {\n --widget-wash: 22%;\n --widget-wash-2: 11%;\n --glass-wash: var(--widget-wash);\n --glass-wash-2: var(--widget-wash-2);\n --glass-wash-angle: 135deg;\n --glass-base: color-mix(in srgb, var(--card) var(--material-clarity, 60%), transparent);\n --glass-rim: 0.3;\n --glass-lift: 0.45;\n --glass-shade: 0.05;\n backdrop-filter: var(--widget-backdrop, none);\n -webkit-backdrop-filter: var(--widget-backdrop, none);\n}\n/* A fill widget keeps a neutral card; the fill carries the tone, so a full fill is the toned card. */\n.glasshome-widget:has(.glasshome-widget-slider-fill) {\n --glass-wash: 0%;\n --glass-wash-2: 0%;\n}\n.dark .glasshome-widget {\n --glass-shade: 0;\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 border-radius: var(--widget-radius);\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 > * {\n align-items: center;\n text-align: center;\n }\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 {\n font-size: clamp(11px, 6cqmin, 14px);\n max-width: 100%;\n }\n .glasshome-widget-status,\n .glasshome-widget-value {\n font-size: clamp(13px, 7cqmin, 17px);\n }\n .glasshome-widget-content [class*=\"ml-auto\"] {\n margin-left: 0 !important;\n }\n}\n.glasshome-widget-content [class*=\"ml-auto\"] {\n position: relative;\n z-index: 30;\n}\n\n/* === Slot: icon ===\n * ui's tinted icon pill (ICON_PILL_TINT): the disc is .glass, so the material shapes it. */\n.glasshome-widget-icon {\n width: var(--widget-icon-box);\n height: var(--widget-icon-box);\n border-radius: var(--radius-sm, 12px);\n --glass-tone: var(--widget-icon-color, var(--widget-color));\n --glass-rim: 0.4;\n --glass-wash: 18%;\n}\n.glasshome-widget-icon-glyph {\n font-size: var(--widget-icon-glyph);\n line-height: 1;\n}\n.glasshome-widget-icon-stack {\n --stack-step: calc(var(--widget-icon-box) * 0.12);\n position: absolute;\n /* The pill's border would otherwise shrink each plate and widen the first step. */\n inset: -1px;\n width: auto;\n height: auto;\n border-radius: var(--radius-sm, 12px);\n}\n.glasshome-widget-icon-stack-2 {\n translate: var(--stack-step) var(--stack-step);\n opacity: 0.45;\n z-index: -2;\n}\n.glasshome-widget-icon-stack-3 {\n translate: calc(2 * var(--stack-step)) calc(2 * var(--stack-step));\n opacity: 0.2;\n z-index: -3;\n}\n\n/* === Slot: text === */\n.glasshome-widget-title {\n font-size: var(--widget-title-size);\n line-height: 1.2;\n color: var(--color-muted-foreground, currentColor);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.glasshome-widget-subtitle {\n font-size: var(--widget-subtitle);\n color: var(--color-muted-foreground, currentColor);\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: linear-gradient(\n 135deg,\n color-mix(\n in srgb,\n var(--widget-color) calc(var(--widget-wash) * var(--material-tint, 1)),\n transparent\n ),\n color-mix(\n in srgb,\n var(--widget-color-to, var(--widget-color))\n calc(var(--widget-wash-2) * var(--material-tint, 1)),\n transparent\n )\n );\n /* Inset: the clip-path cuts any outer shadow. */\n box-shadow: inset 0 0 calc(var(--material-glow, 0px) * 1.2)\n oklch(from var(--widget-color) l c h / 0.55);\n clip-path: inset(0 calc((100 - var(--widget-fill-value)) * 1%) 0 0 round var(--widget-radius));\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 round var(--widget-radius));\n }\n}\n\n/* === Anatomy: Widget.Content with a Widget.Head lays its parts out as a tile === */\n.glasshome-widget-grid {\n position: relative;\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr);\n grid-template-rows: auto minmax(0, 1fr) auto;\n gap: var(--widget-grid-gap);\n height: 100%;\n padding: var(--widget-grid-pad);\n overflow: hidden;\n isolation: isolate;\n color: var(--foreground);\n}\n\n/* In-flow content spans the tile (the max-content first column would collapse it); absolute parts keep the padding box. */\n.glasshome-widget-grid\n > :where(:not(.glasshome-widget-backdrop, .glasshome-widget-layer, .glasshome-widget-glyph)) {\n grid-column: 1 / -1;\n}\n.glasshome-widget-layer {\n position: absolute;\n inset: 0;\n z-index: -1;\n overflow: hidden;\n}\n.glasshome-widget-head {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n min-width: 0;\n}\n.glasshome-widget-head-text {\n min-height: var(--widget-icon-box);\n justify-content: center;\n display: flex;\n flex-direction: column;\n gap: 1px;\n min-width: 0;\n flex: 1;\n}\n.glasshome-widget-head-aside {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: 6px;\n max-height: 26px;\n overflow: hidden;\n flex-shrink: 1;\n}\n.glasshome-widget-eyebrow {\n font-size: var(--widget-text-meta);\n font-weight: 500;\n line-height: 1.25;\n color: var(--muted-foreground);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.glasshome-widget-name {\n font-size: var(--widget-text-name);\n font-weight: 650;\n line-height: 1.2;\n letter-spacing: -0.01em;\n color: var(--foreground);\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n overflow: hidden;\n overflow-wrap: break-word;\n}\n\n.glasshome-widget-hero {\n display: flex;\n align-items: flex-end;\n justify-content: space-between;\n gap: var(--widget-grid-gap);\n min-height: 0;\n min-width: 0;\n}\n.glasshome-widget-reading {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n z-index: 1;\n}\n.glasshome-widget-reading-value {\n font-size: var(--widget-text-value);\n font-weight: 650;\n line-height: 1;\n letter-spacing: -0.03em;\n font-variant-numeric: tabular-nums;\n color: var(--foreground);\n white-space: nowrap;\n}\n/* Alternating names restart the rise on each change without rebuilding the text. */\n.glasshome-widget-reading-value[data-turn=\"a\"] {\n animation: tile-value-in-a var(--duration-morph) var(--ease-morph);\n}\n.glasshome-widget-reading-value[data-turn=\"b\"] {\n animation: tile-value-in-b var(--duration-morph) var(--ease-morph);\n}\n@keyframes tile-value-in-a {\n from {\n opacity: 0;\n transform: translateY(0.25em);\n }\n}\n@keyframes tile-value-in-b {\n from {\n opacity: 0;\n transform: translateY(0.25em);\n }\n}\n.glasshome-widget-unit {\n font-size: 0.5em;\n font-weight: 550;\n letter-spacing: 0;\n margin-left: 0.08em;\n color: var(--muted-foreground);\n}\n.glasshome-widget-sub {\n font-size: var(--widget-text-sub);\n font-weight: 500;\n color: var(--muted-foreground);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n/* Placed on the tile, not the hero, so a control row never moves or shrinks it. */\n.glasshome-widget-art {\n position: absolute;\n right: var(--widget-grid-pad);\n bottom: var(--widget-grid-pad);\n height: min(\n calc(100% - 2 * var(--widget-grid-pad) - var(--widget-icon-box) - var(--widget-grid-gap)),\n 48cqi\n );\n display: flex;\n align-items: flex-end;\n justify-content: flex-end;\n pointer-events: none;\n}\n/* Beside art a long word (\"Unlocked\") shrinks to end where the art starts, at about 50cqi; short values keep full size. */\n@container widget (min-width: 180px) and (min-height: 130px) {\n .glasshome-widget-hero[data-art] .glasshome-widget-reading-value {\n font-size: max(18px, min(var(--widget-text-value), calc(50cqi / (var(--value-len, 1) * 0.56))));\n }\n}\n.glasshome-widget-backdrop > img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n/* Art keeps its shape and clears a state word at its largest (15cqi, about 3.4em); each art sets --art-aspect. */\n.glasshome-widget-grid .glasshome-widget-art > * {\n height: min(\n 100%,\n calc((52cqi - 2 * var(--widget-grid-pad) - var(--widget-grid-gap)) / var(--art-aspect, 1))\n );\n flex-shrink: 0;\n}\n.glasshome-widget-grid .glasshome-widget-art > img {\n width: auto;\n height: auto;\n max-height: 100%;\n max-width: calc(52cqi - 2 * var(--widget-grid-pad) - var(--widget-grid-gap));\n}\n\n.glasshome-widget-controls {\n display: flex;\n gap: 8px;\n min-width: 0;\n}\n\n.glasshome-widget-glyph {\n display: none;\n position: absolute;\n right: -8%;\n bottom: -22%;\n font-size: min(130cqb, 80cqi);\n line-height: 1;\n color: var(--widget-color);\n opacity: 0.16;\n pointer-events: none;\n z-index: -1;\n}\n\n/* Controls sit on the bottom row beside the value, or under it on a narrow tile; the art lifts above them. */\n.glasshome-widget-head {\n grid-column: 1 / -1;\n}\n.glasshome-widget-hero {\n grid-row: 3;\n grid-column: 1 / -1;\n}\n.glasshome-widget-grid:has(> .glasshome-widget-controls) > .glasshome-widget-hero {\n grid-column: 1;\n}\n.glasshome-widget-controls {\n grid-row: 3;\n grid-column: 2;\n align-self: end;\n position: relative;\n z-index: 1;\n}\n.glasshome-widget-grid:has(> .glasshome-widget-hero[data-empty]) > .glasshome-widget-controls {\n grid-column: 1 / -1;\n}\n@container widget (max-width: 519px) {\n .glasshome-widget-grid:has(> .glasshome-widget-controls) {\n grid-template-rows: auto minmax(0, 1fr) auto auto;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) > .glasshome-widget-hero {\n grid-column: 1 / -1;\n }\n .glasshome-widget-controls {\n grid-row: 4;\n grid-column: 1 / -1;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) .glasshome-widget-art {\n bottom: calc(var(--widget-grid-pad) + var(--widget-control-h) + var(--widget-grid-gap));\n height: min(\n calc(\n 100% - 2 * var(--widget-grid-pad) - var(--widget-icon-box) - 2 * var(--widget-grid-gap) -\n var(--widget-control-h)\n ),\n 48cqi\n );\n }\n}\n@container widget (max-height: 229px) {\n .glasshome-widget-controls {\n display: none;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) {\n grid-template-rows: auto minmax(0, 1fr) auto;\n }\n .glasshome-widget-grid:has(> .glasshome-widget-controls) .glasshome-widget-art {\n bottom: var(--widget-grid-pad);\n height: min(\n calc(100% - 2 * var(--widget-grid-pad) - var(--widget-icon-box) - var(--widget-grid-gap)),\n 48cqi\n );\n }\n}\n@container widget (max-width: 179px) {\n .glasshome-widget-art {\n display: none;\n }\n}\n@container widget (max-height: 169px) {\n .glasshome-widget-head-aside {\n display: none;\n }\n}\n@container widget ((max-height: 129px) or (max-width: 139px)) {\n .glasshome-widget-head-text {\n min-height: 0;\n }\n .glasshome-widget-name {\n -webkit-line-clamp: 1;\n }\n .glasshome-widget-grid {\n display: flex;\n flex-direction: column;\n justify-content: space-between;\n gap: 2px;\n }\n .glasshome-widget-head-icon,\n .glasshome-widget-art,\n .glasshome-widget-eyebrow,\n .glasshome-widget-sub {\n display: none;\n }\n .glasshome-widget-glyph {\n display: block;\n }\n .glasshome-widget-reading-value {\n font-size: clamp(18px, min(34cqb, 20cqi), 30px);\n }\n .glasshome-widget-name {\n font-size: clamp(12px, min(18cqb, 11cqi), 15px);\n font-weight: 550;\n color: var(--muted-foreground);\n }\n}\n@container widget (max-height: 129px) and (min-width: 260px) {\n .glasshome-widget-grid {\n flex-direction: row;\n align-items: center;\n justify-content: space-between;\n gap: var(--widget-grid-gap);\n }\n .glasshome-widget-name {\n font-size: clamp(14px, 22cqb, 18px);\n font-weight: 650;\n color: var(--foreground);\n }\n .glasshome-widget-eyebrow {\n display: block;\n }\n}\n\n.glasshome-widget-backdrop {\n position: absolute;\n inset: 0;\n z-index: -2;\n overflow: hidden;\n}\n.glasshome-widget-backdrop > img.area-night {\n opacity: 0;\n transition: opacity 700ms ease;\n}\n.glasshome-widget-backdrop > img.area-night[data-visible] {\n opacity: 1;\n}\n.glasshome-widget-backdrop > .glasshome-widget-backdrop-light {\n position: absolute;\n inset: 0;\n background: radial-gradient(90% 80% at 25% 30%, oklch(0.8 0.14 70), transparent 70%);\n mix-blend-mode: soft-light;\n opacity: var(--backdrop-light, 0);\n transition: opacity 500ms ease;\n}\n.glasshome-widget-backdrop::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background: linear-gradient(\n to bottom,\n color-mix(in oklch, var(--card) 82%, transparent) 0%,\n color-mix(in oklch, var(--card) 40%, transparent) 22%,\n transparent 42%,\n transparent 62%,\n color-mix(in oklch, var(--card) 45%, transparent) 82%,\n color-mix(in oklch, var(--card) 80%, transparent) 100%\n );\n}\n\n@container widget (max-width: 239px) {\n .glasshome-widget-choice [data-optional] {\n display: none;\n }\n}\n@container widget (max-width: 259px) {\n .glasshome-widget-head-aside {\n display: none;\n }\n}\n@container widget (max-width: 139px) {\n .glasshome-widget-controls {\n display: none;\n }\n}\n\n.glasshome-widget-controls {\n justify-content: flex-start;\n align-items: center;\n}\n.glasshome-widget-control.glasshome-widget-control {\n width: var(--widget-control-h);\n height: var(--widget-control-h);\n}\n.glasshome-widget-choice .glasshome-widget-control.glasshome-widget-control {\n width: calc(var(--widget-control-h) - 8px);\n height: calc(var(--widget-control-h) - 8px);\n}\n@container widget (max-width: 179px) and (min-height: 130px) {\n .glasshome-widget-grid:not(:has(> .glasshome-widget-backdrop)) .glasshome-widget-head-text {\n min-height: 0;\n }\n .glasshome-widget-grid:not(:has(> .glasshome-widget-backdrop)) .glasshome-widget-head {\n flex-direction: column;\n gap: 6px;\n }\n}\n.glasshome-widget-unit[data-degree] {\n vertical-align: top;\n margin-left: 0.02em;\n color: var(--foreground);\n}\n.glasshome-widget-control-wide.glasshome-widget-control-wide {\n height: var(--widget-control-h);\n gap: 6px;\n padding: 0 14px;\n}\n@container widget (max-width: 179px) {\n .glasshome-widget-grid:has(> .glasshome-widget-backdrop) .glasshome-widget-head-icon {\n display: none;\n }\n}\n.glasshome-widget-grid:has(> .glasshome-widget-backdrop)\n :is(\n .glasshome-widget-reading-value,\n .glasshome-widget-sub,\n .glasshome-widget-name,\n .glasshome-widget-eyebrow\n ) {\n text-shadow: 0 1px 8px color-mix(in oklch, var(--card) 75%, transparent);\n}\n.glasshome-widget-grid:has(> .glasshome-widget-backdrop)\n :is(.glasshome-widget-sub, .glasshome-widget-eyebrow) {\n color: color-mix(in oklch, var(--foreground) 82%, transparent);\n}\n/* Control glyphs and words keep their share of a control as it grows past 4x4. */\n.glasshome-widget-grid\n :is(.glasshome-widget-control, .glasshome-widget-control-wide)\n :is(iconify-icon, svg) {\n font-size: calc(var(--widget-control-h) * 0.43);\n width: 1em;\n height: 1em;\n}\n:is(.glasshome-widget-controls, .glasshome-widget-control-wide) {\n font-size: max(13px, calc(var(--widget-control-h) * 0.3));\n}\n/* A big tile with no art of its own carries its icon large and faint, so the glass is not an empty box. */\n@container widget (min-width: 300px) and (min-height: 260px) {\n .glasshome-widget-grid:not(\n :has(> .glasshome-widget-backdrop),\n :has(> .glasshome-widget-hero[data-art], > .glasshome-widget-art, > .glasshome-widget-layer)\n )\n > .glasshome-widget-glyph {\n display: block;\n right: -3%;\n bottom: -12%;\n font-size: min(78cqb, 52cqi);\n opacity: 0.1;\n }\n}\n/* A one-shot action confirms on its control and on the glyph, the one thing a compact tile shows. */\n.glasshome-widget-grid > .glasshome-widget-glyph {\n transition: opacity var(--duration-state) var(--ease-morph);\n}\n.glasshome-widget[data-confirmed] .glasshome-widget-glyph {\n opacity: 0.5;\n}\n.glasshome-widget-grid\n [data-confirmed]:is(.glasshome-widget-control, .glasshome-widget-control-wide) {\n --glass-base: color-mix(in srgb, var(--widget-color) 35%, transparent);\n transition: background-color var(--duration-state) var(--ease-morph);\n}\n\n/* === Sheet: what a held tile opens (framework/panel) ===\n * Rendered in a popover or dialog outside the widget's shadow root, so every rule\n * the sheet needs lives here, never in a widget's own utilities. */\n.glasshome-sheet {\n display: flex;\n flex-direction: column;\n gap: 18px;\n color: var(--foreground);\n}\n.glasshome-sheet [data-slot=\"swatch-picker\"] {\n max-width: 360px;\n}\n.glasshome-panel-section {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.glasshome-panel-label {\n margin: 0;\n font-size: 13px;\n font-weight: 500;\n color: var(--muted-foreground);\n}\n.glasshome-panel-rows {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 8px;\n}\n.glasshome-panel-row-wrap {\n position: relative;\n min-width: 0;\n}\n.glasshome-panel-row {\n --widget-wash: 22%;\n --widget-wash-2: 11%;\n --widget-radius: var(--radius-lg, 16px);\n --widget-icon-box: 34px;\n --widget-icon-glyph: 19px;\n position: relative;\n width: 100%;\n height: 56px;\n justify-content: flex-start;\n gap: 10px;\n padding: 0 12px 0 10px;\n overflow: hidden;\n text-align: left;\n touch-action: pan-y;\n}\n.glasshome-panel-row-static {\n cursor: default;\n scale: none;\n}\n.glasshome-panel-row-fill {\n position: absolute;\n inset: 0;\n pointer-events: none;\n}\n.glasshome-panel-row-icon {\n position: relative;\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n}\n.glasshome-panel-row-icon-off {\n color: var(--muted-foreground);\n}\n.glasshome-panel-row-text {\n position: relative;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n.glasshome-panel-row-name,\n.glasshome-panel-row-state,\n.glasshome-panel-fact-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n/* A reading may take a second line rather than lose its end (\"3 Moderate\"). */\n.glasshome-panel-fact-value {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 2;\n}\n.glasshome-panel-row-name,\n.glasshome-panel-fact-value {\n font-size: 14px;\n font-weight: 500;\n color: var(--foreground);\n}\n.glasshome-panel-row-state,\n.glasshome-panel-fact-label {\n font-size: 12px;\n font-weight: 400;\n color: var(--muted-foreground);\n}\n.glasshome-panel-row-trailing {\n position: absolute;\n top: 50%;\n right: 10px;\n translate: 0 -50%;\n}\n.glasshome-panel-facts {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));\n row-gap: 10px;\n padding: 10px 4px 10px 0;\n overflow: hidden;\n border-radius: var(--radius-lg, 16px);\n --glass-base: color-mix(in srgb, var(--card) var(--material-clarity, 60%), transparent);\n backdrop-filter: blur(calc(var(--material-blur, 24px) * 0.6)) saturate(1.6);\n -webkit-backdrop-filter: blur(calc(var(--material-blur, 24px) * 0.6)) saturate(1.6);\n}\n/* Without blur a see-through strip is just see-through: it takes the card fill. */\n.reduce-blur .glasshome-panel-facts {\n --glass-base: var(--card);\n}\n.glasshome-panel-fact {\n display: flex;\n min-width: 0;\n align-items: center;\n gap: 8px;\n padding: 0 12px;\n box-shadow: -1px 0 0 color-mix(in oklch, var(--foreground) 10%, transparent);\n}\n.glasshome-panel-fact-icon {\n flex-shrink: 0;\n font-size: 18px;\n color: var(--muted-foreground);\n}\n\n/* Inset: the grid cell clips anything drawn outside the tile. */\n.glasshome-widget:focus-visible {\n outline: 2px solid var(--ring);\n outline-offset: -2px;\n}\n\n/* === Hold flood: the dock's hold fill (ui HOLD_MOTION), grown from the finger === */\n.glasshome-widget-hold-flood {\n position: absolute;\n translate: -50% -50%;\n border-radius: 9999px;\n background: color-mix(in oklch, var(--primary) 45%, transparent);\n pointer-events: none;\n scale: 0;\n opacity: 0;\n transition:\n scale var(--duration-state, 200ms) var(--ease-contract, ease-in),\n opacity var(--duration-state, 200ms) var(--ease-contract, ease-in);\n}\n.glasshome-widget-hold-flood[data-hold] {\n scale: 1;\n opacity: 1;\n transition-duration: var(--duration-morph, 400ms);\n transition-timing-function: linear;\n}\n.glasshome-widget-hold-flood[data-fired] {\n scale: 1;\n opacity: 0;\n}\n/* A row of ui buttons in a sheet (scenes, one-tap actions). */\n.glasshome-sheet-actions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n/* The sheet wears its tile: the tile's tone washed into the glass, its picture as a band behind\n the head that fades into the glass, so the tile reads as having grown open. */\n.glasshome-sheet-surface {\n --glass-wash: 20%;\n --glass-wash-2: 7%;\n --widget-icon-box: 48px;\n --widget-icon-glyph: 26px;\n}\n.glasshome-sheet-band {\n position: absolute;\n inset: 0 0 auto;\n height: 150px;\n overflow: hidden;\n pointer-events: none;\n mask-image: linear-gradient(180deg, #000 30%, transparent);\n}\n.glasshome-sheet-band > img {\n width: 100%;\n height: 100%;\n object-fit: cover;\n opacity: 0.55;\n}\n.glasshome-sheet-surface > [data-slot$=\"-header\"],\n.glasshome-sheet-surface > [data-slot$=\"-body\"] {\n position: relative;\n}\n.glasshome-sheet-icon {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n", er = !1, tr, nr = /* @__PURE__ */ new WeakSet();
|
|
710
710
|
function rr() {
|
|
711
711
|
return tr || (tr = new CSSStyleSheet(), tr.replaceSync($n.replace(/:root\b/g, ":host").replace(/^\.dark\b/gm, ":host(.dark)"))), tr;
|
|
712
712
|
}
|
|
@@ -1113,7 +1113,7 @@ function Nr(e) {
|
|
|
1113
1113
|
}
|
|
1114
1114
|
//#endregion
|
|
1115
1115
|
//#region src/framework/core/Widget.tsx
|
|
1116
|
-
var Pr = /*#__PURE__*/ B("<span class=glasshome-widget-hold-flood aria-hidden=true>"), Fr = /*#__PURE__*/ B("<div><div class=\"relative h-full w-full\">"), Ir = /*#__PURE__*/ B("<div class=\"glasshome-widget-loading pointer-events-none absolute inset-0 animate-pulse\">"), Lr = /*#__PURE__*/ B("<div class=\"flex h-full w-full flex-col items-center justify-center gap-2 text-center\">"), Rr = /*#__PURE__*/ B("<div class=\"flex items-center justify-center
|
|
1116
|
+
var Pr = /*#__PURE__*/ B("<span class=glasshome-widget-hold-flood aria-hidden=true>"), Fr = /*#__PURE__*/ B("<div><div class=\"relative h-full w-full\">"), Ir = /*#__PURE__*/ B("<div class=\"glasshome-widget-loading pointer-events-none absolute inset-0 animate-pulse\">"), Lr = /*#__PURE__*/ B("<div class=\"flex h-full w-full flex-col items-center justify-center gap-2 text-center\">"), Rr = /*#__PURE__*/ B("<div class=\"text-muted-foreground flex items-center justify-center\">"), zr = /*#__PURE__*/ B("<h3 class=\"text-foreground text-sm font-semibold\">"), Br = /*#__PURE__*/ B("<p class=\"text-muted-foreground text-xs\">");
|
|
1117
1117
|
function Vr(t) {
|
|
1118
1118
|
let n = A(W), r = xn(() => ({ hold: { action: () => (q.get(n ?? {}) ?? q.get(d))?.("edit") } }));
|
|
1119
1119
|
k(r.dispose);
|
|
@@ -1240,7 +1240,7 @@ function Vr(t) {
|
|
|
1240
1240
|
return P((t) => R(e, "z-index", K.OVERLAY)), e;
|
|
1241
1241
|
})();
|
|
1242
1242
|
})(), null), P((r) => {
|
|
1243
|
-
var i = X("glasshome-widget glass", "relative h-full w-full
|
|
1243
|
+
var i = X("glasshome-widget glass", "relative h-full w-full overflow-hidden rounded-xl select-none", f()?.class, t.class), a = m(), o = t.confirmed || void 0, s = h() ? 0 : void 0, c = K.CONTENT;
|
|
1244
1244
|
return i !== r.e && M(e, r.e = i), r.t = z(e, a, r.t), o !== r.a && L(e, "data-confirmed", r.a = o), s !== r.o && L(e, "tabindex", r.o = s), c !== r.i && R(n, "z-index", r.i = c), r;
|
|
1245
1245
|
}, {
|
|
1246
1246
|
e: void 0,
|
|
@@ -1687,7 +1687,7 @@ function vi(e) {
|
|
|
1687
1687
|
}
|
|
1688
1688
|
//#endregion
|
|
1689
1689
|
//#region src/framework/dialogs/debug-view.tsx
|
|
1690
|
-
var yi = /*#__PURE__*/ B("<pre class=\"max-h-64 overflow-auto p-3 font-mono text-
|
|
1690
|
+
var yi = /*#__PURE__*/ B("<pre class=\"text-muted-foreground max-h-64 overflow-auto p-3 font-mono text-xs leading-relaxed\">"), bi = /*#__PURE__*/ B("<span class=\"flex items-center gap-2\">"), xi = /*#__PURE__*/ B("<section class=\"flex min-w-0 flex-col gap-2\"><header class=\"flex items-center gap-3\"><div class=\"flex min-w-0 flex-1 flex-col\"></div></header><div class=\"flex flex-col gap-2\">"), Si = /*#__PURE__*/ B("<div class=\"flex flex-col\">"), Ci = /*#__PURE__*/ B("<pre class=\"text-muted-foreground max-h-96 overflow-auto p-3 font-mono text-xs leading-relaxed whitespace-pre-wrap\">");
|
|
1691
1691
|
function wi(e, t, n) {
|
|
1692
1692
|
return {
|
|
1693
1693
|
widgetConfig: e,
|
|
@@ -1773,7 +1773,7 @@ function Fi(e) {
|
|
|
1773
1773
|
},
|
|
1774
1774
|
get children() {
|
|
1775
1775
|
return [N(Se, {
|
|
1776
|
-
class: "
|
|
1776
|
+
class: "text-muted-foreground py-3 text-xs",
|
|
1777
1777
|
get children() {
|
|
1778
1778
|
var t = bi();
|
|
1779
1779
|
return F(t, () => e.label, null), F(t, N(w, {
|
|
@@ -2071,7 +2071,7 @@ function Ui(e, t) {
|
|
|
2071
2071
|
}
|
|
2072
2072
|
//#endregion
|
|
2073
2073
|
//#region src/framework/dialogs/WidgetDialog.tsx
|
|
2074
|
-
var Wi = /*#__PURE__*/ B("<svg xmlns=http://www.w3.org/2000/svg class=size-3.5 fill=none viewBox=\"0 0 24 24\"stroke=currentColor stroke-width=2><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"></path><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\">"), Gi = /*#__PURE__*/ B("<div class=\"
|
|
2074
|
+
var Wi = /*#__PURE__*/ B("<svg xmlns=http://www.w3.org/2000/svg class=size-3.5 fill=none viewBox=\"0 0 24 24\"stroke=currentColor stroke-width=2><path d=\"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7\"></path><path d=\"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z\">"), Gi = /*#__PURE__*/ B("<div class=\"bg-muted/30 rounded-lg p-2 text-center md:p-6\"><p class=\"text-muted-foreground text-sm\">No edit options available"), Ki = /*#__PURE__*/ B("<svg xmlns=http://www.w3.org/2000/svg class=size-3.5 fill=none viewBox=\"0 0 24 24\"stroke=currentColor stroke-width=2><line x1=4 y1=21 x2=4 y2=14></line><line x1=4 y1=10 x2=4 y2=3></line><line x1=12 y1=21 x2=12 y2=12></line><line x1=12 y1=8 x2=12 y2=3></line><line x1=20 y1=21 x2=20 y2=16></line><line x1=20 y1=12 x2=20 y2=3></line><line x1=1 y1=14 x2=7 y2=14></line><line x1=9 y1=8 x2=15 y2=8></line><line x1=17 y1=16 x2=23 y2=16>"), qi = /*#__PURE__*/ B("<svg xmlns=http://www.w3.org/2000/svg class=size-3.5 fill=none viewBox=\"0 0 24 24\"stroke=currentColor stroke-width=2><path d=\"m8 2 1.88 1.88\"></path><path d=\"M14.12 3.88 16 2\"></path><path d=\"M9 7.13v-1a3.003 3.003 0 1 1 6 0v1\"></path><path d=\"M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6\"></path><path d=\"M12 20v-9\"></path><path d=\"M6.53 9C4.6 8.8 3 7.1 3 5\"></path><path d=\"M6 13H2\"></path><path d=\"M3 21c0-2.1 1.7-3.9 3.8-4\"></path><path d=\"M20.97 5c0 2.1-1.6 3.8-3.5 4\"></path><path d=\"M22 13h-4\"></path><path d=\"M17.2 17c2.1.1 3.8 1.9 3.8 4\">"), Ji = /*#__PURE__*/ B("<div class=glasshome-sheet>"), Yi = /*#__PURE__*/ B("<div class=glasshome-sheet-band aria-hidden=true><img alt>"), Xi = /*#__PURE__*/ B("<span class=\"glasshome-widget-icon glass glass-tint glasshome-sheet-icon\">"), Zi = /*#__PURE__*/ B("<span class=\"inline-flex size-3.5 shrink-0 items-center\">"), Qi = (e) => Wt(e), $i = {
|
|
2075
2075
|
sm: "sm",
|
|
2076
2076
|
md: "md",
|
|
2077
2077
|
lg: "lg",
|
|
@@ -2107,13 +2107,17 @@ function ta(e) {
|
|
|
2107
2107
|
}, [f, p] = O(t.defaultTab ?? "controls"), m = () => t.activeTab ?? f(), h = (e) => {
|
|
2108
2108
|
t.onActiveTabChange ? t.onActiveTabChange(e) : p(e);
|
|
2109
2109
|
}, g = () => $i[t.maxWidth ?? "xl"], _ = () => {
|
|
2110
|
-
if (t.tabs) return t.tabs
|
|
2110
|
+
if (t.tabs) return t.tabs.map((e) => ({
|
|
2111
|
+
...e,
|
|
2112
|
+
icon: () => e.icon,
|
|
2113
|
+
content: () => e.content
|
|
2114
|
+
}));
|
|
2111
2115
|
let e = [];
|
|
2112
2116
|
return e.push({
|
|
2113
2117
|
id: "edit",
|
|
2114
2118
|
label: "Edit",
|
|
2115
|
-
icon: Wi(),
|
|
2116
|
-
content: t.editContent ?? (() => {
|
|
2119
|
+
icon: () => Wi(),
|
|
2120
|
+
content: () => t.editContent ?? (() => {
|
|
2117
2121
|
let e = t.SchemaForm, o = n() ? a() : void 0;
|
|
2118
2122
|
return !o || !e ? Gi() : N(e, {
|
|
2119
2123
|
schema: o,
|
|
@@ -2129,8 +2133,8 @@ function ta(e) {
|
|
|
2129
2133
|
}), e.push({
|
|
2130
2134
|
id: "controls",
|
|
2131
2135
|
label: "Controls",
|
|
2132
|
-
icon: Ki(),
|
|
2133
|
-
content: t.controlsContent ?? N(Be, { get children() {
|
|
2136
|
+
icon: () => Ki(),
|
|
2137
|
+
content: () => t.controlsContent ?? N(Be, { get children() {
|
|
2134
2138
|
return N(He, { get children() {
|
|
2135
2139
|
return [N(Ue, { children: "No controls" }), N(Ve, { children: "This widget is shown as is." })];
|
|
2136
2140
|
} });
|
|
@@ -2138,8 +2142,8 @@ function ta(e) {
|
|
|
2138
2142
|
}), e.push({
|
|
2139
2143
|
id: "debug",
|
|
2140
2144
|
label: "Debug",
|
|
2141
|
-
icon: qi(),
|
|
2142
|
-
content: t.debugContent ?? N(Vi, { get data() {
|
|
2145
|
+
icon: () => qi(),
|
|
2146
|
+
content: () => t.debugContent ?? N(Vi, { get data() {
|
|
2143
2147
|
return t.debugData;
|
|
2144
2148
|
} })
|
|
2145
2149
|
}), e;
|
|
@@ -2273,7 +2277,7 @@ function ta(e) {
|
|
|
2273
2277
|
get children() {
|
|
2274
2278
|
return [(() => {
|
|
2275
2279
|
var t = Zi();
|
|
2276
|
-
return F(t, () => e.icon), t;
|
|
2280
|
+
return F(t, () => e.icon()), t;
|
|
2277
2281
|
})(), I(() => e.label)];
|
|
2278
2282
|
}
|
|
2279
2283
|
})
|
|
@@ -2311,7 +2315,7 @@ function ta(e) {
|
|
|
2311
2315
|
return e.id;
|
|
2312
2316
|
},
|
|
2313
2317
|
get children() {
|
|
2314
|
-
return e.content;
|
|
2318
|
+
return e.content();
|
|
2315
2319
|
}
|
|
2316
2320
|
})
|
|
2317
2321
|
});
|
|
@@ -2774,17 +2778,31 @@ function Qa() {
|
|
|
2774
2778
|
//#region src/version.ts
|
|
2775
2779
|
var $a = {
|
|
2776
2780
|
name: "@glasshome/widget-sdk",
|
|
2777
|
-
version: "1.
|
|
2781
|
+
version: "1.19.0",
|
|
2778
2782
|
description: "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
2779
2783
|
keywords: [
|
|
2784
|
+
"dashboard",
|
|
2780
2785
|
"glasshome",
|
|
2781
|
-
"
|
|
2786
|
+
"home-assistant",
|
|
2782
2787
|
"sdk",
|
|
2788
|
+
"smart-home",
|
|
2783
2789
|
"solidjs",
|
|
2784
2790
|
"vite",
|
|
2785
|
-
"
|
|
2786
|
-
|
|
2787
|
-
|
|
2791
|
+
"widget"
|
|
2792
|
+
],
|
|
2793
|
+
homepage: "https://github.com/glasshome/widget-sdk#readme",
|
|
2794
|
+
bugs: { url: "https://github.com/glasshome/widget-sdk/issues" },
|
|
2795
|
+
license: "MIT",
|
|
2796
|
+
author: "GlassHome Labs",
|
|
2797
|
+
repository: {
|
|
2798
|
+
type: "git",
|
|
2799
|
+
url: "https://github.com/glasshome/widget-sdk.git"
|
|
2800
|
+
},
|
|
2801
|
+
files: [
|
|
2802
|
+
"dist",
|
|
2803
|
+
"preview",
|
|
2804
|
+
"tailwind-sources.css",
|
|
2805
|
+
"guide"
|
|
2788
2806
|
],
|
|
2789
2807
|
type: "module",
|
|
2790
2808
|
sideEffects: !1,
|
|
@@ -2819,11 +2837,7 @@ var $a = {
|
|
|
2819
2837
|
"./tailwind-sources": "./tailwind-sources.css",
|
|
2820
2838
|
"./package.json": "./package.json"
|
|
2821
2839
|
},
|
|
2822
|
-
|
|
2823
|
-
"dist",
|
|
2824
|
-
"preview",
|
|
2825
|
-
"tailwind-sources.css"
|
|
2826
|
-
],
|
|
2840
|
+
publishConfig: { access: "public" },
|
|
2827
2841
|
scripts: {
|
|
2828
2842
|
build: "vite build && tsc && bun scripts/check-host-singleton.ts",
|
|
2829
2843
|
dev: "vite build --watch",
|
|
@@ -2831,12 +2845,8 @@ var $a = {
|
|
|
2831
2845
|
"test:dom": "vitest run",
|
|
2832
2846
|
"test:dom:watch": "vitest",
|
|
2833
2847
|
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
|
|
2834
|
-
"check:dead": "fallow dead-code --fail-on-issues"
|
|
2835
|
-
|
|
2836
|
-
peerDependencies: {
|
|
2837
|
-
"@glasshome/sync-layer": "0.9.1",
|
|
2838
|
-
"@glasshome/ui": "1.21.1",
|
|
2839
|
-
"solid-js": "^1.9.11"
|
|
2848
|
+
"check:dead": "fallow dead-code --fail-on-issues",
|
|
2849
|
+
lint: "oxlint --type-aware . && oxfmt --check ."
|
|
2840
2850
|
},
|
|
2841
2851
|
dependencies: {
|
|
2842
2852
|
"@glasshome/widget-contract": "0.3.0",
|
|
@@ -2854,20 +2864,19 @@ var $a = {
|
|
|
2854
2864
|
"@types/bun": "^1.4.0",
|
|
2855
2865
|
"@types/node": "^25.2.2",
|
|
2856
2866
|
fallow: "3.17.0",
|
|
2867
|
+
oxfmt: "0.65.0",
|
|
2868
|
+
oxlint: "1.81.0",
|
|
2869
|
+
"oxlint-tsgolint": "7.0.2001",
|
|
2857
2870
|
typescript: "^7.0.2",
|
|
2858
2871
|
vite: "^8.1.0",
|
|
2859
2872
|
"vite-plugin-solid": "^2.11.12",
|
|
2860
2873
|
vitest: "^4.1.10"
|
|
2861
2874
|
},
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2875
|
+
peerDependencies: {
|
|
2876
|
+
"@glasshome/sync-layer": "0.9.1",
|
|
2877
|
+
"@glasshome/ui": "1.21.1",
|
|
2878
|
+
"solid-js": "^1.9.11"
|
|
2865
2879
|
},
|
|
2866
|
-
homepage: "https://github.com/glasshome/widget-sdk#readme",
|
|
2867
|
-
bugs: { url: "https://github.com/glasshome/widget-sdk/issues" },
|
|
2868
|
-
author: "GlassHome Labs",
|
|
2869
|
-
license: "MIT",
|
|
2870
|
-
publishConfig: { access: "public" },
|
|
2871
2880
|
engines: { node: ">=18" }
|
|
2872
2881
|
}.version;
|
|
2873
2882
|
//#endregion
|
package/guide/widgets.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# GlassHome widgets
|
|
2
|
+
|
|
3
|
+
Matches the installed `@glasshome/widget-sdk`. Wins over memory and older docs.
|
|
4
|
+
|
|
5
|
+
## Loop
|
|
6
|
+
|
|
7
|
+
1. `bun widget add --name <name>`: `src/<name>/` with a working sensor tile (`index.tsx`, `manifest.json`). Start from it.
|
|
8
|
+
2. Write it. `examples` in `defineWidget` (label, size, config) are what preview renders. Demo entity ids: `node_modules/@glasshome/sync-layer/dist/demo/ids.d.ts`; a wrong id renders the empty state. Missing a kind of entity: use a stand-in and config to show each state.
|
|
9
|
+
3. `bun widget build`: typecheck, bundle, validate. Fix every error before previewing.
|
|
10
|
+
4. `bun widget preview <name> --sizes grid`: every example at common tile sizes, light and dark, into `preview/sweep/` with a contact sheet each. Open the images and look. First run: `bun add -d playwright && bunx playwright install chromium`. A crash fails the render with its error.
|
|
11
|
+
- Narrow: `--sizes 150x156,340x242`, `--theme dark`, `--example 0`.
|
|
12
|
+
- Change state: `--config '<json>'`, `--service '<domain.service>|<entity_id>|<json>'`, `--at 2026-06-15T21:00:00`, `--click '<selector>'`.
|
|
13
|
+
- Inspect: `--eval '<expr>'` prints per render (`root` = shadow root).
|
|
14
|
+
5. Repeat 3-4 until 1×1, 2×2 and a big tile read right in both themes.
|
|
15
|
+
6. Live: `bun widget connect <dashboard-url>`.
|
|
16
|
+
7. Ship: `bun widget publish --name <name> --bump patch|minor|major`. Version lives in `manifest.json`.
|
|
17
|
+
|
|
18
|
+
## Imports
|
|
19
|
+
|
|
20
|
+
- Everything from `@glasshome/widget-sdk`, UI primitives included.
|
|
21
|
+
- Never `@glasshome/ui` (unchecked version, dead tile on mismatch) or `@glasshome/sync-layer` (second store). Keep `@glasshome/ui` installed; the build reads its CSS.
|
|
22
|
+
- Home Assistant only via SDK hooks (`useEntity`, `useEntities`, `useService`, `useToggle`, …). Declare every read/controlled domain in manifest capabilities.
|
|
23
|
+
- SDK too old for something in the docs: `bun widget upgrade`.
|
|
24
|
+
- Edit the inline `manifest` in `defineWidget`; build copies it into `manifest.json` (name, icon, sizes, capabilities, examples, schema). `version` and `sdkVersion` live only in `manifest.json`.
|
|
25
|
+
- Config: `defineConfig` + `field.*`. `field.entity(domain)` stores a one-item array: read `config.entityIds[0]`.
|
|
26
|
+
|
|
27
|
+
## Tile
|
|
28
|
+
|
|
29
|
+
Parts inside `Widget.Content`; they lay out and resize themselves. Never measure to lay out.
|
|
30
|
+
|
|
31
|
+
| Need | Part |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Colour | `tone` (`accent info success warning danger neutral`) or `color` on `Widget`; `accent` on `Widget.Content` for state colour |
|
|
34
|
+
| Icon, name, eyebrow, chips | `Widget.Head` (`active`, `count`, `aside`) |
|
|
35
|
+
| Big value, unit, art | `Widget.Hero` |
|
|
36
|
+
| Control row | `Widget.Controls` with `Widget.Stepper`, `Widget.Choice`, `Widget.Action` |
|
|
37
|
+
| Stateless action | `Widget.Action`; `useConfirm()` + `<Widget confirmed>` when the whole tile runs it |
|
|
38
|
+
| Small fact | `Widget.Chip` |
|
|
39
|
+
| Photo behind tile | `Widget.Backdrop` |
|
|
40
|
+
| Own full-bleed layer | `Widget.Layer` |
|
|
41
|
+
| Faint corner icon | `Widget.Glyph` |
|
|
42
|
+
| Controllable continuous value | `Widget.SliderFill` |
|
|
43
|
+
|
|
44
|
+
## Sizes
|
|
45
|
+
|
|
46
|
+
1×1 to 8×8. Under 130px tall or 140px wide the tile is compact: `eyebrow` and `sub` hide, so carry state in `tone` and the icon. Tokens, never px: `--widget-text-value`, `--widget-text-name`, `--widget-text-meta`, `--widget-text-sub`, `--widget-text-caption`, `--widget-control-h`, `--widget-grid-pad`, `--widget-grid-gap`, `--widget-icon-box`, `--widget-icon-glyph`. Layout by container query (`@[150px]:…`, `@container widget (…)`). What renders by `useWidgetDimensions()`, inside `<Widget>` only.
|
|
47
|
+
|
|
48
|
+
## Sheet
|
|
49
|
+
|
|
50
|
+
Render `<WidgetDialog>` next to `<Widget>`, spreading the dialog parts (`ResponsiveDialog*`, `Button`, `SchemaForm`, `Tabs*`, see the scaffold) and `useWidgetDialog().dialogProps`. Hold opens the sheet on its own once `sheet` is passed; no gesture wiring. Only what the tile lacks (group members, colours, modes, forecast). Pass `sheet` only when there is something: `{...(extras() ? { sheet: () => <MySheet /> } : {})}`. Build from `PanelSection`, `PanelRows`, `PanelRow`, `PanelEntityRow`, `PanelFacts`, `ToggleGroup`, `SwatchPicker`, `TemperatureBar`, `ColorDisc`. Never repeat the tile's value, name or art. Widget CSS does not reach it.
|
|
51
|
+
|
|
52
|
+
## Primitives
|
|
53
|
+
|
|
54
|
+
`Badge`, `Button`, `ButtonGroup`, `CountPill`, `Icon`, `Input`, `Label`, `Progress`, `SchemaForm`, `SectionIcon`, `SectionTitle`, `Select`, `Slider`, `Switch`, `Tabs`, `Toggle`, `ToggleGroup`, `Carousel`, `ColorWheel`, `ColorSlider`, `ResponsiveDialog` + parts. `Icon`, never `iconify-icon`. Never restyle with own classes.
|
|
55
|
+
|
|
56
|
+
## Rules
|
|
57
|
+
|
|
58
|
+
- No hand-rolling what the SDK has: sliders, steppers, dialogs, sheets, hold gestures, loading/error states.
|
|
59
|
+
- Theme vars only (`--foreground`, `--muted-foreground`, `--card`, `--primary`, `--border`, `--success`, `--warning`, `--destructive`, `--radius`). Never declare them on `:host`; no own colour/radius tokens; no `--color-*`.
|
|
60
|
+
- Own CSS = layout and bespoke art. No custom panel/row/chip/button chrome.
|
|
61
|
+
- Tailwind classes literal in source, never concatenated. `dark:` works; `isDark()` for the boolean.
|
|
62
|
+
- No dot status lamps. No thin vertical bars (read as a text caret). No all-caps. Separator ` · `.
|
|
63
|
+
- One icon per item, not per line. Icons, fills and pictures before text.
|
|
64
|
+
- Lead with the answer (value + unit, or a verdict); one small line explains it. Never show a value twice. States per device class (Open/Closed, Detected/Clear).
|
|
65
|
+
- Continuous values keep the full-tile slider; buttons beside it. A stepper never stands alone.
|
|
66
|
+
- Dialog content scrolls in `ResponsiveDialogBody`; no own `overflow`/`max-height` in a dialog.
|
|
67
|
+
- Groups: one tap brings all members to one state; locks never unlock in one tap; doors, gates, garage doors never join bulk actions.
|
|
68
|
+
- Readings update text in place; never rebuild DOM. `Index` for lists of readings. Over 20 s of changing demo data a widget adds and removes no DOM nodes.
|
|
69
|
+
- Everything sits on an edge; text left, values/actions right. One accent per view; defaults quiet. No card in a card.
|
|
70
|
+
- Motion: colours morph, never snap; time transitions with `--duration-state` / `--ease-morph` (zeroed under reduced motion). Decorative loops scale by `var(--motion-ambient, 0)`. Gate on `useReducedMotion()`; pause offscreen with `useIntersectionPause()`.
|
|
71
|
+
|
|
72
|
+
## More
|
|
73
|
+
|
|
74
|
+
Docs as markdown: https://glasshome.app/llms.txt, `https://glasshome.app/md/widgets/<page>` (`widget-sheets`, `widget-styling`, `widget-sdk`, `widget-api-reference`, `widget-capabilities`, `widget-previews`). Official widgets as worked examples: https://github.com/glasshome/widgets (`src/button`, `src/sensor`); where they differ from this guide, the guide wins.
|
package/package.json
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glasshome/widget-sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.0",
|
|
4
4
|
"description": "SDK for building GlassHome dashboard widgets with SolidJS",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"dashboard",
|
|
6
7
|
"glasshome",
|
|
7
|
-
"
|
|
8
|
+
"home-assistant",
|
|
8
9
|
"sdk",
|
|
10
|
+
"smart-home",
|
|
9
11
|
"solidjs",
|
|
10
12
|
"vite",
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
"widget"
|
|
14
|
+
],
|
|
15
|
+
"homepage": "https://github.com/glasshome/widget-sdk#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/glasshome/widget-sdk/issues"
|
|
18
|
+
},
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"author": "GlassHome Labs",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/glasshome/widget-sdk.git"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"preview",
|
|
28
|
+
"tailwind-sources.css",
|
|
29
|
+
"guide"
|
|
14
30
|
],
|
|
15
31
|
"type": "module",
|
|
16
32
|
"sideEffects": false,
|
|
@@ -45,11 +61,9 @@
|
|
|
45
61
|
"./tailwind-sources": "./tailwind-sources.css",
|
|
46
62
|
"./package.json": "./package.json"
|
|
47
63
|
},
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
|
|
51
|
-
"tailwind-sources.css"
|
|
52
|
-
],
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
},
|
|
53
67
|
"scripts": {
|
|
54
68
|
"build": "vite build && tsc && bun scripts/check-host-singleton.ts",
|
|
55
69
|
"dev": "vite build --watch",
|
|
@@ -57,12 +71,8 @@
|
|
|
57
71
|
"test:dom": "vitest run",
|
|
58
72
|
"test:dom:watch": "vitest",
|
|
59
73
|
"typecheck:test": "tsc --noEmit -p tsconfig.test.json",
|
|
60
|
-
"check:dead": "fallow dead-code --fail-on-issues"
|
|
61
|
-
|
|
62
|
-
"peerDependencies": {
|
|
63
|
-
"@glasshome/sync-layer": "0.9.1",
|
|
64
|
-
"@glasshome/ui": "1.21.1",
|
|
65
|
-
"solid-js": "^1.9.11"
|
|
74
|
+
"check:dead": "fallow dead-code --fail-on-issues",
|
|
75
|
+
"lint": "oxlint --type-aware . && oxfmt --check ."
|
|
66
76
|
},
|
|
67
77
|
"dependencies": {
|
|
68
78
|
"@glasshome/widget-contract": "0.3.0",
|
|
@@ -80,23 +90,18 @@
|
|
|
80
90
|
"@types/bun": "^1.4.0",
|
|
81
91
|
"@types/node": "^25.2.2",
|
|
82
92
|
"fallow": "3.17.0",
|
|
93
|
+
"oxfmt": "0.65.0",
|
|
94
|
+
"oxlint": "1.81.0",
|
|
95
|
+
"oxlint-tsgolint": "7.0.2001",
|
|
83
96
|
"typescript": "^7.0.2",
|
|
84
97
|
"vite": "^8.1.0",
|
|
85
98
|
"vite-plugin-solid": "^2.11.12",
|
|
86
99
|
"vitest": "^4.1.10"
|
|
87
100
|
},
|
|
88
|
-
"
|
|
89
|
-
"
|
|
90
|
-
"
|
|
91
|
-
|
|
92
|
-
"homepage": "https://github.com/glasshome/widget-sdk#readme",
|
|
93
|
-
"bugs": {
|
|
94
|
-
"url": "https://github.com/glasshome/widget-sdk/issues"
|
|
95
|
-
},
|
|
96
|
-
"author": "GlassHome Labs",
|
|
97
|
-
"license": "MIT",
|
|
98
|
-
"publishConfig": {
|
|
99
|
-
"access": "public"
|
|
101
|
+
"peerDependencies": {
|
|
102
|
+
"@glasshome/sync-layer": "0.9.1",
|
|
103
|
+
"@glasshome/ui": "1.21.1",
|
|
104
|
+
"solid-js": "^1.9.11"
|
|
100
105
|
},
|
|
101
106
|
"engines": {
|
|
102
107
|
"node": ">=18"
|
package/preview/preview.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!
|
|
1
|
+
<!doctype html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
@@ -34,11 +34,16 @@
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
body {
|
|
37
|
-
font-family:
|
|
37
|
+
font-family:
|
|
38
|
+
system-ui,
|
|
39
|
+
-apple-system,
|
|
40
|
+
sans-serif;
|
|
38
41
|
background: var(--color-background);
|
|
39
42
|
color: var(--color-foreground);
|
|
40
43
|
min-height: 100vh;
|
|
41
|
-
transition:
|
|
44
|
+
transition:
|
|
45
|
+
background 0.2s,
|
|
46
|
+
color 0.2s;
|
|
42
47
|
}
|
|
43
48
|
</style>
|
|
44
49
|
</head>
|