@apliteni/apliteni-ui 0.91.0 → 0.93.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 (55) hide show
  1. package/README.md +5 -7
  2. package/package.json +1 -1
  3. package/react/README.md +2 -2
  4. package/react/dist/index.d.ts +3 -3
  5. package/react/dist/index.js +79 -111
  6. package/src/assets/icons.js +1 -1
  7. package/src/components/account-nav.js +2 -2
  8. package/src/components/back.js +2 -2
  9. package/src/components/command-palette.js +2 -2
  10. package/src/components/drawer.js +1 -1
  11. package/src/components/dropdown.js +12 -12
  12. package/src/components/filter-bar.js +2 -2
  13. package/src/components/footer.js +1 -1
  14. package/src/components/index.js +1 -1
  15. package/src/components/loading.js +1 -1
  16. package/src/components/shell.js +19 -19
  17. package/src/components/stat.js +3 -3
  18. package/src/components/success.js +3 -3
  19. package/src/components/tooltip.js +1 -1
  20. package/src/components/topbar.js +1 -1
  21. package/src/html.js +1 -1
  22. package/src/index.css +1 -1
  23. package/src/index.d.ts +1 -1
  24. package/src/logic/accents.js +1 -1
  25. package/src/logic/filter-bar.js +4 -4
  26. package/src/styles/back.css +3 -3
  27. package/src/styles/base.css +3 -3
  28. package/src/styles/button.css +3 -3
  29. package/src/styles/callout.css +2 -2
  30. package/src/styles/card.css +8 -8
  31. package/src/styles/command-palette.css +14 -14
  32. package/src/styles/confirm.css +7 -7
  33. package/src/styles/drawer.css +8 -8
  34. package/src/styles/dropdown.css +21 -21
  35. package/src/styles/feedback.css +2 -2
  36. package/src/styles/field-zoom.css +1 -1
  37. package/src/styles/file-drop.css +1 -1
  38. package/src/styles/filter-bar.css +6 -6
  39. package/src/styles/footer.css +1 -1
  40. package/src/styles/input.css +6 -6
  41. package/src/styles/layout.css +32 -32
  42. package/src/styles/loading.css +1 -1
  43. package/src/styles/motion.css +1 -1
  44. package/src/styles/nav.css +4 -4
  45. package/src/styles/pagination.css +2 -2
  46. package/src/styles/reduced-motion.css +2 -2
  47. package/src/styles/segmented.css +3 -3
  48. package/src/styles/stat.css +3 -3
  49. package/src/styles/success.css +1 -1
  50. package/src/styles/table.css +6 -6
  51. package/src/styles/tap-zone.css +1 -1
  52. package/src/styles/tooltip.css +3 -3
  53. package/src/styles/topbar.css +8 -8
  54. package/src/tokens/accents.css +1 -1
  55. package/src/tokens/tokens.css +25 -25
package/README.md CHANGED
@@ -61,7 +61,7 @@ import { DataTable, Modal } from '@apliteni/apliteni-ui/react';
61
61
 
62
62
  The `appShell()` factory provides the complete product layout: the sticky rail, the page body
63
63
  and a topbar over it when you ask for one, so no product rebuilds it. The
64
- [component catalog](docs/library.md#component-catalog) lists this factory and every other one.
64
+ [component catalog](docs/components.md#component-catalog) lists this factory and every other one.
65
65
  Account and personal settings belong in a modal over the product rather than on a page of
66
66
  their own — see [Guidelines / Account and settings](guidelines/account-and-settings.md).
67
67
 
@@ -100,7 +100,7 @@ only `data-accent` is present, the kit uses that accent with the dark theme. An
100
100
  `data-theme` does *not* mean “follow the system”: the kit has no `prefers-color-scheme`
101
101
  rule. If the host wants to follow the operating system preference, its JavaScript must
102
102
  choose the theme and add the attribute. See
103
- [an absent attribute means dark](docs/library.md#an-absent-attribute-means-dark).
103
+ [an absent attribute means dark](docs/components.md#an-absent-attribute-means-dark).
104
104
 
105
105
  You can change the settings at runtime with `applyTheme('light')` and
106
106
  `applyAccent('phoenix')`. These functions save the choices to `localStorage`. You can also
@@ -121,12 +121,10 @@ section.
121
121
 
122
122
  | Page | What it answers |
123
123
  | --- | --- |
124
- | [specification.md](docs/specification.md) | What the kit guarantees and what it refuses to do. A gate on `npm test` holds every statement. |
125
- | [library.md](docs/library.md) | Architecture, the `src/` layout, tokens and theming, the component catalog. |
124
+ | [foundations.md](docs/foundations.md) | The tokens and the floors: widths, breakpoints, spacing, type, colour, elevation, the focus ring, the tap floor. |
125
+ | [components.md](docs/components.md) | The package: what to import, theming, the component catalog, and what each component guarantees. |
126
126
  | [react/README.md](react/README.md) | The React components, their props, and the Storybook on port 6007. |
127
- | [storybook.md](docs/storybook.md) | The workbench: config, theming toolbar, story conventions. |
128
- | [landing-page.md](docs/landing-page.md) | ui.apli.tech: the chrome, the static build, the hosting. |
129
- | [changelog.md](docs/changelog.md) | The changelog's data model and its Storybook deeplinks. |
127
+ | [contributing.md](docs/contributing.md) | Working on the kit: the repository's layout, Storybook, the guidelines collection, the site, the changelog and releases. |
130
128
 
131
129
  ## Contribute
132
130
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.91.0",
3
+ "version": "0.93.0",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
package/react/README.md CHANGED
@@ -246,7 +246,7 @@ this component and compares what is left.
246
246
  Two things the factory has that this does not, both deliberate. It emits no `data-dropdown`
247
247
  on the container, so a page that calls `wireDropdown(document)` cannot adopt a dropdown React
248
248
  owns — the same decision `Drawer` makes about `data-drawer`; the row and panel hooks stay,
249
- because they are the row contract `docs/library.md` publishes. And it has no `portal: true`: the
249
+ because they are the row contract `docs/components.md` publishes. And it has no `portal: true`: the
250
250
  panel is a child of the trigger's container, so a dropdown inside `.ui-app__rail`
251
251
  (`position: sticky` with `overflow-y: auto`) still wants the vanilla factory.
252
252
 
@@ -541,7 +541,7 @@ An optional `id` lets the caller choose the control ID; it must be unique.
541
541
  `type="search"` input, and no visible label, so the row keeps the height of the
542
542
  unlabelled controls beside it. `ariaLabel` is required and is the control's only
543
543
  name. It forwards its ref and the native input props, and `className` joins
544
- `.ui-input`. There is no clear button — see the specification for why.
544
+ `.ui-input`. There is no clear button — see docs/components.md#react-search-field for why.
545
545
 
546
546
  ```tsx
547
547
  <div className="ui-toolbar">
@@ -730,7 +730,7 @@ type SearchFieldProps = Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'ar
730
730
  * No clear button. The browser paints its own near-black on the light field and
731
731
  * white on the dark one, beside the kit's `--muted` magnifier — two glyphs, and
732
732
  * only one answers to a token — so `input.css` suppresses it.
733
- * why: docs/specification.md#react-search-field
733
+ * why: docs/components.md#react-search-field
734
734
  */
735
735
  declare const SearchField: react.ForwardRefExoticComponent<Omit<InputHTMLAttributes<HTMLInputElement>, "type" | "aria-label"> & {
736
736
  /** The control's name. A toolbar search box shows no label, so this is the
@@ -850,7 +850,7 @@ type SuccessProps = Omit<HTMLAttributes<HTMLDivElement>, 'title' | 'children'> &
850
850
  check?: SuccessMark;
851
851
  title?: string;
852
852
  /** One short line under the title, or nothing. There is no eyebrow tier.
853
- * why: docs/specification.md#success-confirmations */
853
+ * why: docs/components.md#success-confirmations */
854
854
  body?: string;
855
855
  /** Removed: a confirmation carries one title and at most one line. Typed as
856
856
  * `never` so a caller still passing it is told where it went rather than
@@ -869,7 +869,7 @@ declare const Success: react.ForwardRefExoticComponent<Omit<HTMLAttributes<HTMLD
869
869
  check?: SuccessMark;
870
870
  title?: string;
871
871
  /** One short line under the title, or nothing. There is no eyebrow tier.
872
- * why: docs/specification.md#success-confirmations */
872
+ * why: docs/components.md#success-confirmations */
873
873
  body?: string;
874
874
  /** Removed: a confirmation carries one title and at most one line. Typed as
875
875
  * `never` so a caller still passing it is told where it went rather than
@@ -935,7 +935,7 @@ function rowId(uid, groups, item) {
935
935
  }
936
936
 
937
937
  // src/DataTable.tsx
938
- import { useEffect as useEffect5, useId as useId7, useMemo as useMemo2, useRef as useRef9, useState as useState5 } from "react";
938
+ import { useMemo as useMemo2, useState as useState5 } from "react";
939
939
  import { DEFAULT_PAGE_SIZE as DEFAULT_PAGE_SIZE2 } from "@apliteni/apliteni-ui";
940
940
 
941
941
  // src/Pagination.tsx
@@ -1199,31 +1199,6 @@ function DataTable({
1199
1199
  hasMore = false
1200
1200
  }) {
1201
1201
  const scrollable = stickyHeader || pinnedIdentity;
1202
- const scrollId = useId7();
1203
- const scrollRegion = useRef9(null);
1204
- const [columnScroll, setColumnScroll] = useState5({ overflow: false, start: true, end: false });
1205
- const measureColumns = () => {
1206
- const region = scrollRegion.current;
1207
- if (region) setColumnScroll({
1208
- overflow: region.scrollWidth > region.clientWidth + 1,
1209
- start: region.scrollLeft <= 1,
1210
- end: region.scrollLeft + region.clientWidth >= region.scrollWidth - 1
1211
- });
1212
- };
1213
- useEffect5(() => {
1214
- if (!scrollable) return;
1215
- const region = scrollRegion.current;
1216
- if (!region) return;
1217
- const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(measureColumns);
1218
- observer?.observe(region);
1219
- if (region.firstElementChild) observer?.observe(region.firstElementChild);
1220
- measureColumns();
1221
- return () => observer?.disconnect();
1222
- }, [scrollable, columns, rows]);
1223
- const scrollColumns = (direction) => {
1224
- const region = scrollRegion.current;
1225
- if (region) region.scrollBy({ left: direction * region.clientWidth / 2, behavior: "instant" });
1226
- };
1227
1202
  const [localSort, setLocalSort] = useState5(
1228
1203
  { key: columns.find((c) => c.sortable)?.key, dir: -1 }
1229
1204
  );
@@ -1276,20 +1251,13 @@ function DataTable({
1276
1251
  );
1277
1252
  const pageAllOn = selectable && slice.length > 0 && slice.every((r) => selected.has(r.name));
1278
1253
  return /* @__PURE__ */ jsxs13(Fragment3, { children: [
1279
- scrollable && columnScroll.overflow && /* @__PURE__ */ jsxs13("div", { className: "ui-card__row", role: "group", "aria-label": `${scrollLabel} columns`, children: [
1280
- /* @__PURE__ */ jsx17(Button, { size: "sm", icon: "arrowLeft", "aria-controls": scrollId, disabled: columnScroll.start, onClick: () => scrollColumns(-1), children: "Previous columns" }),
1281
- /* @__PURE__ */ jsx17(Button, { size: "sm", iconRight: "arrowRight", "aria-controls": scrollId, disabled: columnScroll.end, onClick: () => scrollColumns(1), children: "More columns" })
1282
- ] }),
1283
1254
  /* @__PURE__ */ jsx17(
1284
1255
  "div",
1285
1256
  {
1286
- id: scrollId,
1287
- ref: scrollRegion,
1288
- onScroll: measureColumns,
1289
- className: stickyHeader || pinnedIdentity ? "ui-table-scroll" : void 0,
1290
- role: stickyHeader || pinnedIdentity ? "region" : void 0,
1291
- "aria-label": stickyHeader || pinnedIdentity ? scrollLabel : void 0,
1292
- tabIndex: stickyHeader || pinnedIdentity ? 0 : void 0,
1257
+ className: scrollable ? "ui-table-scroll" : void 0,
1258
+ role: scrollable ? "region" : void 0,
1259
+ "aria-label": scrollable ? scrollLabel : void 0,
1260
+ tabIndex: scrollable ? 0 : void 0,
1293
1261
  children: /* @__PURE__ */ jsxs13(
1294
1262
  "table",
1295
1263
  {
@@ -1442,9 +1410,9 @@ function Denied({
1442
1410
  // src/DatePicker.tsx
1443
1411
  import {
1444
1412
  useCallback as useCallback2,
1445
- useEffect as useEffect6,
1413
+ useEffect as useEffect5,
1446
1414
  useMemo as useMemo3,
1447
- useRef as useRef10,
1415
+ useRef as useRef9,
1448
1416
  useState as useState6
1449
1417
  } from "react";
1450
1418
  import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
@@ -1457,7 +1425,7 @@ var DAY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
1457
1425
  var PHONE_MAX = 560;
1458
1426
  function usePhone() {
1459
1427
  const [phone, setPhone] = useState6(false);
1460
- useEffect6(() => {
1428
+ useEffect5(() => {
1461
1429
  const query = typeof window === "undefined" ? null : window.matchMedia?.(`(max-width: ${PHONE_MAX}px)`);
1462
1430
  if (!query) return;
1463
1431
  const read = () => setPhone(query.matches);
@@ -1552,9 +1520,9 @@ function DatePicker({
1552
1520
  const picksRange = mode === "range" || mode === "day-range";
1553
1521
  const phone = usePhone();
1554
1522
  const asSheet = sheet ?? phone;
1555
- const root = useRef10(null);
1556
- const trigger = useRef10(null);
1557
- const grid = useRef10(null);
1523
+ const root = useRef9(null);
1524
+ const trigger = useRef9(null);
1525
+ const grid = useRef9(null);
1558
1526
  const [selfOpen, setSelfOpen] = useState6(defaultOpen);
1559
1527
  const open = openProp ?? selfOpen;
1560
1528
  const [selfValue, setSelfValue] = useState6(defaultValue);
@@ -1591,19 +1559,19 @@ function DatePicker({
1591
1559
  }, [today, grain]);
1592
1560
  const anchor = toIndex(picked, grain) ?? toIndex(picksRange ? span.start ?? span.end : null, grain) ?? todayIndex;
1593
1561
  const [cursor, setCursor] = useState6(() => clamp(anchor, lo, hi));
1594
- const land = useRef10(false);
1562
+ const land = useRef9(false);
1595
1563
  const setOpen = useCallback2((next) => {
1596
1564
  if (openProp === void 0) setSelfOpen(next);
1597
1565
  onOpenChange?.(next);
1598
1566
  }, [openProp, onOpenChange]);
1599
- const close = useRef10(setOpen);
1567
+ const close = useRef9(setOpen);
1600
1568
  useIsoLayoutEffect(() => {
1601
1569
  close.current = setOpen;
1602
1570
  });
1603
- useEffect6(() => {
1571
+ useEffect5(() => {
1604
1572
  if (open) setCursor(clamp(anchor, lo, hi));
1605
1573
  }, [open]);
1606
- useEffect6(() => {
1574
+ useEffect5(() => {
1607
1575
  if (!open || asSheet) return;
1608
1576
  const doc = root.current?.ownerDocument ?? document;
1609
1577
  const onClick = (e) => {
@@ -1913,9 +1881,9 @@ function DatePicker({
1913
1881
  import {
1914
1882
  Fragment as Fragment4,
1915
1883
  useCallback as useCallback3,
1916
- useEffect as useEffect7,
1917
- useId as useId8,
1918
- useRef as useRef11,
1884
+ useEffect as useEffect6,
1885
+ useId as useId7,
1886
+ useRef as useRef10,
1919
1887
  useState as useState7
1920
1888
  } from "react";
1921
1889
  import {
@@ -1987,19 +1955,19 @@ function Dropdown({
1987
1955
  const [pick, setPick] = useState7(null);
1988
1956
  const [query, setQuery] = useState7(() => search && search !== true ? search.query || "" : "");
1989
1957
  const [activeKey, setActiveKey] = useState7(null);
1990
- const root = useRef11(null);
1991
- const panel = useRef11(null);
1992
- const trigger = useRef11(null);
1993
- const field = useRef11(null);
1994
- const landOn = useRef11(null);
1995
- const at = useRef11("");
1996
- const auto = useId8().replace(/:/g, "");
1958
+ const root = useRef10(null);
1959
+ const panel = useRef10(null);
1960
+ const trigger = useRef10(null);
1961
+ const field = useRef10(null);
1962
+ const landOn = useRef10(null);
1963
+ const at = useRef10("");
1964
+ const auto = useId7().replace(/:/g, "");
1997
1965
  const uid = id ?? auto;
1998
1966
  const setOpen = useCallback3((next) => {
1999
1967
  if (openProp === void 0) setSelfOpen(next);
2000
1968
  onOpenChange?.(next);
2001
1969
  }, [openProp, onOpenChange]);
2002
- const close = useRef11(setOpen);
1970
+ const close = useRef10(setOpen);
2003
1971
  useIsoLayoutEffect(() => {
2004
1972
  close.current = setOpen;
2005
1973
  });
@@ -2052,7 +2020,7 @@ function Dropdown({
2052
2020
  stop.forEach((off) => off());
2053
2021
  };
2054
2022
  }, [open]);
2055
- const opened = useRef11(false);
2023
+ const opened = useRef10(false);
2056
2024
  useIsoLayoutEffect(() => {
2057
2025
  const el = panel.current;
2058
2026
  if (open) {
@@ -2091,7 +2059,7 @@ function Dropdown({
2091
2059
  const given = rows.find((it) => it.selected);
2092
2060
  const givenKey = given ? keyOf(given) : null;
2093
2061
  const pickedKey = pick && pick.against === givenKey ? pick.key : null;
2094
- useEffect7(() => {
2062
+ useEffect6(() => {
2095
2063
  if (pick && pick.against !== givenKey) setPick(null);
2096
2064
  }, [givenKey, pick]);
2097
2065
  const picked = pickedKey != null ? rows.find((it) => keyOf(it) === pickedKey) : void 0;
@@ -2105,7 +2073,7 @@ function Dropdown({
2105
2073
  const rowIds = /* @__PURE__ */ new Map();
2106
2074
  rows.forEach((it, i) => rowIds.set(it, `${uid}-opt-${i}`));
2107
2075
  const listId = `${uid}-list`;
2108
- useEffect7(() => {
2076
+ useEffect6(() => {
2109
2077
  if (!open) return;
2110
2078
  const mine = () => close.current(false);
2111
2079
  for (const other of [...openNow]) other();
@@ -2114,7 +2082,7 @@ function Dropdown({
2114
2082
  openNow.delete(mine);
2115
2083
  };
2116
2084
  }, [open]);
2117
- useEffect7(() => {
2085
+ useEffect6(() => {
2118
2086
  if (!open) return;
2119
2087
  const doc = root.current?.ownerDocument ?? document;
2120
2088
  const onClick = (e) => {
@@ -2495,12 +2463,12 @@ function Segmented({ options, value, onChange, label, appearance = "pill", disab
2495
2463
  }
2496
2464
 
2497
2465
  // src/FilterBar.tsx
2498
- import { useRef as useRef12, useState as useState8 } from "react";
2466
+ import { useRef as useRef11, useState as useState8 } from "react";
2499
2467
  import { filterChipText, filterChipName, filterChipUnset, filterChipItems, focusNextStop } from "@apliteni/apliteni-ui";
2500
2468
  import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
2501
2469
  function FilterBar({ filters, label = "Filters", clearLabel = "Clear all filters", disabled = false, busy = false, onRemove, onClear, onChange }) {
2502
- const bar = useRef12(null);
2503
- const focused = useRef12(null);
2470
+ const bar = useRef11(null);
2471
+ const focused = useRef11(null);
2504
2472
  const [opened, setOpened] = useState8({});
2505
2473
  const blocked = disabled || busy;
2506
2474
  useIsoLayoutEffect(() => {
@@ -2606,13 +2574,13 @@ function DrawerSection({ title, headingLevel = 3, className, children, ...rest }
2606
2574
  }
2607
2575
 
2608
2576
  // src/Snippet.tsx
2609
- import { useEffect as useEffect8, useRef as useRef13, useState as useState9 } from "react";
2577
+ import { useEffect as useEffect7, useRef as useRef12, useState as useState9 } from "react";
2610
2578
  import { Fragment as Fragment6, jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
2611
2579
  function Snippet({ label = "shell", code = "", children, copy = true, reveal = false, copyLabel = "Copy code" }) {
2612
2580
  const [status, setStatus] = useState9("idle");
2613
- const request = useRef13(0);
2614
- const timer = useRef13(void 0);
2615
- useEffect8(() => {
2581
+ const request = useRef12(0);
2582
+ const timer = useRef12(void 0);
2583
+ useEffect7(() => {
2616
2584
  setStatus("idle");
2617
2585
  return () => {
2618
2586
  request.current++;
@@ -2655,15 +2623,15 @@ function Snippet({ label = "shell", code = "", children, copy = true, reveal = f
2655
2623
  }
2656
2624
 
2657
2625
  // src/Tabs.tsx
2658
- import { useEffect as useEffect9, useId as useId9, useRef as useRef14 } from "react";
2626
+ import { useEffect as useEffect8, useId as useId8, useRef as useRef13 } from "react";
2659
2627
  import { playEntrance } from "@apliteni/apliteni-ui/motion";
2660
2628
  import { Fragment as Fragment7, jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
2661
2629
  function Tabs({ items, value, onChange, label }) {
2662
- const id = useId9();
2663
- const previous = useRef14(value);
2664
- const activePanel = useRef14(null);
2665
- const buttons = useRef14([]);
2666
- useEffect9(() => {
2630
+ const id = useId8();
2631
+ const previous = useRef13(value);
2632
+ const activePanel = useRef13(null);
2633
+ const buttons = useRef13([]);
2634
+ useEffect8(() => {
2667
2635
  if (previous.current !== value) playEntrance(activePanel.current);
2668
2636
  previous.current = value;
2669
2637
  }, [value]);
@@ -2732,14 +2700,14 @@ function Tabs({ items, value, onChange, label }) {
2732
2700
  }
2733
2701
 
2734
2702
  // src/Timeline.tsx
2735
- import { useEffect as useEffect10, useRef as useRef15 } from "react";
2703
+ import { useEffect as useEffect9, useRef as useRef14 } from "react";
2736
2704
  import { playEntrance as playEntrance2, prefersReducedMotion } from "@apliteni/apliteni-ui/motion";
2737
2705
  import { jsx as jsx28, jsxs as jsxs23 } from "react/jsx-runtime";
2738
2706
  var kindIcons = { person: "user", rule: "bolt", reversal: "refresh" };
2739
2707
  function Timeline({ events, className, ...rest }) {
2740
- const previousIds = useRef15(new Set(events.map((event) => event.id)));
2741
- const rows = useRef15(/* @__PURE__ */ new Map());
2742
- useEffect10(() => {
2708
+ const previousIds = useRef14(new Set(events.map((event) => event.id)));
2709
+ const rows = useRef14(/* @__PURE__ */ new Map());
2710
+ useEffect9(() => {
2743
2711
  if (previousIds.current.size > 0 && !prefersReducedMotion()) {
2744
2712
  for (const event of events) {
2745
2713
  if (previousIds.current.has(event.id)) continue;
@@ -2782,7 +2750,7 @@ function Timeline({ events, className, ...rest }) {
2782
2750
  }
2783
2751
 
2784
2752
  // src/Field.tsx
2785
- import { forwardRef as forwardRef6, useEffect as useEffect11, useId as useId10, useRef as useRef16, useState as useState10 } from "react";
2753
+ import { forwardRef as forwardRef6, useEffect as useEffect10, useId as useId9, useRef as useRef15, useState as useState10 } from "react";
2786
2754
  import { esc, icon as icon4 } from "@apliteni/apliteni-ui";
2787
2755
  import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2788
2756
  function messages(id, hint, error) {
@@ -2811,12 +2779,12 @@ function wiring(id, hint, error) {
2811
2779
  return { id, "aria-describedby": messages(id, hint, error), "aria-invalid": error ? true : void 0 };
2812
2780
  }
2813
2781
  function Field({ id: suppliedId, label, hint, error, required, children }) {
2814
- const generatedId = useId10();
2782
+ const generatedId = useId9();
2815
2783
  const id = suppliedId ?? generatedId;
2816
2784
  return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: children({ ...wiring(id, hint, error), required }) });
2817
2785
  }
2818
2786
  var TextField = forwardRef6(function TextField2({ label, hint, error, required, type = "text", unit, icon: glyph, className = "", ...props }, ref) {
2819
- const id = useId10();
2787
+ const id = useId9();
2820
2788
  const hasUnit = type === "number" && !!unit;
2821
2789
  const describedBy = [messages(id, hint, error), hasUnit ? `${id}-unit` : ""].filter(Boolean).join(" ") || void 0;
2822
2790
  const control = /* @__PURE__ */ jsx29(
@@ -2843,7 +2811,7 @@ var TextField = forwardRef6(function TextField2({ label, hint, error, required,
2843
2811
  ] }) : control });
2844
2812
  });
2845
2813
  function TextArea({ label, hint, error, required, className = "", ...props }) {
2846
- const id = useId10();
2814
+ const id = useId9();
2847
2815
  return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: /* @__PURE__ */ jsx29(
2848
2816
  "textarea",
2849
2817
  {
@@ -2856,7 +2824,7 @@ function TextArea({ label, hint, error, required, className = "", ...props }) {
2856
2824
  ) });
2857
2825
  }
2858
2826
  function SelectField({ label, hint, error, required, className = "", children, ...props }) {
2859
- const id = useId10();
2827
+ const id = useId9();
2860
2828
  return /* @__PURE__ */ jsx29(Frame, { ...{ id, label, hint, error, required }, children: /* @__PURE__ */ jsx29(
2861
2829
  "select",
2862
2830
  {
@@ -2869,11 +2837,11 @@ function SelectField({ label, hint, error, required, className = "", children, .
2869
2837
  ) });
2870
2838
  }
2871
2839
  function FileField({ label, hint, error, required, disabled, onFileChange, className = "", ...props }) {
2872
- const id = useId10();
2873
- const input = useRef16(null);
2840
+ const id = useId9();
2841
+ const input = useRef15(null);
2874
2842
  const [file, setFile] = useState10(null);
2875
2843
  const [dragging, setDragging] = useState10(false);
2876
- useEffect11(() => {
2844
+ useEffect10(() => {
2877
2845
  const form = input.current?.form;
2878
2846
  const reset = () => {
2879
2847
  setFile(null);
@@ -2932,7 +2900,7 @@ function FileField({ label, hint, error, required, disabled, onFileChange, class
2932
2900
  }
2933
2901
 
2934
2902
  // src/FileDrop.tsx
2935
- import { useRef as useRef17, useState as useState11 } from "react";
2903
+ import { useRef as useRef16, useState as useState11 } from "react";
2936
2904
  import { Fragment as Fragment8, jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2937
2905
  var MARKS = {
2938
2906
  uploading: "clock",
@@ -2966,7 +2934,7 @@ function FileDrop({
2966
2934
  children,
2967
2935
  className
2968
2936
  }) {
2969
- const input = useRef17(null);
2937
+ const input = useRef16(null);
2970
2938
  const [over, setOver] = useState11(false);
2971
2939
  const status = file?.status ?? "uploading";
2972
2940
  const word = status === "error" ? file?.error ?? WORDS.error : file?.state ?? WORDS[status];
@@ -3101,7 +3069,7 @@ var SearchField = forwardRef7(function SearchField2({ ariaLabel, className, ...p
3101
3069
  });
3102
3070
 
3103
3071
  // src/Toast.tsx
3104
- import { createContext as createContext2, useCallback as useCallback4, useContext as useContext2, useEffect as useEffect12, useLayoutEffect as useLayoutEffect4, useRef as useRef18, useState as useState12 } from "react";
3072
+ import { createContext as createContext2, useCallback as useCallback4, useContext as useContext2, useEffect as useEffect11, useLayoutEffect as useLayoutEffect4, useRef as useRef17, useState as useState12 } from "react";
3105
3073
  import { createPortal as createPortal4 } from "react-dom";
3106
3074
  import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
3107
3075
  var Context = createContext2(null);
@@ -3112,10 +3080,10 @@ function useToast() {
3112
3080
  return push;
3113
3081
  }
3114
3082
  function Notice({ notice, remove }) {
3115
- const root = useRef18(null);
3083
+ const root = useRef17(null);
3116
3084
  const [leaving2, setLeaving] = useState12(false);
3117
- const dismissed = useRef18(false);
3118
- const remaining = useRef18(5e3);
3085
+ const dismissed = useRef17(false);
3086
+ const remaining = useRef17(5e3);
3119
3087
  const [hovered, setHovered] = useState12(false);
3120
3088
  const [focused, setFocused] = useState12(false);
3121
3089
  const paused = hovered || focused;
@@ -3126,7 +3094,7 @@ function Notice({ notice, remove }) {
3126
3094
  if (typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches) remove();
3127
3095
  else setLeaving(true);
3128
3096
  }, [remove]);
3129
- useEffect12(() => {
3097
+ useEffect11(() => {
3130
3098
  if (leaving2) {
3131
3099
  const element = root.current;
3132
3100
  const finish = (event) => {
@@ -3140,7 +3108,7 @@ function Notice({ notice, remove }) {
3140
3108
  };
3141
3109
  }
3142
3110
  }, [leaving2, remove]);
3143
- useEffect12(() => {
3111
+ useEffect11(() => {
3144
3112
  if (leaving2 || notice.action || paused) return;
3145
3113
  const started = Date.now();
3146
3114
  const timer = setTimeout(dismiss, remaining.current);
@@ -3218,8 +3186,8 @@ function usePublishedReach(stack2, count) {
3218
3186
  }
3219
3187
  function Toast({ children }) {
3220
3188
  const [entries, setEntries] = useState12([]);
3221
- const nextId = useRef18(0);
3222
- const stack2 = useRef18(null);
3189
+ const nextId = useRef17(0);
3190
+ const stack2 = useRef17(null);
3223
3191
  const push = useCallback4((notice) => {
3224
3192
  const id = nextId.current++;
3225
3193
  const remove = () => setEntries((current) => current.filter((entry) => entry.id !== id));
@@ -3236,22 +3204,22 @@ function Toast({ children }) {
3236
3204
  }
3237
3205
 
3238
3206
  // src/AppShell.tsx
3239
- import { useEffect as useEffect14, useId as useId12, useState as useState14 } from "react";
3207
+ import { useEffect as useEffect13, useId as useId11, useState as useState14 } from "react";
3240
3208
  import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
3241
3209
 
3242
3210
  // src/SidebarNav.tsx
3243
- import { forwardRef as forwardRef8, useEffect as useEffect13, useId as useId11, useState as useState13 } from "react";
3211
+ import { forwardRef as forwardRef8, useEffect as useEffect12, useId as useId10, useState as useState13 } from "react";
3244
3212
  import { ENTRANCE_FALLBACK_MS } from "@apliteni/apliteni-ui/motion";
3245
3213
  import { Fragment as Fragment9, jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
3246
3214
  var badgeText = (badge) => typeof badge === "object" ? badge.text : badge;
3247
3215
  function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
3248
- const uid = useId11();
3216
+ const uid = useId10();
3249
3217
  const group = !sub && !!item.items?.length;
3250
3218
  const childActive = group && item.items.some((child) => child.id === active);
3251
3219
  const [expanded, setExpanded] = useState13();
3252
3220
  const open = expanded ?? item.defaultOpen ?? childActive;
3253
3221
  const [entering, setEntering] = useState13(false);
3254
- useEffect13(() => {
3222
+ useEffect12(() => {
3255
3223
  if (!entering) return;
3256
3224
  const timer = setTimeout(() => setEntering(false), ENTRANCE_FALLBACK_MS);
3257
3225
  return () => clearTimeout(timer);
@@ -3302,7 +3270,7 @@ function Row({ item, active, activeIs, collapsed, renderLink, sub }) {
3302
3270
  return /* @__PURE__ */ jsx33("li", { children: renderLink ? renderLink(item, props) : /* @__PURE__ */ jsx33("a", { ...props }) });
3303
3271
  }
3304
3272
  function Section({ section, ...props }) {
3305
- const uid = useId11();
3273
+ const uid = useId10();
3306
3274
  return /* @__PURE__ */ jsxs28("div", { className: "ui-nav__section", children: [
3307
3275
  section.label && /* @__PURE__ */ jsx33("div", { className: "ui-nav__cap", id: uid, "aria-hidden": props.collapsed || void 0, children: section.label }),
3308
3276
  /* @__PURE__ */ jsx33("ul", { className: "ui-nav__list", "aria-labelledby": section.label ? uid : void 0, children: section.items.map((item) => /* @__PURE__ */ jsx33(Row, { item, ...props }, item.id)) })
@@ -3358,21 +3326,21 @@ function AppShell({
3358
3326
  palette,
3359
3327
  renderLink
3360
3328
  }) {
3361
- const uid = useId12();
3329
+ const uid = useId11();
3362
3330
  const [collapsed, setCollapsed] = useState14(defaultCollapsed);
3363
3331
  const [search, setSearch] = useState14(false);
3364
3332
  const [more, setMore] = useState14(false);
3365
3333
  const [theme, setTheme] = useState14("dark");
3366
3334
  const current = path(pathname);
3367
3335
  const active = sections.filter((section) => current === path(section.href) || path(section.href) !== "/" && current.startsWith(`${path(section.href)}/`)).sort((a, b) => path(b.href).length - path(a.href).length)[0];
3368
- useEffect14(() => {
3336
+ useEffect13(() => {
3369
3337
  setCollapsed(railCollapsed() ?? defaultCollapsed);
3370
3338
  }, [defaultCollapsed]);
3371
- useEffect14(() => {
3339
+ useEffect13(() => {
3372
3340
  setMore(false);
3373
3341
  setSearch(false);
3374
3342
  }, [pathname]);
3375
- useEffect14(() => {
3343
+ useEffect13(() => {
3376
3344
  const root = document.documentElement;
3377
3345
  const sync2 = () => setTheme(root.getAttribute("data-theme") === "light" ? "light" : "dark");
3378
3346
  sync2();
@@ -3380,7 +3348,7 @@ function AppShell({
3380
3348
  observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
3381
3349
  return () => observer.disconnect();
3382
3350
  }, []);
3383
- useEffect14(() => {
3351
+ useEffect13(() => {
3384
3352
  const hotkey = (event) => {
3385
3353
  if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== "k") return;
3386
3354
  const target = event.target;
@@ -3607,7 +3575,7 @@ function ThemeToggle({ labelled = false }) {
3607
3575
  }
3608
3576
 
3609
3577
  // src/Success.tsx
3610
- import { forwardRef as forwardRef9, useEffect as useEffect15, useRef as useRef19, useState as useState15 } from "react";
3578
+ import { forwardRef as forwardRef9, useEffect as useEffect14, useRef as useRef18, useState as useState15 } from "react";
3611
3579
  import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";
3612
3580
  var SuccessCheck = forwardRef9(function SuccessCheck2({ variant = "line", className, ...rest }, ref) {
3613
3581
  const circled = variant === "circled";
@@ -3655,11 +3623,11 @@ var pieces = [
3655
3623
  ];
3656
3624
  function Countdown({ seconds, label = "Redirecting", onDone }) {
3657
3625
  const [remaining, setRemaining] = useState15(seconds);
3658
- const callback = useRef19(onDone);
3659
- useEffect15(() => {
3626
+ const callback = useRef18(onDone);
3627
+ useEffect14(() => {
3660
3628
  callback.current = onDone;
3661
3629
  }, [onDone]);
3662
- useEffect15(() => {
3630
+ useEffect14(() => {
3663
3631
  let count = seconds;
3664
3632
  const timer = setInterval(() => {
3665
3633
  count -= 1;
@@ -166,7 +166,7 @@ export const iconNames = Object.keys(ICONS);
166
166
  // `filter` — Lucide's funnel — fails the first test. Depicting a thing is not the
167
167
  // fault; `copy` is on this list and depicts one too. The kit reads the funnel alone
168
168
  // as not saying whether the control filters, sorts or exports — its own inference
169
- // from the sources in docs/specification.md, not a measured recognition finding.
169
+ // from the sources in docs/foundations.md, not a measured recognition finding.
170
170
  // See #565.
171
171
  export const iconOnlyAllowed = {
172
172
  x: 'close or dismiss',
@@ -2,8 +2,8 @@
2
2
  // that interpolates raw. It lives here rather than in shell.js because both
3
3
  // shell.js and topbar.js need it and shell.js already imports topbar.js — the
4
4
  // other direction would be a cycle. shell.js re-exports ACCOUNT_NAV, which is
5
- // the published name docs/library.md documents.
6
- // why: docs/specification.md#the-page-shell
5
+ // the published name docs/components.md documents.
6
+ // why: docs/components.md#the-page-shell
7
7
  import { esc } from './index.js';
8
8
 
9
9
  // nav.js item objects. Labels are raw text — every nav primitive escapes, so a
@@ -11,7 +11,7 @@
11
11
  // docs/reviews/270-back-control.html and the owner chose this one, so the other
12
12
  // three — a bordered button above the title, an icon-only arrow beside it, and
13
13
  // the breadcrumb trail alone — are not built here.
14
- // why: docs/specification.md#the-back-link
14
+ // why: docs/components.md#the-back-link
15
15
  import { esc, icon } from './index.js';
16
16
 
17
17
  // "Back" names a direction rather than a place. It is what a caller who names no
@@ -29,7 +29,7 @@ const TAB_OR_NEWLINE = /[\t\n\r]/g;
29
29
 
30
30
  // A label that already says "Back to Invoices" names the place after those words, or the
31
31
  // link would be read as "Back to Back to Invoices". The whole phrase, since "Backups" and
32
- // "Back office" are places too. why: docs/specification.md#the-back-link
32
+ // "Back office" are places too. why: docs/components.md#the-back-link
33
33
  const SAID = /^back\s+to(?:\s+|$)/i;
34
34
 
35
35
  const text = (v) => (typeof v === 'string' || typeof v === 'number' ? String(v).trim() : '');
@@ -9,7 +9,7 @@ export { SCORE, scoreCommand, rankCommands, rankGroups, paletteHotkey } from '..
9
9
  // The kit ships the shell and the ranking and names no result kinds; a row goes
10
10
  // somewhere, runs something, or asks a confirm first. Inertness, Escape and the
11
11
  // focus trap come from ./overlay.js, the stack the drawer and the confirm share.
12
- // why: docs/specification.md#the-command-palette
12
+ // why: docs/components.md#the-command-palette
13
13
  import { esc, icon } from './index.js';
14
14
  import { OVERLAY_LAYER, adoptOverlay, popOverlay, pushOverlay, returnFocus, syncOverlays } from './overlay.js';
15
15
  import { safeUrl } from '../html.js';
@@ -61,7 +61,7 @@ function keysFor(shortcut) {
61
61
  * the wiring, which is also what makes Cmd+Enter open it in a tab.
62
62
  *
63
63
  * A destructive item that names no confirm is rendered DISABLED. The kit's rule
64
- * is that a delete asks first (docs/specification.md#the-command-palette), and a
64
+ * is that a delete asks first (docs/components.md#the-command-palette), and a
65
65
  * palette is the one surface where a reader is typing fast and choosing from a
66
66
  * list that reorders under them.
67
67
  */
@@ -75,7 +75,7 @@ export function drawer({
75
75
  *
76
76
  * drawer({ title, body: drawerSection({ title: 'Source', rows: [['Statement', '#4102']] }) })
77
77
  *
78
- * why: docs/specification.md#the-drawer
78
+ * why: docs/components.md#the-drawer
79
79
  *
80
80
  * @param {object} [o]
81
81
  * @param {string} [o.title] group heading (escaped)