@mt-gloss/ui 0.1.114 → 0.1.116

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,812 @@
1
+ import { jsx as d, jsxs as k } from "react/jsx-runtime";
2
+ import { motion as ie } from "framer-motion";
3
+ import M, { useSyncExternalStore as se, useState as H, useRef as N, useMemo as V, useEffect as T, useCallback as O, createContext as le, useContext as W, useReducer as ae, useLayoutEffect as X } from "react";
4
+ import { z as w } from "zod";
5
+ const ce = "(prefers-reduced-motion: reduce)";
6
+ function G() {
7
+ return typeof window > "u" || typeof window.matchMedia != "function" ? null : window.matchMedia(ce);
8
+ }
9
+ function de(e) {
10
+ const n = G();
11
+ return n ? (n.addEventListener("change", e), () => {
12
+ n.removeEventListener("change", e);
13
+ }) : () => {
14
+ };
15
+ }
16
+ function ue() {
17
+ const e = G();
18
+ return e ? e.matches : !1;
19
+ }
20
+ function fe() {
21
+ return !1;
22
+ }
23
+ function J() {
24
+ return se(de, ue, fe);
25
+ }
26
+ const pe = {
27
+ threshold: "Threshold",
28
+ timeframe: "Timeframe",
29
+ slots: "Slots",
30
+ color: "Color"
31
+ };
32
+ function me({
33
+ dimensions: e,
34
+ activeTab: n,
35
+ onSelect: t,
36
+ reduced: r,
37
+ labels: o,
38
+ ariaLabel: l = "Settings dimensions"
39
+ }) {
40
+ const c = J(), s = r ?? c;
41
+ if (e.length < 2) return null;
42
+ const p = (a) => o?.[a] ?? pe[a], u = (a, m) => {
43
+ a.key === "ArrowRight" ? (a.preventDefault(), t(e[(m + 1) % e.length])) : a.key === "ArrowLeft" ? (a.preventDefault(), t(e[(m - 1 + e.length) % e.length])) : a.key === "Home" ? (a.preventDefault(), t(e[0])) : a.key === "End" && (a.preventDefault(), t(e[e.length - 1]));
44
+ };
45
+ return /* @__PURE__ */ d(
46
+ "div",
47
+ {
48
+ role: "tablist",
49
+ "aria-label": l,
50
+ className: "settings-tab-strip",
51
+ children: e.map((a, m) => {
52
+ const _ = n === a;
53
+ return /* @__PURE__ */ k(
54
+ "button",
55
+ {
56
+ role: "tab",
57
+ type: "button",
58
+ id: `settings-tab-${a}`,
59
+ "aria-selected": _,
60
+ "aria-controls": `settings-tabpanel-${a}`,
61
+ tabIndex: _ ? 0 : -1,
62
+ className: `settings-tab-strip__tab${_ ? " settings-tab-strip__tab--active" : ""}`,
63
+ onClick: () => t(a),
64
+ onKeyDown: (L) => u(L, m),
65
+ children: [
66
+ /* @__PURE__ */ d("span", { className: "settings-tab-strip__label", children: p(a) }),
67
+ _ && !s && /* @__PURE__ */ d(
68
+ ie.span,
69
+ {
70
+ layoutId: "settings-active-tab-indicator",
71
+ "data-layout-id": "settings-active-tab-indicator",
72
+ className: "settings-tab-strip__indicator",
73
+ "aria-hidden": "true",
74
+ transition: { type: "spring", stiffness: 380, damping: 30 }
75
+ }
76
+ )
77
+ ]
78
+ },
79
+ a
80
+ );
81
+ })
82
+ }
83
+ );
84
+ }
85
+ me.displayName = "SettingsTabStrip";
86
+ function U(e, n) {
87
+ for (const t of e)
88
+ if (t.items.some((r) => r.id === n))
89
+ return t.zoneKey;
90
+ return null;
91
+ }
92
+ function Q(e, n) {
93
+ const t = e.find((r) => r.zoneKey === n);
94
+ return t ? t.zoneKey : U(e, n);
95
+ }
96
+ function ge(e, n, t) {
97
+ const r = [...e], [o] = r.splice(n, 1);
98
+ return r.splice(t, 0, o), r;
99
+ }
100
+ function he(e) {
101
+ return e.map((n) => `${n.zoneKey}:${n.items.map((t) => t.id).join(",")}`).join("|");
102
+ }
103
+ function ve(e) {
104
+ const [n, t] = H(
105
+ () => e.zones.map((i) => i.maxItems !== void 0 && i.items.length > i.maxItems ? (process.env.NODE_ENV !== "production" && console.warn(
106
+ `useSortableZones: zone "${i.zoneKey}" exceeds maxItems (${i.items.length} > ${i.maxItems}); trimming to cap.`
107
+ ), { ...i, items: i.items.slice(0, i.maxItems) }) : i)
108
+ ), [r, o] = H(null), [l, c] = H(null), s = N(null), p = N(e.onZoneChange);
109
+ p.current = e.onZoneChange;
110
+ const u = V(
111
+ () => he(e.zones),
112
+ [e.zones]
113
+ ), a = N(u);
114
+ T(() => {
115
+ a.current !== u && (a.current = u, !r && t(
116
+ e.zones.map((i) => i.maxItems !== void 0 && i.items.length > i.maxItems ? (process.env.NODE_ENV !== "production" && console.warn(
117
+ `useSortableZones: zone "${i.zoneKey}" exceeds maxItems (${i.items.length} > ${i.maxItems}); trimming to cap.`
118
+ ), { ...i, items: i.items.slice(0, i.maxItems) }) : i)
119
+ ));
120
+ }, [u, e.zones, r]);
121
+ const m = O(
122
+ (i) => {
123
+ const g = i.active.id;
124
+ let C = null, E = null;
125
+ for (const b of n) {
126
+ const P = b.items.find((f) => f.id === g);
127
+ if (P) {
128
+ C = P, E = b.zoneKey;
129
+ break;
130
+ }
131
+ }
132
+ s.current = E, o(C), c(E);
133
+ },
134
+ [n]
135
+ ), _ = O(
136
+ (i) => {
137
+ if (!i.over) {
138
+ c(s.current);
139
+ return;
140
+ }
141
+ const g = Q(n, i.over.id);
142
+ c(g);
143
+ },
144
+ [n]
145
+ ), L = O(
146
+ (i) => {
147
+ const { active: g, over: C } = i, E = () => {
148
+ s.current = null, o(null), c(null);
149
+ };
150
+ if (!C) {
151
+ E();
152
+ return;
153
+ }
154
+ const b = U(n, g.id), P = Q(n, C.id);
155
+ if (!b || !P) {
156
+ E();
157
+ return;
158
+ }
159
+ let f = n.map((I) => ({ ...I, items: [...I.items] }));
160
+ if (b === P) {
161
+ const I = f.findIndex((D) => D.zoneKey === b), y = f[I], h = y.items.findIndex((D) => D.id === g.id), S = y.items.findIndex((D) => D.id === C.id), A = S >= 0 ? S : Math.max(0, y.items.length - 1);
162
+ f[I] = {
163
+ ...y,
164
+ items: ge(y.items, h, A)
165
+ };
166
+ } else {
167
+ const I = f.findIndex((v) => v.zoneKey === b), y = f.findIndex((v) => v.zoneKey === P), h = f[I], S = f[y], A = h.items.find((v) => v.id === g.id);
168
+ if (!A) {
169
+ E();
170
+ return;
171
+ }
172
+ const D = h.items.filter((v) => v.id !== g.id), z = S.items.findIndex((v) => v.id === C.id), re = z >= 0 ? z : S.items.length;
173
+ let j = D, x = [...S.items];
174
+ if (S.maxItems !== void 0 && x.length >= S.maxItems) {
175
+ const v = x[x.length - 1];
176
+ x = x.slice(0, S.maxItems - 1), j = [...D, v];
177
+ }
178
+ const q = Math.min(re, x.length), oe = [
179
+ ...x.slice(0, q),
180
+ A,
181
+ ...x.slice(q)
182
+ ];
183
+ f[I] = { ...h, items: j }, f[y] = { ...S, items: oe };
184
+ }
185
+ t(f), p.current?.(f), E();
186
+ },
187
+ [n]
188
+ ), B = O(
189
+ (i) => ({
190
+ id: i,
191
+ "data-zone-key": i
192
+ }),
193
+ []
194
+ ), Z = O(
195
+ (i) => {
196
+ const g = U(n, i.id) ?? "";
197
+ return {
198
+ id: i.id,
199
+ "data-item-id": i.id,
200
+ "data-zone-key": g
201
+ };
202
+ },
203
+ [n]
204
+ );
205
+ return {
206
+ zones: n,
207
+ activeZone: l,
208
+ activeItem: r,
209
+ getZoneProps: B,
210
+ getItemProps: Z,
211
+ handleDragStart: m,
212
+ handleDragOver: _,
213
+ handleDragEnd: L
214
+ };
215
+ }
216
+ let R = null, F = null, K = !1;
217
+ const Ee = [
218
+ "DndContext",
219
+ "DragOverlay",
220
+ "useSensors",
221
+ "useSensor",
222
+ "useDroppable",
223
+ "PointerSensor",
224
+ "KeyboardSensor",
225
+ "closestCenter"
226
+ ], Ie = [
227
+ "SortableContext",
228
+ "useSortable",
229
+ "verticalListSortingStrategy"
230
+ ];
231
+ function ye(e, n) {
232
+ const t = [], r = e ?? {};
233
+ for (const l of Ee)
234
+ r[l] === void 0 && t.push(`@dnd-kit/core:${l}`);
235
+ const o = n ?? {};
236
+ for (const l of Ie)
237
+ o[l] === void 0 && t.push(`@dnd-kit/sortable:${l}`);
238
+ return t.length === 0 ? { ok: !0 } : { ok: !1, missing: t };
239
+ }
240
+ function Se() {
241
+ return R ? Promise.resolve(R) : K ? Promise.resolve(null) : F || (F = (async () => {
242
+ try {
243
+ const [e, n, t] = await Promise.all([
244
+ import("@dnd-kit/core"),
245
+ import("@dnd-kit/sortable"),
246
+ import("@dnd-kit/utilities").catch(() => (process.env.NODE_ENV !== "production" && console.warn(
247
+ "SortableZones: @dnd-kit/utilities failed to load; drag-overlay positioning will fall back (items may teleport instead of slide)."
248
+ ), {}))
249
+ ]), r = ye(e, n);
250
+ return r.ok ? (R = {
251
+ core: e,
252
+ sortable: n,
253
+ utilities: t
254
+ }, R) : (K = !0, process.env.NODE_ENV !== "production" && console.error(
255
+ `SortableZones: @dnd-kit shape mismatch — missing exports: ${r.missing.join(", ")}. Check installed versions against optionalDependencies in @mt-gloss/ui.`
256
+ ), null);
257
+ } catch {
258
+ return K = !0, null;
259
+ }
260
+ })(), F);
261
+ }
262
+ function Ce(e) {
263
+ const { zones: n, onZoneChange: t, renderZone: r, renderItem: o, renderDragOverlay: l } = e, c = ve({ zones: n, onZoneChange: t }), [s, p] = M.useState(R);
264
+ return M.useEffect(() => {
265
+ if (s) return;
266
+ let u = !0;
267
+ return Se().then((a) => {
268
+ u && p(a);
269
+ }), () => {
270
+ u = !1;
271
+ };
272
+ }, [s]), s ? /* @__PURE__ */ d(
273
+ ee,
274
+ {
275
+ state: c,
276
+ modules: s,
277
+ renderZone: r,
278
+ renderItem: o,
279
+ renderDragOverlay: l
280
+ }
281
+ ) : /* @__PURE__ */ d("div", { "data-sortable-zones-passive": "true", children: c.zones.map((u) => {
282
+ const a = u.items.map((m) => /* @__PURE__ */ d(M.Fragment, { children: o(m) }, m.id));
283
+ return /* @__PURE__ */ d("div", { ...c.getZoneProps(u.zoneKey), children: r(u.zoneKey, u.items, a) }, u.zoneKey);
284
+ }) });
285
+ }
286
+ Ce.displayName = "SortableZones";
287
+ function ee({
288
+ state: e,
289
+ modules: n,
290
+ renderZone: t,
291
+ renderItem: r,
292
+ renderDragOverlay: o
293
+ }) {
294
+ const { core: l, sortable: c, utilities: s } = n, { DndContext: p, DragOverlay: u, useSensors: a, useSensor: m, useDroppable: _, PointerSensor: L, KeyboardSensor: B, closestCenter: Z } = l, { SortableContext: i, verticalListSortingStrategy: g } = c, C = a(
295
+ m(L, { activationConstraint: { distance: 5 } }),
296
+ m(B)
297
+ ), E = p, b = u, P = i;
298
+ return /* @__PURE__ */ k(
299
+ E,
300
+ {
301
+ sensors: C,
302
+ collisionDetection: Z,
303
+ onDragStart: e.handleDragStart,
304
+ onDragOver: e.handleDragOver,
305
+ onDragEnd: e.handleDragEnd,
306
+ children: [
307
+ e.zones.map((f) => {
308
+ const I = f.items.map((h) => h.id), y = f.items.map((h) => /* @__PURE__ */ d(
309
+ ne,
310
+ {
311
+ id: h.id,
312
+ useSortable: c.useSortable,
313
+ cssHelper: s.CSS,
314
+ children: r(h)
315
+ },
316
+ h.id
317
+ ));
318
+ return /* @__PURE__ */ d(
319
+ te,
320
+ {
321
+ zoneKey: f.zoneKey,
322
+ zoneProps: e.getZoneProps(f.zoneKey),
323
+ useDroppable: _,
324
+ children: /* @__PURE__ */ d(P, { items: I, strategy: g, children: t(f.zoneKey, f.items, y) })
325
+ },
326
+ f.zoneKey
327
+ );
328
+ }),
329
+ /* @__PURE__ */ d(b, { children: e.activeItem ? o?.(e.activeItem) ?? r(e.activeItem) : null })
330
+ ]
331
+ }
332
+ );
333
+ }
334
+ ee.displayName = "SortableZonesInner";
335
+ function ne({ id: e, useSortable: n, cssHelper: t, children: r }) {
336
+ const { attributes: o, listeners: l, setNodeRef: c, transform: s, transition: p, isDragging: u } = n({ id: e }), a = {
337
+ transform: t?.Transform?.toString(s) ?? void 0,
338
+ transition: p,
339
+ opacity: u ? 0.5 : 1
340
+ };
341
+ return /* @__PURE__ */ d("div", { ref: c, style: a, "data-sortable-item-id": e, ...o, ...l, children: r });
342
+ }
343
+ ne.displayName = "SortableItem";
344
+ function te({
345
+ zoneKey: e,
346
+ zoneProps: n,
347
+ useDroppable: t,
348
+ children: r
349
+ }) {
350
+ const { setNodeRef: o, isOver: l } = t({ id: e });
351
+ return /* @__PURE__ */ d("div", { ref: o, "data-zone-is-over": l || void 0, ...n, children: r });
352
+ }
353
+ te.displayName = "ZoneDroppable";
354
+ const $ = le(null);
355
+ function be() {
356
+ const e = W($);
357
+ if (!e)
358
+ throw new Error(
359
+ "[panels] usePanelContext must be used inside <PanelProvider>. See @mt-gloss/ui/composites/panels."
360
+ );
361
+ return e;
362
+ }
363
+ function ze() {
364
+ return W($);
365
+ }
366
+ const _e = w.number().min(0).max(100), Pe = w.number().min(7).max(90), De = w.enum(["#1d6fd8", "#10b981", "#f59e0b", "#ef4444", "#8b5cf6"]), xe = w.array(w.string().min(1)).max(3), Oe = w.boolean();
367
+ function Ne(e, n) {
368
+ if (e === "threshold") {
369
+ const t = _e.safeParse(n);
370
+ return t.success ? t.data : 80;
371
+ }
372
+ if (e === "timeframe") {
373
+ const t = Pe.safeParse(n);
374
+ return t.success ? t.data : 30;
375
+ }
376
+ if (e === "accent") {
377
+ const t = De.safeParse(n);
378
+ return t.success ? t.data : null;
379
+ }
380
+ if (e === "slots") {
381
+ const t = xe.safeParse(n);
382
+ return t.success ? t.data : [];
383
+ }
384
+ if (e === "thresholdEnabled" || e === "accentEnabled") {
385
+ const t = Oe.safeParse(n);
386
+ return t.success ? t.data : !1;
387
+ }
388
+ return n;
389
+ }
390
+ const Te = {
391
+ activePanelId: null,
392
+ activeTrigger: null,
393
+ bellCutoutOpen: !1,
394
+ isDirty: !1,
395
+ dirtySessionCount: 0,
396
+ lockHint: null,
397
+ bufferByCard: {},
398
+ lastClose: null,
399
+ pendingSideEffects: []
400
+ }, Y = /* @__PURE__ */ new Set(["settings"]);
401
+ function we(e, n) {
402
+ if (e == null || e.kind !== n.kind) return !1;
403
+ switch (e.kind) {
404
+ case "bell":
405
+ return !0;
406
+ case "card-overflow":
407
+ return e.cardId === n.cardId && e.sectionIndex === n.sectionIndex && e.action === n.action;
408
+ case "empty-cell":
409
+ return e.sectionIndex === n.sectionIndex && e.cellPosition.row === n.cellPosition.row && e.cellPosition.col === n.cellPosition.col;
410
+ case "manage-pages-action":
411
+ return e.global === n.global && e.align === n.align;
412
+ case "layout-action-blocked":
413
+ return e.action === n.action && e.blockedCardId === n.blockedCardId && e.activeCardId === n.activeCardId;
414
+ }
415
+ }
416
+ function Le(e, n) {
417
+ switch (n.type) {
418
+ case "OPEN_PANEL": {
419
+ if (e.activePanelId && e.isDirty)
420
+ return {
421
+ ...e,
422
+ pendingSideEffects: [
423
+ ...e.pendingSideEffects,
424
+ {
425
+ type: "SET_LOCK_HINT",
426
+ hint: {
427
+ blockedTrigger: n.trigger,
428
+ activeCardId: e.activeTrigger?.cardId ?? null,
429
+ expiresAt: Date.now() + 4e3
430
+ }
431
+ }
432
+ ]
433
+ };
434
+ if (e.activePanelId === n.panelId && we(e.activeTrigger, n.trigger))
435
+ return e;
436
+ const t = e.activePanelId != null ? [
437
+ {
438
+ type: "HANDOFF_CLOSE",
439
+ reason: "mutex-handoff",
440
+ previousPanelId: e.activePanelId
441
+ }
442
+ ] : [], r = n.panelId === "notification-center" && e.bellCutoutOpen;
443
+ return {
444
+ ...e,
445
+ activePanelId: n.panelId,
446
+ activeTrigger: n.trigger,
447
+ isDirty: !1,
448
+ dirtySessionCount: 0,
449
+ // Phase 16 P2 CC-17 — fresh open starts a clean session
450
+ bellCutoutOpen: r ? !1 : e.bellCutoutOpen,
451
+ lastClose: null,
452
+ // reset on fresh open
453
+ pendingSideEffects: [...e.pendingSideEffects, ...t]
454
+ };
455
+ }
456
+ case "CLOSE_PANEL": {
457
+ if (e.activePanelId == null)
458
+ return e;
459
+ const t = e.activePanelId, r = e.activeTrigger?.cardId ?? null, o = r != null ? e.bufferByCard[r] : void 0, l = e.isDirty ? n.reason === "apply-button" ? r != null && o != null ? [
460
+ {
461
+ type: "COMMIT_BUFFER",
462
+ payload: { cardId: r, delta: o }
463
+ }
464
+ ] : [{ type: "COMMIT_BUFFER" }] : [{ type: "DISCARD_BUFFER" }] : [], c = r != null && o != null ? (() => {
465
+ const s = { ...e.bufferByCard };
466
+ return delete s[r], s;
467
+ })() : e.bufferByCard;
468
+ return {
469
+ ...e,
470
+ activePanelId: null,
471
+ activeTrigger: null,
472
+ isDirty: !1,
473
+ dirtySessionCount: 0,
474
+ // Phase 16 P2 CC-17 — session ends on panel close (apply or discard)
475
+ lockHint: null,
476
+ // closing clears any active lockHint (dirty-buffer condition gone)
477
+ bufferByCard: c,
478
+ lastClose: { panelId: t, reason: n.reason },
479
+ pendingSideEffects: [...e.pendingSideEffects, ...l]
480
+ };
481
+ }
482
+ case "OPEN_BELL_CUTOUT":
483
+ return e.bellCutoutOpen ? e : e.activePanelId === "notification-center" ? {
484
+ ...e,
485
+ activePanelId: null,
486
+ activeTrigger: null,
487
+ bellCutoutOpen: !0,
488
+ pendingSideEffects: [
489
+ ...e.pendingSideEffects,
490
+ { type: "AUTO_CLOSE_NOTIF_CENTER" }
491
+ ]
492
+ } : { ...e, bellCutoutOpen: !0 };
493
+ case "CLOSE_BELL_CUTOUT":
494
+ return e.bellCutoutOpen ? { ...e, bellCutoutOpen: !1 } : e;
495
+ case "SET_BUFFER_VALUE": {
496
+ if (e.activePanelId == null || !Y.has(e.activePanelId))
497
+ return {
498
+ ...e,
499
+ pendingSideEffects: [
500
+ ...e.pendingSideEffects,
501
+ { type: "INVALID_DISPATCH", reason: "no-buffer-panel" }
502
+ ]
503
+ };
504
+ const t = e.bufferByCard[n.cardId], r = t == null, o = Ne(n.key, n.value);
505
+ if (!r && t.changes[n.key] === o)
506
+ return e;
507
+ const c = { kind: "settings", changes: {
508
+ ...t?.kind === "settings" ? t.changes : {},
509
+ [n.key]: o
510
+ } }, s = { ...e.bufferByCard, [n.cardId]: c }, p = r ? [
511
+ {
512
+ type: "SCHEDULE_TIMER",
513
+ ms: 0,
514
+ action: { type: "BUFFER_BECOMES_DIRTY" }
515
+ }
516
+ ] : [];
517
+ return {
518
+ ...e,
519
+ bufferByCard: s,
520
+ pendingSideEffects: [...e.pendingSideEffects, ...p]
521
+ };
522
+ }
523
+ case "CLEAR_BUFFER": {
524
+ if (e.bufferByCard[n.cardId] == null)
525
+ return e;
526
+ const r = Object.keys(e.bufferByCard).length === 1, o = { ...e.bufferByCard };
527
+ delete o[n.cardId];
528
+ const l = r && e.isDirty ? [
529
+ {
530
+ type: "SCHEDULE_TIMER",
531
+ ms: 0,
532
+ action: { type: "BUFFER_BECOMES_CLEAN" }
533
+ }
534
+ ] : [];
535
+ return {
536
+ ...e,
537
+ bufferByCard: o,
538
+ pendingSideEffects: [...e.pendingSideEffects, ...l]
539
+ };
540
+ }
541
+ case "BUFFER_BECOMES_DIRTY":
542
+ return e.activePanelId == null || !Y.has(e.activePanelId) ? {
543
+ ...e,
544
+ pendingSideEffects: [
545
+ ...e.pendingSideEffects,
546
+ { type: "INVALID_DISPATCH", reason: "no-buffer-panel" }
547
+ ]
548
+ } : e.isDirty ? e : { ...e, isDirty: !0, dirtySessionCount: e.dirtySessionCount + 1 };
549
+ case "BUFFER_BECOMES_CLEAN":
550
+ return e.isDirty ? { ...e, isDirty: !1, dirtySessionCount: 0 } : e;
551
+ case "SET_LOCK_HINT":
552
+ return {
553
+ ...e,
554
+ lockHint: n.hint,
555
+ pendingSideEffects: [
556
+ ...e.pendingSideEffects,
557
+ {
558
+ type: "SCHEDULE_TIMER",
559
+ ms: Math.max(0, n.hint.expiresAt - Date.now()),
560
+ action: { type: "CLEAR_LOCK_HINT" }
561
+ }
562
+ ]
563
+ };
564
+ case "CLEAR_LOCK_HINT":
565
+ return e.lockHint == null ? e : { ...e, lockHint: null };
566
+ case "DRAIN_SIDE_EFFECTS":
567
+ return e.pendingSideEffects.length === 0 ? e : typeof n.count == "number" && n.count > 0 ? n.count >= e.pendingSideEffects.length ? { ...e, pendingSideEffects: [] } : {
568
+ ...e,
569
+ pendingSideEffects: e.pendingSideEffects.slice(n.count)
570
+ } : {
571
+ ...e,
572
+ pendingSideEffects: []
573
+ };
574
+ default:
575
+ return e;
576
+ }
577
+ }
578
+ function Re(e) {
579
+ return e.activePanelId === "notification-center" ? "notif-center" : e.activePanelId != null && e.bellCutoutOpen ? e.isDirty ? "panel-dirty+cutout" : "panel-clean+cutout" : e.activePanelId != null ? e.isDirty ? "panel-dirty" : "panel-clean" : e.bellCutoutOpen ? "bell-cutout-only" : "idle";
580
+ }
581
+ function ke() {
582
+ const [e, n] = ae(Le, Te), t = N([]), r = N(null);
583
+ T(() => () => {
584
+ r.current != null && (window.clearTimeout(r.current), r.current = null);
585
+ }, []), T(() => {
586
+ const c = e.pendingSideEffects.length;
587
+ if (c !== 0) {
588
+ for (const s of e.pendingSideEffects)
589
+ s.type === "SCHEDULE_TIMER" ? (r.current != null && window.clearTimeout(r.current), r.current = window.setTimeout(() => {
590
+ r.current = null, n(s.action);
591
+ }, Math.max(0, s.ms))) : s.type === "SET_LOCK_HINT" ? n({ type: "SET_LOCK_HINT", hint: s.hint }) : s.type === "HANDOFF_CLOSE" ? t.current = [
592
+ ...t.current.slice(-99),
593
+ {
594
+ panelId: s.previousPanelId,
595
+ reason: "mutex-handoff",
596
+ at: Date.now(),
597
+ note: "F22: mutex-handoff synthetic close-log entry"
598
+ }
599
+ ] : s.type === "INVALID_DISPATCH" && process.env.NODE_ENV !== "production" && console.warn("[panels] INVALID_DISPATCH:", s.reason);
600
+ n({ type: "DRAIN_SIDE_EFFECTS", count: c });
601
+ }
602
+ }, [e.pendingSideEffects, n]), T(() => {
603
+ if (!e.lastClose) return;
604
+ const c = {
605
+ panelId: e.lastClose.panelId,
606
+ reason: e.lastClose.reason,
607
+ at: Date.now(),
608
+ note: e.lastClose.reason === "nav-escape-hatch" ? "F21: backdrop-click OR page-nav escape" : void 0
609
+ };
610
+ t.current = [
611
+ ...t.current.slice(-99),
612
+ c
613
+ ];
614
+ }, [e.lastClose]);
615
+ const o = V(
616
+ () => Re(e),
617
+ [e.activePanelId, e.bellCutoutOpen, e.isDirty]
618
+ ), l = O(() => t.current.slice(), []);
619
+ return { state: e, surfaceState: o, dispatch: n, getCloseLog: l };
620
+ }
621
+ function je({ children: e }) {
622
+ const n = ke(), t = V(
623
+ () => n,
624
+ // Re-publish only when one of the four coordinator fields changes identity.
625
+ // dispatch + getCloseLog are stable. state + surfaceState change on dispatch.
626
+ [n.state, n.surfaceState, n.dispatch, n.getCloseLog]
627
+ );
628
+ return /* @__PURE__ */ d($.Provider, { value: t, children: e });
629
+ }
630
+ const Ae = {
631
+ position: "relative",
632
+ pointerEvents: "auto"
633
+ };
634
+ function Fe({ children: e, onClose: n, footer: t, title: r, variant: o = "default" }) {
635
+ return /* @__PURE__ */ k(
636
+ "div",
637
+ {
638
+ "data-chrome": "panel",
639
+ "data-chrome-variant": o,
640
+ role: "dialog",
641
+ "aria-modal": "true",
642
+ className: o === "merged-stage" ? "gloss-panel-chrome gloss-panel-chrome--merged-stage" : "gloss-panel-chrome",
643
+ style: Ae,
644
+ children: [
645
+ /* @__PURE__ */ k("div", { className: "gloss-panel-chrome__header", children: [
646
+ r != null && /* @__PURE__ */ d("span", { className: "gloss-panel-chrome__title", children: r }),
647
+ /* @__PURE__ */ d(
648
+ "button",
649
+ {
650
+ id: "panel-close",
651
+ type: "button",
652
+ "aria-label": "Close panel",
653
+ className: "gloss-panel-chrome__close",
654
+ onClick: n,
655
+ children: "×"
656
+ }
657
+ )
658
+ ] }),
659
+ /* @__PURE__ */ d("div", { className: "gloss-panel-chrome__body", children: e }),
660
+ t != null && /* @__PURE__ */ d("div", { className: "gloss-panel-chrome__footer", children: t })
661
+ ]
662
+ }
663
+ );
664
+ }
665
+ function He(e) {
666
+ if (process.env.NODE_ENV === "production" || !e || !e.parentElement) return;
667
+ const n = e.parentElement, t = window.getComputedStyle(n);
668
+ t.perspective === "none" && console.warn(
669
+ "[PanelMount] §2.7 violation: parent of .gloss-panel-mount has perspective:none. Rotated mount will render flat. Parent:",
670
+ n
671
+ ), (t.overflow === "hidden" || t.overflowY === "hidden") && console.warn(
672
+ "[PanelMount] §2.9 violation: parent of .gloss-panel-mount has overflow:hidden. Rising panel will be clipped. Parent:",
673
+ n
674
+ );
675
+ }
676
+ function Be({ isOpen: e, children: n }) {
677
+ const t = J(), r = N(null);
678
+ X(() => {
679
+ He(r.current);
680
+ }, []);
681
+ const [o, l] = H(!1);
682
+ T(() => {
683
+ if (!e) {
684
+ l(!1);
685
+ return;
686
+ }
687
+ const s = requestAnimationFrame(() => l(!0));
688
+ return () => cancelAnimationFrame(s);
689
+ }, [e]);
690
+ const c = [
691
+ "gloss-panel-mount",
692
+ o ? "has-panel" : "",
693
+ t ? "reduced-motion" : ""
694
+ ].filter(Boolean).join(" ");
695
+ return /* @__PURE__ */ d(
696
+ "div",
697
+ {
698
+ ref: r,
699
+ className: c,
700
+ "data-mount": "panel",
701
+ "data-open": e ? "true" : "false",
702
+ children: n
703
+ }
704
+ );
705
+ }
706
+ const Ze = {
707
+ position: "fixed",
708
+ top: 0,
709
+ left: 0,
710
+ right: 0,
711
+ height: "var(--tb-h, 60px)",
712
+ perspective: "var(--panel-perspective, 1500px)",
713
+ perspectiveOrigin: "center bottom",
714
+ overflow: "visible",
715
+ pointerEvents: "none",
716
+ zIndex: 55
717
+ }, Me = {
718
+ position: "fixed",
719
+ top: "var(--tb-h, 60px)",
720
+ left: 0,
721
+ right: 0,
722
+ bottom: 0,
723
+ zIndex: 1,
724
+ // CR-03 fix: pass-through tint — scrim is visual-only and must not intercept
725
+ // grid card clicks. Click-outside / scrim-dismiss is handled via the onClick
726
+ // on the div below, which still fires because pointer events on the element
727
+ // itself are honoured when explicitly set; using 'none' prevents the invisible
728
+ // overlay from swallowing clicks on underlying controls (same class as the
729
+ // panel-wrapper-covers-grid-cards bug fixed at V2FeedbackSurface).
730
+ pointerEvents: "none",
731
+ background: "rgba(0, 0, 0, 0.04)"
732
+ };
733
+ function qe({ panelComponents: e }) {
734
+ const { state: n, dispatch: t, getCloseLog: r } = be(), o = n.activePanelId ? e[n.activePanelId] : null;
735
+ n.activePanelId && !o && process.env.NODE_ENV !== "production" && console.error(
736
+ "[panels] No panel component registered for activePanelId:",
737
+ n.activePanelId
738
+ ), T(() => {
739
+ if (n.activePanelId == null) return;
740
+ function p(u) {
741
+ u.key === "Escape" && t({ type: "CLOSE_PANEL", reason: "escape-key" });
742
+ }
743
+ return document.addEventListener("keydown", p), () => {
744
+ document.removeEventListener("keydown", p);
745
+ };
746
+ }, [n.activePanelId, t]);
747
+ const l = N(n);
748
+ X(() => {
749
+ l.current = n;
750
+ }), T(() => {
751
+ if (process.env.NODE_ENV === "production") return;
752
+ const p = window;
753
+ return p.__panelsHostHandle && console.warn("[panels] multiple PanelHost/PanelHostShell instances mounted; dev handle is unreliable"), p.__panelsHostHandle = {
754
+ dispatch: t,
755
+ getState: () => l.current,
756
+ getCloseLog: r
757
+ }, () => {
758
+ p.__panelsHostHandle?.dispatch === t && delete p.__panelsHostHandle;
759
+ };
760
+ }, [t, r]);
761
+ const c = O(() => {
762
+ t({ type: "CLOSE_PANEL", reason: "nav-escape-hatch" });
763
+ }, [t]), s = n.activePanelId != null && o != null;
764
+ return /* @__PURE__ */ k("div", { "data-panel-host": "true", style: Ze, children: [
765
+ s && /* @__PURE__ */ d(
766
+ "div",
767
+ {
768
+ "data-panel-scrim": "true",
769
+ style: Me,
770
+ onClick: c
771
+ }
772
+ ),
773
+ /* @__PURE__ */ d(Be, { isOpen: s, children: s && /* @__PURE__ */ d(
774
+ Fe,
775
+ {
776
+ isOpen: !0,
777
+ onClose: () => t({ type: "CLOSE_PANEL", reason: "header-x" }),
778
+ children: /* @__PURE__ */ d(o, { isOpen: !0 })
779
+ }
780
+ ) })
781
+ ] });
782
+ }
783
+ const Qe = {
784
+ "apply-button": !0,
785
+ // the ONLY commit reason
786
+ "cancel-button": !1,
787
+ "header-x": !1,
788
+ "escape-key": !1,
789
+ "page-nav": !1,
790
+ "mutex-handoff": !1,
791
+ "ghost-anchor": !1,
792
+ "viewport-collapse": !1,
793
+ unmount: !1,
794
+ "nav-escape-hatch": !1
795
+ };
796
+ export {
797
+ Qe as C,
798
+ je as P,
799
+ Ce as S,
800
+ ve as a,
801
+ be as b,
802
+ qe as c,
803
+ Fe as d,
804
+ ke as e,
805
+ Re as f,
806
+ me as g,
807
+ J as h,
808
+ Te as i,
809
+ Be as j,
810
+ Le as r,
811
+ ze as u
812
+ };