cursedbelt 5.94.0 → 5.95.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/react/components/AppShell.d.ts +12 -1
  2. package/dist/react/components/AppShell.d.ts.map +1 -1
  3. package/dist/react/components/AppShell.js +24 -5
  4. package/dist/react/components/AppShell.js.map +1 -1
  5. package/dist/react/components/ComboboxField.js +2 -2
  6. package/dist/react/components/ComboboxField.js.map +1 -1
  7. package/dist/react/components/DatePicker.d.ts +9 -5
  8. package/dist/react/components/DatePicker.d.ts.map +1 -1
  9. package/dist/react/components/DatePicker.js +192 -27
  10. package/dist/react/components/DatePicker.js.map +1 -1
  11. package/dist/react/components/InlineEditableText.d.ts +12 -2
  12. package/dist/react/components/InlineEditableText.d.ts.map +1 -1
  13. package/dist/react/components/InlineEditableText.js +11 -8
  14. package/dist/react/components/InlineEditableText.js.map +1 -1
  15. package/dist/react/components/suggestionList.js +2 -2
  16. package/dist/react/components/suggestionList.js.map +1 -1
  17. package/dist/react/lib/dateText.d.ts +21 -0
  18. package/dist/react/lib/dateText.d.ts.map +1 -0
  19. package/dist/react/lib/dateText.js +94 -0
  20. package/dist/react/lib/dateText.js.map +1 -0
  21. package/dist/react/lib/floatingStyles.d.ts +12 -0
  22. package/dist/react/lib/floatingStyles.d.ts.map +1 -1
  23. package/dist/react/lib/floatingStyles.js +12 -0
  24. package/dist/react/lib/floatingStyles.js.map +1 -1
  25. package/dist/styles-areas/analytics.css +11 -10
  26. package/dist/styles-areas/data-table.css +13 -12
  27. package/dist/styles-areas/fields.css +12 -10
  28. package/dist/styles-areas/stats.css +12 -11
  29. package/dist/styles-areas/wizard.css +6 -6
  30. package/dist/styles-utilities.css +15 -0
  31. package/package.json +1 -1
  32. package/src/react/components/AppShell.spec.tsx +53 -0
  33. package/src/react/components/AppShell.tsx +79 -23
  34. package/src/react/components/ComboboxField.tsx +2 -2
  35. package/src/react/components/ControlHeightScale.spec.tsx +2 -2
  36. package/src/react/components/DatePicker.spec.tsx +186 -5
  37. package/src/react/components/DatePicker.tsx +416 -63
  38. package/src/react/components/InlineEditableText.spec.tsx +29 -0
  39. package/src/react/components/InlineEditableText.tsx +26 -10
  40. package/src/react/components/suggestionList.tsx +2 -2
  41. package/src/react/lib/dateText.ts +94 -0
  42. package/src/react/lib/floatingStyles.spec.ts +36 -1
  43. package/src/react/lib/floatingStyles.ts +13 -0
  44. package/src/styles-areas/analytics.css +11 -10
  45. package/src/styles-areas/data-table.css +13 -12
  46. package/src/styles-areas/fields.css +12 -10
  47. package/src/styles-areas/stats.css +12 -11
  48. package/src/styles-areas/wizard.css +6 -6
  49. package/src/styles-utilities.css +15 -0
@@ -14,9 +14,9 @@ import { RAW_BUTTON } from '../lib/cn.js';
14
14
  * primitive, used anywhere a title/heading should be edited in place. Fully
15
15
  * token-driven (accent border + ring on the editor); the editor focuses and
16
16
  * selects its text on mount so typing replaces (and the cursor is never stranded
17
- * at position 0).
17
+ * at position 0) — or, with `caret='end'`, puts the caret after the text.
18
18
  */
19
- export function InlineEditableText({ value, onChange, canEdit = true, multiline = false, placeholder = 'Add text…', className, inputClassName, trigger = 'text', wrap = true, ariaLabel, ...rest }) {
19
+ export function InlineEditableText({ value, onChange, canEdit = true, multiline = false, placeholder = 'Add text…', className, inputClassName, trigger = 'text', wrap = true, caret = 'select-all', maxLength = 10_000, ariaLabel, ...rest }) {
20
20
  const [editing, setEditing] = useState(false);
21
21
  const [draft, setDraft] = useState(value);
22
22
  const begin = () => {
@@ -35,20 +35,23 @@ export function InlineEditableText({ value, onChange, canEdit = true, multiline
35
35
  setDraft(value);
36
36
  setEditing(false);
37
37
  };
38
- // Focus + select-all as soon as the editor mounts (it only renders while editing).
39
- // MUST be identity-stable: an inline ref callback gets detached/re-attached on every
38
+ // Focus + select-all (or caret-at-end) as soon as the editor mounts (it only renders while
39
+ // editing). MUST be identity-stable: an inline ref callback gets detached/re-attached on every
40
40
  // render, so each keystroke's setDraft re-render would re-run el.select() and the next
41
41
  // character would replace everything typed so far (one-character-survives bug).
42
42
  const focusOnMount = useCallback((el) => {
43
43
  if (!el)
44
44
  return;
45
45
  el.focus();
46
- el.select();
47
- }, []);
46
+ if (caret === 'end')
47
+ el.setSelectionRange(el.value.length, el.value.length);
48
+ else
49
+ el.select();
50
+ }, [caret]);
48
51
  if (editing) {
49
52
  const editorClass = cn('block w-full rounded-sm border border-accent bg-surface px-1 -mx-1 text-foreground outline-none ring-2 ring-accent/30', className, inputClassName);
50
53
  if (multiline) {
51
- return (_jsx(AutoGrowTextarea, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: cn(editorClass, 'resize-none'), maxLength: 10_000, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
54
+ return (_jsx(AutoGrowTextarea, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: cn(editorClass, 'resize-none'), maxLength: maxLength, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
52
55
  if (e.key === 'Escape') {
53
56
  // Escape cancels THIS edit only — never bubbles on to close a
54
57
  // surrounding Modal/Drawer in the same keystroke.
@@ -59,7 +62,7 @@ export function InlineEditableText({ value, onChange, canEdit = true, multiline
59
62
  commit();
60
63
  } }));
61
64
  }
62
- return (_jsx(Input, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: editorClass, maxLength: 10_000, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
65
+ return (_jsx(Input, { ...rest, ref: focusOnMount, "aria-label": ariaLabel, className: editorClass, maxLength: maxLength, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: commit, onKeyDown: (e) => {
63
66
  if (e.key === 'Escape') {
64
67
  // Escape cancels THIS edit only — never bubbles on to close a
65
68
  // surrounding Modal/Drawer in the same keystroke.
@@ -1 +1 @@
1
- {"version":3,"file":"InlineEditableText.js","sourceRoot":"","sources":["../../../src/react/components/InlineEditableText.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"InlineEditableText.js","sourceRoot":"","sources":["../../../src/react/components/InlineEditableText.tsx"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Autocomplete } from '@base-ui/react/autocomplete';
4
4
  import { cn } from '../lib/cn.js';
5
- import { FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE } from '../lib/floatingStyles.js';
5
+ import { BASE_UI_POSITIONER, FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE } from '../lib/floatingStyles.js';
6
6
  /**
7
7
  * The dropdown of an Autocomplete root — every kit field that suggests as you type draws its list
8
8
  * with this one, so the list is the kit's in every theme (`TagsInput` as well as
@@ -12,7 +12,7 @@ import { FLOATING_COLLISION_PADDING, FLOATING_CONTENT_BASE, FLOATING_ITEM_BASE }
12
12
  */
13
13
  // no-attribute-forward: its root is Base UI's Portal — no element of its own; the popup is the kit's, not the caller's
14
14
  export function SuggestionList({ itemToString = (item) => String(item), renderSuggestion, }) {
15
- return (_jsx(Autocomplete.Portal, { children: _jsx(Autocomplete.Positioner, { sideOffset: 4, collisionPadding: FLOATING_COLLISION_PADDING, className: 'z-[var(--z-popover)]', children: _jsx(Autocomplete.Popup, { className: cn(FLOATING_CONTENT_BASE,
15
+ return (_jsx(Autocomplete.Portal, { children: _jsx(Autocomplete.Positioner, { sideOffset: 4, collisionPadding: FLOATING_COLLISION_PADDING, className: BASE_UI_POSITIONER, children: _jsx(Autocomplete.Popup, { className: cn(FLOATING_CONTENT_BASE,
16
16
  // guardrails-ignore no-raw-overflow: floating popover panel outside the page's ScrollArea, height-capped by max-h-64 — no keyboard-inset concern.
17
17
  'max-h-64 min-w-[var(--anchor-width)] overflow-y-auto p-0 py-1', 'transition-[transform,opacity] duration-100', 'data-[starting-style]:scale-95 data-[starting-style]:opacity-0', 'data-[ending-style]:scale-95 data-[ending-style]:opacity-0'), children: _jsx(Autocomplete.List, { children: (s, index) => (_jsx(Autocomplete.Item, { value: s, index: index, className: cn(FLOATING_ITEM_BASE, 'data-[highlighted]:bg-accent/10 data-[highlighted]:text-accent-strong'), children: renderSuggestion ? renderSuggestion(s) : itemToString(s) }, itemToString(s))) }) }) }) }));
18
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"suggestionList.js","sourceRoot":"","sources":["../../../src/react/components/suggestionList.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,0BAA0B,EACjC,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9E;;;;;;GAMG;AACH,uHAAuH;AACvH,MAAM,UAAU,cAAc,CAAI,EAChC,YAAY,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EACrC,gBAAgB,GAIjB;IACC,OAAO,CACL,KAAC,YAAY,CAAC,MAAM,cAOlB,KAAC,YAAY,CAAC,UAAU,IACtB,UAAU,EAAE,CAAC,EACb,gBAAgB,EAAE,0BAA0B,EAC5C,SAAS,EAAC,sBAAsB,YAEhC,KAAC,YAAY,CAAC,KAAK,IACjB,SAAS,EAAE,EAAE,CACX,qBAAqB;gBACrB,kJAAkJ;gBAClJ,+DAA+D,EAC/D,6CAA6C,EAC7C,gEAAgE,EAChE,4DAA4D,CAC7D,YAKD,KAAC,YAAY,CAAC,IAAI,cACf,CAAC,CAAI,EAAE,KAAa,EAAE,EAAE,CAAC,CACxB,KAAC,YAAY,CAAC,IAAI,IAEhB,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,EAAE,CAAC,kBAAkB,EAAE,uEAAuE,CAAC,YAEzG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IALpD,YAAY,CAAC,CAAC,CAAC,CAMF,CACrB,GACiB,GACD,GACG,GACN,CACvB,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"suggestionList.js","sourceRoot":"","sources":["../../../src/react/components/suggestionList.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EACrD,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9E;;;;;;GAMG;AACH,uHAAuH;AACvH,MAAM,UAAU,cAAc,CAAI,EAChC,YAAY,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EACrC,gBAAgB,GAIjB;IACC,OAAO,CACL,KAAC,YAAY,CAAC,MAAM,cAOlB,KAAC,YAAY,CAAC,UAAU,IACtB,UAAU,EAAE,CAAC,EACb,gBAAgB,EAAE,0BAA0B,EAC5C,SAAS,EAAE,kBAAkB,YAE7B,KAAC,YAAY,CAAC,KAAK,IACjB,SAAS,EAAE,EAAE,CACX,qBAAqB;gBACrB,kJAAkJ;gBAClJ,+DAA+D,EAC/D,6CAA6C,EAC7C,gEAAgE,EAChE,4DAA4D,CAC7D,YAKD,KAAC,YAAY,CAAC,IAAI,cACf,CAAC,CAAI,EAAE,KAAa,EAAE,EAAE,CAAC,CACxB,KAAC,YAAY,CAAC,IAAI,IAEhB,KAAK,EAAE,CAAC,EACR,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,EAAE,CAAC,kBAAkB,EAAE,uEAAuE,CAAC,YAEzG,gBAAgB,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IALpD,YAAY,CAAC,CAAC,CAAC,CAMF,CACrB,GACiB,GACD,GACG,GACN,CACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Typed-date parsing for {@link DatePicker} (forge task 3764).
3
+ *
4
+ * Accepts what a person actually types, American English first:
5
+ * `2015-12-05`, `12/5/2015` (month first), `12-5-2015`, `12.5.2015`, `Dec 5 2015`,
6
+ * `December 5, 2015`, `5 Dec 2015`, `Dec 5th, 2015`, `2015 Dec 5`. A two-digit year is
7
+ * read as the nearest sensible century: `15` is 2015, and `99` is 1999 (a year more than
8
+ * ten years ahead of today reads as the century before). Anything else — a day that does
9
+ * not exist, a three-digit year, stray words — is refused with a short reason, never
10
+ * guessed at.
11
+ */
12
+ export type DateTextResult = {
13
+ ok: true;
14
+ iso: string;
15
+ } | {
16
+ ok: false;
17
+ reason: string;
18
+ };
19
+ /** Parse typed text into an ISO `YYYY-MM-DD` day. Pure; `now` only picks the century of a 2-digit year. */
20
+ export declare function parseDateText(text: string, now?: Date): DateTextResult;
21
+ //# sourceMappingURL=dateText.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateText.d.ts","sourceRoot":"","sources":["../../../src/react/lib/dateText.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAkDvF,2GAA2G;AAC3G,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,cAAc,CA8BlF"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Typed-date parsing for {@link DatePicker} (forge task 3764).
3
+ *
4
+ * Accepts what a person actually types, American English first:
5
+ * `2015-12-05`, `12/5/2015` (month first), `12-5-2015`, `12.5.2015`, `Dec 5 2015`,
6
+ * `December 5, 2015`, `5 Dec 2015`, `Dec 5th, 2015`, `2015 Dec 5`. A two-digit year is
7
+ * read as the nearest sensible century: `15` is 2015, and `99` is 1999 (a year more than
8
+ * ten years ahead of today reads as the century before). Anything else — a day that does
9
+ * not exist, a three-digit year, stray words — is refused with a short reason, never
10
+ * guessed at.
11
+ */
12
+ const MONTHS = [
13
+ 'january',
14
+ 'february',
15
+ 'march',
16
+ 'april',
17
+ 'may',
18
+ 'june',
19
+ 'july',
20
+ 'august',
21
+ 'september',
22
+ 'october',
23
+ 'november',
24
+ 'december',
25
+ ];
26
+ /** A month index (0-11) for a name or a 3+ letter prefix of one ("sept", "Dec"), else -1. */
27
+ function monthIndex(word) {
28
+ const w = word.toLowerCase().replace(/\.$/, '');
29
+ if (w.length < 3)
30
+ return -1;
31
+ return MONTHS.findIndex((name) => name.startsWith(w));
32
+ }
33
+ function expandYear(token, now) {
34
+ if (/^\d{4}$/.test(token)) {
35
+ const year = Number(token);
36
+ return year >= 1000 ? year : null;
37
+ }
38
+ if (/^\d{2}$/.test(token)) {
39
+ const year = 2000 + Number(token);
40
+ return year > now.getFullYear() + 10 ? year - 100 : year;
41
+ }
42
+ return null;
43
+ }
44
+ function build(year, month, day) {
45
+ if (year === null)
46
+ return { ok: false, reason: 'Use a four-digit year' };
47
+ if (month < 0 || month > 11)
48
+ return { ok: false, reason: 'That month does not exist' };
49
+ const date = new Date(year, month, day);
50
+ if (date.getFullYear() !== year || date.getMonth() !== month || date.getDate() !== day) {
51
+ return { ok: false, reason: 'That date does not exist' };
52
+ }
53
+ const mm = String(month + 1).padStart(2, '0');
54
+ const dd = String(day).padStart(2, '0');
55
+ return { ok: true, iso: `${String(year).padStart(4, '0')}-${mm}-${dd}` };
56
+ }
57
+ const INVALID = { ok: false, reason: 'Enter a date like Dec 5, 2015 or 12/5/2015' };
58
+ /** Parse typed text into an ISO `YYYY-MM-DD` day. Pure; `now` only picks the century of a 2-digit year. */
59
+ export function parseDateText(text, now = new Date()) {
60
+ const raw = text.trim();
61
+ if (!raw)
62
+ return INVALID;
63
+ // Numeric forms: Y-M-D (ISO first), then M/D/Y with / - or . between.
64
+ const numeric = raw.match(/^(\d{1,4})([/.-])(\d{1,2})\2(\d{1,4})$/);
65
+ if (numeric) {
66
+ const [, a, , b, c] = numeric;
67
+ if (a.length === 4)
68
+ return build(expandYear(a, now), Number(b) - 1, Number(c));
69
+ return build(expandYear(c, now), Number(a) - 1, Number(b));
70
+ }
71
+ // Month-name forms, in any order of day / month / year.
72
+ const tokens = raw
73
+ .replace(/,/g, ' ')
74
+ .split(/\s+/)
75
+ .filter(Boolean)
76
+ .filter((t) => t.toLowerCase() !== 'of');
77
+ if (tokens.length !== 3)
78
+ return INVALID;
79
+ const nameAt = tokens.findIndex((t) => monthIndex(t) >= 0);
80
+ if (nameAt < 0)
81
+ return INVALID;
82
+ const month = monthIndex(tokens[nameAt]);
83
+ const nums = tokens
84
+ .filter((_, i) => i !== nameAt)
85
+ .map((t) => t.replace(/^(\d{1,2})(st|nd|rd|th)$/i, '$1'));
86
+ if (!nums.every((t) => /^\d+$/.test(t)))
87
+ return INVALID;
88
+ const [first, second] = nums;
89
+ // "2015 Dec 5": a four-digit first number is the year. Otherwise day then year.
90
+ if (first.length === 4)
91
+ return build(expandYear(first, now), month, Number(second));
92
+ return build(expandYear(second, now), month, Number(first));
93
+ }
94
+ //# sourceMappingURL=dateText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dateText.js","sourceRoot":"","sources":["../../../src/react/lib/dateText.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,18 @@
23
23
  * so it reads as a floating surface rather than as a pane that ran out of screen.
24
24
  */
25
25
  export declare const FLOATING_COLLISION_PADDING = 16;
26
+ /**
27
+ * The class every Base UI `*.Positioner` carries (ComboboxField's list, the Autocomplete
28
+ * suggestion list) — its stacking AND its pointer events, one string so the two cannot drift.
29
+ *
30
+ * 🔴 `pointer-events-auto`: Base UI portals the list into `<body>`, and an open Radix modal
31
+ * (a belt `Modal` or `Drawer`) sets `body { pointer-events: none }`, so a list opened inside a
32
+ * dialog painted over it and every tap fell through to the dialog (measured 2026-10-25, photos'
33
+ * place picker in the phone's Details drawer, forge task 3764). Radix still counts a press on the
34
+ * list as inside — its DismissableLayer tracks the React tree — so the dialog stays open.
35
+ * `floatingStyles.spec.ts` fails on a Positioner in this library that does not use it.
36
+ */
37
+ export declare const BASE_UI_POSITIONER = "pointer-events-auto z-[var(--z-popover)]";
26
38
  /**
27
39
  * Base surface for any floating panel: overlay elevation, popover tokens, ring-free.
28
40
  *
@@ -1 +1 @@
1
- {"version":3,"file":"floatingStyles.d.ts","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAIY,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAOS,CAAC;AAEzC;;8BAE8B;AAC9B,eAAO,MAAM,kBAAkB,QAAkB,CAAC"}
1
+ {"version":3,"file":"floatingStyles.d.ts","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,6CAA6C,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,QAIY,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,QAOS,CAAC;AAEzC;;8BAE8B;AAC9B,eAAO,MAAM,kBAAkB,QAAkB,CAAC"}
@@ -25,6 +25,18 @@ import { TAP_TARGET_HEIGHT } from './tapTarget.js';
25
25
  * so it reads as a floating surface rather than as a pane that ran out of screen.
26
26
  */
27
27
  export const FLOATING_COLLISION_PADDING = 16;
28
+ /**
29
+ * The class every Base UI `*.Positioner` carries (ComboboxField's list, the Autocomplete
30
+ * suggestion list) — its stacking AND its pointer events, one string so the two cannot drift.
31
+ *
32
+ * 🔴 `pointer-events-auto`: Base UI portals the list into `<body>`, and an open Radix modal
33
+ * (a belt `Modal` or `Drawer`) sets `body { pointer-events: none }`, so a list opened inside a
34
+ * dialog painted over it and every tap fell through to the dialog (measured 2026-10-25, photos'
35
+ * place picker in the phone's Details drawer, forge task 3764). Radix still counts a press on the
36
+ * list as inside — its DismissableLayer tracks the React tree — so the dialog stays open.
37
+ * `floatingStyles.spec.ts` fails on a Positioner in this library that does not use it.
38
+ */
39
+ export const BASE_UI_POSITIONER = 'pointer-events-auto z-[var(--z-popover)]';
28
40
  /**
29
41
  * Base surface for any floating panel: overlay elevation, popover tokens, ring-free.
30
42
  *
@@ -1 +1 @@
1
- {"version":3,"file":"floatingStyles.js","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,0FAA0F;IAC1F,8CAA8C;IAC9C,iFAAiF;IACjF,4CAA4C,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,0GAA0G;IAC1G,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,GAAG,iBAAiB,GAAG;IACvB,iEAAiE;IACjE,sCAAsC,CAAC;AAEzC;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"floatingStyles.js","sourceRoot":"","sources":["../../../src/react/lib/floatingStyles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,0CAA0C,CAAC;AAE7E;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,0FAA0F;IAC1F,8CAA8C;IAC9C,iFAAiF;IACjF,4CAA4C,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAC7B,0GAA0G;IAC1G,+EAA+E;IAC/E,gFAAgF;IAChF,6EAA6E;IAC7E,GAAG,iBAAiB,GAAG;IACvB,iEAAiE;IACjE,sCAAsC,CAAC;AAEzC;;8BAE8B;AAC9B,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 142 candidate(s).
21
+ * 150 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -34,14 +34,15 @@
34
34
  @source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 active:cursor-grabbing basis basis-full");
35
35
  @source inline("bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30 bg-muted/50");
36
36
  @source inline("border-accent-solid border-accent/60 border-b-0! border-field-error border-solid border-t-2 col");
37
- @source inline("collapse cursor-grab disabled:opacity-40 fade-in focus-visible:ring-field-ring/40 from-card");
38
- @source inline("gap-x-6 h-0 h-105 has-[[aria-invalid=true]]:border-field-error hover:bg-background");
39
- @source inline("hover:bg-muted/40 hover:border-field-ring hover:text-warning italic justify-self-end");
40
- @source inline("last:border-0 left-11 left-2.5 list-none max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0");
37
+ @source inline("collapse cursor-grab disabled:opacity-30 disabled:opacity-40 fade-in");
38
+ @source inline("focus-visible:ring-field-ring/40 from-card gap-x-6 grid-cols-3 grid-cols-4 h-0 h-105 h-3.5");
39
+ @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
40
+ @source inline("hover:border-field-ring hover:text-warning italic justify-self-end last:border-0 left-11");
41
+ @source inline("left-2.5 list-none max-h-60 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0");
41
42
  @source inline("max-sm:min-w-24 max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48");
42
43
  @source inline("max-w-60 min-h-20 min-h-dvh min-w-0! min-w-20 min-w-4 min-w-52 min-w-max mr-2 mt-1.5 mt-3 mt-4");
43
- @source inline("not-italic ordinal p-0! pb-1.5 pl-1.5 pr-0.5 pr-10 pr-16 pr-3 pr-9 pt-1 py-0! resize-none");
44
- @source inline("resize-y right-3 ring-field-error/30 self self-stretch shadow-[1px_0_0_0_var(--color-border)]");
45
- @source inline("shrink-[2] size-7 slide-in-from-top-1 table-fixed text-[length:0] text-accent-solid-foreground");
46
- @source inline("text-field-placeholder text-success text-xl to-transparent top-3 w-0 w-0! w-11 w-12 w-20 w-32");
47
- @source inline("w-36 w-40 w-88");
44
+ @source inline("not-italic ordinal p-0! pb-1.5 pl-1.5 pointer-coarse:pr-12 pr-0.5 pr-10 pr-11 pr-16 pr-3 pr-9");
45
+ @source inline("pt-1 py-0! resize-none resize-y right-3 ring-field-error/30 self self-stretch");
46
+ @source inline("shadow-[1px_0_0_0_var(--color-border)] shrink-[2] size-7 slide-in-from-top-1 table-fixed");
47
+ @source inline("text-[length:0] text-accent-solid-foreground text-field-placeholder text-success text-xl");
48
+ @source inline("to-transparent top-3 w-0 w-0! w-11 w-12 w-20 w-3.5 w-32 w-36 w-40 w-88");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 137 candidate(s).
21
+ * 145 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -34,14 +34,15 @@
34
34
  @source inline("[&>*]:hidden [&>*]:max-w-none [&>svg]:size-3.5 active:cursor-grabbing basis-full");
35
35
  @source inline("bg-[var(--cb-frozen-bg)] bg-accent/15 bg-card/95 bg-gradient-to-t bg-muted/30 bg-muted/50");
36
36
  @source inline("border-accent-solid border-accent/60 border-b-0! border-field-error border-solid border-t-2 col");
37
- @source inline("collapse cursor-grab disabled:opacity-40 fade-in focus-visible:ring-field-ring/40 from-card");
38
- @source inline("gap-x-6 h-0 h-105 has-[[aria-invalid=true]]:border-field-error hover:bg-background");
39
- @source inline("hover:bg-muted/40 hover:border-field-ring hover:text-warning justify-self-end last:border-0");
40
- @source inline("left-11 left-2.5 list-none max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0");
41
- @source inline("max-sm:min-w-24 max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48");
42
- @source inline("max-w-60 min-h-20 min-h-dvh min-w-0! min-w-4 min-w-52 min-w-max mr-2 mt-1.5 mt-3 mt-4 not-italic");
43
- @source inline("ordinal p-0! pb-1.5 pl-1.5 pr-0.5 pr-10 pr-16 pr-3 pr-9 pt-1 py-0! resize-none resize-y right-3");
44
- @source inline("ring-field-error/30 self self-stretch shadow-[1px_0_0_0_var(--color-border)] size-7");
45
- @source inline("slide-in-from-top-1 table-fixed text-[length:0] text-accent-solid-foreground");
46
- @source inline("text-field-placeholder text-success text-xl to-transparent top-3 w-0! w-11 w-12 w-20 w-32 w-36");
47
- @source inline("w-40 w-88");
37
+ @source inline("collapse cursor-grab disabled:opacity-30 disabled:opacity-40 fade-in");
38
+ @source inline("focus-visible:ring-field-ring/40 from-card gap-x-6 grid-cols-3 grid-cols-4 h-0 h-105 h-3.5");
39
+ @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-background hover:bg-muted/40");
40
+ @source inline("hover:border-field-ring hover:text-warning justify-self-end last:border-0 left-11 left-2.5");
41
+ @source inline("list-none max-h-60 max-h-88 max-sm:flex-1 max-sm:flex-wrap max-sm:min-w-0 max-sm:min-w-24");
42
+ @source inline("max-sm:min-w-32 max-sm:ml-13.5 max-sm:w-auto max-w-32 max-w-36 max-w-45 max-w-48 max-w-60");
43
+ @source inline("min-h-20 min-h-dvh min-w-0! min-w-4 min-w-52 min-w-max mr-2 mt-1.5 mt-3 mt-4 not-italic ordinal");
44
+ @source inline("p-0! pb-1.5 pl-1.5 pointer-coarse:pr-12 pr-0.5 pr-10 pr-11 pr-16 pr-3 pr-9 pt-1 py-0!");
45
+ @source inline("resize-none resize-y right-3 ring-field-error/30 self self-stretch");
46
+ @source inline("shadow-[1px_0_0_0_var(--color-border)] size-7 slide-in-from-top-1 table-fixed text-[length:0]");
47
+ @source inline("text-accent-solid-foreground text-field-placeholder text-success text-xl to-transparent top-3");
48
+ @source inline("w-0! w-11 w-12 w-20 w-3.5 w-32 w-36 w-40 w-88");
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 87 candidate(s).
21
+ * 94 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -28,17 +28,19 @@
28
28
  @source inline("[scrollbar-width:none] appearance-none aria-checked:ring-2 aria-checked:ring-foreground");
29
29
  @source inline("aria-checked:ring-offset-1 bg-accent/15 bg-surface-sunken/80 bg-warning/10 border-border/40");
30
30
  @source inline("border-field-error caret-transparent cursor-text data-[state=checked]:ring-foreground");
31
- @source inline("data-[state=checked]:ring-offset-1 disabled:hover:scale-100 disabled:opacity-40");
32
- @source inline("focus-visible:border-border-strong focus-visible:ring-0 focus-visible:ring-field-ring/40");
33
- @source inline("focus-within:ring-offset-1 focus-within:ring-ring group-focus-within:opacity-100 h-105 h-12");
34
- @source inline("h-3.5 has-[[aria-invalid=true]]:border-field-error hover:bg-muted/50 hover:border-field-ring");
35
- @source inline("hover:scale-110 hover:text-warning italic left-2.5 li list-none md:flex-col md:gap-0.5");
31
+ @source inline("data-[state=checked]:ring-offset-1 disabled:hover:scale-100 disabled:opacity-30");
32
+ @source inline("disabled:opacity-40 focus-visible:border-border-strong focus-visible:ring-0");
33
+ @source inline("focus-visible:ring-field-ring/40 focus-within:ring-offset-1 focus-within:ring-ring grid-cols-3");
34
+ @source inline("grid-cols-4 group-focus-within:opacity-100 h-105 h-12 h-3.5");
35
+ @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-muted/50 hover:border-field-ring");
36
+ @source inline("hover:scale-110 hover:text-warning italic left-2.5 li list-none max-h-60 md:flex-col md:gap-0.5");
36
37
  @source inline("md:justify-start md:overflow-visible md:text-left md:w-full min-h-20 min-w-64 min-w-full");
37
38
  @source inline("min-w-max mix-blend-difference mr-2 pl-1.5 pl-2.5 pointer-coarse:before:-left-1");
38
39
  @source inline("pointer-coarse:before:-right-1 pointer-coarse:before:absolute");
39
40
  @source inline("pointer-coarse:before:bottom-[calc(var(--hit-slop-y)*-1)]");
40
- @source inline("pointer-coarse:before:top-[calc(var(--hit-slop-y)*-1)] pointer-coarse:items-center pr-16 pr-2");
41
- @source inline("pr-3 pr-9 pt-1 resize-none resize-y right-1.5 right-3 ring-accent/30 ring-field-error/30");
42
- @source inline("self-stretch size-7 size-9 size-full sm:w-11 sm:w-56 text-field-placeholder text-success text-xl");
43
- @source inline("top-1.5 w-20 w-32 w-36 w-40 w-88 w-fit w-max whitespace-pre");
41
+ @source inline("pointer-coarse:before:top-[calc(var(--hit-slop-y)*-1)] pointer-coarse:items-center");
42
+ @source inline("pointer-coarse:pr-12 pr-11 pr-16 pr-2 pr-3 pr-9 pt-1 resize-none resize-y right-1.5 right-3");
43
+ @source inline("ring-accent/30 ring-field-error/30 self-stretch size-7 size-9 size-full sm:w-11 sm:w-56");
44
+ @source inline("text-field-placeholder text-success text-xl top-1.5 w-20 w-3.5 w-32 w-36 w-40 w-88 w-fit w-max");
45
+ @source inline("whitespace-pre");
44
46
  @source inline('pointer-coarse:before:content-[""]');
@@ -18,7 +18,7 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 86 candidate(s).
21
+ * 94 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
@@ -27,16 +27,17 @@
27
27
  @source inline("[&>div]:sm:[grid-template-columns:…] [&_svg]:size-3.5 basis-40 bg-accent/15 bg-accent/40");
28
28
  @source inline("bg-background/70 bg-success/10 bg-warning/10 bg-warning/20 border-field-error border-l-2");
29
29
  @source inline("border-l-border-strong border-l-destructive border-l-warning bottom-3 col col-span-2 col-start-2");
30
- @source inline("collapse cursor-help divide-x first:pl-1 focus-visible:ring-field-ring/40 gap-x-1.5 gap-x-2");
31
- @source inline("gap-y-0.5 grid-cols-[minmax(0,1fr)_auto] group-focus-visible/meter:opacity-100");
32
- @source inline("group-focus-visible/stat:opacity-100 group-focus-visible/tile:opacity-100");
33
- @source inline("group-hover/meter:opacity-100 group-hover/stat:opacity-100 group-hover/tile:opacity-100");
34
- @source inline("group/meter group/stat group/tile h-105 has-[[aria-invalid=true]]:border-field-error");
35
- @source inline("hover:bg-muted/50 hover:border-field-ring hover:text-warning last:pr-1 left-3");
36
- @source inline("left-[var(--timeline-rail-x)] list-none min-h-20 min-h-9 min-w-34 min-w-full mr-0.5 mr-1.5 mr-2");
37
- @source inline("normal-case pl-1.5 pr-16 pr-3 pt-1 resize-none resize-y ring-field-error/30 rounded-b-md");
38
- @source inline("rounded-t-md row-start-1 self-stretch shadow-md size-7 sm:col-auto sm:col-span-1 sm:flex-nowrap");
30
+ @source inline("collapse cursor-help disabled:opacity-30 divide-x first:pl-1 focus-visible:ring-field-ring/40");
31
+ @source inline("gap-x-1.5 gap-x-2 gap-y-0.5 grid-cols-3 grid-cols-4 grid-cols-[minmax(0,1fr)_auto]");
32
+ @source inline("group-focus-visible/meter:opacity-100 group-focus-visible/stat:opacity-100");
33
+ @source inline("group-focus-visible/tile:opacity-100 group-hover/meter:opacity-100 group-hover/stat:opacity-100");
34
+ @source inline("group-hover/tile:opacity-100 group/meter group/stat group/tile h-105 h-3.5");
35
+ @source inline("has-[[aria-invalid=true]]:border-field-error hover:bg-muted/50 hover:border-field-ring");
36
+ @source inline("hover:text-warning last:pr-1 left-3 left-[var(--timeline-rail-x)] list-none max-h-60 min-h-20");
37
+ @source inline("min-h-9 min-w-34 min-w-full mr-0.5 mr-1.5 mr-2 normal-case pl-1.5 pointer-coarse:pr-12 pr-11");
38
+ @source inline("pr-16 pr-3 pt-1 resize-none resize-y ring-field-error/30 rounded-b-md rounded-t-md row-start-1");
39
+ @source inline("self-stretch shadow-md size-7 sm:col-auto sm:col-span-1 sm:flex-nowrap");
39
40
  @source inline("sm:grid-cols-[minmax(7rem,18rem)_minmax(4rem,1fr)_auto]");
40
41
  @source inline("sm:grid-cols-[minmax(7rem,34rem)_minmax(4rem,1fr)_auto] sm:row-auto sm:truncate text-3xl");
41
42
  @source inline("text-[length:var(--text-glyph)] text-field-placeholder text-foreground/80 text-success text-xl");
42
- @source inline("top-7 tracking-normal w-36 w-48 w-88 whitespace-pre z-[1]");
43
+ @source inline("top-7 tracking-normal w-3.5 w-36 w-48 w-88 whitespace-pre z-[1]");
@@ -18,14 +18,14 @@
18
18
  * after an app's responsive variant (task 2124). An app that imports every area gets exactly
19
19
  * what `cursedbelt/styles-utilities.css` emits — proved rule by rule in
20
20
  * src/stylesAreas.spec.ts, which is also what fails when this file is stale.
21
- * 25 candidate(s).
21
+ * 33 candidate(s).
22
22
  *
23
23
  * Written by scripts/generateAreaStyles.ts (`bun run styles:areas`), which
24
24
  * `bun run build` runs. The map of what is in which area, and why the areas are
25
25
  * import closures rather than a hand-written list, is scripts/styleAreas.ts.
26
26
  */
27
- @source inline("border-field-error collapse disabled:bg-transparent disabled:opacity-100");
28
- @source inline("focus-visible:ring-field-ring/40 h-105 has-[[aria-invalid=true]]:border-field-error");
29
- @source inline("hover:border-field-ring hover:text-warning min-h-20 mr-1.5 mr-2 pl-1.5 pr-16 pr-3 resize-none");
30
- @source inline("resize-y ring-field-error/30 self-stretch size-7 sm:inline text-field-placeholder text-xl w-36");
31
- @source inline("w-88");
27
+ @source inline("border-field-error collapse disabled:bg-transparent disabled:opacity-100 disabled:opacity-30");
28
+ @source inline("focus-visible:ring-field-ring/40 grid-cols-3 grid-cols-4 h-105 h-3.5");
29
+ @source inline("has-[[aria-invalid=true]]:border-field-error hover:border-field-ring hover:text-warning max-h-60");
30
+ @source inline("min-h-20 mr-1.5 mr-2 pl-1.5 pointer-coarse:pr-12 pr-11 pr-16 pr-3 resize-none resize-y");
31
+ @source inline("ring-field-error/30 self-stretch size-7 sm:inline text-field-placeholder text-xl w-3.5 w-36 w-88");
@@ -773,6 +773,9 @@
773
773
  .max-h-56 {
774
774
  max-height: calc(var(--spacing) * 56);
775
775
  }
776
+ .max-h-60 {
777
+ max-height: calc(var(--spacing) * 60);
778
+ }
776
779
  .max-h-64 {
777
780
  max-height: calc(var(--spacing) * 64);
778
781
  }
@@ -1371,6 +1374,9 @@
1371
1374
  .grid-cols-2 {
1372
1375
  grid-template-columns: repeat(2, minmax(0, 1fr));
1373
1376
  }
1377
+ .grid-cols-3 {
1378
+ grid-template-columns: repeat(3, minmax(0, 1fr));
1379
+ }
1374
1380
  .grid-cols-4 {
1375
1381
  grid-template-columns: repeat(4, minmax(0, 1fr));
1376
1382
  }
@@ -2733,6 +2739,9 @@
2733
2739
  .pr-10 {
2734
2740
  padding-right: calc(var(--spacing) * 10);
2735
2741
  }
2742
+ .pr-11 {
2743
+ padding-right: calc(var(--spacing) * 11);
2744
+ }
2736
2745
  .pr-16 {
2737
2746
  padding-right: calc(var(--spacing) * 16);
2738
2747
  }
@@ -4183,6 +4192,9 @@
4183
4192
  .disabled\:opacity-0:disabled {
4184
4193
  opacity: 0%;
4185
4194
  }
4195
+ .disabled\:opacity-30:disabled {
4196
+ opacity: 30%;
4197
+ }
4186
4198
  .disabled\:opacity-40:disabled {
4187
4199
  opacity: 40%;
4188
4200
  }
@@ -5243,6 +5255,9 @@
5243
5255
  .pointer-coarse\:p-0 {
5244
5256
  padding: 0px;
5245
5257
  }
5258
+ .pointer-coarse\:pr-12 {
5259
+ padding-right: calc(var(--spacing) * 12);
5260
+ }
5246
5261
  .pointer-coarse\:opacity-100 {
5247
5262
  opacity: 100%;
5248
5263
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cursedbelt",
3
- "version": "5.94.0",
3
+ "version": "5.95.0",
4
4
  "license": "ISC",
5
5
  "type": "module",
6
6
  "description": "The React design system of the cursedbelt split — components, styles, theme. cursedbelt-core below it; server tier in cursedbelt-server.",
@@ -58,6 +58,59 @@ describe('AppShell', () => {
58
58
  expect(q(container, '[data-cb-nav-backdrop]')).toBeNull();
59
59
  });
60
60
 
61
+ test('🔴 navCollapsed folds the lg sidebar to the icon rail, and the fold button in the rail head asks for the next state (forge task 3773)', () => {
62
+ const changes: boolean[] = [];
63
+ const { container, rerender } = render(
64
+ <AppShell nav={station} navHeader={<span data-brand=''>Photos</span>} account={<span>CU</span>} onNavCollapsedChange={(next) => changes.push(next)}>
65
+ x
66
+ </AppShell>,
67
+ );
68
+ const [icon, full] = qa(container, '[data-cb-nav-rail]') as [HTMLElement, HTMLElement];
69
+ // Open: the icon rail hides at lg, the full sidebar shows there — as before.
70
+ expect(icon.className).toContain('lg:hidden');
71
+ expect(full.className).toContain('lg:flex');
72
+ // The fold button is in BOTH heads (the rail must be able to unfold itself), and says what it does.
73
+ const folds = qa(container, '[data-cb-rail-fold]');
74
+ expect(folds).toHaveLength(2);
75
+ expect(folds.map((b) => b.getAttribute('aria-label'))).toEqual(['Show the sidebar', 'Hide the sidebar']);
76
+ // With no ⌘K row it sits on the brand line; with one, beside the ⌘K trigger — never a third control
77
+ // beside a worded account launcher, which left the brand no width (photos, WebKit, 2026-10-25).
78
+ const { container: noCommand } = render(
79
+ <AppShell nav={station} commandPalette={false} navHeader={<span>Photos</span>} account={<span>CU</span>} onNavCollapsedChange={() => {}}>
80
+ x
81
+ </AppShell>,
82
+ );
83
+ const fullNoCommand = qa(noCommand, '[data-cb-nav-rail]')[1] as HTMLElement;
84
+ expect(q(fullNoCommand, '[data-cb-rail-command-row]')).toBeNull();
85
+ expect(q(fullNoCommand, '[data-cb-account]')?.parentElement?.querySelector('[data-cb-rail-fold]')).not.toBeNull();
86
+ const { container: withCommand } = render(
87
+ <AppShell nav={station} navHeader={<span>Photos</span>} account={<span>Curt Phillips ▾</span>} onOpenCommand={() => {}} onNavCollapsedChange={() => {}}>
88
+ x
89
+ </AppShell>,
90
+ );
91
+ const fullWithCommand = qa(withCommand, '[data-cb-nav-rail]')[1] as HTMLElement;
92
+ expect(q(fullWithCommand, '[data-cb-account]')?.parentElement?.querySelector('[data-cb-rail-fold]')).toBeNull();
93
+ expect(q(fullWithCommand, '[data-cb-rail-command-row] [data-cb-rail-fold]')).not.toBeNull();
94
+ expect(q(fullWithCommand, '[data-cb-rail-command-row] [data-cb-command-trigger]')).not.toBeNull();
95
+ fireEvent.click(folds[1] as HTMLElement);
96
+ expect(changes).toEqual([true]);
97
+ // Folded: the icon rail stays at lg and the full sidebar is gone from every width.
98
+ rerender(
99
+ <AppShell nav={station} navHeader={<span data-brand=''>Photos</span>} account={<span>CU</span>} navCollapsed onNavCollapsedChange={(next) => changes.push(next)}>
100
+ x
101
+ </AppShell>,
102
+ );
103
+ const [iconFolded, fullFolded] = qa(container, '[data-cb-nav-rail]') as [HTMLElement, HTMLElement];
104
+ expect(iconFolded.className).not.toContain('lg:hidden');
105
+ expect(fullFolded.className).not.toContain('lg:flex');
106
+ expect(fullFolded.className).toContain('hidden');
107
+ fireEvent.click(q(iconFolded, '[data-cb-rail-fold]') as HTMLElement);
108
+ expect(changes).toEqual([true, false]);
109
+ // Without the callback nothing is offered: a shell that cannot remember the choice does not ask for it.
110
+ rerender(<AppShell nav={station}>x</AppShell>);
111
+ expect(qa(container, '[data-cb-rail-fold]')).toHaveLength(0);
112
+ });
113
+
61
114
  test('🔴 NAV-15 · with a sidebar the header is phone-only, and the brand, account and ⌘K head the sidebar (owner, 2026-10-20)', () => {
62
115
  const { container } = render(
63
116
  <AppShell