@hashsome/ui 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.
Files changed (192) hide show
  1. package/LICENSE +674 -0
  2. package/dist/.tsbuildinfo +1 -0
  3. package/dist/entities/action-button.d.ts +17 -0
  4. package/dist/entities/action-button.js +56 -0
  5. package/dist/entities/action-button.js.map +1 -0
  6. package/dist/entities/artwork-ring.d.ts +17 -0
  7. package/dist/entities/artwork-ring.js +89 -0
  8. package/dist/entities/artwork-ring.js.map +1 -0
  9. package/dist/entities/climate-tile.d.ts +20 -0
  10. package/dist/entities/climate-tile.js +98 -0
  11. package/dist/entities/climate-tile.js.map +1 -0
  12. package/dist/entities/light-color.d.ts +52 -0
  13. package/dist/entities/light-color.js +148 -0
  14. package/dist/entities/light-color.js.map +1 -0
  15. package/dist/entities/light-tile.d.ts +24 -0
  16. package/dist/entities/light-tile.js +139 -0
  17. package/dist/entities/light-tile.js.map +1 -0
  18. package/dist/entities/media-browser.d.ts +15 -0
  19. package/dist/entities/media-browser.js +67 -0
  20. package/dist/entities/media-browser.js.map +1 -0
  21. package/dist/entities/media-controls.d.ts +20 -0
  22. package/dist/entities/media-controls.js +53 -0
  23. package/dist/entities/media-controls.js.map +1 -0
  24. package/dist/entities/media-player-bar.d.ts +14 -0
  25. package/dist/entities/media-player-bar.js +78 -0
  26. package/dist/entities/media-player-bar.js.map +1 -0
  27. package/dist/entities/media-player-body.d.ts +16 -0
  28. package/dist/entities/media-player-body.js +18 -0
  29. package/dist/entities/media-player-body.js.map +1 -0
  30. package/dist/entities/media-player-column.d.ts +18 -0
  31. package/dist/entities/media-player-column.js +34 -0
  32. package/dist/entities/media-player-column.js.map +1 -0
  33. package/dist/entities/media-player-page.d.ts +12 -0
  34. package/dist/entities/media-player-page.js +13 -0
  35. package/dist/entities/media-player-page.js.map +1 -0
  36. package/dist/entities/media-progress.d.ts +7 -0
  37. package/dist/entities/media-progress.js +30 -0
  38. package/dist/entities/media-progress.js.map +1 -0
  39. package/dist/entities/media-seek.d.ts +9 -0
  40. package/dist/entities/media-seek.js +19 -0
  41. package/dist/entities/media-seek.js.map +1 -0
  42. package/dist/entities/nav-dock.d.ts +13 -0
  43. package/dist/entities/nav-dock.js +22 -0
  44. package/dist/entities/nav-dock.js.map +1 -0
  45. package/dist/entities/nav-rail.d.ts +19 -0
  46. package/dist/entities/nav-rail.js +18 -0
  47. package/dist/entities/nav-rail.js.map +1 -0
  48. package/dist/entities/now-playing.d.ts +20 -0
  49. package/dist/entities/now-playing.js +34 -0
  50. package/dist/entities/now-playing.js.map +1 -0
  51. package/dist/entities/scene-button.d.ts +13 -0
  52. package/dist/entities/scene-button.js +6 -0
  53. package/dist/entities/scene-button.js.map +1 -0
  54. package/dist/entities/seek-line.d.ts +9 -0
  55. package/dist/entities/seek-line.js +71 -0
  56. package/dist/entities/seek-line.js.map +1 -0
  57. package/dist/entities/sensor-readout.d.ts +28 -0
  58. package/dist/entities/sensor-readout.js +95 -0
  59. package/dist/entities/sensor-readout.js.map +1 -0
  60. package/dist/entities/top-bar.d.ts +68 -0
  61. package/dist/entities/top-bar.js +248 -0
  62. package/dist/entities/top-bar.js.map +1 -0
  63. package/dist/entity-handle.d.ts +21 -0
  64. package/dist/entity-handle.js +16 -0
  65. package/dist/entity-handle.js.map +1 -0
  66. package/dist/gallery/fixtures.d.ts +4 -0
  67. package/dist/gallery/fixtures.js +119 -0
  68. package/dist/gallery/fixtures.js.map +1 -0
  69. package/dist/gallery/gallery.d.ts +9 -0
  70. package/dist/gallery/gallery.js +355 -0
  71. package/dist/gallery/gallery.js.map +1 -0
  72. package/dist/gallery/index.d.ts +2 -0
  73. package/dist/gallery/index.js +2 -0
  74. package/dist/gallery/index.js.map +1 -0
  75. package/dist/gallery/props-data.d.ts +10 -0
  76. package/dist/gallery/props-data.js +718 -0
  77. package/dist/gallery/props-data.js.map +1 -0
  78. package/dist/hooks.d.ts +19 -0
  79. package/dist/hooks.js +53 -0
  80. package/dist/hooks.js.map +1 -0
  81. package/dist/icon-data.d.ts +6 -0
  82. package/dist/icon-data.js +59193 -0
  83. package/dist/icon-data.js.map +1 -0
  84. package/dist/icon.d.ts +9 -0
  85. package/dist/icon.js +28 -0
  86. package/dist/icon.js.map +1 -0
  87. package/dist/index.d.ts +30 -0
  88. package/dist/index.js +24 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/layout/cover.d.ts +5 -0
  91. package/dist/layout/cover.js +8 -0
  92. package/dist/layout/cover.js.map +1 -0
  93. package/dist/layout/date-time-field.d.ts +15 -0
  94. package/dist/layout/date-time-field.js +108 -0
  95. package/dist/layout/date-time-field.js.map +1 -0
  96. package/dist/layout/detail-provider.d.ts +22 -0
  97. package/dist/layout/detail-provider.js +26 -0
  98. package/dist/layout/detail-provider.js.map +1 -0
  99. package/dist/layout/drawer-controls.d.ts +46 -0
  100. package/dist/layout/drawer-controls.js +93 -0
  101. package/dist/layout/drawer-controls.js.map +1 -0
  102. package/dist/layout/energy-chart.d.ts +37 -0
  103. package/dist/layout/energy-chart.js +85 -0
  104. package/dist/layout/energy-chart.js.map +1 -0
  105. package/dist/layout/energy-usage.d.ts +6 -0
  106. package/dist/layout/energy-usage.js +18 -0
  107. package/dist/layout/energy-usage.js.map +1 -0
  108. package/dist/layout/entity-drawer.d.ts +15 -0
  109. package/dist/layout/entity-drawer.js +77 -0
  110. package/dist/layout/entity-drawer.js.map +1 -0
  111. package/dist/layout/fade-scroll.d.ts +14 -0
  112. package/dist/layout/fade-scroll.js +59 -0
  113. package/dist/layout/fade-scroll.js.map +1 -0
  114. package/dist/layout/grid.d.ts +9 -0
  115. package/dist/layout/grid.js +6 -0
  116. package/dist/layout/grid.js.map +1 -0
  117. package/dist/layout/history-section.d.ts +19 -0
  118. package/dist/layout/history-section.js +39 -0
  119. package/dist/layout/history-section.js.map +1 -0
  120. package/dist/layout/hold-progress.d.ts +2 -0
  121. package/dist/layout/hold-progress.js +9 -0
  122. package/dist/layout/hold-progress.js.map +1 -0
  123. package/dist/layout/icon-button-styles.d.ts +9 -0
  124. package/dist/layout/icon-button-styles.js +28 -0
  125. package/dist/layout/icon-button-styles.js.map +1 -0
  126. package/dist/layout/page.d.ts +17 -0
  127. package/dist/layout/page.js +29 -0
  128. package/dist/layout/page.js.map +1 -0
  129. package/dist/layout/plain-button.d.ts +11 -0
  130. package/dist/layout/plain-button.js +17 -0
  131. package/dist/layout/plain-button.js.map +1 -0
  132. package/dist/layout/room-header.d.ts +12 -0
  133. package/dist/layout/room-header.js +11 -0
  134. package/dist/layout/room-header.js.map +1 -0
  135. package/dist/layout/round-button.d.ts +13 -0
  136. package/dist/layout/round-button.js +19 -0
  137. package/dist/layout/round-button.js.map +1 -0
  138. package/dist/layout/sensor-history.d.ts +15 -0
  139. package/dist/layout/sensor-history.js +91 -0
  140. package/dist/layout/sensor-history.js.map +1 -0
  141. package/dist/layout/sensor-stats.d.ts +21 -0
  142. package/dist/layout/sensor-stats.js +79 -0
  143. package/dist/layout/sensor-stats.js.map +1 -0
  144. package/dist/layout/series-chart.d.ts +25 -0
  145. package/dist/layout/series-chart.js +46 -0
  146. package/dist/layout/series-chart.js.map +1 -0
  147. package/dist/layout/stepper.d.ts +10 -0
  148. package/dist/layout/stepper.js +12 -0
  149. package/dist/layout/stepper.js.map +1 -0
  150. package/dist/layout/swatch-row.d.ts +19 -0
  151. package/dist/layout/swatch-row.js +20 -0
  152. package/dist/layout/swatch-row.js.map +1 -0
  153. package/dist/layout/tile.d.ts +67 -0
  154. package/dist/layout/tile.js +207 -0
  155. package/dist/layout/tile.js.map +1 -0
  156. package/dist/layout/use-drawer.d.ts +33 -0
  157. package/dist/layout/use-drawer.js +34 -0
  158. package/dist/layout/use-drawer.js.map +1 -0
  159. package/dist/layout/use-hold.d.ts +21 -0
  160. package/dist/layout/use-hold.js +61 -0
  161. package/dist/layout/use-hold.js.map +1 -0
  162. package/dist/provider.d.ts +37 -0
  163. package/dist/provider.js +95 -0
  164. package/dist/provider.js.map +1 -0
  165. package/dist/status.d.ts +8 -0
  166. package/dist/status.js +27 -0
  167. package/dist/status.js.map +1 -0
  168. package/dist/theme/density.d.ts +16 -0
  169. package/dist/theme/density.js +7 -0
  170. package/dist/theme/density.js.map +1 -0
  171. package/dist/theme/global-styles.d.ts +5 -0
  172. package/dist/theme/global-styles.js +44 -0
  173. package/dist/theme/global-styles.js.map +1 -0
  174. package/dist/theme/index.d.ts +19 -0
  175. package/dist/theme/index.js +106 -0
  176. package/dist/theme/index.js.map +1 -0
  177. package/dist/theme/overrides.d.ts +28 -0
  178. package/dist/theme/overrides.js +24 -0
  179. package/dist/theme/overrides.js.map +1 -0
  180. package/dist/theme/tokens.d.ts +78 -0
  181. package/dist/theme/tokens.js +1 -0
  182. package/dist/theme/tokens.js.map +1 -0
  183. package/dist/theme/use-typography.d.ts +4 -0
  184. package/dist/theme/use-typography.js +9 -0
  185. package/dist/theme/use-typography.js.map +1 -0
  186. package/dist/use-entity-history.d.ts +12 -0
  187. package/dist/use-entity-history.js +43 -0
  188. package/dist/use-entity-history.js.map +1 -0
  189. package/dist/use-media-browser.d.ts +31 -0
  190. package/dist/use-media-browser.js +87 -0
  191. package/dist/use-media-browser.js.map +1 -0
  192. package/package.json +57 -0
@@ -0,0 +1,207 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { Box, Flex, Typography, useColorByKey } from 'e-prim';
4
+ import { AnimatePresence, motion } from 'motion/react';
5
+ import { useEffect, useMemo, useRef, useState, } from 'react';
6
+ import { Icon } from '../icon.js';
7
+ import { RoundButton } from './round-button.js';
8
+ import { statusLabels } from '../status.js';
9
+ import { useDrawer } from './use-drawer.js';
10
+ import { EnergyChart } from './energy-chart.js';
11
+ import { HistorySection } from './history-section.js';
12
+ const DRAG_THRESHOLD = 8;
13
+ const KEY_STEP = 0.05;
14
+ const HOLD_MS = 500;
15
+ const COLOR_TRANSITION = { duration: 0.28 };
16
+ const clamp01 = (value) => Math.min(1, Math.max(0, value));
17
+ const valueAt = (event) => {
18
+ const rect = event.currentTarget.getBoundingClientRect();
19
+ return rect.width > 0 ? clamp01((event.clientX - rect.left) / rect.width) : 0;
20
+ };
21
+ /** Base pill used by all entity tiles. Tap to press; hold (with `detail` set) to open the drawer. */
22
+ export function Tile({ label, icon, secondary, status = 'ready', active = false, feedback, onPress, fill, onFillChange, trailing, overlay, title, detail, kind, energy, history, }) {
23
+ const ready = status === 'ready';
24
+ const adjustable = fill !== undefined && onFillChange !== undefined && ready;
25
+ const holdable = (detail !== undefined || energy !== undefined || history !== undefined) && ready;
26
+ const drawerBody = useMemo(() => (_jsxs(_Fragment, { children: [detail, energy ? _jsx(EnergyChart, { ...energy }) : null, history ? _jsx(HistorySection, { entries: history }) : null] })), [detail, energy, history]);
27
+ const [drag, setDrag] = useState(null);
28
+ const [holding, setHolding] = useState(false);
29
+ const start = useRef(null);
30
+ const justDragged = useRef(false);
31
+ const holdFired = useRef(false);
32
+ const holdTimer = useRef(null);
33
+ const clearHoldTimer = () => {
34
+ if (holdTimer.current !== null) {
35
+ clearTimeout(holdTimer.current);
36
+ holdTimer.current = null;
37
+ }
38
+ setHolding(false);
39
+ };
40
+ // `clearHoldTimer` only touches refs and a state setter, so it is safe to run once on unmount.
41
+ // oxlint-disable-next-line react/exhaustive-effect-dependencies
42
+ useEffect(() => clearHoldTimer, []);
43
+ // `onFillChange` only requests the new value — the entity's actual `fill` doesn't update until
44
+ // the backend confirms it and a new prop flows back down, which is never in the same tick. If a
45
+ // drag/key-nudge cleared `drag` immediately, `shownFill` would fall through to the still-stale
46
+ // `fill` prop and visibly snap back to the pre-drag position until the round trip completes.
47
+ // Keeping `drag` set to the just-committed value instead, and only clearing it once `fill`
48
+ // actually catches up to match, makes the handoff from optimistic to real state seamless.
49
+ // Adjusting state during render (React's documented pattern for this, not an effect) so the
50
+ // clear-out and the `fill` update that triggered it land in the same commit, with no extra
51
+ // stale-`drag` frame in between.
52
+ if (drag !== null && fill !== undefined && Math.abs(fill - drag) < 0.005) {
53
+ setDrag(null);
54
+ }
55
+ const { open: openDrawer } = useDrawer({ icon, label, kind, body: drawerBody });
56
+ const onPointerDown = (event) => {
57
+ if (adjustable) {
58
+ start.current = { x: event.clientX, dragging: false };
59
+ event.currentTarget.setPointerCapture?.(event.pointerId);
60
+ }
61
+ if (holdable) {
62
+ holdFired.current = false;
63
+ setHolding(true);
64
+ holdTimer.current = setTimeout(() => {
65
+ holdFired.current = true;
66
+ clearHoldTimer();
67
+ start.current = null;
68
+ openDrawer();
69
+ }, HOLD_MS);
70
+ }
71
+ };
72
+ const onPointerMove = (event) => {
73
+ const s = start.current;
74
+ if (!s) {
75
+ return;
76
+ }
77
+ if (!s.dragging && Math.abs(event.clientX - s.x) > DRAG_THRESHOLD) {
78
+ s.dragging = true;
79
+ clearHoldTimer();
80
+ }
81
+ if (s.dragging) {
82
+ setDrag(valueAt(event));
83
+ }
84
+ };
85
+ const onPointerUp = (event) => {
86
+ clearHoldTimer();
87
+ const s = start.current;
88
+ start.current = null;
89
+ if (!s?.dragging) {
90
+ return;
91
+ }
92
+ justDragged.current = true;
93
+ const next = valueAt(event);
94
+ setDrag(next);
95
+ onFillChange?.(next);
96
+ };
97
+ const onPointerCancel = () => {
98
+ clearHoldTimer();
99
+ start.current = null;
100
+ setDrag(null);
101
+ };
102
+ const onKeyDown = (event) => {
103
+ if (!adjustable) {
104
+ return;
105
+ }
106
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
107
+ event.preventDefault();
108
+ const delta = event.key === 'ArrowRight' ? KEY_STEP : -KEY_STEP;
109
+ const next = clamp01(Math.round(((drag ?? fill ?? 0) + delta) * 100) / 100);
110
+ setDrag(next);
111
+ onFillChange?.(next);
112
+ }
113
+ };
114
+ const onClick = () => {
115
+ if (justDragged.current) {
116
+ justDragged.current = false;
117
+ return;
118
+ }
119
+ if (holdFired.current) {
120
+ holdFired.current = false;
121
+ return;
122
+ }
123
+ onPress?.();
124
+ };
125
+ const shownFill = drag ?? fill;
126
+ // `active` reflects the entity's current (pre-drag) on/off state — gating the fill bar on it
127
+ // alone hides it entirely while dragging an off light up to a brightness, since it doesn't turn
128
+ // on until the backend confirms. `drag !== null` keeps it visible for the live preview regardless.
129
+ const overlaid = overlay !== undefined;
130
+ const hasFill = shownFill !== undefined && ready && (active || drag !== null);
131
+ // Stays mounted (just transparent) while an overlay is open: unmounting it would replay the
132
+ // 0 → value grow animation on close, as if the light had just been turned on.
133
+ const showFill = hasFill && !overlaid;
134
+ // While actively dragging, show the live percentage instead of the (now stale) `secondary` prop
135
+ // — the entity's own state, and thus `secondary`, only catches up once `onFillChange` commits.
136
+ const liveSecondary = drag !== null ? `${Math.round(drag * 100)}%` : secondary;
137
+ const surface = useColorByKey('surface') ?? '';
138
+ const surfaceRaised = useColorByKey('surfaceRaised') ?? '';
139
+ const accent = useColorByKey('accent') ?? '';
140
+ const text = useColorByKey('text') ?? '';
141
+ const textMuted = useColorByKey('textMuted') ?? '';
142
+ const accentText = useColorByKey('accentText') ?? '';
143
+ const line = useColorByKey('line') ?? '';
144
+ const accented = active && ready;
145
+ const isDimmable = fill !== undefined;
146
+ // A dimmable tile never gets a solid accent-colored CARD — its own `fill` bar is the "how on"
147
+ // indicator instead, growing from neutral `surface`/`text` — unlike a solid on/off tile, whose
148
+ // whole card turns `accent`. `solidAccent` still gates `cardBg` (and the hold-progress wash
149
+ // below, which genuinely needs to pick a dark-on-orange vs light-on-neutral highlight) for that
150
+ // reason, but text/icon colors below do NOT follow it — seeing both a solid-accent tile and a
151
+ // dimmable one at 100% fill side by side (same visible solid-orange card either way) with two
152
+ // different text colors reads as a bug even though each one is "correct" for its own case.
153
+ // `text`/`line` read fine against `accent` in both themes (dark-on-orange and near-white-on-orange
154
+ // are each decent contrast) as well as against `surface`, where `accentText` (white) is only
155
+ // readable in dark mode and invisible in light — so every accented state just uses the normal
156
+ // ready-state color, never a separate white-on-accent one.
157
+ const solidAccent = accented && !isDimmable;
158
+ let cardBg = surface;
159
+ // The label keeps the neutral light `text` everywhere; only the status line (e.g. "100%"), which
160
+ // sits at the far right, goes near-black (`onAccent`) once the card behind it is orange — the
161
+ // muted `line` grey it normally uses is ~1.6:1 on orange. `fill`'s opacity scales with
162
+ // brightness, so it only flips once the fill is nearly solid (or the whole card is accent).
163
+ const wholeCardAccent = (solidAccent && !overlaid) || feedback === 'done';
164
+ const fillLevel = showFill ? (shownFill ?? 0) : 0;
165
+ const statusOnAccent = ready && (wholeCardAccent || fillLevel > 0.85);
166
+ const cardColor = ready ? text : textMuted;
167
+ if ((solidAccent && !overlaid) || feedback === 'done') {
168
+ cardBg = accent;
169
+ }
170
+ const iconBg = accented && !overlaid ? 'rgba(0, 0, 0, 0.25)' : surfaceRaised;
171
+ // The icon glyph sits inside that translucent badge, which self-adapts to whatever's behind it
172
+ // (the orange fill at a high `fill`, plain `surface` at a low one) — so it still needs the
173
+ // lighter `accentText` glyph once accented, unlike the flat label/secondary text above.
174
+ const iconColor = accented && !overlaid ? accentText : ready ? line : textMuted;
175
+ // The label always stays full brightness; only the icon glyph and this line dim when the tile is
176
+ // off (matches the converged mockup — `#726C6A`/`line`, not `textMuted`, which is reserved for
177
+ // the unavailable/unknown/missing/loading statuses below).
178
+ const dimColorKey = ready ? 'line' : 'textMuted';
179
+ const cardOpacity = !ready || feedback === 'pending' ? 0.7 : 1;
180
+ const iconBadge = icon ? (_jsx(Flex, { as: motion.span, align: "center", justify: "center", position: "relative", radius: "full", width: 32, height: 32, animate: { backgroundColor: iconBg, color: iconColor }, transition: COLOR_TRANSITION, css: { flex: 'none' }, children: _jsx(Icon, { name: icon, size: 16 }) })) : null;
181
+ return (_jsxs(Flex, { as: motion.div, radius: "card", position: "relative", align: "center", overflow: "hidden", minWidth: 0, "data-status": status, "data-active": accented, "data-solid-accent": solidAccent, "data-fill": fill !== undefined, "data-fill-visible": showFill, "data-overlay": overlay !== undefined, "data-pending": feedback === 'pending', "data-feedback": feedback === 'pending' ? undefined : feedback, animate: { backgroundColor: cardBg, color: cardColor, opacity: cardOpacity }, transition: COLOR_TRANSITION, css: ({ palette }) => feedback === 'error' ? { outline: `2px solid ${palette.danger}`, outlineOffset: -2 } : null, children: [hasFill ? (_jsx(Box, { as: motion.span, position: "absolute", background: "accent",
182
+ // Spans the whole card, including behind `trailing` (e.g. a color-capable light's
183
+ // picker button) — it's a sibling of the button and `trailing`, not nested inside the
184
+ // button, specifically so its width isn't capped at the button's own narrower bounds.
185
+ animate: { width: `${(shownFill ?? 0) * 100}%`, opacity: overlaid ? 0 : shownFill }, transition: drag !== null ? { duration: 0 } : COLOR_TRANSITION, css: { inset: '0 auto 0 0', pointerEvents: 'none' } })) : null, holdable && holding ? (_jsx(Box, { as: motion.span, position: "absolute", initial: { width: '0%' }, animate: { width: '100%' }, transition: { duration: HOLD_MS / 1000, ease: 'linear' }, height: 2, css: {
186
+ inset: 'auto auto 0 0',
187
+ pointerEvents: 'none',
188
+ // Visible against either background: a light wash normally, a dark wash on an
189
+ // accent-colored (non-dimmable-active) card, where a light or accent bar would
190
+ // disappear or clash.
191
+ backgroundColor: solidAccent ? 'rgba(0, 0, 0, 0.35)' : 'rgba(255, 255, 255, 0.55)',
192
+ } }, "hold-progress")) : null, overlay ? (_jsxs(Flex, { align: "center", gap: 2.5, minWidth: 0, grow: 1, position: "relative", pt: 2, pr: 2, pb: 2, pl: 2, css: { alignSelf: 'stretch' }, children: [iconBadge, _jsx(Flex, { grow: 1, minWidth: 0, justify: "center", align: "center", children: overlay({ openDetail: openDrawer }) })] })) : (_jsxs(Flex, { as: "button", type: "button", disabled: !ready || (!onPress && !adjustable && !holdable), onClick: onClick, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: onPointerUp, onPointerCancel: onPointerCancel, onKeyDown: onKeyDown, title: title, "aria-label": label, align: "center", gap: 2.5, minWidth: 0, grow: 1, position: "relative", cursor: ready ? 'pointer' : 'default', pt: 2, pr: 3.5, pb: 2, pl: 2, css: {
193
+ alignSelf: 'stretch',
194
+ background: 'none',
195
+ textAlign: 'left',
196
+ touchAction: 'pan-y',
197
+ WebkitTapHighlightColor: 'transparent',
198
+ '&:focus-visible': {
199
+ outline: `2px solid ${accent}`,
200
+ outlineOffset: -2,
201
+ borderRadius: 'inherit',
202
+ },
203
+ }, children: [iconBadge, _jsx(Typography, { variant: "label", as: "span", noWrap: true, textOverflow: "ellipsis", position: "relative", grow: 1, minWidth: 0, children: label }), !ready ? (_jsx(Typography, { variant: "secondary", as: "span", noWrap: true, textOverflow: "ellipsis", color: "textMuted", position: "relative", css: { flex: 'none' }, children: statusLabels[status] })) : liveSecondary ? (_jsx(Typography, { variant: "secondary", as: "span", noWrap: true, textOverflow: "ellipsis", color: statusOnAccent ? 'onAccent' : dimColorKey, position: "relative", css: { flex: 'none' }, children: liveSecondary })) : null] })), trailing ? (_jsx(Flex, { align: "center", gap: 2, pr: 2.5, position: "relative", children: trailing })) : null] }));
204
+ }
205
+ export function IconButton({ icon, label, onClick, disabled, active, pressed, primary, size, glyph, color, feedback, }) {
206
+ return (_jsx(RoundButton, { as: motion.button, ...(size !== undefined ? { size: size * 2 } : {}), ...(color ? { css: { '&&&': { color } } } : {}), whileTap: disabled ? {} : { scale: 0.88 }, transition: { duration: 0.12 }, "data-feedback": feedback, "aria-label": label, title: label, disabled: disabled, "data-active": active, "aria-pressed": pressed, "data-primary": primary, onClick: onClick, children: _jsx(AnimatePresence, { initial: false, mode: "popLayout", children: _jsx(Box, { as: motion.span, css: { display: 'grid' }, initial: { rotate: -90, opacity: 0, scale: 0.6 }, animate: { rotate: 0, opacity: 1, scale: 1 }, exit: { rotate: 90, opacity: 0, scale: 0.6 }, transition: { duration: 0.18 }, children: _jsx(Icon, { name: icon, ...(glyph !== undefined ? { size: glyph } : size !== undefined ? { size } : {}) }) }, icon) }) }));
207
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tile.js","sourceRoot":"","sources":["../../src/layout/tile.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { IconName } from '../icon-data.ts';
3
+ export declare function DrawerHeader({ icon, label, kind, }: {
4
+ icon?: IconName | undefined;
5
+ label: string;
6
+ kind?: string | undefined;
7
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
8
+ /** Opens the shared `EntityDrawer` for one owner (a tile, a readout) and keeps it live: `body` is
9
+ * a fresh element every render (its props can change while the drawer is open — including from an
10
+ * action taken inside it), but `open` only fires once, so an effect re-pushes the content for as
11
+ * long as this owner's drawer is the open one. */
12
+ export declare function useDrawer({ icon, label, kind, body, }: {
13
+ icon?: IconName | undefined;
14
+ label: string;
15
+ kind?: string | undefined;
16
+ body: ReactNode;
17
+ }): {
18
+ open: () => void;
19
+ openExpanded: () => void;
20
+ isOpen: boolean;
21
+ };
22
+ /** `useDrawer` for an owner that must not itself subscribe to the drawer's context. Re-pushing the
23
+ * body on every context change re-renders whoever called the hook; if that is the same component
24
+ * that built `body`, it makes a fresh element each time and loops. Hosting the hook in this child
25
+ * keeps `body`'s identity tied to the parent's own renders (entity updates), not the drawer's. */
26
+ export declare function DrawerTrigger({ icon, label, kind, body, children, }: {
27
+ icon?: IconName | undefined;
28
+ label: string;
29
+ kind?: string | undefined;
30
+ body: ReactNode;
31
+ /** Gets `open` (the side panel) and `openExpanded` (full size). */
32
+ children: (open: () => void, openExpanded: () => void) => ReactNode;
33
+ }): import("@emotion/react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
2
+ /** @jsxImportSource @emotion/react */
3
+ import { Flex, Typography } from 'e-prim';
4
+ import { useEffect, useId } from 'react';
5
+ import { Icon } from '../icon.js';
6
+ import { useDetail } from './detail-provider.js';
7
+ export function DrawerHeader({ icon, label, kind, }) {
8
+ return (_jsxs(Flex, { align: "center", gap: 2.5, children: [icon ? (_jsx(Flex, { align: "center", justify: "center", background: "surfaceRaised", color: "text", radius: "full", width: 34, height: 34, css: { flex: 'none' }, children: _jsx(Icon, { name: icon, size: 16 }) })) : null, _jsxs(Flex, { direction: "column", children: [_jsx(Typography, { as: "span", variant: "heading", children: label }), kind ? (_jsx(Typography, { as: "span", variant: "secondary", color: "textMuted", children: kind })) : null] })] }));
9
+ }
10
+ /** Opens the shared `EntityDrawer` for one owner (a tile, a readout) and keeps it live: `body` is
11
+ * a fresh element every render (its props can change while the drawer is open — including from an
12
+ * action taken inside it), but `open` only fires once, so an effect re-pushes the content for as
13
+ * long as this owner's drawer is the open one. */
14
+ export function useDrawer({ icon, label, kind, body, }) {
15
+ const id = useId();
16
+ const { detail, openDetail, updateDetail } = useDetail();
17
+ const isOpen = detail?.id === id;
18
+ useEffect(() => {
19
+ if (isOpen) {
20
+ updateDetail(id, _jsx(DrawerHeader, { icon: icon, label: label, kind: kind }), body);
21
+ }
22
+ }, [isOpen, id, icon, label, kind, body, updateDetail]);
23
+ const open = () => openDetail(id, _jsx(DrawerHeader, { icon: icon, label: label, kind: kind }), body);
24
+ const openExpanded = () => openDetail(id, _jsx(DrawerHeader, { icon: icon, label: label, kind: kind }), body, true);
25
+ return { open, openExpanded, isOpen };
26
+ }
27
+ /** `useDrawer` for an owner that must not itself subscribe to the drawer's context. Re-pushing the
28
+ * body on every context change re-renders whoever called the hook; if that is the same component
29
+ * that built `body`, it makes a fresh element each time and loops. Hosting the hook in this child
30
+ * keeps `body`'s identity tied to the parent's own renders (entity updates), not the drawer's. */
31
+ export function DrawerTrigger({ icon, label, kind, body, children, }) {
32
+ const { open, openExpanded } = useDrawer({ icon, label, kind, body });
33
+ return _jsx(_Fragment, { children: children(open, openExpanded) });
34
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-drawer.js","sourceRoot":"","sources":["../../src/layout/use-drawer.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { type PointerEvent } from 'react';
2
+ /** How long a press has to last to count as a hold. The same as a tile's. */
3
+ export declare const HOLD_MS = 500;
4
+ /** Calls `onHold` when a press on the element lasts `HOLD_MS`, ignoring presses that start on a
5
+ * button, slider, input or link inside it (those have their own meaning). `holding` is true while
6
+ * the press is being timed, for a progress cue. Spread `handlers` on the element. */
7
+ export declare function useHold(onHold: () => void, enabled: boolean): {
8
+ holding: boolean;
9
+ handlers: {
10
+ onPointerDown: (event: PointerEvent<HTMLElement>) => void;
11
+ onPointerMove: (event: PointerEvent<HTMLElement>) => void;
12
+ onPointerUp: () => void;
13
+ onPointerLeave: () => void;
14
+ onPointerCancel: () => void;
15
+ /** The click that ends a hold would otherwise also fire whatever it lands on. */
16
+ onClickCapture: (event: {
17
+ stopPropagation(): void;
18
+ preventDefault(): void;
19
+ }) => void;
20
+ };
21
+ };
@@ -0,0 +1,61 @@
1
+ import { useEffect, useRef, useState } from 'react';
2
+ /** How long a press has to last to count as a hold. The same as a tile's. */
3
+ export const HOLD_MS = 500;
4
+ /** A finger that moves further than this is scrolling or dragging, not holding. */
5
+ const MOVE_TOLERANCE = 10;
6
+ /** Calls `onHold` when a press on the element lasts `HOLD_MS`, ignoring presses that start on a
7
+ * button, slider, input or link inside it (those have their own meaning). `holding` is true while
8
+ * the press is being timed, for a progress cue. Spread `handlers` on the element. */
9
+ export function useHold(onHold, enabled) {
10
+ const [holding, setHolding] = useState(false);
11
+ const timer = useRef(undefined);
12
+ const origin = useRef(null);
13
+ const fired = useRef(false);
14
+ const latest = useRef(onHold);
15
+ useEffect(() => {
16
+ latest.current = onHold;
17
+ });
18
+ useEffect(() => () => clearTimeout(timer.current), []);
19
+ const cancel = () => {
20
+ clearTimeout(timer.current);
21
+ origin.current = null;
22
+ setHolding(false);
23
+ };
24
+ return {
25
+ holding,
26
+ handlers: {
27
+ onPointerDown: (event) => {
28
+ fired.current = false;
29
+ if (!enabled ||
30
+ event.target.closest('button, [role="button"], [role="slider"], input, a')) {
31
+ return;
32
+ }
33
+ origin.current = { x: event.clientX, y: event.clientY };
34
+ setHolding(true);
35
+ timer.current = setTimeout(() => {
36
+ fired.current = true;
37
+ cancel();
38
+ latest.current();
39
+ }, HOLD_MS);
40
+ },
41
+ onPointerMove: (event) => {
42
+ const start = origin.current;
43
+ if (start &&
44
+ Math.hypot(event.clientX - start.x, event.clientY - start.y) > MOVE_TOLERANCE) {
45
+ cancel();
46
+ }
47
+ },
48
+ onPointerUp: cancel,
49
+ onPointerLeave: cancel,
50
+ onPointerCancel: cancel,
51
+ /** The click that ends a hold would otherwise also fire whatever it lands on. */
52
+ onClickCapture: (event) => {
53
+ if (fired.current) {
54
+ fired.current = false;
55
+ event.preventDefault();
56
+ event.stopPropagation();
57
+ }
58
+ },
59
+ },
60
+ };
61
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-hold.js","sourceRoot":"","sources":["../../src/layout/use-hold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAqB,MAAM,OAAO,CAAC;AAEvE,6EAA6E;AAC7E,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,CAAC;AAE3B,mFAAmF;AACnF,MAAM,cAAc,GAAG,EAAE,CAAC;AAE1B;;qFAEqF;AACrF,MAAM,UAAU,OAAO,CAAC,MAAkB,EAAE,OAAgB;IAC1D,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,CAA4C,SAAS,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,CAAkC,IAAI,CAAC,CAAC;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;IAEvD,MAAM,MAAM,GAAG,GAAG,EAAE;QAClB,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5B,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,UAAU,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,OAAO;QACL,OAAO;QACP,QAAQ,EAAE;YACR,aAAa,EAAE,CAAC,KAAgC,EAAE,EAAE;gBAClD,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;gBACtB,IACE,CAAC,OAAO;oBACP,KAAK,CAAC,MAAsB,CAAC,OAAO,CACnC,oDAAoD,CACrD,EACD,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;gBACxD,UAAU,CAAC,IAAI,CAAC,CAAC;gBACjB,KAAK,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC9B,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;oBACrB,MAAM,EAAE,CAAC;oBACT,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,CAAC,EAAE,OAAO,CAAC,CAAC;YACd,CAAC;YACD,aAAa,EAAE,CAAC,KAAgC,EAAE,EAAE;gBAClD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC;gBAC7B,IACE,KAAK;oBACL,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,cAAc,EAC7E,CAAC;oBACD,MAAM,EAAE,CAAC;gBACX,CAAC;YACH,CAAC;YACD,WAAW,EAAE,MAAM;YACnB,cAAc,EAAE,MAAM;YACtB,eAAe,EAAE,MAAM;YAEvB,iFAAiF;YACjF,cAAc,EAAE,CAAC,KAA0D,EAAE,EAAE;gBAC7E,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;oBAClB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;oBACtB,KAAK,CAAC,cAAc,EAAE,CAAC;oBACvB,KAAK,CAAC,eAAe,EAAE,CAAC;gBAC1B,CAAC;YACH,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,37 @@
1
+ import { type Client, type RemoteClientOptions } from '@hashsome/core';
2
+ import { type ReactNode } from 'react';
3
+ import { type Density } from './theme/density.ts';
4
+ import { type ThemeOverrides } from './theme/overrides.ts';
5
+ export type ThemeMode = 'light' | 'dark' | 'system';
6
+ export interface HashsomeProviderProps {
7
+ /** Runtime WebSocket URL. Defaults to `/ws` on the current origin. */
8
+ url?: string;
9
+ /** Provide a ready-made client (gallery, tests). */
10
+ client?: Client;
11
+ /** Options for the default `RemoteClient` (reconnect timing, a custom socket factory). Ignored when `client` is given. */
12
+ clientOptions?: Omit<RemoteClientOptions, 'url'>;
13
+ /** `'system'` follows the browser/OS preference and updates live. Default `'dark'`. A `useThemeToggle()` caller (e.g. `NavRail`'s dev toggle) can still override this at runtime. */
14
+ theme?: ThemeMode;
15
+ /** Any Google Fonts family name (e.g. `'Inter'`, `'Roboto'`, `'Poppins'`), loaded dynamically. Default `'Inter'`. */
16
+ font?: string;
17
+ /** `'compact'` for small square displays: tighter spacing, shorter tiles, smaller icon circles. Default `'comfortable'`. */
18
+ density?: Density;
19
+ /** Partial changes to the built-in theme — colors per light/dark, radii, typography, spacing, density sizes. Pass a constant defined outside the component: a new object each render rebuilds the theme each render. */
20
+ overrides?: ThemeOverrides;
21
+ /** The app. */
22
+ children: ReactNode;
23
+ }
24
+ export interface ThemeModeState {
25
+ /** The theme actually in effect right now — never `'system'`, already resolved against the
26
+ * live OS preference and any `useThemeToggle()` override. */
27
+ resolved: 'light' | 'dark';
28
+ /** Flips between light and dark, overriding the configured `theme` (including `'system'`) until
29
+ * the page reloads. */
30
+ toggle: () => void;
31
+ }
32
+ /** Reads the live light/dark state and a way to flip it — e.g. a dev-only toggle in `NavRail`.
33
+ * Must be rendered inside `<HashsomeProvider>`. */
34
+ export declare function useThemeToggle(): ThemeModeState;
35
+ /** Connects the tree to the runtime proxy. Render only on the client. */
36
+ export declare function HashsomeProvider({ url, client, clientOptions, theme, font, density, overrides, children, }: HashsomeProviderProps): import("react").JSX.Element;
37
+ export declare function useClient(): Client;
@@ -0,0 +1,95 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { RemoteClient } from '@hashsome/core';
3
+ import { Global, ThemeProvider as EmotionThemeProvider } from '@emotion/react';
4
+ import { ThemeProvider } from 'e-prim';
5
+ import { createContext, useContext, useEffect, useMemo, useState } from 'react';
6
+ import { DetailProvider } from './layout/detail-provider.js';
7
+ import { EntityDrawer } from './layout/entity-drawer.js';
8
+ import { UNITS_PER_SPACE } from './theme/density.js';
9
+ import { globalStyles } from './theme/global-styles.js';
10
+ import { applyThemeOverrides, densityTokens } from './theme/overrides.js';
11
+ import { DEFAULT_FONT, darkTheme, googleFontHref, lightTheme, withFontFamily, } from './theme/index.js';
12
+ const HashsomeContext = createContext(null);
13
+ /** Loads a Google Fonts family at runtime via a single `<link>` this hook owns and reuses (keyed
14
+ * by a `data-hashsome-font` marker, not the family, so switching fonts updates it in place instead of
15
+ * accumulating stale `<link>` tags). A no-op outside the browser (SSR has no `document`). */
16
+ function useGoogleFont(family) {
17
+ useEffect(() => {
18
+ if (typeof document === 'undefined') {
19
+ return;
20
+ }
21
+ let link = document.querySelector('link[data-hashsome-font]');
22
+ if (!link) {
23
+ link = document.createElement('link');
24
+ link.rel = 'stylesheet';
25
+ link.dataset.hashsomeFont = '';
26
+ document.head.appendChild(link);
27
+ }
28
+ link.href = googleFontHref(family);
29
+ }, [family]);
30
+ }
31
+ function defaultUrl() {
32
+ const { protocol, host } = window.location;
33
+ return `${protocol === 'https:' ? 'wss:' : 'ws:'}//${host}/ws`;
34
+ }
35
+ // jsdom (unit tests) has no `matchMedia` — guarded so only `theme="system"` ever touches it, and
36
+ // even then it degrades to `false` instead of throwing if a test environment lacks it too.
37
+ function systemPrefersDark() {
38
+ return (typeof window !== 'undefined' &&
39
+ typeof window.matchMedia === 'function' &&
40
+ window.matchMedia('(prefers-color-scheme: dark)').matches);
41
+ }
42
+ const ThemeModeContext = createContext(null);
43
+ /** Reads the live light/dark state and a way to flip it — e.g. a dev-only toggle in `NavRail`.
44
+ * Must be rendered inside `<HashsomeProvider>`. */
45
+ export function useThemeToggle() {
46
+ const value = useContext(ThemeModeContext);
47
+ if (!value) {
48
+ throw new Error('useThemeToggle() must be used inside <HashsomeProvider>');
49
+ }
50
+ return value;
51
+ }
52
+ /** Resolves `'system'` against the live OS/browser preference, updating if it changes while open —
53
+ * a kiosk tablet left running overnight should follow a scheduled OS-level dark mode, for example.
54
+ * `override` (set via `useThemeToggle().toggle()`) wins over both until the page reloads. */
55
+ function useThemeMode(mode) {
56
+ const [systemDark, setSystemDark] = useState(() => mode === 'system' && systemPrefersDark());
57
+ const [override, setOverride] = useState(null);
58
+ useEffect(() => {
59
+ if (mode !== 'system' || typeof window.matchMedia !== 'function') {
60
+ return;
61
+ }
62
+ const query = window.matchMedia('(prefers-color-scheme: dark)');
63
+ const onChange = () => setSystemDark(query.matches);
64
+ query.addEventListener('change', onChange);
65
+ return () => query.removeEventListener('change', onChange);
66
+ }, [mode]);
67
+ const configured = mode === 'light' ? 'light' : mode === 'dark' ? 'dark' : systemDark ? 'dark' : 'light';
68
+ const resolved = override ?? configured;
69
+ return { resolved, toggle: () => setOverride(resolved === 'dark' ? 'light' : 'dark') };
70
+ }
71
+ /** Connects the tree to the runtime proxy. Render only on the client. */
72
+ export function HashsomeProvider({ url, client, clientOptions, theme = 'dark', font = DEFAULT_FONT, density = 'comfortable', overrides, children, }) {
73
+ const instance = useMemo(() => client ?? new RemoteClient({ url: url ?? defaultUrl(), ...clientOptions }), [client, url, clientOptions]);
74
+ const themeMode = useThemeMode(theme);
75
+ // Memoized: Emotion recomputes the merged theme (and every `css` prop) when the function changes.
76
+ const withDensity = useMemo(() => (outer) => ({ ...outer, density: densityTokens(density, overrides) }), [density, overrides]);
77
+ useGoogleFont(font);
78
+ const resolvedTheme = useMemo(() => ({
79
+ ...withFontFamily(applyThemeOverrides(themeMode.resolved === 'dark' ? darkTheme : lightTheme, themeMode.resolved, overrides), font),
80
+ // The spacing unit follows density, so `gap={3}` is always one space and the rest scale.
81
+ spacing: densityTokens(density, overrides).space / UNITS_PER_SPACE,
82
+ }), [themeMode.resolved, font, overrides, density]);
83
+ useEffect(() => {
84
+ instance.connect();
85
+ return () => instance.close();
86
+ }, [instance]);
87
+ return (_jsx(HashsomeContext.Provider, { value: instance, children: _jsx(ThemeModeContext.Provider, { value: themeMode, children: _jsx(ThemeProvider, { theme: resolvedTheme, children: _jsxs(EmotionThemeProvider, { theme: withDensity, children: [_jsx(Global, { styles: globalStyles }), _jsxs(DetailProvider, { children: [children, _jsx(EntityDrawer, {})] })] }) }) }) }));
88
+ }
89
+ export function useClient() {
90
+ const client = useContext(HashsomeContext);
91
+ if (!client) {
92
+ throw new Error('Hashsome hooks must be used inside <HashsomeProvider>');
93
+ }
94
+ return client;
95
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Entity } from '@hashsome/core';
2
+ /** Uniform lifecycle of an entity as seen by components. */
3
+ export type EntityStatus = 'loading' | 'missing' | 'unsupported' | 'unavailable' | 'unknown' | 'ready';
4
+ /** `undefined` = still loading, `null` = no such entity. */
5
+ export declare function entityStatus(entity: Entity | null | undefined): EntityStatus;
6
+ export declare const statusLabels: Record<Exclude<EntityStatus, 'ready'>, string>;
7
+ /** Capitalizes just the first character; a no-op on text that's already capitalized. */
8
+ export declare function capitalize(text: string): string;
package/dist/status.js ADDED
@@ -0,0 +1,27 @@
1
+ /** `undefined` = still loading, `null` = no such entity. */
2
+ export function entityStatus(entity) {
3
+ if (entity === undefined) {
4
+ return 'loading';
5
+ }
6
+ if (entity === null) {
7
+ return 'missing';
8
+ }
9
+ if (entity.availability === 'unavailable') {
10
+ return 'unavailable';
11
+ }
12
+ if (entity.availability === 'unknown') {
13
+ return 'unknown';
14
+ }
15
+ return 'ready';
16
+ }
17
+ export const statusLabels = {
18
+ loading: '…',
19
+ missing: 'Not found',
20
+ unsupported: 'Unsupported',
21
+ unavailable: 'Unavailable',
22
+ unknown: 'Unknown',
23
+ };
24
+ /** Capitalizes just the first character; a no-op on text that's already capitalized. */
25
+ export function capitalize(text) {
26
+ return text.length > 0 ? text.charAt(0).toUpperCase() + text.slice(1) : text;
27
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAWA,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAAC,MAAiC;IAC5D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,KAAK,aAAa,EAAE,CAAC;QAC1C,OAAO,aAAa,CAAC;IACvB,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAmD;IAC1E,OAAO,EAAE,GAAG;IACZ,OAAO,EAAE,WAAW;IACpB,WAAW,EAAE,aAAa;IAC1B,WAAW,EAAE,aAAa;IAC1B,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF,wFAAwF;AACxF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/E,CAAC"}
@@ -0,0 +1,16 @@
1
+ export type Density = 'comfortable' | 'compact';
2
+ /** How many of the theme's spacing units make one `space`: `gap={3}` is one space. */
3
+ export declare const UNITS_PER_SPACE = 3;
4
+ /** Sizes that scale with how much room a display has, in px. Spacing follows it on its own: the
5
+ * theme's spacing unit is `space / 3`, so e-prim's `gap={3}`, `p={3}` and friends are one space,
6
+ * and every other spacing prop scales with it. The rest are read from the theme in a `css` prop:
7
+ * `css={({ density }) => ({ height: density.tileHeight })}`. */
8
+ export interface DensityTokens {
9
+ /** The gap between tiles, in px: three spacing units, so `gap={3}`. Changing it rescales all spacing. */
10
+ space: number;
11
+ tileHeight: number;
12
+ iconSize: number;
13
+ /** The diameter of the round icon buttons and media art. */
14
+ iconCircle: number;
15
+ }
16
+ export declare const DENSITY: Record<Density, DensityTokens>;
@@ -0,0 +1,7 @@
1
+ /** How many of the theme's spacing units make one `space`: `gap={3}` is one space. */
2
+ export const UNITS_PER_SPACE = 3;
3
+ export const DENSITY = {
4
+ comfortable: { space: 12, tileHeight: 68, iconSize: 24, iconCircle: 44 },
5
+ // Small square displays (e.g. a Shelly wall display).
6
+ compact: { space: 8, tileHeight: 52, iconSize: 20, iconCircle: 36 },
7
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"density.js","sourceRoot":"","sources":["../../src/theme/density.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAgBjC,MAAM,CAAC,MAAM,OAAO,GAAmC;IACrD,WAAW,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;IACxE,sDAAsD;IACtD,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;CACpE,CAAC"}