vireuikit 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1314 @@
1
+ import { halfMinDp, concentricRadius, activeMaterial, applyAccessibility, resolveOptics, VIREGLASS_SHEET_MATERIAL } from 'vireglass';
2
+
3
+ // src/kit/a11y.ts
4
+ var A11Y_NONE = {
5
+ reduceTransparency: false,
6
+ increaseContrast: false,
7
+ reduceMotion: false
8
+ };
9
+ function resolveA11y(system, override = {}) {
10
+ return {
11
+ reduceTransparency: override.reduceTransparency ?? system.reduceTransparency,
12
+ increaseContrast: override.increaseContrast ?? system.increaseContrast,
13
+ reduceMotion: override.reduceMotion ?? system.reduceMotion
14
+ };
15
+ }
16
+ function presentationFor(mode, wantsGlass) {
17
+ return {
18
+ glass: wantsGlass,
19
+ frost: wantsGlass && mode.reduceTransparency,
20
+ surface: wantsGlass ? "glass" : "plain",
21
+ contrastBorder: mode.increaseContrast,
22
+ instantSprings: mode.reduceMotion
23
+ };
24
+ }
25
+ function sameA11y(a, b) {
26
+ return a.reduceTransparency === b.reduceTransparency && a.increaseContrast === b.increaseContrast && a.reduceMotion === b.reduceMotion;
27
+ }
28
+
29
+ // src/kit/adaptive/classes.ts
30
+ var WIDTH_CLASSES = ["compact", "medium", "expanded", "large", "xlarge"];
31
+ var HEIGHT_CLASSES = ["compact", "medium", "expanded"];
32
+ var WIDTH_BREAKPOINTS = { medium: 600, expanded: 840, large: 1200, xlarge: 1600 };
33
+ var HEIGHT_BREAKPOINTS = { medium: 480, expanded: 900 };
34
+ function widthClass(width) {
35
+ if (!(width >= WIDTH_BREAKPOINTS.medium)) return "compact";
36
+ if (width < WIDTH_BREAKPOINTS.expanded) return "medium";
37
+ if (width < WIDTH_BREAKPOINTS.large) return "expanded";
38
+ return width < WIDTH_BREAKPOINTS.xlarge ? "large" : "xlarge";
39
+ }
40
+ function heightClass(height) {
41
+ if (!(height >= HEIGHT_BREAKPOINTS.medium)) return "compact";
42
+ return height < HEIGHT_BREAKPOINTS.expanded ? "medium" : "expanded";
43
+ }
44
+ function classifyWindow(size) {
45
+ return { width: widthClass(size.width), height: heightClass(size.height) };
46
+ }
47
+ function widthAtLeast(value, minimum) {
48
+ return WIDTH_CLASSES.indexOf(value) >= WIDTH_CLASSES.indexOf(minimum);
49
+ }
50
+
51
+ // src/kit/geometry.ts
52
+ function intersectRect(a, b) {
53
+ const x = Math.max(a.x, b.x);
54
+ const y = Math.max(a.y, b.y);
55
+ const right = Math.min(a.x + a.width, b.x + b.width);
56
+ const bottom = Math.min(a.y + a.height, b.y + b.height);
57
+ return right > x && bottom > y ? { x, y, width: right - x, height: bottom - y } : null;
58
+ }
59
+ function rectContains(outer, inner, tolerance = 0) {
60
+ return inner.x >= outer.x - tolerance && inner.y >= outer.y - tolerance && inner.x + inner.width <= outer.x + outer.width + tolerance && inner.y + inner.height <= outer.y + outer.height + tolerance;
61
+ }
62
+
63
+ // src/kit/adaptive/context.ts
64
+ var INPUT_TOUCH = { primary: "coarse", anyCoarse: true, hover: false, keyboard: false };
65
+ var INPUT_POINTER = { primary: "fine", anyCoarse: false, hover: true, keyboard: true };
66
+ var INPUT_MIXED = { primary: "fine", anyCoarse: true, hover: true, keyboard: true };
67
+ var INPUT_TABLET_KEYBOARD = { primary: "coarse", anyCoarse: true, hover: true, keyboard: true };
68
+ var POSTURE_FLAT = { kind: "flat" };
69
+ function adaptiveContext(partial) {
70
+ return {
71
+ window: partial.window,
72
+ input: partial.input ?? INPUT_POINTER,
73
+ posture: partial.posture ?? POSTURE_FLAT,
74
+ reduceMotion: partial.reduceMotion ?? false
75
+ };
76
+ }
77
+ function halfOpen(kind, window, options = {}) {
78
+ const band = Math.max(options.band ?? 0, 0);
79
+ const span = kind === "tabletop" ? window.height : window.width;
80
+ const at = options.at ?? span / 2;
81
+ const start = at - band / 2;
82
+ const bounds = kind === "tabletop" ? { x: 0, y: start, width: window.width, height: band } : { x: start, y: 0, width: band, height: window.height };
83
+ return { kind, fold: { bounds, occluding: options.occluding ?? band > 0 } };
84
+ }
85
+ function windowSegments(ctx) {
86
+ const { width, height } = ctx.window;
87
+ const whole = { x: 0, y: 0, width, height };
88
+ if (ctx.posture.kind === "flat") return [whole];
89
+ const { bounds, occluding } = ctx.posture.fold;
90
+ const tabletop = ctx.posture.kind === "tabletop";
91
+ const span = tabletop ? height : width;
92
+ const lo = Math.min(Math.max(tabletop ? bounds.y : bounds.x, 0), span);
93
+ const hi = Math.min(Math.max(lo + (tabletop ? bounds.height : bounds.width), lo), span);
94
+ const end = occluding ? lo : (lo + hi) / 2;
95
+ const start = occluding ? hi : (lo + hi) / 2;
96
+ const parts = tabletop ? [
97
+ { x: 0, y: 0, width, height: end },
98
+ { x: 0, y: start, width, height: height - start }
99
+ ] : [
100
+ { x: 0, y: 0, width: end, height },
101
+ { x: start, y: 0, width: width - start, height }
102
+ ];
103
+ const kept = parts.filter((r) => r.width > 0 && r.height > 0);
104
+ return kept.length ? kept : [whole];
105
+ }
106
+ function postureFromSegments(window, segments) {
107
+ if (segments.length !== 2) return POSTURE_FLAT;
108
+ const [a, b] = [...segments].sort((p, q) => p.x - q.x || p.y - q.y);
109
+ const gapX = b.x - (a.x + a.width);
110
+ if (gapX >= 0 && a.y < b.y + b.height && b.y < a.y + a.height) {
111
+ return { kind: "book", fold: { bounds: { x: a.x + a.width, y: 0, width: gapX, height: window.height }, occluding: gapX > 0 } };
112
+ }
113
+ const [top, bottom] = [...segments].sort((p, q) => p.y - q.y);
114
+ const gapY = bottom.y - (top.y + top.height);
115
+ if (gapY >= 0) {
116
+ return { kind: "tabletop", fold: { bounds: { x: 0, y: top.y + top.height, width: window.width, height: gapY }, occluding: gapY > 0 } };
117
+ }
118
+ return POSTURE_FLAT;
119
+ }
120
+ var area = (r) => r ? r.width * r.height : 0;
121
+ function segmentOf(box2, segments) {
122
+ const cx = box2.x + box2.width / 2;
123
+ const cy = box2.y + box2.height / 2;
124
+ const holding = segments.find((s) => cx >= s.x && cx < s.x + s.width && cy >= s.y && cy < s.y + s.height);
125
+ if (holding) return holding;
126
+ let best;
127
+ let most = 0;
128
+ for (const s of segments) {
129
+ const overlap = area(intersectRect(s, box2));
130
+ if (overlap > most) {
131
+ most = overlap;
132
+ best = s;
133
+ }
134
+ }
135
+ if (best) return best;
136
+ let nearest;
137
+ let distance = Number.POSITIVE_INFINITY;
138
+ for (const s of segments) {
139
+ const d = Math.hypot(s.x + s.width / 2 - cx, s.y + s.height / 2 - cy);
140
+ if (d < distance) {
141
+ distance = d;
142
+ nearest = s;
143
+ }
144
+ }
145
+ return nearest;
146
+ }
147
+ function surfaceRegion(ctx, source) {
148
+ const segments = windowSegments(ctx);
149
+ if (source) return segmentOf(source, segments) ?? segments[0];
150
+ return segments.reduce((best, s) => area(s) > area(best) ? s : best, segments[0]);
151
+ }
152
+ function stepSpring(s, dt, response, damping, instant = false) {
153
+ if (instant) {
154
+ const moved = s.x !== s.target || s.v !== 0;
155
+ s.x = s.target;
156
+ s.v = 0;
157
+ return moved;
158
+ }
159
+ const stiffness = (2 * Math.PI / response) ** 2;
160
+ const friction = 4 * Math.PI * damping / response;
161
+ const h = 1 / 240;
162
+ for (let left = Math.min(dt, 0.05); left > 0; left -= h) {
163
+ const step = Math.min(left, h);
164
+ s.v += (stiffness * (s.target - s.x) - friction * s.v) * step;
165
+ s.x += s.v * step;
166
+ }
167
+ if (Math.abs(s.target - s.x) < 1e-3 && Math.abs(s.v) < 0.01) {
168
+ s.x = s.target;
169
+ s.v = 0;
170
+ return false;
171
+ }
172
+ return true;
173
+ }
174
+ var smoothstep = (e0, e1, x) => {
175
+ const t = Math.min(Math.max((x - e0) / (e1 - e0), 0), 1);
176
+ return t * t * (3 - 2 * t);
177
+ };
178
+ var mix = (a, b, t) => a + (b - a) * t;
179
+ var DROP_DIAMETER = 1.1;
180
+ var DROP_LIFT = 0.5;
181
+ var NECK_GAIN = 0.3;
182
+ var NECK_RAMP = 4;
183
+ var CORNER_EASE_START = 0.55;
184
+ var INK_OUT_END = 0.4;
185
+ var INK_IN_START = 0.9;
186
+ var INK_IN_END = 1;
187
+ function growthInk(phase) {
188
+ const a = Math.min(Math.max(phase.shrink, 0), 1);
189
+ const b = Math.max(phase.grow, 0);
190
+ return {
191
+ outgoing: 1 - smoothstep(0, INK_OUT_END, a),
192
+ incoming: smoothstep(INK_IN_START, INK_IN_END, b)
193
+ };
194
+ }
195
+ function largerOf(a, b) {
196
+ return halfMinDp(a.geometry) >= halfMinDp(b.geometry) ? { main: a, other: b } : { main: b, other: a };
197
+ }
198
+ function growthFrame(source, target, anchor, phase) {
199
+ const dx = target.center.x - source.center.x;
200
+ const dy = target.center.y - source.center.y;
201
+ const len = Math.hypot(dx, dy) || 1;
202
+ const sourceSize = halfMinDp(source.geometry) * 2;
203
+ const lift = DROP_LIFT * sourceSize;
204
+ const drop2 = {
205
+ x: source.center.x + dx / len * lift,
206
+ y: source.center.y + dy / len * lift
207
+ };
208
+ const a = Math.min(Math.max(phase.shrink, 0), 1);
209
+ const b = Math.max(phase.grow, 0);
210
+ const keep = Math.max(1 - b, 0);
211
+ const d = DROP_DIAMETER * sourceSize;
212
+ const shrunkGeometry = {
213
+ width: mix(source.geometry.width, 0.6 * d, a) * keep,
214
+ height: mix(source.geometry.height, 0.6 * d, a) * keep,
215
+ cornerRadius: 0
216
+ };
217
+ shrunkGeometry.cornerRadius = Math.min(
218
+ mix(source.geometry.cornerRadius, halfMinDp(shrunkGeometry), a),
219
+ halfMinDp(shrunkGeometry)
220
+ );
221
+ const shrunk = {
222
+ geometry: shrunkGeometry,
223
+ center: { x: mix(source.center.x, drop2.x, a), y: mix(source.center.y, drop2.y, a) }
224
+ };
225
+ const seed = d * Math.sqrt(a);
226
+ const cornerX = Math.sign(anchor.x - source.center.x);
227
+ const cornerY = Math.sign(anchor.y - source.center.y);
228
+ const edgeX = mix(drop2.x + cornerX * (seed / 2), anchor.x, Math.min(b, 1));
229
+ const edgeY = mix(drop2.y + cornerY * (seed / 2), anchor.y, Math.min(b, 1));
230
+ const width = mix(seed, target.geometry.width, b);
231
+ const height = mix(seed, target.geometry.height, b);
232
+ const grownGeometry = { width, height, cornerRadius: 0 };
233
+ grownGeometry.cornerRadius = Math.min(
234
+ mix(halfMinDp(grownGeometry), target.geometry.cornerRadius, smoothstep(CORNER_EASE_START, 1, b)),
235
+ halfMinDp(grownGeometry)
236
+ );
237
+ const grown = {
238
+ geometry: grownGeometry,
239
+ center: { x: edgeX - cornerX * (width / 2), y: edgeY - cornerY * (height / 2) }
240
+ };
241
+ const { main, other } = largerOf(grown, shrunk);
242
+ const neck = NECK_GAIN * sourceSize * Math.min(a * NECK_RAMP, 1) * keep;
243
+ const united = neck > 0.01 && other.geometry.width > 0.5 && other.geometry.height > 0.5;
244
+ return {
245
+ body: main,
246
+ grown,
247
+ merge: united ? {
248
+ offsetX: other.center.x - main.center.x,
249
+ offsetY: other.center.y - main.center.y,
250
+ width: other.geometry.width,
251
+ height: other.geometry.height,
252
+ cornerRadius: other.geometry.cornerRadius,
253
+ smoothing: neck
254
+ } : void 0,
255
+ ink: growthInk(phase)
256
+ };
257
+ }
258
+ var createGrowthState = () => ({
259
+ shrink: { x: 0, v: 0, target: 0 },
260
+ grow: { x: 0, v: 0, target: 0 }
261
+ });
262
+ var growthPhase = (state) => ({
263
+ shrink: state.shrink.x,
264
+ grow: state.grow.x
265
+ });
266
+ var DEFAULT_GROWTH_TIMING = {
267
+ shrinkOpen: 0.21,
268
+ shrinkClose: 0.14,
269
+ growOpen: 0.38,
270
+ growClose: 0.4,
271
+ growDampingOpen: 0.72
272
+ };
273
+ var GROW_DAMPING_CLOSE = 1;
274
+ var SHRINK_DAMPING = 1;
275
+ var GROW_STARTS_AT = 0.8;
276
+ var SHRINK_RETURNS_AT = 0.12;
277
+ function stepGrowth(state, dt, open, reduceMotion = false, timing = DEFAULT_GROWTH_TIMING) {
278
+ const { shrink, grow } = state;
279
+ if (reduceMotion) {
280
+ shrink.target = open ? 1 : 0;
281
+ grow.target = open ? 1 : 0;
282
+ } else if (open) {
283
+ shrink.target = 1;
284
+ if (shrink.x > GROW_STARTS_AT) grow.target = 1;
285
+ } else {
286
+ grow.target = 0;
287
+ if (grow.x < SHRINK_RETURNS_AT) shrink.target = 0;
288
+ }
289
+ const movedShrink = stepSpring(
290
+ shrink,
291
+ dt,
292
+ open ? timing.shrinkOpen : timing.shrinkClose,
293
+ SHRINK_DAMPING,
294
+ reduceMotion
295
+ );
296
+ const movedGrow = stepSpring(
297
+ grow,
298
+ dt,
299
+ open ? timing.growOpen : timing.growClose,
300
+ open ? timing.growDampingOpen : GROW_DAMPING_CLOSE,
301
+ reduceMotion
302
+ );
303
+ return movedShrink || movedGrow;
304
+ }
305
+
306
+ // src/kit/adaptive/form-morph.ts
307
+ var HIDDEN = /* @__PURE__ */ new Set(["hidden", "closed", "none"]);
308
+ function formMorph(change, ctx, options = {}) {
309
+ const instant = ctx.reduceMotion;
310
+ if (options.anchorGone) return { ...change, mode: "retract", staysOpen: false, instant };
311
+ if (change.from === change.to) return { ...change, mode: "none", staysOpen: true, instant };
312
+ const mode = HIDDEN.has(change.from) ? "appear" : HIDDEN.has(change.to) ? "disappear" : "reshape";
313
+ return { ...change, mode, staysOpen: mode !== "disappear", instant };
314
+ }
315
+ var FORM_MORPH_TIMING = { response: DEFAULT_GROWTH_TIMING.growOpen, damping: 1 };
316
+ var createFormMorph = () => ({ t: { x: 0, v: 0, target: 0 }, fromInk: 1 });
317
+ function stepFormMorph(state, dt, toward, instant = false) {
318
+ state.t.target = toward;
319
+ return stepSpring(state.t, dt, FORM_MORPH_TIMING.response, FORM_MORPH_TIMING.damping, instant);
320
+ }
321
+ var OUT_END = 0.45;
322
+ var IN_START = 0.55;
323
+ function formMorphFrame(from, to, state) {
324
+ const t = Math.min(Math.max(state.t.x, 0), 1);
325
+ const mix3 = (a, b) => a + (b - a) * t;
326
+ return {
327
+ box: {
328
+ x: mix3(from.x, to.x),
329
+ y: mix3(from.y, to.y),
330
+ width: mix3(from.width, to.width),
331
+ height: mix3(from.height, to.height),
332
+ radius: mix3(from.radius, to.radius)
333
+ },
334
+ ink: inkAt(state, t),
335
+ showing: t < 0.5 ? "from" : "to"
336
+ };
337
+ }
338
+ function inkAt(state, t) {
339
+ return { outgoing: (state.fromInk ?? 1) * (1 - smoothstep(0, OUT_END, t)), incoming: smoothstep(IN_START, 1, t) };
340
+ }
341
+ function rebaseFormMorph(state) {
342
+ const t = Math.min(Math.max(state.t.x, 0), 1);
343
+ const ink = inkAt(state, t);
344
+ state.fromInk = t < 0.5 ? ink.outgoing : ink.incoming;
345
+ state.t = { x: 0, v: 0, target: 0 };
346
+ }
347
+ var CONTROL_SIZES = ["touch", "small", "medium", "large", "xlarge"];
348
+ var MIN_TOUCH_TARGET_DP = 44;
349
+ var MIN_POINTER_TARGET_DP = 24;
350
+ var capsule = (height, paddingX) => ({
351
+ height,
352
+ paddingX,
353
+ icon: Math.round(height / 2),
354
+ radius: height / 2,
355
+ shape: "capsule"
356
+ });
357
+ var rounded = (height, paddingX) => ({
358
+ height,
359
+ paddingX,
360
+ icon: Math.round(height / 2),
361
+ radius: height / 4,
362
+ shape: "rounded"
363
+ });
364
+ var BUTTON_SIZES = {
365
+ touch: capsule(44, 20),
366
+ small: rounded(24, 10),
367
+ medium: rounded(32, 14),
368
+ large: capsule(40, 18),
369
+ xlarge: capsule(48, 22)
370
+ };
371
+ function iconButtonSize(size) {
372
+ const spec = BUTTON_SIZES[size];
373
+ return { ...spec, width: spec.height, paddingX: 0 };
374
+ }
375
+ var MENU_PADDING = 0.3;
376
+ function menuSize(size) {
377
+ const spec = BUTTON_SIZES[size];
378
+ const padding = Math.round(spec.height * MENU_PADDING);
379
+ return {
380
+ row: spec.height,
381
+ padding,
382
+ itemRadius: spec.radius,
383
+ radius: spec.radius + padding,
384
+ icon: spec.icon,
385
+ labelPad: spec.paddingX
386
+ };
387
+ }
388
+ function nestedRadius(outerRadius, inset2, minimum = 0) {
389
+ return concentricRadius(outerRadius, inset2, minimum);
390
+ }
391
+ function defaultSize(input) {
392
+ return input.touch ? "touch" : "medium";
393
+ }
394
+ function hitSlop(bodyDp, minTargetDp) {
395
+ return bodyDp >= minTargetDp ? 0 : (minTargetDp - bodyDp) / 2;
396
+ }
397
+ function minTarget(input) {
398
+ return input.touch ? MIN_TOUCH_TARGET_DP : MIN_POINTER_TARGET_DP;
399
+ }
400
+
401
+ // src/kit/adaptive/input.ts
402
+ function sizeTier(input) {
403
+ return defaultSize({ touch: input.primary === "coarse" });
404
+ }
405
+ function hitTarget(input) {
406
+ return minTarget({ touch: input.anyCoarse });
407
+ }
408
+ function hoversWith(pointerType) {
409
+ return pointerType !== "touch";
410
+ }
411
+
412
+ // src/kit/adaptive/navigation.ts
413
+ var BAR_SLOTS = 5;
414
+ function formFor(ctx, spec) {
415
+ const { width, height } = classifyWindow(ctx.window);
416
+ if (ctx.posture.kind === "tabletop") return "bar";
417
+ if (widthAtLeast(width, "large")) return spec.sidebarCollapsed ? "rail" : "sidebar";
418
+ if (height === "compact") return "rail";
419
+ if (width === "compact") return "bar";
420
+ if (width === "medium" && (spec.panes ?? 1) > 1 && ctx.posture.kind !== "book") return "bar";
421
+ return "rail";
422
+ }
423
+ function navigationForm(ctx, spec) {
424
+ const form = formFor(ctx, spec);
425
+ const width = classifyWindow(ctx.window).width;
426
+ const large = widthAtLeast(width, "large");
427
+ const slots = BAR_SLOTS - (spec.search ? 1 : 0);
428
+ const primary = Math.max(0, Math.min(spec.primary, slots));
429
+ const secondary = Math.max(0, spec.secondary ?? 0) + Math.max(0, spec.primary - primary);
430
+ let search = null;
431
+ if (spec.search) {
432
+ const shortcut = large || ctx.input.keyboard;
433
+ if (large) search = { form: "field", place: form === "sidebar" ? "sidebar" : "toolbar", shortcut };
434
+ else search = form === "bar" ? { form: "button", place: "bar", shortcut } : { form: "item", place: "rail", shortcut };
435
+ }
436
+ return {
437
+ form,
438
+ edge: form === "bar" ? "bottom" : "leading",
439
+ primary,
440
+ secondary,
441
+ secondaryVisible: form === "sidebar",
442
+ search,
443
+ expandable: form !== "rail" ? null : large ? "beside" : width === "expanded" ? "over" : null,
444
+ collapsible: form === "sidebar"
445
+ };
446
+ }
447
+ function bottomAccessory(nav, state = {}) {
448
+ if (nav.form !== "bar") return { placement: "content-bottom", compact: false };
449
+ return state.barMinimized ? { placement: "in-bar", compact: true } : { placement: "above-bar", compact: false };
450
+ }
451
+ var ATOM_AT_REST = {
452
+ pressed: 0,
453
+ hovered: 0,
454
+ selected: 0,
455
+ disabled: false
456
+ };
457
+ function atomLift(kind) {
458
+ switch (kind) {
459
+ case "iconButton":
460
+ case "switch":
461
+ case "slider":
462
+ return 1;
463
+ default:
464
+ return 0;
465
+ }
466
+ }
467
+ var DISABLED_PRESENCE = 0.25;
468
+ var HOVER_GAIN = 0.35;
469
+ function activation(state) {
470
+ if (state.disabled) return 0;
471
+ const clamp2 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
472
+ return Math.max(clamp2(state.selected), clamp2(state.pressed), clamp2(state.hovered) * HOVER_GAIN);
473
+ }
474
+ function atomMaterial(base, kind, state = ATOM_AT_REST) {
475
+ const out = activeMaterial(base, activation(state));
476
+ if (!state.disabled) return out;
477
+ return { ...out, presence: out.presence * DISABLED_PRESENCE };
478
+ }
479
+ function atomAtRest(state) {
480
+ return state.pressed === 0 && state.hovered === 0 && state.selected === 0 && state.disabled === false;
481
+ }
482
+ var REST_BODY = { alpha: 0.25, white: 0.15, lift: 0.1 };
483
+ var PRESSED_BODY = { alpha: 0.34, white: 0.35, lift: 0 };
484
+ var FROST_LIFT = 0.26;
485
+ function atomBody(base, state = ATOM_AT_REST, frost = false) {
486
+ const on = activation(state);
487
+ const mix3 = (key) => REST_BODY[key] + (PRESSED_BODY[key] - REST_BODY[key]) * on;
488
+ let alpha = mix3("alpha");
489
+ let white = mix3("white");
490
+ let lift = mix3("lift");
491
+ if (state.disabled) alpha *= DISABLED_PRESENCE;
492
+ if (frost) {
493
+ const density = applyAccessibility(resolveOptics(base), {
494
+ reduceTransparency: true,
495
+ increaseContrast: false,
496
+ reduceMotion: false
497
+ }).bodyDensity;
498
+ alpha = Math.max(alpha, density);
499
+ white = 0;
500
+ lift = FROST_LIFT;
501
+ }
502
+ return { alpha, white, lift };
503
+ }
504
+
505
+ // src/kit/behavior/focus.ts
506
+ function stepFocus(event) {
507
+ return event.type === "focus" && event.visible;
508
+ }
509
+
510
+ // src/kit/behavior/hover.ts
511
+ function stepHover(event, canHover) {
512
+ return canHover && event.type === "enter";
513
+ }
514
+
515
+ // src/kit/behavior/press.ts
516
+ var PRESS_IDLE = { by: null };
517
+ function stepPress(state, event, enabled2) {
518
+ switch (event.type) {
519
+ case "down":
520
+ if (!enabled2 || state.by !== null) return { state, activated: false };
521
+ return { state: { by: event.source }, activated: false };
522
+ case "up":
523
+ if (state.by !== event.source) return { state, activated: false };
524
+ return { state: PRESS_IDLE, activated: enabled2 };
525
+ case "cancel":
526
+ return { state: PRESS_IDLE, activated: false };
527
+ case "leave":
528
+ return state.by === "pointer" ? { state: PRESS_IDLE, activated: false } : { state, activated: false };
529
+ }
530
+ }
531
+
532
+ // src/kit/behavior/control.ts
533
+ var TOUCH_INPUT = { hover: false };
534
+ var POINTER_INPUT = { hover: true };
535
+ var CONTROL_IDLE = {
536
+ press: PRESS_IDLE,
537
+ hovered: false,
538
+ focused: false,
539
+ disabled: false
540
+ };
541
+ function stepControl(state, event, caps) {
542
+ switch (event.type) {
543
+ case "down":
544
+ case "up":
545
+ case "cancel": {
546
+ const next = stepPress(state.press, event, !state.disabled);
547
+ return { state: { ...state, press: next.state }, activated: next.activated };
548
+ }
549
+ case "leave": {
550
+ const next = stepPress(state.press, event, !state.disabled);
551
+ return { state: { ...state, press: next.state, hovered: false }, activated: false };
552
+ }
553
+ case "enter":
554
+ return {
555
+ state: { ...state, hovered: !state.disabled && stepHover(event, caps.hover) },
556
+ activated: false
557
+ };
558
+ case "focus":
559
+ case "blur": {
560
+ const press = event.type === "blur" && state.press.by === "keyboard" ? PRESS_IDLE : state.press;
561
+ return {
562
+ state: { ...state, press, focused: !state.disabled && stepFocus(event) },
563
+ activated: false
564
+ };
565
+ }
566
+ case "disable":
567
+ return {
568
+ state: event.disabled ? { press: PRESS_IDLE, hovered: false, focused: false, disabled: true } : { ...state, disabled: false },
569
+ activated: false
570
+ };
571
+ }
572
+ }
573
+ function controlTargets(state) {
574
+ return {
575
+ pressed: state.press.by !== null ? 1 : 0,
576
+ hovered: state.hovered ? 1 : 0,
577
+ focus: state.focused ? 1 : 0,
578
+ disabled: state.disabled
579
+ };
580
+ }
581
+
582
+ // src/kit/behavior/visual.ts
583
+ var createControlVisual = () => ({
584
+ pressed: { x: 0, v: 0, target: 0 },
585
+ hovered: { x: 0, v: 0, target: 0 },
586
+ focus: { x: 0, v: 0, target: 0 }
587
+ });
588
+ var PRESS = { response: 0.18, damping: 0.78 };
589
+ var HOVER = { response: 0.22, damping: 1 };
590
+ var FOCUS = { response: 0.16, damping: 1 };
591
+ function stepControlVisual(visual, dt, state, instant = false) {
592
+ const t = controlTargets(state);
593
+ visual.pressed.target = t.pressed;
594
+ visual.hovered.target = t.hovered;
595
+ visual.focus.target = t.focus;
596
+ const a = stepSpring(visual.pressed, dt, PRESS.response, PRESS.damping, instant);
597
+ const b = stepSpring(visual.hovered, dt, HOVER.response, HOVER.damping, instant);
598
+ const c = stepSpring(visual.focus, dt, FOCUS.response, FOCUS.damping, instant);
599
+ return a || b || c;
600
+ }
601
+ function visualAtomState(visual, state) {
602
+ return {
603
+ pressed: visual.pressed.x,
604
+ hovered: visual.hovered.x,
605
+ selected: 0,
606
+ disabled: state.disabled
607
+ };
608
+ }
609
+ function settleControlVisual(visual, state) {
610
+ stepControlVisual(visual, 0, state, true);
611
+ }
612
+ var MENU_CLOSED = { open: false, active: -1, typed: "", typedAt: Number.NEGATIVE_INFINITY };
613
+ var TYPEAHEAD_SECONDS = 0.5;
614
+ var exists = (items, i) => i >= 0 && i < items.length;
615
+ var enabled = (items, i) => exists(items, i) && !items[i].disabled;
616
+ var reachable = (items, i) => exists(items, i) && (items[i].focusable ?? !items[i].disabled);
617
+ function seek(items, from, step) {
618
+ const n = items.length;
619
+ for (let k = 1; k <= n; k += 1) {
620
+ const i = ((from + step * k) % n + n) % n;
621
+ if (reachable(items, i)) return i;
622
+ }
623
+ return -1;
624
+ }
625
+ var first = (items) => seek(items, -1, 1);
626
+ var last = (items) => seek(items, items.length, -1);
627
+ var toItem = (index) => index < 0 ? { to: "menu" } : { to: "item", index };
628
+ function typeahead(state, items, key, at) {
629
+ const fresh = at - state.typedAt > TYPEAHEAD_SECONDS;
630
+ const typed = (fresh ? "" : state.typed) + key.toLowerCase();
631
+ const repeated = typed.length > 1 && [...typed].every((c) => c === typed[0]);
632
+ const needle = repeated ? typed[0] : typed;
633
+ const start = needle.length > 1 ? state.active - 1 : state.active;
634
+ const n = items.length;
635
+ for (let k = 1; k <= n; k += 1) {
636
+ const i = ((start + k) % n + n) % n;
637
+ if (reachable(items, i) && items[i].label.trim().toLowerCase().startsWith(needle)) {
638
+ return { state: { ...state, active: i, typed, typedAt: at }, focus: { to: "item", index: i }, chosen: null };
639
+ }
640
+ }
641
+ return { state: { ...state, typed, typedAt: at }, focus: { to: "stay" }, chosen: null };
642
+ }
643
+ function stepMenu(state, event, items) {
644
+ const stay = { state, focus: { to: "stay" }, chosen: null };
645
+ if (event.type === "open") {
646
+ const active = event.focus === "first" ? first(items) : event.focus === "last" ? last(items) : -1;
647
+ return { state: { ...MENU_CLOSED, open: true, active }, focus: toItem(active), chosen: null };
648
+ }
649
+ if (!state.open) return stay;
650
+ switch (event.type) {
651
+ case "move": {
652
+ const active = event.to === "first" ? first(items) : event.to === "last" ? last(items) : seek(items, state.active < 0 && event.to === "previous" ? items.length : state.active, event.to === "next" ? 1 : -1);
653
+ if (active < 0) return stay;
654
+ return { state: { ...state, active, typed: "" }, focus: { to: "item", index: active }, chosen: null };
655
+ }
656
+ case "point":
657
+ if (!reachable(items, event.index) || event.index === state.active) return stay;
658
+ return { state: { ...state, active: event.index, typed: "" }, focus: { to: "item", index: event.index }, chosen: null };
659
+ case "type":
660
+ return typeahead(state, items, event.key, event.at);
661
+ case "choose":
662
+ if (!enabled(items, event.index)) return stay;
663
+ return { state: MENU_CLOSED, focus: { to: "trigger" }, chosen: event.index };
664
+ case "dismiss":
665
+ return { state: MENU_CLOSED, focus: event.reason === "outside" ? { to: "none" } : { to: "trigger" }, chosen: null };
666
+ }
667
+ }
668
+ var MENU_CORNERS = {
669
+ "top-left": { x: -1, y: -1 },
670
+ "top-right": { x: 1, y: -1 },
671
+ "bottom-left": { x: -1, y: 1 },
672
+ "bottom-right": { x: 1, y: 1 }
673
+ };
674
+ function menuCorner(trigger, region) {
675
+ const x0 = region.x ?? 0;
676
+ const y0 = region.y ?? 0;
677
+ return {
678
+ x: trigger.x + trigger.width / 2 <= x0 + region.width / 2 ? -1 : 1,
679
+ y: trigger.y + trigger.height / 2 <= y0 + region.height / 2 ? -1 : 1
680
+ };
681
+ }
682
+ function along(start, length, size, side) {
683
+ return side < 0 ? start : start + length - size;
684
+ }
685
+ function fits(at, size, lo, hi, margin) {
686
+ return at >= lo + margin && at + size <= hi - margin;
687
+ }
688
+ var clamp = (v, lo, hi) => Math.min(Math.max(v, lo), Math.max(lo, hi));
689
+ function menuPlacement(trigger, menu, region, options = {}) {
690
+ const margin = options.margin ?? 8;
691
+ const x0 = region.x ?? 0;
692
+ const y0 = region.y ?? 0;
693
+ const x1 = x0 + region.width;
694
+ const y1 = y0 + region.height;
695
+ const width = Math.max(0, Math.min(menu.width, region.width - 2 * margin));
696
+ const height = Math.max(0, Math.min(menu.height, region.height - 2 * margin));
697
+ const corner = { ...options.corner ?? menuCorner(trigger, region) };
698
+ let x = along(trigger.x, trigger.width, width, corner.x);
699
+ let y = along(trigger.y, trigger.height, height, corner.y);
700
+ if (!options.corner) {
701
+ const fx = along(trigger.x, trigger.width, width, corner.x === -1 ? 1 : -1);
702
+ if (!fits(x, width, x0, x1, margin) && fits(fx, width, x0, x1, margin)) {
703
+ corner.x = corner.x === -1 ? 1 : -1;
704
+ x = fx;
705
+ }
706
+ const fy = along(trigger.y, trigger.height, height, corner.y === -1 ? 1 : -1);
707
+ if (!fits(y, height, y0, y1, margin) && fits(fy, height, y0, y1, margin)) {
708
+ corner.y = corner.y === -1 ? 1 : -1;
709
+ y = fy;
710
+ }
711
+ }
712
+ x = clamp(x, x0 + margin, x1 - margin - width);
713
+ y = clamp(y, y0 + margin, y1 - margin - height);
714
+ const rect = { x, y, width, height };
715
+ return {
716
+ rect,
717
+ corner,
718
+ anchor: { x: corner.x < 0 ? x : x + width, y: corner.y < 0 ? y : y + height }
719
+ };
720
+ }
721
+ var center = (r) => ({ x: r.x + r.width / 2, y: r.y + r.height / 2 });
722
+ function menuActivity(phase) {
723
+ const shrink = Math.min(Math.max(phase.shrink, 0), 1);
724
+ const grow = Math.min(Math.max(phase.grow, 0), 1);
725
+ return shrink * (1 - grow);
726
+ }
727
+ function menuSurfaceFrame(trigger, menu, anchor, phase, bounds) {
728
+ const frame = growthFrame(
729
+ { geometry: { width: trigger.width, height: trigger.height, cornerRadius: trigger.radius }, center: center(trigger) },
730
+ { geometry: { width: menu.width, height: menu.height, cornerRadius: menu.radius }, center: center(menu) },
731
+ anchor,
732
+ phase
733
+ );
734
+ const { geometry, center: c } = frame.body;
735
+ let box2 = {
736
+ x: c.x - geometry.width / 2,
737
+ y: c.y - geometry.height / 2,
738
+ width: geometry.width,
739
+ height: geometry.height,
740
+ radius: geometry.cornerRadius
741
+ };
742
+ let morph = frame.merge;
743
+ const held = bounds ? intersectRect(box2, bounds) : null;
744
+ if (held && (held.width < box2.width || held.height < box2.height)) {
745
+ const was = center(box2);
746
+ const now = center(held);
747
+ box2 = { ...held, radius: Math.min(box2.radius, held.width / 2, held.height / 2) };
748
+ if (morph) morph = { ...morph, offsetX: morph.offsetX + was.x - now.x, offsetY: morph.offsetY + was.y - now.y };
749
+ }
750
+ return {
751
+ box: box2,
752
+ morph,
753
+ content: { x: menu.x - box2.x, y: menu.y - box2.y },
754
+ ink: frame.ink,
755
+ activity: menuActivity(phase)
756
+ };
757
+ }
758
+ function menuGlass(base) {
759
+ return { ...base, roughness: Math.max(base.roughness, VIREGLASS_SHEET_MATERIAL.roughness) };
760
+ }
761
+ function menuMaterial(base, activity) {
762
+ return atomMaterial(menuGlass(base), "menu", { ...ATOM_AT_REST, pressed: activity });
763
+ }
764
+ var MENU_DROP_BODY = { alpha: 0.9, white: 0.1, lift: 0.2 };
765
+ function menuBody(base, activity, frost = false) {
766
+ const rest = atomBody(base, ATOM_AT_REST, frost);
767
+ if (frost) return rest;
768
+ const k = Math.min(Math.max(activity, 0), 1);
769
+ const mix3 = (key) => rest[key] + (MENU_DROP_BODY[key] - rest[key]) * k;
770
+ return { alpha: mix3("alpha"), white: mix3("white"), lift: mix3("lift") };
771
+ }
772
+ var MENU_ITEM_FILL = { active: 0.1, pressed: 0.18 };
773
+ function menuItemFill(state) {
774
+ if (state.disabled) return 0;
775
+ if (state.pressed) return MENU_ITEM_FILL.pressed;
776
+ return state.active ? MENU_ITEM_FILL.active : 0;
777
+ }
778
+
779
+ // src/kit/nav/layout.ts
780
+ var NAV_SIZES = {
781
+ bar: { height: 56, item: 74, minItem: 44, inset: 3, margin: 16, searchGap: 8, minimized: 44 },
782
+ rail: { width: 80, item: 56, gap: 4, padding: 8, margin: 8, head: 48 },
783
+ sidebar: { width: 280, row: { touch: 44, pointer: 36 }, padding: 8, margin: 8, head: 48, separator: 12 },
784
+ accessory: { height: 48, gap: 8, inline: 44, content: 56, margin: 8 },
785
+ lens: { inflate: 6 },
786
+ toggle: 44
787
+ };
788
+ var box = (x, y, width, height, radius) => ({ x, y, width, height, radius });
789
+ function barLayout(input, items, minimized) {
790
+ const { bar, accessory } = NAV_SIZES;
791
+ const { width, height } = input.window;
792
+ const bottom = Math.max(input.bottomInset ?? 0, bar.margin);
793
+ const hasSearch = input.search?.form === "button";
794
+ const placement = input.accessory ? bottomAccessory({ form: "bar" }, { barMinimized: minimized }).placement : null;
795
+ const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
796
+ if (minimized) {
797
+ const centre = height - bottom - bar.height / 2;
798
+ const d = bar.minimized;
799
+ const body2 = box(bar.margin, centre - d / 2, d, d, d / 2);
800
+ const search2 = hasSearch ? box(width - bar.margin - d, body2.y, d, d, d / 2) : null;
801
+ let acc2 = null;
802
+ if (placement) {
803
+ const x2 = body2.x + d + accessory.gap;
804
+ const right = search2 ? search2.x - accessory.gap : width - bar.margin;
805
+ acc2 = box(x2, body2.y, Math.max(0, right - x2), accessory.inline, accessory.inline / 2);
806
+ }
807
+ return {
808
+ form: "bar",
809
+ minimized: true,
810
+ body: body2,
811
+ items: Array.from({ length: items }, () => ({ ...body2 })),
812
+ pill: selected >= 0 ? { ...body2 } : null,
813
+ search: search2,
814
+ accessory: acc2,
815
+ toggle: null,
816
+ accessoryPlacement: placement,
817
+ insets: { bottom: height - body2.y, start: 0 }
818
+ };
819
+ }
820
+ const searchBlock = hasSearch ? bar.height + bar.searchGap : 0;
821
+ const fit = items > 0 ? (width - 2 * bar.margin - searchBlock - 2 * bar.inset) / items : bar.item;
822
+ const itemWidth = Math.max(bar.minItem, Math.min(bar.item, fit));
823
+ const bodyWidth = items * itemWidth + 2 * bar.inset;
824
+ const x = Math.max(bar.margin, (width - bodyWidth - searchBlock) / 2);
825
+ const body = box(x, height - bottom - bar.height, bodyWidth, bar.height, bar.height / 2);
826
+ const cellHeight = bar.height - 2 * bar.inset;
827
+ const cells = Array.from(
828
+ { length: items },
829
+ (_, i) => box(x + bar.inset + i * itemWidth, body.y + bar.inset, itemWidth, cellHeight, cellHeight / 2)
830
+ );
831
+ const search = hasSearch ? box(x + bodyWidth + bar.searchGap, body.y, bar.height, bar.height, bar.height / 2) : null;
832
+ let acc = null;
833
+ if (placement) {
834
+ acc = box(body.x, body.y - accessory.gap - accessory.height, bodyWidth + searchBlock, accessory.height, accessory.height / 2);
835
+ }
836
+ return {
837
+ form: "bar",
838
+ minimized: false,
839
+ body,
840
+ items: cells,
841
+ pill: selected >= 0 ? { ...cells[selected] } : null,
842
+ search,
843
+ accessory: acc,
844
+ toggle: null,
845
+ accessoryPlacement: placement,
846
+ insets: { bottom: height - body.y + (acc ? accessory.height + accessory.gap : 0), start: 0 }
847
+ };
848
+ }
849
+ function contentAccessory(input, start) {
850
+ const { accessory } = NAV_SIZES;
851
+ const base = input.bottomInset ?? 0;
852
+ if (!input.accessory) return { box: null, bottom: base };
853
+ const bottom = Math.max(base, accessory.margin);
854
+ const width = Math.max(0, input.window.width - start - accessory.margin);
855
+ const acc = box(start, input.window.height - bottom - accessory.content, width, accessory.content, accessory.content / 2);
856
+ return { box: acc, bottom: bottom + accessory.content };
857
+ }
858
+ function headToggle(body, padding, head, dx) {
859
+ const d = NAV_SIZES.toggle;
860
+ return box(body.x + dx, body.y + padding + (head - d) / 2, d, d, d / 2);
861
+ }
862
+ function inset(b, d) {
863
+ return box(b.x + d, b.y + d, b.width - 2 * d, b.height - 2 * d, (Math.min(b.width, b.height) - 2 * d) / 2);
864
+ }
865
+ function railLayout(input, items) {
866
+ const { rail, bar } = NAV_SIZES;
867
+ const hasSearch = input.search?.form === "item";
868
+ const n = items + (hasSearch ? 1 : 0);
869
+ const head = input.toggle ? rail.head : 0;
870
+ const natural = rail.padding * 2 + head + n * rail.item + Math.max(n - 1, 0) * rail.gap;
871
+ const height = Math.min(natural, Math.max(input.window.height - 2 * rail.margin, 0));
872
+ const body = box(rail.margin, rail.margin, rail.width, height, 28);
873
+ const cell = (i) => box(body.x + rail.padding, body.y + rail.padding + head + i * (rail.item + rail.gap), rail.width - 2 * rail.padding, rail.item, 24);
874
+ const cells = Array.from({ length: items }, (_, i) => cell(i));
875
+ const selected = input.selected >= 0 && input.selected < items ? input.selected : -1;
876
+ const start = body.x + body.width + rail.margin;
877
+ const acc = contentAccessory(input, start);
878
+ return {
879
+ form: "rail",
880
+ minimized: false,
881
+ body,
882
+ items: cells,
883
+ pill: selected >= 0 ? inset(cells[selected], bar.inset) : null,
884
+ search: hasSearch ? cell(items) : null,
885
+ accessory: acc.box,
886
+ toggle: input.toggle ? headToggle(body, rail.padding, rail.head, (rail.width - NAV_SIZES.toggle) / 2) : null,
887
+ accessoryPlacement: acc.box ? "content-bottom" : null,
888
+ insets: { bottom: acc.bottom, start }
889
+ };
890
+ }
891
+ function sidebarLayout(input, items, secondary) {
892
+ const { sidebar } = NAV_SIZES;
893
+ const row = sidebar.row[input.tier];
894
+ const body = box(sidebar.margin, sidebar.margin, sidebar.width, input.window.height - 2 * sidebar.margin, 24);
895
+ const rowWidth = sidebar.width - 2 * sidebar.padding;
896
+ const rowAt = (y2) => box(body.x + sidebar.padding, y2, rowWidth, row, row / 2);
897
+ let y = body.y + sidebar.padding + (input.toggle ? sidebar.head : 0);
898
+ let search = null;
899
+ if (input.search?.form === "field") {
900
+ search = rowAt(y);
901
+ y += row;
902
+ }
903
+ const cells = [];
904
+ for (let i = 0; i < items; i += 1, y += row) cells.push(rowAt(y));
905
+ if (secondary > 0) y += sidebar.separator;
906
+ for (let i = 0; i < secondary; i += 1, y += row) cells.push(rowAt(y));
907
+ const selected = input.selected >= 0 && input.selected < cells.length ? input.selected : -1;
908
+ const start = body.x + body.width + sidebar.margin;
909
+ const acc = contentAccessory(input, start);
910
+ return {
911
+ form: "sidebar",
912
+ minimized: false,
913
+ body,
914
+ items: cells,
915
+ pill: selected >= 0 ? { ...cells[selected] } : null,
916
+ search,
917
+ accessory: acc.box,
918
+ toggle: input.toggle ? headToggle(body, sidebar.padding, sidebar.head, sidebar.width - sidebar.padding - NAV_SIZES.toggle) : null,
919
+ accessoryPlacement: acc.box ? "content-bottom" : null,
920
+ insets: { bottom: acc.bottom, start }
921
+ };
922
+ }
923
+ function navLayout(input) {
924
+ const items = Math.max(0, Math.floor(input.items));
925
+ const secondary = Math.max(0, Math.floor(input.secondary));
926
+ if (input.form === "bar") return barLayout(input, items, !!input.minimized);
927
+ return input.form === "rail" ? railLayout(input, items) : sidebarLayout(input, items, secondary);
928
+ }
929
+
930
+ // src/kit/nav/drag.ts
931
+ var MAX_STRETCH = 0.25;
932
+ var SATURATION_SPEED = 1500;
933
+ var STRETCH_TAU = 0.05;
934
+ var OVERHANG = 4;
935
+ var LAND = { response: 0.2, damping: 0.8 };
936
+ var LIFT = { response: 0.2, damping: 1 };
937
+ var createNavDrag = () => ({
938
+ active: false,
939
+ x: { x: 0, v: 0, target: 0 },
940
+ lift: { x: 0, v: 0, target: 0 },
941
+ stretch: 0,
942
+ index: 0,
943
+ lastX: 0
944
+ });
945
+ var draggable = (layout) => layout.form === "bar" && !layout.minimized && layout.items.length > 0;
946
+ function lensSize(layout) {
947
+ const cell = layout.items[0];
948
+ const pad = 2 * NAV_SIZES.lens.inflate;
949
+ return { width: cell.width + pad, height: cell.height + pad };
950
+ }
951
+ function clampCentre(layout, x) {
952
+ const half = lensSize(layout).width / 2;
953
+ const { body } = layout;
954
+ return Math.min(Math.max(x, body.x + half - OVERHANG), body.x + body.width - half + OVERHANG);
955
+ }
956
+ function indexAt(layout, x) {
957
+ const first2 = layout.items[0];
958
+ const i = Math.floor((x - first2.x) / first2.width);
959
+ return Math.min(Math.max(i, 0), layout.items.length - 1);
960
+ }
961
+ var centreOf = (layout, index) => layout.items[index].x + layout.items[index].width / 2;
962
+ function drop(state) {
963
+ state.active = false;
964
+ state.lift.x = 0;
965
+ state.lift.v = 0;
966
+ state.lift.target = 0;
967
+ }
968
+ function settleTowards(state, layout, index) {
969
+ state.active = false;
970
+ state.index = index;
971
+ state.x.target = centreOf(layout, index);
972
+ state.lift.target = 0;
973
+ }
974
+ function pressNavDrag(state, layout, pointerX) {
975
+ if (!draggable(layout)) return;
976
+ const x = clampCentre(layout, pointerX);
977
+ state.active = true;
978
+ state.x.x = x;
979
+ state.x.v = 0;
980
+ state.x.target = x;
981
+ state.lift.target = 1;
982
+ state.stretch = 0;
983
+ state.lastX = pointerX;
984
+ state.index = indexAt(layout, x);
985
+ }
986
+ function moveNavDrag(state, layout, pointerX, dt) {
987
+ if (!state.active || !draggable(layout)) return;
988
+ const x = clampCentre(layout, pointerX);
989
+ state.x.x = x;
990
+ state.x.v = 0;
991
+ state.x.target = x;
992
+ const speed = dt > 0 ? Math.abs(pointerX - state.lastX) / dt : 0;
993
+ const goal = MAX_STRETCH * Math.min(speed / SATURATION_SPEED, 1);
994
+ state.stretch += (goal - state.stretch) * (dt > 0 ? 1 - Math.exp(-dt / STRETCH_TAU) : 1);
995
+ state.lastX = pointerX;
996
+ state.index = indexAt(layout, x);
997
+ }
998
+ function releaseNavDrag(state, layout) {
999
+ if (!draggable(layout)) {
1000
+ drop(state);
1001
+ return state.index;
1002
+ }
1003
+ const index = indexAt(layout, state.x.x);
1004
+ settleTowards(state, layout, index);
1005
+ return index;
1006
+ }
1007
+ function cancelNavDrag(state, layout, selected) {
1008
+ if (!draggable(layout)) {
1009
+ drop(state);
1010
+ return;
1011
+ }
1012
+ const index = selected >= 0 && selected < layout.items.length ? selected : indexAt(layout, state.x.x);
1013
+ settleTowards(state, layout, index);
1014
+ }
1015
+ function stepNavDrag(state, dt, reduceMotion = false) {
1016
+ let moving = state.active;
1017
+ if (state.stretch > 0) {
1018
+ state.stretch *= Math.exp(-Math.max(dt, 0) / STRETCH_TAU);
1019
+ if (reduceMotion || state.stretch < 1e-3) state.stretch = 0;
1020
+ else moving = true;
1021
+ }
1022
+ if (!state.active && stepSpring(state.x, dt, LAND.response, LAND.damping, reduceMotion)) moving = true;
1023
+ if (stepSpring(state.lift, dt, LIFT.response, LIFT.damping, reduceMotion)) moving = true;
1024
+ return moving;
1025
+ }
1026
+ function navDragFrame(state, layout) {
1027
+ if (!draggable(layout)) return null;
1028
+ const lift = Math.min(Math.max(state.lift.x, 0), 1);
1029
+ if (!state.active && lift === 0 && state.x.x === state.x.target) return null;
1030
+ const size = lensSize(layout);
1031
+ const width = size.width * (1 + state.stretch);
1032
+ const height = size.height * (1 - state.stretch / 2);
1033
+ const cy = layout.body.y + layout.body.height / 2;
1034
+ return {
1035
+ lens: { x: state.x.x - width / 2, y: cy - height / 2, width, height, radius: height / 2 },
1036
+ lift,
1037
+ hover: indexAt(layout, state.x.x)
1038
+ };
1039
+ }
1040
+
1041
+ // src/kit/nav/form.ts
1042
+ var SEARCH = {
1043
+ bar: { form: "button", place: "bar" },
1044
+ rail: { form: "item", place: "rail" },
1045
+ sidebar: { form: "field", place: "sidebar" }
1046
+ };
1047
+ function fixedNavigationForm(nav, form) {
1048
+ if (nav.form === form) return nav;
1049
+ return {
1050
+ ...nav,
1051
+ form,
1052
+ edge: form === "bar" ? "bottom" : "leading",
1053
+ secondaryVisible: form === "sidebar",
1054
+ search: nav.search ? { ...nav.search, ...SEARCH[form] } : null,
1055
+ expandable: form === "rail" ? nav.expandable : null,
1056
+ collapsible: form === "sidebar"
1057
+ };
1058
+ }
1059
+
1060
+ // src/kit/nav/minimize.ts
1061
+ var MINIMIZE_THRESHOLD = 24;
1062
+ var MINIMIZE_JITTER = 4;
1063
+ var createMinimize = (scrollTop = 0) => ({ minimized: false, run: 0, last: scrollTop });
1064
+ function stepMinimize(state, scrollTop) {
1065
+ const delta = scrollTop - state.last;
1066
+ state.last = scrollTop;
1067
+ if (scrollTop <= 0) {
1068
+ state.minimized = false;
1069
+ state.run = 0;
1070
+ return false;
1071
+ }
1072
+ if (delta === 0) return state.minimized;
1073
+ const along2 = Math.sign(delta) === Math.sign(state.run) || Math.abs(delta) < MINIMIZE_JITTER;
1074
+ state.run = along2 ? state.run + delta : delta;
1075
+ if (state.run >= MINIMIZE_THRESHOLD) state.minimized = true;
1076
+ else if (state.run <= -MINIMIZE_THRESHOLD) state.minimized = false;
1077
+ return state.minimized;
1078
+ }
1079
+ var clampScrollTop = (scrollTop, scrollHeight, clientHeight) => Math.min(Math.max(scrollTop, 0), Math.max(scrollHeight - clientHeight, 0));
1080
+ function expandMinimize(state) {
1081
+ state.minimized = false;
1082
+ state.run = 0;
1083
+ }
1084
+
1085
+ // src/kit/nav/morph.ts
1086
+ var NAV_MORPH_TIMING = { response: 1.06, damping: 1 };
1087
+ var NAV_MINIMIZE_TIMING = FORM_MORPH_TIMING;
1088
+ var LIFT_END = 0.12;
1089
+ var CONTRACT = [0.08, 0.38];
1090
+ var TRAVEL = [0.36, 0.56];
1091
+ var GROW = [0.52, 0.86];
1092
+ var SETTLE_START = 0.82;
1093
+ var INK_OUT = [0.08, 0.28];
1094
+ var INK_IN = [0.62, 0.86];
1095
+ var SWAP_AT = 0.46;
1096
+ var SMOOTHING = 24;
1097
+ var SEED = 8;
1098
+ var STAND_IN = 44;
1099
+ var clamp01 = (x) => Math.min(Math.max(x, 0), 1);
1100
+ var mix2 = (a, b, t) => a + (b - a) * t;
1101
+ var mixBox = (a, b, t) => ({
1102
+ x: mix2(a.x, b.x, t),
1103
+ y: mix2(a.y, b.y, t),
1104
+ width: mix2(a.width, b.width, t),
1105
+ height: mix2(a.height, b.height, t),
1106
+ radius: mix2(a.radius, b.radius, t)
1107
+ });
1108
+ var inflate = (b, d) => ({
1109
+ x: b.x - d,
1110
+ y: b.y - d,
1111
+ width: b.width + 2 * d,
1112
+ height: b.height + 2 * d,
1113
+ radius: b.radius + d
1114
+ });
1115
+ var centred = (cx, cy, size) => ({ x: cx - size / 2, y: cy - size / 2, width: size, height: size, radius: size / 2 });
1116
+ var trailingEnd = (body, size) => centred(body.x + body.width - body.height / 2, body.y + body.height / 2, size);
1117
+ var pillOrStandIn = (layout) => layout.pill ?? centred(layout.body.x + layout.body.width / 2, layout.body.y + layout.body.height / 2, STAND_IN);
1118
+ var separateSearch = (layout) => layout.form === "bar" ? layout.search : null;
1119
+ function restFrame(layout, side) {
1120
+ const search = separateSearch(layout);
1121
+ return {
1122
+ phase: "rest",
1123
+ body: layout.body,
1124
+ second: search ? { box: search, smoothing: 0 } : null,
1125
+ lens: null,
1126
+ ink: side === "from" ? { outgoing: 1, incoming: 0 } : { outgoing: 0, incoming: 1 },
1127
+ showing: side
1128
+ };
1129
+ }
1130
+ var layoutOrigin = (layout) => {
1131
+ const search = separateSearch(layout);
1132
+ return {
1133
+ body: layout.body,
1134
+ second: search ? { box: search, smoothing: 0 } : null,
1135
+ lens: { box: pillOrStandIn(layout), lift: 0 },
1136
+ ink: 1,
1137
+ lead: 0,
1138
+ hold: null
1139
+ };
1140
+ };
1141
+ var frameOrigin = (frame) => ({
1142
+ body: frame.body,
1143
+ second: frame.second,
1144
+ lens: frame.lens ?? { box: centred(frame.body.x + frame.body.width / 2, frame.body.y + frame.body.height / 2, STAND_IN), lift: 0 },
1145
+ ink: Math.max(frame.ink.outgoing, frame.ink.incoming),
1146
+ lead: CONTRACT[0],
1147
+ hold: null
1148
+ });
1149
+ function phaseAt(tau) {
1150
+ if (tau < CONTRACT[0]) return "lift";
1151
+ if (tau < TRAVEL[0]) return "contract";
1152
+ if (tau < GROW[0]) return "travel";
1153
+ return tau < SETTLE_START ? "grow" : "settle";
1154
+ }
1155
+ function lensFrame(o, to, t) {
1156
+ const tau = o.lead + (1 - o.lead) * t;
1157
+ const u = smoothstep(o.lead, LIFT_END, tau);
1158
+ const base = inflate(o.lens.box, NAV_SIZES.lens.inflate * (1 - o.lens.lift) * u);
1159
+ const lift = mix2(o.lens.lift, 1, u);
1160
+ const toPill = pillOrStandIn(to);
1161
+ const k = smoothstep(TRAVEL[0], TRAVEL[1], tau);
1162
+ const kk = o.hold ? clamp01((k - o.hold.k0) / (1 - o.hold.k0)) : k;
1163
+ const travelling = mixBox(o.hold?.box ?? base, inflate(toPill, NAV_SIZES.lens.inflate), kk);
1164
+ const c = smoothstep(CONTRACT[0], CONTRACT[1], tau);
1165
+ const g = smoothstep(GROW[0], GROW[1], tau);
1166
+ const s = smoothstep(SETTLE_START, 1, tau);
1167
+ const contracted = mixBox(o.body, travelling, c);
1168
+ const body = mixBox(contracted, to.body, g);
1169
+ let second = null;
1170
+ const budding = separateSearch(to);
1171
+ if (o.second && c < 1) {
1172
+ second = {
1173
+ box: mixBox(o.second.box, trailingEnd(contracted, 0), c),
1174
+ smoothing: mix2(o.second.smoothing, SMOOTHING, c)
1175
+ };
1176
+ } else if (budding && g > 0) {
1177
+ second = { box: mixBox(trailingEnd(body, SEED), budding, g), smoothing: SMOOTHING * (1 - g) };
1178
+ }
1179
+ return {
1180
+ phase: phaseAt(tau),
1181
+ body,
1182
+ second,
1183
+ lens: { box: mixBox(travelling, toPill, s), lift: lift * (1 - s) },
1184
+ ink: {
1185
+ outgoing: o.ink * (1 - smoothstep(INK_OUT[0], INK_OUT[1], tau)),
1186
+ incoming: smoothstep(INK_IN[0], INK_IN[1], tau)
1187
+ },
1188
+ showing: tau < SWAP_AT ? "from" : "to"
1189
+ };
1190
+ }
1191
+ function contractFrame(o, to, t) {
1192
+ const body = mixBox(o.body, to.body, t);
1193
+ const target = separateSearch(to);
1194
+ let second = null;
1195
+ if (o.second && target) {
1196
+ second = { box: mixBox(o.second.box, target, t), smoothing: mix2(o.second.smoothing, 0, t) };
1197
+ } else if (o.second) {
1198
+ second = { box: mixBox(o.second.box, trailingEnd(body, 0), t), smoothing: mix2(o.second.smoothing, SMOOTHING, t) };
1199
+ } else if (target) {
1200
+ second = { box: mixBox(trailingEnd(body, SEED), target, t), smoothing: SMOOTHING * (1 - t) };
1201
+ }
1202
+ return {
1203
+ phase: "contract",
1204
+ body,
1205
+ second,
1206
+ lens: null,
1207
+ ink: { outgoing: o.ink * (1 - smoothstep(0, 0.45, t)), incoming: smoothstep(0.55, 1, t) },
1208
+ showing: t < 0.5 ? "from" : "to"
1209
+ };
1210
+ }
1211
+ var frameFrom = (o, to, t, via) => via === "lens" ? lensFrame(o, to, t) : contractFrame(o, to, t);
1212
+ function navMorphFrame(from, to, t, via = "lens") {
1213
+ if (t <= 0) return restFrame(from, "from");
1214
+ if (t >= 1) return restFrame(to, "to");
1215
+ return frameFrom(layoutOrigin(from), to, t, via);
1216
+ }
1217
+ var createNavMorph = (at) => ({
1218
+ t: { x: 0, v: 0, target: 0 },
1219
+ from: at,
1220
+ to: at,
1221
+ via: "lens",
1222
+ pivot: null,
1223
+ pivotAt: 0
1224
+ });
1225
+ var sameForm = (a, b) => a.form === b.form && a.minimized === b.minimized;
1226
+ var atRest = (s) => s.t.v === 0 && s.t.x === s.t.target && (s.t.x === 0 || s.t.x === 1);
1227
+ function currentNavFrame(state) {
1228
+ const x = clamp01(state.t.x);
1229
+ if (x >= 1) return restFrame(state.to, "to");
1230
+ const { pivot } = state;
1231
+ if (!pivot) return navMorphFrame(state.from, state.to, x, state.via);
1232
+ if (state.pivotAt > 0 && pivot.lens) {
1233
+ const origin = layoutOrigin(state.from);
1234
+ origin.hold = { box: pivot.lens.box, k0: smoothstep(TRAVEL[0], TRAVEL[1], state.pivotAt) };
1235
+ return lensFrame(origin, state.to, x);
1236
+ }
1237
+ return frameFrom(frameOrigin(pivot), state.to, x, state.via);
1238
+ }
1239
+ function aimNavMorph(state, next, via = "lens") {
1240
+ const { t } = state;
1241
+ if (atRest(state)) {
1242
+ const current = t.x >= 1 ? state.to : state.from;
1243
+ if (sameForm(current, next)) {
1244
+ if (t.x >= 1) state.to = next;
1245
+ else state.from = next;
1246
+ state.pivot = null;
1247
+ return;
1248
+ }
1249
+ state.from = current;
1250
+ state.to = next;
1251
+ state.via = via;
1252
+ state.pivot = null;
1253
+ state.pivotAt = 0;
1254
+ t.x = 0;
1255
+ t.v = 0;
1256
+ t.target = 1;
1257
+ return;
1258
+ }
1259
+ if (!state.pivot) {
1260
+ if (sameForm(next, state.from)) {
1261
+ state.from = next;
1262
+ t.target = 0;
1263
+ return;
1264
+ }
1265
+ if (sameForm(next, state.to)) {
1266
+ state.to = next;
1267
+ t.target = 1;
1268
+ return;
1269
+ }
1270
+ const x = clamp01(t.x);
1271
+ if (via === "lens" && state.via === "lens" && x < GROW[0]) {
1272
+ if (x >= TRAVEL[0]) {
1273
+ state.pivot = currentNavFrame(state);
1274
+ state.pivotAt = x;
1275
+ }
1276
+ state.to = next;
1277
+ t.target = 1;
1278
+ return;
1279
+ }
1280
+ } else if (sameForm(next, state.to)) {
1281
+ state.to = next;
1282
+ t.target = 1;
1283
+ return;
1284
+ }
1285
+ const frozen = currentNavFrame(state);
1286
+ if (frozen.showing === "to") state.from = state.to;
1287
+ state.pivot = frozen;
1288
+ state.pivotAt = 0;
1289
+ state.to = next;
1290
+ state.via = via;
1291
+ t.x = 0;
1292
+ t.v = 0;
1293
+ t.target = 1;
1294
+ }
1295
+ function stepNavMorph(state, dt, reduceMotion = false) {
1296
+ const timing = state.via === "lens" ? NAV_MORPH_TIMING : NAV_MINIMIZE_TIMING;
1297
+ const moved = stepSpring(state.t, dt, timing.response, timing.damping, reduceMotion);
1298
+ if (!moved && state.t.x >= 1) {
1299
+ state.pivot = null;
1300
+ state.pivotAt = 0;
1301
+ }
1302
+ return moved;
1303
+ }
1304
+
1305
+ // src/kit/response.ts
1306
+ var LIFTING = { grow: 0.18, defocus: 1.6 };
1307
+ var SINKING = { grow: 0.06, defocus: 1 };
1308
+ function pressResponse(kind) {
1309
+ return atomLift(kind) === 1 ? LIFTING : SINKING;
1310
+ }
1311
+
1312
+ export { A11Y_NONE, ATOM_AT_REST, BAR_SLOTS, BUTTON_SIZES, CONTROL_IDLE, CONTROL_SIZES, DEFAULT_GROWTH_TIMING, FORM_MORPH_TIMING, HEIGHT_BREAKPOINTS, HEIGHT_CLASSES, INPUT_MIXED, INPUT_POINTER, INPUT_TABLET_KEYBOARD, INPUT_TOUCH, MENU_CLOSED, MENU_CORNERS, MENU_DROP_BODY, MENU_ITEM_FILL, MINIMIZE_JITTER, MINIMIZE_THRESHOLD, MIN_POINTER_TARGET_DP, MIN_TOUCH_TARGET_DP, NAV_MINIMIZE_TIMING, NAV_MORPH_TIMING, NAV_SIZES, POINTER_INPUT, POSTURE_FLAT, PRESS_IDLE, TOUCH_INPUT, TYPEAHEAD_SECONDS, WIDTH_BREAKPOINTS, WIDTH_CLASSES, adaptiveContext, aimNavMorph, atomAtRest, atomBody, atomLift, atomMaterial, bottomAccessory, cancelNavDrag, clampScrollTop, classifyWindow, controlTargets, createControlVisual, createFormMorph, createGrowthState, createMinimize, createNavDrag, createNavMorph, currentNavFrame, defaultSize, expandMinimize, fixedNavigationForm, formMorph, formMorphFrame, growthFrame, growthInk, growthPhase, halfOpen, heightClass, hitSlop, hitTarget, hoversWith, iconButtonSize, intersectRect, menuActivity, menuBody, menuCorner, menuGlass, menuItemFill, menuMaterial, menuPlacement, menuSize, menuSurfaceFrame, minTarget, moveNavDrag, navDragFrame, navLayout, navMorphFrame, navigationForm, nestedRadius, postureFromSegments, presentationFor, pressNavDrag, pressResponse, rebaseFormMorph, rectContains, releaseNavDrag, resolveA11y, sameA11y, segmentOf, settleControlVisual, sizeTier, smoothstep, stepControl, stepControlVisual, stepFocus, stepFormMorph, stepGrowth, stepHover, stepMenu, stepMinimize, stepNavDrag, stepNavMorph, stepPress, stepSpring, surfaceRegion, visualAtomState, widthAtLeast, widthClass, windowSegments };
1313
+ //# sourceMappingURL=chunk-7A3XUTXH.js.map
1314
+ //# sourceMappingURL=chunk-7A3XUTXH.js.map