prop-for-that 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,328 @@
1
+ import { S as Source } from './types-BYVnuPvd.js';
2
+
3
+ /**
4
+ * `--live-scroll-velocity` (signed px/frame, decays toward 0 when idle),
5
+ * `--live-scroll-direction` (1 down / -1 up / 0).
6
+ *
7
+ * Idles when the page isn't scrolling: a scroll event wakes a per-frame sampler
8
+ * that decays the velocity and then stops itself once it reaches 0.
9
+ */
10
+ declare const scrollVelocity: Source;
11
+
12
+ /** `--live-online` (0/1) */
13
+ declare const online: Source;
14
+
15
+ /**
16
+ * `--live-page-focused` (1/0) — whether the document currently has focus: the
17
+ * tab is frontmost *and* its window is focused. Goes `0` when the user switches
18
+ * to another tab, window, or app, and back to `1` on return. Driven by window
19
+ * `focus`/`blur`, seeded from `document.hasFocus()`.
20
+ *
21
+ * Note: a blurred-but-still-visible window (e.g. clicking another app side by
22
+ * side) keeps rAF alive, so the `0` flushes and CSS can react. Switching tabs
23
+ * pauses rAF, so the change applies on return — invisible while hidden anyway.
24
+ */
25
+ declare const pageFocused: Source;
26
+
27
+ /**
28
+ * `--live-page-visible` (1/0) — whether the tab is currently visible
29
+ * (foreground) versus hidden (backgrounded, minimized, or switched away).
30
+ *
31
+ * Distinct from `page-focused`: a *visible* tab can still be *unfocused* (another
32
+ * window holds focus while this one stays on screen). Visibility gates the things
33
+ * that should pause when truly hidden — video, polling, expensive effects — where
34
+ * focus gates the things that react to "is the user looking here right now."
35
+ * Driven by `visibilitychange`, seeded from `document.visibilityState`.
36
+ */
37
+ declare const pageVisible: Source;
38
+
39
+ /**
40
+ * `--const-nav-type` — how the user arrived at this page, read once from
41
+ * `performance.getEntriesByType('navigation')[0].type`:
42
+ *
43
+ * - `navigate` — a fresh navigation (typed URL, link, form, or script)
44
+ * - `reload` — the page was reloaded
45
+ * - `back_forward` — history traversal (Back/Forward, often a bfcache restore)
46
+ * - `prerender` — the page was prerendered
47
+ *
48
+ * Written once as a string on the `const` cadence, so it pairs with style queries
49
+ * — `@container style(--const-nav-type: reload) { … }` — to branch styling on how
50
+ * the visit started. Falls back to `navigate` where Navigation Timing is absent.
51
+ *
52
+ * Typed (`configure({ typed: true })`) registers it as a `<custom-ident>` so the
53
+ * string survives `@property`. Note it rides the batched writer like any plugin,
54
+ * so it lands the frame *after* bind, not before first paint — for a FOUC-safe,
55
+ * pre-paint constant, read the same value synchronously in a `<head>` script.
56
+ */
57
+ declare const navType: Source;
58
+
59
+ /**
60
+ * `--live-net-downlink`, `--live-net-rtt`, `--live-net-save-data` (0/1),
61
+ * `--live-net-type` (slow-2g=1, 2g=2, 3g=3, 4g=4, else 0).
62
+ */
63
+ declare const network: Source;
64
+
65
+ /** `--live-battery-level` (0–1), `--live-battery-charging` (0/1) */
66
+ declare const battery: Source;
67
+
68
+ /**
69
+ * `--live-now` (epoch seconds), `--live-hours`, `--live-minutes`, `--live-seconds`.
70
+ * Re-armed to each whole second so it doesn't drift.
71
+ */
72
+ declare const clock: Source;
73
+
74
+ /**
75
+ * `--live-fps`, a short exponential moving average of recent frame rates so the
76
+ * value is stable rather than jittering frame to frame. Runs on the shared frame
77
+ * loop while bound.
78
+ */
79
+ declare const fps: Source;
80
+
81
+ /**
82
+ * `--live-vvp-scale`, `--live-vvp-offset-top`, `--live-vvp-height`
83
+ * from `window.visualViewport`.
84
+ */
85
+ declare const visualViewport: Source;
86
+
87
+ /**
88
+ * `--live-local-pointer-x-ratio`, `--live-local-pointer-y-ratio` (0–1 within the
89
+ * element's box), `--live-local-pointer-inside` (0/1).
90
+ *
91
+ * Shares the page's single `pointermove` listener. The rect is read per move
92
+ * (cheap when layout is clean) so it stays correct through layout shifts, which
93
+ * a cached rect would miss.
94
+ */
95
+ declare const pointerLocal: Source;
96
+
97
+ /**
98
+ * For `<video>`/`<audio>`: `--live-current-time`, `--live-duration`,
99
+ * `--live-progress` (0–1), `--live-paused` (0/1), `--live-volume` (0–1).
100
+ *
101
+ * Bind it to the media element, or to a container that holds one. When bound to
102
+ * a container the props are written on the container, so sibling readers (a
103
+ * progress ring, a scrubber, a time label) all inherit them.
104
+ */
105
+ declare const media: Source;
106
+
107
+ /**
108
+ * For form fields: `--live-length` (value length), `--live-empty` (0/1),
109
+ * `--live-valid` (0/1 from `el.validity.valid`).
110
+ *
111
+ * Plus the per-reason validity flags CSS can't distinguish (each 0/1):
112
+ * `--live-value-missing`, `--live-type-mismatch`, `--live-pattern-mismatch`,
113
+ * `--live-too-long`, `--live-too-short`, `--live-range-underflow`,
114
+ * `--live-range-overflow`, `--live-step-mismatch`, `--live-bad-input`,
115
+ * `--live-custom-error` — so a hint can react to the *specific* failure.
116
+ *
117
+ * And, when the field has a `maxlength` (inputs + textareas): `--live-remaining`
118
+ * (chars left) and `--live-fill-pct` (length ÷ maxlength, 0–1) for a pure-CSS
119
+ * character counter. Neither is written without a `maxlength`, so keep a
120
+ * `var(--live-remaining, …)` fallback.
121
+ *
122
+ * Bind it to the field itself, or to a container that holds one. When bound to
123
+ * a container the props are written on the container, so the field *and* sibling
124
+ * readers (a counter, a meter, a status word) all inherit them downward.
125
+ */
126
+ declare const field: Source;
127
+
128
+ /**
129
+ * The interaction-history states form libraries track (Angular, Formik, React
130
+ * Hook Form, final-form) but CSS pseudo-classes can't express — all `0`/`1`:
131
+ *
132
+ * - `--live-dirty` / `--live-pristine` — the user has edited a field *at all*
133
+ * since it mounted. Latches on the first `input`/`change` and stays there even
134
+ * if you type the value back (Angular's `.dirty`). `pristine` is the inverse.
135
+ * - `--live-touched` / `--live-untouched` — a field has been blurred at least
136
+ * once. Latches on the first blur (Angular's `.touched`); `untouched` inverts it.
137
+ * - `--live-changed` — the *current* value differs from the value at mount
138
+ * (React Hook Form's per-field dirty). Unlike `dirty` it un-latches when you
139
+ * type back to the original.
140
+ * - `--live-submitted` — the owning `<form>` has been submitted (RHF's
141
+ * `isSubmitted`). Latches on `submit`, clears on `reset`.
142
+ *
143
+ * Both latch pairs reset when the owning form is reset.
144
+ *
145
+ * **Bind it to a single field** for that field's state, or **to a `<form>` (or
146
+ * any wrapper)** for the *aggregate* across every field inside it — exactly like
147
+ * a framework's form-group vs. control state. Aggregated: `dirty`/`touched`/
148
+ * `changed` are on if *any* field is; `submitted` is the form's. Props land on
149
+ * the bound element, so labels, hints, and a submit button can all style from
150
+ * them (e.g. reveal errors only once `--live-touched` or `--live-submitted`).
151
+ *
152
+ * Validity stays in the `field` plugin (`--live-valid`); focus stays in `:focus`.
153
+ */
154
+ declare const fieldState: Source;
155
+
156
+ /**
157
+ * Form-level **validity & completion** that CSS can't compute. `:invalid` matches
158
+ * a *single* control, but CSS can't count across a form or gate on "all valid" —
159
+ * so this aggregates a form's constraint-validation state into readable numbers,
160
+ * all written on the bound element:
161
+ *
162
+ * - `--live-field-count` — controls subject to constraint validation (excludes
163
+ * disabled / readonly / buttons / hidden — i.e. `willValidate === false`).
164
+ * - `--live-valid-count` / `--live-invalid-count` — how many currently pass / fail.
165
+ * - `--live-all-valid` (`1`/`0`) — the submit gate: no invalid controls remain.
166
+ * - `--live-completion` (0–1) — how much of the *required* part of the form is
167
+ * satisfied (valid required controls ÷ required controls; `1` if none required).
168
+ *
169
+ * Bind it to a `<form>` (or any wrapper holding fields). It pairs with
170
+ * `field-state` (interaction history) and the `field` plugin (per-field
171
+ * `--live-valid`): use `field` on a control, `form-state` on the form.
172
+ */
173
+ declare const formState: Source;
174
+
175
+ /**
176
+ * A native `<select>`'s state as numbers CSS can compute with. CSS can match
177
+ * `:open`, `:checked` (on `<option>`s under `appearance: base-select`) and
178
+ * `:focus` — but it can never get *which* option, *as a number*, into `calc()`,
179
+ * nor count selections in a `<select multiple>`. This does:
180
+ *
181
+ * - `--live-index` — `selectedIndex` (0-based; `-1` when nothing is selected).
182
+ * Turns a `<select>` into a discrete CSS state machine: slide a segmented-
183
+ * control indicator with `translate: calc(var(--live-index) * 100%) 0`, or pick
184
+ * the Nth palette/theme. With `configure({ typed: true })` the index animates.
185
+ * - `--live-option-count` — `options.length`.
186
+ * - `--live-index-pct` — `index ÷ (count − 1)`, 0–1: the choice's position in the
187
+ * list (`0` for a one-option list or nothing selected).
188
+ * - `--live-value-num` — `Number(value)` *when the chosen value is numeric*, so a
189
+ * choice's meaning drives layout directly:
190
+ * `grid-template-columns: repeat(var(--live-value-num), 1fr)`. Not written for a
191
+ * non-numeric value — keep a `var(--live-value-num, …)` fallback.
192
+ * - `--live-selected-count` — how many options are selected (the count CSS can't
193
+ * compute: `:checked` matches them, nothing tallies them). `1`/`0` for a single
194
+ * select; `0`…N for `<select multiple>`.
195
+ * - `--live-selected-pct` — `selected-count ÷ option-count`, 0–1: a multi-select
196
+ * fill ratio.
197
+ *
198
+ * Bind the `<select>` itself, or a container holding one (props land on the
199
+ * container, so a sibling readout/indicator inherits them downward).
200
+ */
201
+ declare const select: Source;
202
+
203
+ /**
204
+ * The colour chosen in an `<input type="color">`, which CSS has no way to read.
205
+ * Exposed as one sRGB hex string, `--live-color` — pull whatever channels you
206
+ * need out of it with relative colour syntax / `color-mix()`, so a picker can
207
+ * live-theme a whole region with zero consumer JS:
208
+ *
209
+ * .panel { background: var(--live-color); }
210
+ * .accent { background: oklch(from var(--live-color) l c h / 50%); }
211
+ *
212
+ * Typed (`configure({ typed: true })`) registers it as a `<color>`, so the theme
213
+ * interpolates between picks. Bind the input, or a container holding one (props
214
+ * land on the container, so siblings inherit the colour).
215
+ */
216
+ declare const colorInput: Source;
217
+
218
+ /**
219
+ * `--live-orient-alpha`, `--live-orient-beta`, `--live-orient-gamma` (degrees)
220
+ * from `deviceorientation`. Requires a user-gesture permission grant on some
221
+ * platforms (notably iOS).
222
+ */
223
+ declare const orientation: Source;
224
+
225
+ /**
226
+ * `--live-accel-x`, `--live-accel-y`, `--live-accel-z` (m/s², gravity included)
227
+ * from `devicemotion`. Requires a user-gesture permission grant on some
228
+ * platforms (notably iOS).
229
+ */
230
+ declare const motion: Source;
231
+
232
+ /**
233
+ * `--live-geo-lat`, `--live-geo-lng`, `--live-geo-accuracy` (meters) from
234
+ * `navigator.geolocation.watchPosition`. Requires a user-gesture permission
235
+ * grant on some platforms.
236
+ */
237
+ declare const geo: Source;
238
+
239
+ /**
240
+ * `--live-cpu-pressure` — the CPU's Compute Pressure state as an ordered tier:
241
+ * nominal=0, fair=1, serious=2, critical=3. Use it to back off expensive CSS
242
+ * work (animations, blur, shadows) as the CPU gets busy.
243
+ *
244
+ * Chromium-only, secure-context, and gated by the `compute-pressure` Permissions
245
+ * Policy. Feature-detects and no-ops (writing nothing) where unavailable, so CSS
246
+ * can fall back via `var(--live-cpu-pressure, 0)`.
247
+ */
248
+ declare const cpuPressure: Source;
249
+
250
+ /**
251
+ * For `<img>`: `--live-natural-w` / `--live-natural-h` (intrinsic pixel size),
252
+ * `--live-loaded` (0/1), `--live-broken` (0/1).
253
+ *
254
+ * Bind it to the image, or to a container that holds one. When bound to a
255
+ * container the props land on the container, so a wrapper/figure can react —
256
+ * skeleton while loading, fallback when broken, `aspect-ratio` from the natural
257
+ * dimensions before the bytes arrive.
258
+ */
259
+ declare const img: Source;
260
+
261
+ /**
262
+ * A small colour palette extracted from an `<img>`, each swatch a single
263
+ * `#rrggbb` colour you drop straight into `var()`:
264
+ *
265
+ * - `--live-img` — the dominant colour (the most common one)
266
+ * - `--live-img-accent` — the most vibrant colour (an extracted accent)
267
+ * - `--live-img-dark` — the darkest colour that isn't black
268
+ * - `--live-img-light` — the lightest colour that isn't white
269
+ * - `--live-img-avg` — the average (mean) of every pixel
270
+ *
271
+ * plus `--live-img-temp` (−1 cool … +1 warm), the image's warm/cool bias. The
272
+ * pixels are sRGB, so a hex colour is enough — pull whatever channels you need
273
+ * out of it with relative colour syntax or mix it, no separate channel props:
274
+ *
275
+ * .card { background: var(--live-img); }
276
+ * .tag { background: oklch(from var(--live-img-accent) l c h / 50%); }
277
+ * .text { color: oklch(from var(--live-img) clamp(0, (0.6 - l) * 9, 1) 0 0); } // legible on the bg
278
+ *
279
+ * Bind the `<img>` or a container holding one (props land on the container, so a
280
+ * caption or overlay can theme itself from the artwork). Recomputed whenever the
281
+ * image loads, so a `src` swap updates it. Every swatch comes from one bucketing
282
+ * pass over just 16×16 px, offloaded via `createImageBitmap` + `OffscreenCanvas`.
283
+ *
284
+ * Cross-origin images need `crossorigin="anonymous"` **and** permissive CORS
285
+ * headers, else the canvas is tainted and the plugin no-ops (writes nothing, so
286
+ * `var(--live-img, …)` fallbacks stay safe). Feature-detects canvas support.
287
+ */
288
+ declare const imgColor: Source;
289
+
290
+ /**
291
+ * The live colours of a playing `<video>`, each a single `#rrggbb` colour: the
292
+ * **dominant** colour as `--live-video`, and the most vibrant **accent** as
293
+ * `--live-video-accent` (it reuses the dominant when a frame is essentially
294
+ * grayscale). The pixels are sRGB, so a hex colour is enough — drop them into
295
+ * `var()` for an ambient glow, scrim, or theme that tracks the footage, and pull
296
+ * channels out with relative colour syntax or mix them, no separate channel props:
297
+ *
298
+ * .player { box-shadow: 0 0 4rem var(--live-video-accent); }
299
+ * .scrim { background: oklch(from var(--live-video) l c h / 50%); }
300
+ *
301
+ * Bind the `<video>` or a container holding one (props land on the container, so
302
+ * a caption or chrome can theme itself from the picture). Both colours come from
303
+ * one 16×16 bucketing pass. Sampling rides `requestVideoFrameCallback` — it fires
304
+ * per *presented* frame and stops when the video is paused, offscreen, or in a
305
+ * background tab, so an idle video costs nothing — and the pixel read is throttled
306
+ * to ~4 Hz on top. The current frame is read from a canvas; falls back to the
307
+ * `timeupdate` event where `requestVideoFrameCallback` is unavailable. A
308
+ * paused/poster frame is seeded on attach.
309
+ *
310
+ * Cross-origin video needs `crossorigin="anonymous"` **and** permissive CORS
311
+ * headers, else the canvas is tainted and the plugin no-ops (writes nothing, so
312
+ * `var(--live-video, …)` fallbacks stay safe). Feature-detects canvas support.
313
+ */
314
+ declare const videoColor: Source;
315
+
316
+ /** Every plugin source, for `registerPlugins()` / bulk registration. */
317
+ declare const allPlugins: Source[];
318
+ /**
319
+ * Register plugin sources with the core registry so they work via
320
+ * `bind`/`global`/`data-props-for`. Defaults to registering all plugins.
321
+ *
322
+ * import { registerPlugins } from 'prop-for-that/plugins'
323
+ * registerPlugins() // register everything
324
+ * registerPlugins(battery, clock) // register a subset
325
+ */
326
+ declare function registerPlugins(...sources: Source[]): void;
327
+
328
+ export { allPlugins, battery, clock, colorInput, cpuPressure, field, fieldState, formState, fps, geo, img, imgColor, media, motion, navType, network, online, orientation, pageFocused, pageVisible, pointerLocal, registerPlugins, scrollVelocity, select, videoColor, visualViewport };
@@ -0,0 +1 @@
1
+ import {d,a,e,f,g}from'./chunk-6DXNWAGS.js';import'./chunk-LWKH66VY.js';var V={key:"scroll-velocity",scope:"global",start(e$1){let t=window.scrollY,r=0,o=null,n=()=>{let c=window.scrollY,l=c-t;t=c,r=l||r*.8,Math.abs(r)<.01&&(r=0);let u=e(r);e$1.write("scroll-velocity",u),e$1.write("scroll-direction",Math.sign(u)),u===0&&o&&(o(),o=null);},i=()=>{o||(o=a(n));};e$1.write("scroll-velocity",0),e$1.write("scroll-direction",0);let s=d("scroll",i);return ()=>{s(),o?.();}}};var D={key:"online",scope:"global",start(e){let t=()=>e.write("online",navigator.onLine?1:0);t();let r=d("online",t),o=d("offline",t);return ()=>{r(),o();}}};var B={key:"page-focused",scope:"global",start(e){let t=()=>e.write("page-focused",document.hasFocus()?1:0);t();let r=d("focus",t),o=d("blur",t);return ()=>{r(),o();}}};var P=new Map;function N(e,t){let r=P.get(e);if(!r){let o=new Set,n=i=>{for(let s of [...o])s(i);};r={handlers:o,dispatch:n},P.set(e,r),document.addEventListener(e,n,{passive:true});}return r.handlers.add(t),()=>{r.handlers.delete(t),r.handlers.size===0&&(document.removeEventListener(e,r.dispatch),P.delete(e));}}var z={key:"page-visible",scope:"global",start(e){let t=()=>e.write("page-visible",document.visibilityState==="visible"?1:0);return t(),N("visibilitychange",t)}};var W={key:"nav-type",scope:"global",props:{"nav-type":{syntax:"<custom-ident>",initial:"navigate"}},start(e){let t=performance.getEntriesByType("navigation")[0];return e.write("nav-type",t?.type??"navigate","const"),()=>{}}};function pe(e){switch(e){case "slow-2g":return 1;case "2g":return 2;case "3g":return 3;case "4g":return 4;default:return 0}}var q={key:"network",scope:"global",start(e){let t=navigator.connection;if(!t)return ()=>{};let r=()=>{e.write("net-downlink",t.downlink??0),e.write("net-rtt",t.rtt??0),e.write("net-save-data",t.saveData?1:0),e.write("net-type",pe(t.effectiveType));};return r(),t.addEventListener("change",r),()=>t.removeEventListener("change",r)}};var _={key:"battery",scope:"global",start(e$1){let t=navigator.getBattery;if(!t)return ()=>{};let r=false,o,n;return t.call(navigator).then(i=>{r||(o=i,n=()=>{e$1.write("battery-level",e(i.level)),e$1.write("battery-charging",i.charging?1:0);},n(),i.addEventListener("levelchange",n),i.addEventListener("chargingchange",n));}),()=>{r=true,o&&n&&(o.removeEventListener("levelchange",n),o.removeEventListener("chargingchange",n));}}};var U={key:"clock",scope:"global",start(e){let t=0,r=()=>{let o=new Date;e.write("now",Math.floor(o.getTime()/1e3)),e.write("hours",o.getHours()),e.write("minutes",o.getMinutes()),e.write("seconds",o.getSeconds()),t=setTimeout(r,1e3-Date.now()%1e3);};return r(),()=>clearTimeout(t)}};var Y={key:"fps",scope:"global",start(e){let t=performance.now(),r=60;return a(o=>{let n=o-t;t=o,!(n<=0)&&(r+=(1e3/n-r)*.1,e.write("fps",Math.round(r)));})}};var G={key:"visual-viewport",scope:"global",start(e$1){let t=window.visualViewport;if(!t)return ()=>{};let r=()=>{e$1.write("vvp-scale",e(t.scale)),e$1.write("vvp-offset-top",e(t.offsetTop)),e$1.write("vvp-height",e(t.height));};return r(),t.addEventListener("resize",r,{passive:true}),t.addEventListener("scroll",r,{passive:true}),()=>{t.removeEventListener("resize",r),t.removeEventListener("scroll",r);}}};var K={key:"pointer-local",scope:"element",start(e$1){let t=e$1.target;return d("pointermove",o=>{let n=o,i=t.getBoundingClientRect(),s=i.width>0?(n.clientX-i.left)/i.width:0,c=i.height>0?(n.clientY-i.top)/i.height:0,l=s>=0&&s<=1&&c>=0&&c<=1;e$1.write("local-pointer-x-ratio",e(s)),e$1.write("local-pointer-y-ratio",e(c)),e$1.write("local-pointer-inside",l?1:0);})}};var Q={key:"media",scope:"element",start(e$1){let t=f(e$1.target,"video, audio");if(!t)return ()=>{};let r=()=>{let n=isFinite(t.duration)?t.duration:0;e$1.write("current-time",e(t.currentTime)),e$1.write("duration",e(n)),e$1.write("progress",n>0?e(t.currentTime/n):0),e$1.write("paused",t.paused?1:0),e$1.write("volume",e(t.volume));};r();let o=["timeupdate","loadedmetadata","play","pause","volumechange"];for(let n of o)t.addEventListener(n,r,{passive:true});return ()=>{for(let n of o)t.removeEventListener(n,r);}}};var de=[["valueMissing","value-missing"],["typeMismatch","type-mismatch"],["patternMismatch","pattern-mismatch"],["tooLong","too-long"],["tooShort","too-short"],["rangeUnderflow","range-underflow"],["rangeOverflow","range-overflow"],["stepMismatch","step-mismatch"],["badInput","bad-input"],["customError","custom-error"]],X={key:"field",scope:"element",start(e$1){let t=f(e$1.target,"input, textarea, select");if(!t)return ()=>{};let r=()=>{let n=t.value.length;e$1.write("length",n),e$1.write("empty",n===0?1:0),e$1.write("valid",t.validity.valid?1:0);for(let[s,c]of de)e$1.write(c,t.validity[s]?1:0);let i=t.maxLength;i>=0&&(e$1.write("remaining",Math.max(0,i-n)),e$1.write("fill-pct",i>0?e(Math.min(1,n/i)):0));};r();let o=["input","change"];for(let n of o)t.addEventListener(n,r,{passive:true});return ()=>{for(let n of o)t.removeEventListener(n,r);}}};var $="input:not([type=hidden]):not([type=submit]):not([type=button]):not([type=reset]):not([type=image]), textarea, select",T=e=>e.matches($)?[e]:Array.from(e.querySelectorAll($));var j=e=>e instanceof HTMLInputElement&&(e.type==="checkbox"||e.type==="radio")?e.checked?"1":"0":e.value,fe=e=>{if(e instanceof HTMLInputElement&&(e.type==="checkbox"||e.type==="radio"))return e.defaultChecked?"1":"0";if(e instanceof HTMLSelectElement){let t=Array.from(e.options).find(r=>r.defaultSelected);return t?t.value:e.options[0]?.value??""}return e.defaultValue},J={key:"field-state",scope:"element",start(e){let t=T(e.target);if(!t.length)return ()=>{};let r=new Set(t),o=new WeakMap;for(let d of t)o.set(d,j(d));let n=new Set,i=new Set,s=new Set,c=false,l=()=>{let d=n.size>0,f=i.size>0;e.write("dirty",d?1:0),e.write("pristine",d?0:1),e.write("touched",f?1:0),e.write("untouched",f?0:1),e.write("changed",s.size>0?1:0),e.write("submitted",c?1:0);};l();let u=d=>{let f=d.target;r.has(f)&&(n.add(f),j(f)===o.get(f)?s.delete(f):s.add(f),l());},p=d=>{let f=d.target;r.has(f)&&(i.add(f),l());};e.target.addEventListener("input",u,{passive:true}),e.target.addEventListener("change",u,{passive:true}),e.target.addEventListener("focusout",p,{passive:true});let m=e.target instanceof HTMLFormElement?e.target:t[0]?.form,S=()=>{c=true,l();},L=()=>{n.clear(),i.clear(),c=false,s.clear();for(let d of t)fe(d)!==o.get(d)&&s.add(d);l();};return m?.addEventListener("submit",S,{passive:true}),m?.addEventListener("reset",L,{passive:true}),()=>{e.target.removeEventListener("input",u),e.target.removeEventListener("change",u),e.target.removeEventListener("focusout",p),m?.removeEventListener("submit",S),m?.removeEventListener("reset",L);}}};var Z={key:"form-state",scope:"element",start(e$1){let t=T(e$1.target);if(!t.length)return ()=>{};let r=()=>{let s=0,c=0,l=0,u=0;for(let p of t){if(!p.willValidate)continue;s++;let m=p.validity.valid;m&&c++,p.required&&(l++,m&&u++);}e$1.write("field-count",s),e$1.write("valid-count",c),e$1.write("invalid-count",s-c),e$1.write("all-valid",s===c?1:0),e$1.write("completion",l?e(u/l):1);};r(),e$1.target.addEventListener("input",r,{passive:true}),e$1.target.addEventListener("change",r,{passive:true});let o=false,n=e$1.target instanceof HTMLFormElement?e$1.target:t[0]?.form,i=()=>{requestAnimationFrame(()=>{o||r();});};return n?.addEventListener("reset",i,{passive:true}),()=>{o=true,e$1.target.removeEventListener("input",r),e$1.target.removeEventListener("change",r),n?.removeEventListener("reset",i);}}};var x={key:"select",scope:"element",start(e$1){let t=f(e$1.target,"select");if(!t)return ()=>{};let r=()=>{let n=t.options.length,i=t.selectedIndex,s=t.selectedOptions.length;e$1.write("index",i),e$1.write("option-count",n),e$1.write("index-pct",i>=0&&n>1?e(i/(n-1)):0),e$1.write("selected-count",s),e$1.write("selected-pct",n>0?e(s/n):0);let c=Number(t.value);t.value.trim()!==""&&Number.isFinite(c)&&e$1.write("value-num",c);};r();let o=["input","change"];for(let n of o)t.addEventListener(n,r,{passive:true});return ()=>{for(let n of o)t.removeEventListener(n,r);}}};var E=({r:e,g:t,b:r})=>`#${(16777216|e<<16|t<<8|r).toString(16).slice(1)}`,y={syntax:"<color>",initial:"transparent"},ee=(e,t,r)=>(.2126*e+.7152*t+.0722*r)/255,ve=(e,t,r)=>{let o=Math.max(e,t,r)/255,n=Math.min(e,t,r)/255,i=o-n;return i===0?0:Math.min(1,i/(1-Math.abs(o+n-1)))};function ge(e){let t=new Map,r=0,o=0,n=0,i=0;for(let s=0;s<e.length;s+=4){if(e[s+3]<128)continue;let c=e[s],l=e[s+1],u=e[s+2];r++,o+=c,n+=l,i+=u;let p=c>>4<<8|l>>4<<4|u>>4,m=t.get(p);m?(m.r+=c,m.g+=l,m.b+=u,m.n++):t.set(p,{r:c,g:l,b:u,n:1});}return {buckets:t,count:r,sumR:o,sumG:n,sumB:i}}var ye=e=>{let t=Math.round(e.r/e.n),r=Math.round(e.g/e.n),o=Math.round(e.b/e.n);return {r:t,g:r,b:o,l:ee(t,r,o)}},we=.04,he=.96,Ee=.15;function I(e){let{buckets:t,count:r,sumR:o,sumG:n,sumB:i}=ge(e);if(!r)return null;let s=0;for(let b of t.values())b.n>s&&(s=b.n);let c=null,l=0,u=null,p=-1/0,m=null,S=1/0,L=null,d=-1/0;for(let b of t.values()){let v=ye(b);b.n>l&&(l=b.n,c=v);let R=ve(v.r,v.g,v.b);if(R>=Ee){let me=1-Math.abs(v.l-.5)*2,A=3*R+1.5*Math.max(0,me)+b.n/s;A>p&&(p=A,u=v);}v.l>=we&&v.l<S&&(S=v.l,m=v),v.l<=he&&v.l>d&&(d=v.l,L=v);}let f=Math.round(o/r),H=Math.round(n/r),F=Math.round(i/r),ue={r:f,g:H,b:F,l:ee(f,H,F)},k=c;return {dominant:k,accent:u??k,dark:m??k,light:L??k,average:ue,temp:(f-F)/255}}var h=16,be=()=>{if(typeof OffscreenCanvas<"u")return new OffscreenCanvas(h,h);if(typeof document>"u")return null;let e=document.createElement("canvas");return e.width=e.height=h,e};function C(){let t=be()?.getContext("2d",{willReadFrequently:true});return t?r=>{t.drawImage(r,0,0,h,h);try{return t.getImageData(0,0,h,h).data}catch{return null}}:null}var te={key:"color-input",scope:"element",props:{color:y},start(e){let t=f(e.target,'input[type="color"]');if(!t)return ()=>{};let r=()=>e.write("color",t.value);r();let o=["input","change"];for(let n of o)t.addEventListener(n,r,{passive:true});return ()=>{for(let n of o)t.removeEventListener(n,r);}}};var re={key:"orientation",scope:"global",start(e$1){if(typeof DeviceOrientationEvent>"u")return ()=>{};let t=r=>{e$1.write("orient-alpha",e(r.alpha??0)),e$1.write("orient-beta",e(r.beta??0)),e$1.write("orient-gamma",e(r.gamma??0));};return window.addEventListener("deviceorientation",t,{passive:true}),()=>window.removeEventListener("deviceorientation",t)}};var oe={key:"motion",scope:"global",start(e$1){if(typeof DeviceMotionEvent>"u")return ()=>{};let t=r=>{let o=r.accelerationIncludingGravity;e$1.write("accel-x",e(o?.x??0)),e$1.write("accel-y",e(o?.y??0)),e$1.write("accel-z",e(o?.z??0));};return window.addEventListener("devicemotion",t,{passive:true}),()=>window.removeEventListener("devicemotion",t)}};var ne={key:"geo",scope:"global",start(e$1){let t=navigator.geolocation;if(!t)return ()=>{};let r=false,o=t.watchPosition(n=>{r||(e$1.write("geo-lat",e(n.coords.latitude)),e$1.write("geo-lng",e(n.coords.longitude)),e$1.write("geo-accuracy",e(n.coords.accuracy)));});return ()=>{r=true,t.clearWatch(o);}}};var Se={nominal:0,fair:1,serious:2,critical:3},ie={key:"cpu-pressure",scope:"global",start(e){let t=globalThis.PressureObserver;if(!t)return ()=>{};e.write("cpu-pressure",0);let r;try{r=new t(o=>{let n=o[o.length-1];n&&e.write("cpu-pressure",Se[n.state]??0);}),r.observe("cpu",{sampleInterval:1e3}).catch(()=>{});}catch{r=void 0;}return ()=>r?.disconnect()}};var se={key:"img",scope:"element",start(e){let t=f(e.target,"img");if(!t)return ()=>{};let r=()=>t.getAttribute("src")!==null||t.getAttribute("srcset")!==null,o=t.complete&&t.naturalWidth===0&&r(),n=()=>{e.write("natural-w",t.naturalWidth),e.write("natural-h",t.naturalHeight),e.write("loaded",t.complete&&t.naturalWidth>0?1:0),e.write("broken",o?1:0);},i=()=>{o=false,n();},s=()=>{o=true,n();};return n(),t.addEventListener("load",i,{passive:true}),t.addEventListener("error",s,{passive:true}),()=>{t.removeEventListener("load",i),t.removeEventListener("error",s);}}};async function Le(e){let t=C();if(!t)return null;let r=e,o;if(typeof createImageBitmap=="function")try{o=await createImageBitmap(e,{resizeWidth:h,resizeHeight:h,resizeQuality:"low"}),r=o;}catch{}let n=t(r);return o?.close(),n}var ae={key:"img-color",scope:"element",props:{img:y,"img-accent":y,"img-dark":y,"img-light":y,"img-avg":y,"img-temp":{syntax:"<number>",initial:"0"}},start(e$1){let t=f(e$1.target,"img");if(!t)return ()=>{};let r=false,o=async()=>{if(!t.complete||t.naturalWidth===0)return;let n=await Le(t).catch(()=>null);if(r||!n)return;let i=I(n);i&&(e$1.write("img",E(i.dominant)),e$1.write("img-accent",E(i.accent)),e$1.write("img-dark",E(i.dark)),e$1.write("img-light",E(i.light)),e$1.write("img-avg",E(i.average)),e$1.write("img-temp",e(i.temp)));};return o(),t.addEventListener("load",o,{passive:true}),()=>{r=true,t.removeEventListener("load",o);}}};var ce=250,ke=2,le={key:"video-color",scope:"element",props:{video:y,"video-accent":y},start(e){let t=f(e.target,"video");if(!t)return ()=>{};let r=C();if(!r)return ()=>{};let o=false,n=-1/0,i=u=>{if(u-n<ce||t.readyState<ke)return;let p=r(t);if(!p)return;let m=I(p);m&&(n=u,e.write("video",E(m.dominant)),e.write("video-accent",E(m.accent)));};i(0);let s=t.requestVideoFrameCallback?.bind(t);if(s){let u=s(function p(m){o||(i(m),u=s(p));});return ()=>{o=true,t.cancelVideoFrameCallback?.(u);}}let c=0,l=()=>i(c+=ce);return t.addEventListener("timeupdate",l,{passive:true}),t.addEventListener("loadeddata",l,{passive:true}),()=>{o=true,t.removeEventListener("timeupdate",l),t.removeEventListener("loadeddata",l);}}};var Me=[V,D,B,z,W,q,_,U,Y,G,K,Q,X,J,Z,x,te,re,oe,ne,ie,se,ae,le];function tr(...e){for(let t of e.length?e:Me)g(t);}export{Me as allPlugins,_ as battery,U as clock,te as colorInput,ie as cpuPressure,X as field,J as fieldState,Z as formState,Y as fps,ne as geo,se as img,ae as imgColor,Q as media,oe as motion,W as navType,q as network,D as online,re as orientation,B as pageFocused,z as pageVisible,K as pointerLocal,tr as registerPlugins,V as scrollVelocity,x as select,le as videoColor,G as visualViewport};
@@ -0,0 +1,64 @@
1
+ type Cadence = 'live' | 'const';
2
+ type Scope = 'global' | 'element';
3
+ type Disposer = () => void;
4
+ interface Config {
5
+ /** Prefix for continuously-updated values. */
6
+ livePrefix: string;
7
+ /** Prefix for write-once constants. */
8
+ constPrefix: string;
9
+ /** Where global sources write. */
10
+ root: HTMLElement;
11
+ /**
12
+ * When true, written `--live-*` properties are registered with `@property`
13
+ * (via `CSS.registerProperty`) as typed, interpolatable custom properties with
14
+ * a guaranteed initial value. Opt in with `configure({ typed: true })` before
15
+ * attaching sources. Off by default.
16
+ */
17
+ typed: boolean;
18
+ /**
19
+ * Initial (default) values for typed properties, keyed by a source's local
20
+ * name (e.g. `'pointer-x-ratio'`). Applied as the `@property` initial-value
21
+ * when `typed` is on, overriding a source's declared initial and the `0`
22
+ * default. The value must be valid for the property's syntax.
23
+ */
24
+ defaults?: Record<string, string | number>;
25
+ /**
26
+ * Optional cap (in Hz) on how often the shared frame loop samples and flushes.
27
+ * Unset (the default) runs every animation frame. Setting e.g. `30` coalesces
28
+ * writes to at most 30/sec — fewer custom-property mutations means less style
29
+ * recalc and a calmer DevTools Styles panel, at the cost of update smoothness.
30
+ * Throttles the whole loop (sampling included), so per-frame samplers like
31
+ * `fps`/`scroll-velocity` measure at this rate too.
32
+ */
33
+ liveHz?: number;
34
+ }
35
+ /** Optional `@property` typing for a source's local name, used when `typed` is on. */
36
+ interface PropSpec {
37
+ /** CSS `@property` syntax. Default `'<number>'`. */
38
+ syntax?: string;
39
+ /** Initial value. Default `'0'`. */
40
+ initial?: string;
41
+ /** Whether the property inherits. Default `true` (required for container-bound sources). */
42
+ inherits?: boolean;
43
+ }
44
+ interface SourceContext {
45
+ /** The element this source instance is attached to (`config.root` for globals). */
46
+ target: HTMLElement;
47
+ config: Config;
48
+ /**
49
+ * Queue a value for the next batched flush. `localName` is prefixed by cadence
50
+ * (e.g. `write('pointer-x', 12)` → `--live-pointer-x: 12`).
51
+ */
52
+ write(localName: string, value: number | string, cadence?: Cadence): void;
53
+ }
54
+ interface Source {
55
+ /** Key used in `data-props-for` / `propsFor()`. */
56
+ key: string;
57
+ scope: Scope;
58
+ /** Optional `@property` typings per local name, applied when `typed` is on. */
59
+ props?: Record<string, PropSpec>;
60
+ /** Attach listeners/observers, seed initial values, return a disposer. */
61
+ start(ctx: SourceContext): Disposer;
62
+ }
63
+
64
+ export type { Cadence as C, Disposer as D, PropSpec as P, Source as S, Config as a, SourceContext as b };
@@ -0,0 +1,64 @@
1
+ type Cadence = 'live' | 'const';
2
+ type Scope = 'global' | 'element';
3
+ type Disposer = () => void;
4
+ interface Config {
5
+ /** Prefix for continuously-updated values. */
6
+ livePrefix: string;
7
+ /** Prefix for write-once constants. */
8
+ constPrefix: string;
9
+ /** Where global sources write. */
10
+ root: HTMLElement;
11
+ /**
12
+ * When true, written `--live-*` properties are registered with `@property`
13
+ * (via `CSS.registerProperty`) as typed, interpolatable custom properties with
14
+ * a guaranteed initial value. Opt in with `configure({ typed: true })` before
15
+ * attaching sources. Off by default.
16
+ */
17
+ typed: boolean;
18
+ /**
19
+ * Initial (default) values for typed properties, keyed by a source's local
20
+ * name (e.g. `'pointer-x-ratio'`). Applied as the `@property` initial-value
21
+ * when `typed` is on, overriding a source's declared initial and the `0`
22
+ * default. The value must be valid for the property's syntax.
23
+ */
24
+ defaults?: Record<string, string | number>;
25
+ /**
26
+ * Optional cap (in Hz) on how often the shared frame loop samples and flushes.
27
+ * Unset (the default) runs every animation frame. Setting e.g. `30` coalesces
28
+ * writes to at most 30/sec — fewer custom-property mutations means less style
29
+ * recalc and a calmer DevTools Styles panel, at the cost of update smoothness.
30
+ * Throttles the whole loop (sampling included), so per-frame samplers like
31
+ * `fps`/`scroll-velocity` measure at this rate too.
32
+ */
33
+ liveHz?: number;
34
+ }
35
+ /** Optional `@property` typing for a source's local name, used when `typed` is on. */
36
+ interface PropSpec {
37
+ /** CSS `@property` syntax. Default `'<number>'`. */
38
+ syntax?: string;
39
+ /** Initial value. Default `'0'`. */
40
+ initial?: string;
41
+ /** Whether the property inherits. Default `true` (required for container-bound sources). */
42
+ inherits?: boolean;
43
+ }
44
+ interface SourceContext {
45
+ /** The element this source instance is attached to (`config.root` for globals). */
46
+ target: HTMLElement;
47
+ config: Config;
48
+ /**
49
+ * Queue a value for the next batched flush. `localName` is prefixed by cadence
50
+ * (e.g. `write('pointer-x', 12)` → `--live-pointer-x: 12`).
51
+ */
52
+ write(localName: string, value: number | string, cadence?: Cadence): void;
53
+ }
54
+ interface Source {
55
+ /** Key used in `data-props-for` / `propsFor()`. */
56
+ key: string;
57
+ scope: Scope;
58
+ /** Optional `@property` typings per local name, applied when `typed` is on. */
59
+ props?: Record<string, PropSpec>;
60
+ /** Attach listeners/observers, seed initial values, return a disposer. */
61
+ start(ctx: SourceContext): Disposer;
62
+ }
63
+
64
+ export type { Cadence as C, Disposer as D, PropSpec as P, Source as S, Config as a, SourceContext as b };