@apliteni/apliteni-ui 0.119.1 → 0.120.1

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.
@@ -28,7 +28,7 @@
28
28
 
29
29
  **Rule:** Drop a button’s words only when its glyph is learned everywhere, its action is on the closed list, and its name reaches assistive technology and a hover title.
30
30
 
31
- **Except:** [Icon-only controls](https://ui.apli.tech/storybook/?path=/story/guidelines-iconography--iconography) is the list. The folded rail shows its names beside the glyphs, so they carry no title.
31
+ **Except:** Icon-only controls on the Iconography page is the list. The folded rail shows its names beside the glyphs, so they carry no title.
32
32
 
33
33
  **Do:** Overflow is on that list; the funnel is not.
34
34
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  **Why:** Stacking gives each label and value the full width of the block.
10
10
 
11
- **Do:** Stack Paid and In transit, as [Stat band tiles](https://ui.apli.tech/storybook/?path=/story/components-stat-band--gallery) do in a narrow panel.
11
+ **Do:** Stack Paid and In transit, as Stat band tiles do in a narrow panel.
12
12
 
13
13
  **Don't:** Force the same figures side by side in that panel.
14
14
 
@@ -36,7 +36,7 @@
36
36
 
37
37
  **Don't:** Make both actions primary or colour every label and value with the accent.
38
38
 
39
- **Except:** Keep [Badge success, pending and danger](https://ui.apli.tech/storybook/?path=/story/components-badge-status--badges) for their named statuses; do not replace those signals with the accent.
39
+ **Except:** Keep Badge success, pending and danger for their named statuses; do not replace those signals with the accent.
40
40
 
41
41
  ## Follow the consequence
42
42
 
@@ -50,4 +50,4 @@
50
50
 
51
51
  **Don't:** Give the source document the leading, wider pane, or let a control inside it hold the only accent on the screen while the saved values hold none.
52
52
 
53
- **Except:** Fade nothing to make a block quieter; a quieter block is smaller, later or uncoloured, never muted. See [Limit muted ink](https://ui.apli.tech/storybook/?path=/story/guidelines-labels-and-titles--page).
53
+ **Except:** Fade nothing to make a block quieter; a quieter block is smaller, later or uncoloured, never muted.
@@ -8,7 +8,7 @@
8
8
 
9
9
  **Why:** Without a legend, readers need labels for unfamiliar glyphs.
10
10
 
11
- **Except:** New entries require a decision and the three tests in [Earn a wordless button](https://ui.apli.tech/storybook/?path=/story/guidelines-button-labels--button-labels). Allowed: close, copy, overflow, expand, collapse and export. Theme toggles, sidebar toggles and collapsed-rail links have separate approval.
11
+ **Except:** New entries require a decision and pass Earn a wordless button on Button labels. Allowed: close, copy, overflow, expand, collapse and export. Theme toggles, sidebar toggles and collapsed-rail links have separate approval.
12
12
 
13
13
  **Do:** Show an overflow menu without words; keep the Settings label.
14
14
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apliteni/apliteni-ui",
3
- "version": "0.119.1",
3
+ "version": "0.120.1",
4
4
  "workspaces": [
5
5
  "react"
6
6
  ],
@@ -852,6 +852,13 @@
852
852
  pointer-events: auto;
853
853
  }
854
854
 
855
+ /* src/ThemeToggle.css */
856
+ .ui-theme-toggle {
857
+ display: inline-flex;
858
+ align-items: center;
859
+ gap: var(--space-3);
860
+ }
861
+
855
862
  /* src/AppShell.css */
856
863
  .ui-react-app__bottom {
857
864
  display: none;
@@ -929,10 +936,3 @@
929
936
  display: none;
930
937
  }
931
938
  }
932
-
933
- /* src/ThemeToggle.css */
934
- .ui-theme-toggle {
935
- display: inline-flex;
936
- align-items: center;
937
- gap: var(--space-3);
938
- }
@@ -79,7 +79,7 @@ var cx = (...a) => a.filter(Boolean).join(" ");
79
79
  var Button = forwardRef3(function Button2({
80
80
  variant = "secondary",
81
81
  size = "md",
82
- icon: icon7,
82
+ icon: icon6,
83
83
  leading,
84
84
  iconRight,
85
85
  iconOnly,
@@ -143,7 +143,7 @@ var Button = forwardRef3(function Button2({
143
143
  className
144
144
  );
145
145
  const labelled = rest["aria-label"] != null || rest["aria-labelledby"] != null;
146
- const fallback = typeof children === "string" && children.trim() ? children.trim() : icon7;
146
+ const fallback = typeof children === "string" && children.trim() ? children.trim() : icon6;
147
147
  const named = iconOnly && !labelled && fallback ? { "aria-label": fallback, title: rest.title ?? fallback } : {};
148
148
  const Root = href === void 0 ? "button" : "a";
149
149
  return /* @__PURE__ */ jsxs3(
@@ -188,7 +188,7 @@ var Button = forwardRef3(function Button2({
188
188
  if (!blockKey(event)) onKeyUp?.(event);
189
189
  },
190
190
  children: [
191
- leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon7 && /* @__PURE__ */ jsx4(Icon, { name: icon7 }),
191
+ leading != null ? /* @__PURE__ */ jsx4("span", { "aria-hidden": "true", style: { display: "inline-flex" }, children: leading }) : icon6 && /* @__PURE__ */ jsx4(Icon, { name: icon6 }),
192
192
  !iconOnly && renderedChildren != null && /* @__PURE__ */ jsx4("span", { className: "ui-btn__label-slot", children: /* @__PURE__ */ jsx4("span", { className: "ui-btn__label", ref: labelRef, children: renderedChildren }) }),
193
193
  iconRight && /* @__PURE__ */ jsx4(Icon, { name: iconRight }),
194
194
  busy && /* @__PURE__ */ jsxs3("span", { className: "ui-btn__dots", "aria-hidden": "true", children: [
@@ -258,9 +258,9 @@ function Card({ title, sub, level = 2, className, children }) {
258
258
  // src/primitives/Callout.tsx
259
259
  import { calloutIcons, icon as kitIcon } from "@apliteni/apliteni-ui";
260
260
  import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
261
- function Callout({ variant, icon: icon7 = calloutIcons[variant ?? "neutral"], children, actions }) {
261
+ function Callout({ variant, icon: icon6 = calloutIcons[variant ?? "neutral"], children, actions }) {
262
262
  return /* @__PURE__ */ jsxs5("div", { className: `ui-callout${variant && variant !== "neutral" ? ` ui-callout--${variant}` : ""}`, role: variant === "danger" ? "alert" : void 0, children: [
263
- /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon7) } }),
263
+ /* @__PURE__ */ jsx9("span", { className: "ui-callout__icon", dangerouslySetInnerHTML: { __html: kitIcon(icon6) } }),
264
264
  /* @__PURE__ */ jsxs5("div", { className: "ui-callout__body", children: [
265
265
  children,
266
266
  actions != null && /* @__PURE__ */ jsx9("div", { className: "ui-toolbar ui-callout__actions", children: actions })
@@ -1566,12 +1566,12 @@ function Denied({
1566
1566
  title = "You don\u2019t have access",
1567
1567
  sub,
1568
1568
  need,
1569
- icon: icon7 = "lock",
1569
+ icon: icon6 = "lock",
1570
1570
  className,
1571
1571
  children
1572
1572
  }) {
1573
1573
  return /* @__PURE__ */ jsxs14("div", { className: cx4("ui-denied", className), children: [
1574
- /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon7 }) }),
1574
+ /* @__PURE__ */ jsx18("div", { className: "ui-denied__seal", children: /* @__PURE__ */ jsx18(Icon, { name: icon6 }) }),
1575
1575
  /* @__PURE__ */ jsx18("div", { className: "ui-denied__title", children: title }),
1576
1576
  sub && /* @__PURE__ */ jsx18("div", { className: "ui-denied__sub", children: sub }),
1577
1577
  need && /* @__PURE__ */ jsxs14("div", { className: "ui-denied__need", children: [
@@ -3518,7 +3518,7 @@ function Toast({ children }) {
3518
3518
 
3519
3519
  // src/AppShell.tsx
3520
3520
  import { useEffect as useEffect14, useId as useId11, useState as useState14 } from "react";
3521
- import { applyTheme, paletteHotkey, prism, railCollapsed, themeIcon, themeName } from "@apliteni/apliteni-ui";
3521
+ import { paletteHotkey, prism, railCollapsed } from "@apliteni/apliteni-ui";
3522
3522
 
3523
3523
  // src/SidebarNav.tsx
3524
3524
  import { forwardRef as forwardRef8, useEffect as useEffect13, useId as useId10, useState as useState13 } from "react";
@@ -3617,8 +3617,79 @@ var SidebarNav = forwardRef8(function SidebarNav2({
3617
3617
  ] });
3618
3618
  });
3619
3619
 
3620
+ // src/ThemeToggle.tsx
3621
+ import { useSyncExternalStore } from "react";
3622
+ import { themeIcon } from "@apliteni/apliteni-ui";
3623
+ import { jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
3624
+ var STORAGE_KEY = "apliteni-strategy-theme";
3625
+ var CHOICE_EVENT = "apliteni-theme-choice";
3626
+ var THEME_INIT_SCRIPT = `(()=>{let t;try{t=localStorage.getItem('apliteni-strategy-theme')}catch{}document.documentElement.setAttribute('data-theme',t==='dark'||t==='light'?t:window.matchMedia?.('(prefers-color-scheme: light)').matches?'light':'dark')})()`;
3627
+ function storedTheme() {
3628
+ try {
3629
+ const value = localStorage.getItem(STORAGE_KEY);
3630
+ return value === "dark" || value === "light" || value === "auto" ? value : null;
3631
+ } catch {
3632
+ return snapshot();
3633
+ }
3634
+ }
3635
+ function snapshot() {
3636
+ const choice = document.documentElement.getAttribute("data-theme-choice");
3637
+ return choice === "dark" || choice === "light" ? choice : "auto";
3638
+ }
3639
+ function subscribe(notify) {
3640
+ const root = document.documentElement;
3641
+ const media = window.matchMedia?.("(prefers-color-scheme: light)");
3642
+ const applyPreference = (choice) => {
3643
+ root.setAttribute("data-theme-choice", choice);
3644
+ root.setAttribute("data-theme", choice === "auto" ? media?.matches ? "light" : "dark" : choice);
3645
+ };
3646
+ const onSystem = () => {
3647
+ if (snapshot() === "auto") applyPreference("auto");
3648
+ };
3649
+ const onChoice = () => notify();
3650
+ const onStorage = (event) => {
3651
+ if (event.key !== null && event.key !== STORAGE_KEY) return;
3652
+ applyPreference(storedTheme() ?? "auto");
3653
+ };
3654
+ const observer = new MutationObserver(notify);
3655
+ observer.observe(root, { attributes: true, attributeFilter: ["data-theme", "data-theme-choice"] });
3656
+ applyPreference(root.hasAttribute("data-theme-choice") ? snapshot() : storedTheme() ?? "auto");
3657
+ media?.addEventListener("change", onSystem);
3658
+ window.addEventListener(CHOICE_EVENT, onChoice);
3659
+ window.addEventListener("storage", onStorage);
3660
+ return () => {
3661
+ observer.disconnect();
3662
+ media?.removeEventListener("change", onSystem);
3663
+ window.removeEventListener(CHOICE_EVENT, onChoice);
3664
+ window.removeEventListener("storage", onStorage);
3665
+ };
3666
+ }
3667
+ function ThemeToggle({ labelled = false }) {
3668
+ const theme = useSyncExternalStore(subscribe, snapshot, () => "auto");
3669
+ const next = theme === "dark" ? "light" : theme === "light" ? "auto" : "dark";
3670
+ const label = theme === "auto" ? "Auto" : theme === "light" ? "Light" : "Dark";
3671
+ const name = `Theme: ${label}. Switch to ${next}.`;
3672
+ const toggle = () => {
3673
+ document.documentElement.setAttribute("data-theme-choice", next);
3674
+ document.documentElement.setAttribute("data-theme", next === "auto" ? window.matchMedia?.("(prefers-color-scheme: light)").matches ? "light" : "dark" : next);
3675
+ try {
3676
+ localStorage.setItem(STORAGE_KEY, next);
3677
+ } catch {
3678
+ }
3679
+ window.dispatchEvent(new Event(CHOICE_EVENT));
3680
+ };
3681
+ const button = /* @__PURE__ */ jsx34("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx34("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) });
3682
+ return labelled ? /* @__PURE__ */ jsxs29("span", { className: "ui-theme-toggle", children: [
3683
+ button,
3684
+ /* @__PURE__ */ jsxs29("span", { children: [
3685
+ label,
3686
+ " theme"
3687
+ ] })
3688
+ ] }) : button;
3689
+ }
3690
+
3620
3691
  // src/AppShell.tsx
3621
- import { Fragment as Fragment12, jsx as jsx34, jsxs as jsxs29 } from "react/jsx-runtime";
3692
+ import { Fragment as Fragment12, jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
3622
3693
  var path = (value) => value.split(/[?#]/)[0].replace(/\/+$/, "") || "/";
3623
3694
  function AppShell({
3624
3695
  sections,
@@ -3645,7 +3716,6 @@ function AppShell({
3645
3716
  const [collapsed, setCollapsed] = useState14(defaultCollapsed);
3646
3717
  const [search, setSearch] = useState14(false);
3647
3718
  const [more, setMore] = useState14(false);
3648
- const [theme, setTheme] = useState14("dark");
3649
3719
  const current = path(pathname);
3650
3720
  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];
3651
3721
  useEffect14(() => {
@@ -3655,14 +3725,6 @@ function AppShell({
3655
3725
  setMore(false);
3656
3726
  setSearch(false);
3657
3727
  }, [pathname]);
3658
- useEffect14(() => {
3659
- const root = document.documentElement;
3660
- const sync2 = () => setTheme(root.getAttribute("data-theme") === "light" ? "light" : "dark");
3661
- sync2();
3662
- const observer = new MutationObserver(sync2);
3663
- observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
3664
- return () => observer.disconnect();
3665
- }, []);
3666
3728
  useEffect14(() => {
3667
3729
  const hotkey = (event) => {
3668
3730
  if (!(event.metaKey || event.ctrlKey) || event.altKey || event.shiftKey || event.key.toLowerCase() !== "k") return;
@@ -3693,15 +3755,15 @@ function AppShell({
3693
3755
  onClick: (event) => {
3694
3756
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
3695
3757
  },
3696
- children: /* @__PURE__ */ jsxs29(Fragment12, { children: [
3697
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx34(Icon, { name: section.icon }) }),
3698
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__label", children: section.label }),
3699
- section.count != null && /* @__PURE__ */ jsx34("span", { className: "ui-nav__badge is-neutral", children: section.count })
3758
+ children: /* @__PURE__ */ jsxs30(Fragment12, { children: [
3759
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx35(Icon, { name: section.icon }) }),
3760
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__label", children: section.label }),
3761
+ section.count != null && /* @__PURE__ */ jsx35("span", { className: "ui-nav__badge is-neutral", children: section.count })
3700
3762
  ] })
3701
3763
  };
3702
- return /* @__PURE__ */ jsx34("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx34("a", { ...linkProps }) }, section.href);
3764
+ return /* @__PURE__ */ jsx35("li", { children: renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx35("a", { ...linkProps }) }, section.href);
3703
3765
  });
3704
- const sidebar = (items, label) => /* @__PURE__ */ jsx34(
3766
+ const sidebar = (items, label) => /* @__PURE__ */ jsx35(
3705
3767
  SidebarNav,
3706
3768
  {
3707
3769
  "aria-label": label,
@@ -3713,7 +3775,7 @@ function AppShell({
3713
3775
  const linkProps = { ...props, onClick: (event) => {
3714
3776
  if (!event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) setMore(false);
3715
3777
  } };
3716
- return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx34("a", { ...linkProps });
3778
+ return renderLink ? renderLink(section, linkProps) : /* @__PURE__ */ jsx35("a", { ...linkProps });
3717
3779
  }
3718
3780
  }
3719
3781
  );
@@ -3722,36 +3784,36 @@ function AppShell({
3722
3784
  const visible = sections.length > 4 ? sections.slice(0, 3) : sections;
3723
3785
  const remaining = sections.slice(visible.length);
3724
3786
  const initials = account.name.trim().split(/\s+/).map((part) => part[0]).slice(0, 2).join("");
3725
- const lockup = /* @__PURE__ */ jsxs29("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
3726
- brand ?? /* @__PURE__ */ jsx34("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
3727
- /* @__PURE__ */ jsx34("span", { children: word })
3787
+ const lockup = /* @__PURE__ */ jsxs30("a", { className: "ui-app__brand", href: brandHref, "aria-label": word, children: [
3788
+ brand ?? /* @__PURE__ */ jsx35("i", { "aria-hidden": "true", className: "ui-react-app__mark", dangerouslySetInnerHTML: { __html: prism(uid, 24) } }),
3789
+ /* @__PURE__ */ jsx35("span", { children: word })
3728
3790
  ] });
3729
- return /* @__PURE__ */ jsxs29("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
3730
- navigable && /* @__PURE__ */ jsxs29("div", { className: "ui-app__rail", role: "region", "aria-label": "Sidebar", children: [
3731
- /* @__PURE__ */ jsx34("div", { className: "ui-app__head", children: lockup }),
3791
+ return /* @__PURE__ */ jsxs30("div", { className: `ui-app ui-app--topbar ui-react-app${collapsed ? " is-collapsed" : ""}`, children: [
3792
+ navigable && /* @__PURE__ */ jsxs30("div", { className: "ui-app__rail", role: "region", "aria-label": "Sidebar", children: [
3793
+ /* @__PURE__ */ jsx35("div", { className: "ui-app__head", children: lockup }),
3732
3794
  sidebar(sections, "Sections"),
3733
- /* @__PURE__ */ jsx34("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx34("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs29("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
3734
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs29("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3735
- /* @__PURE__ */ jsx34("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
3736
- /* @__PURE__ */ jsx34("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
3737
- /* @__PURE__ */ jsx34("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
3795
+ /* @__PURE__ */ jsx35("div", { className: "ui-app__foot", children: /* @__PURE__ */ jsx35("div", { className: "ui-app__fold-row", children: /* @__PURE__ */ jsxs30("button", { type: "button", className: "ui-nav__item ui-app__fold", "aria-expanded": !collapsed, "aria-label": foldLabel, onClick: fold, children: [
3796
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsxs30("svg", { className: "ui-icon", viewBox: "0 0 24 24", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3797
+ /* @__PURE__ */ jsx35("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
3798
+ /* @__PURE__ */ jsx35("path", { className: "ui-app__fold-seam", d: "M9 3v18" }),
3799
+ /* @__PURE__ */ jsx35("path", { className: "ui-app__fold-arrow", d: "m16 15-3-3 3-3" })
3738
3800
  ] }) }),
3739
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__label", children: foldLabel })
3801
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__label", children: foldLabel })
3740
3802
  ] }) }) })
3741
3803
  ] }),
3742
- /* @__PURE__ */ jsxs29("div", { className: "ui-app__well", children: [
3743
- /* @__PURE__ */ jsxs29("header", { className: "ui-app__bar", children: [
3804
+ /* @__PURE__ */ jsxs30("div", { className: "ui-app__well", children: [
3805
+ /* @__PURE__ */ jsxs30("header", { className: "ui-app__bar", children: [
3744
3806
  !navigable && lockup,
3745
- /* @__PURE__ */ jsxs29("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
3746
- /* @__PURE__ */ jsx34("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx34(Icon, { name: "search" }) }),
3747
- /* @__PURE__ */ jsx34("span", { className: "ui-app__search-txt", children: searchLabel }),
3748
- /* @__PURE__ */ jsx34("span", { className: "ui-app__search-short", "aria-hidden": "true", children: searchShortLabel }),
3749
- /* @__PURE__ */ jsx34("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
3807
+ /* @__PURE__ */ jsxs30("button", { type: "button", className: "ui-app__search", "aria-haspopup": "dialog", onClick: () => setSearch(true), children: [
3808
+ /* @__PURE__ */ jsx35("span", { className: "ui-app__search-ic", children: /* @__PURE__ */ jsx35(Icon, { name: "search" }) }),
3809
+ /* @__PURE__ */ jsx35("span", { className: "ui-app__search-txt", children: searchLabel }),
3810
+ /* @__PURE__ */ jsx35("span", { className: "ui-app__search-short", "aria-hidden": "true", children: searchShortLabel }),
3811
+ /* @__PURE__ */ jsx35("kbd", { className: "ui-cmdk__key", children: paletteHotkey() })
3750
3812
  ] }),
3751
- /* @__PURE__ */ jsx34("span", { className: "ui-app__bar-gap" }),
3813
+ /* @__PURE__ */ jsx35("span", { className: "ui-app__bar-gap" }),
3752
3814
  bandControl,
3753
- /* @__PURE__ */ jsx34("button", { type: "button", className: "toggle", "aria-label": themeName(theme), title: themeName(theme), onClick: () => applyTheme(theme === "light" ? "dark" : "light"), children: /* @__PURE__ */ jsx34("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: themeIcon(theme) } }) }),
3754
- /* @__PURE__ */ jsx34("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx34(
3815
+ /* @__PURE__ */ jsx35(ThemeToggle, {}),
3816
+ /* @__PURE__ */ jsx35("div", { className: "ui-app__user ui-app__user--bar", children: /* @__PURE__ */ jsx35(
3755
3817
  Dropdown,
3756
3818
  {
3757
3819
  variant: "menu",
@@ -3760,27 +3822,27 @@ function AppShell({
3760
3822
  ariaLabel: `Signed in as ${account.name}, ${account.email}`,
3761
3823
  triggerClass: "ui-app__user-trigger",
3762
3824
  panelClass: "ui-app__user-panel",
3763
- triggerContent: /* @__PURE__ */ jsx34("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
3764
- header: /* @__PURE__ */ jsxs29("div", { className: "ui-dropdown__head", children: [
3765
- /* @__PURE__ */ jsx34("b", { children: account.name }),
3766
- /* @__PURE__ */ jsx34("span", { children: account.email })
3825
+ triggerContent: /* @__PURE__ */ jsx35("span", { className: "ui-app__av", "aria-hidden": "true", children: initials }),
3826
+ header: /* @__PURE__ */ jsxs30("div", { className: "ui-dropdown__head", children: [
3827
+ /* @__PURE__ */ jsx35("b", { children: account.name }),
3828
+ /* @__PURE__ */ jsx35("span", { children: account.email })
3767
3829
  ] }),
3768
3830
  items: [{ label: "Sign out", icon: "logout", danger: true }],
3769
3831
  onSelect: onSignOut
3770
3832
  }
3771
3833
  ) })
3772
3834
  ] }),
3773
- /* @__PURE__ */ jsxs29("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
3774
- back && /* @__PURE__ */ jsx34(BackLink, { ...back }),
3775
- /* @__PURE__ */ jsx34("h1", { children: title }),
3776
- lede && /* @__PURE__ */ jsx34("p", { className: "ui-app__sub", children: lede }),
3777
- actions && /* @__PURE__ */ jsx34("div", { className: "ui-react-app__actions", children: actions }),
3778
- /* @__PURE__ */ jsx34("div", { className: "ui-app__body", children })
3835
+ /* @__PURE__ */ jsxs30("main", { className: `ui-app__main${width === "wide" ? " ui-app__main--wide" : ""}`, children: [
3836
+ back && /* @__PURE__ */ jsx35(BackLink, { ...back }),
3837
+ /* @__PURE__ */ jsx35("h1", { children: title }),
3838
+ lede && /* @__PURE__ */ jsx35("p", { className: "ui-app__sub", children: lede }),
3839
+ actions && /* @__PURE__ */ jsx35("div", { className: "ui-react-app__actions", children: actions }),
3840
+ /* @__PURE__ */ jsx35("div", { className: "ui-app__body", children })
3779
3841
  ] })
3780
3842
  ] }),
3781
- navigable && /* @__PURE__ */ jsx34("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs29("ul", { className: "ui-nav__list", children: [
3843
+ navigable && /* @__PURE__ */ jsx35("nav", { className: "ui-nav ui-react-app__bottom", "aria-label": "Sections on mobile", children: /* @__PURE__ */ jsxs30("ul", { className: "ui-nav__list", children: [
3782
3844
  links(visible),
3783
- remaining.length > 0 && /* @__PURE__ */ jsx34("li", { children: /* @__PURE__ */ jsxs29(
3845
+ remaining.length > 0 && /* @__PURE__ */ jsx35("li", { children: /* @__PURE__ */ jsxs30(
3784
3846
  "button",
3785
3847
  {
3786
3848
  type: "button",
@@ -3789,110 +3851,39 @@ function AppShell({
3789
3851
  "aria-expanded": more,
3790
3852
  onClick: () => setMore(true),
3791
3853
  children: [
3792
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx34(Icon, { name: "moreHorizontal" }) }),
3793
- /* @__PURE__ */ jsx34("span", { className: "ui-nav__label", children: "More" })
3854
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__ic", children: /* @__PURE__ */ jsx35(Icon, { name: "moreHorizontal" }) }),
3855
+ /* @__PURE__ */ jsx35("span", { className: "ui-nav__label", children: "More" })
3794
3856
  ]
3795
3857
  }
3796
3858
  ) })
3797
3859
  ] }) }),
3798
- /* @__PURE__ */ jsx34(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
3799
- /* @__PURE__ */ jsx34(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
3860
+ /* @__PURE__ */ jsx35(Drawer, { open: more, onClose: () => setMore(false), title: "More sections", side: "bottom", children: sidebar(remaining, "More sections") }),
3861
+ /* @__PURE__ */ jsx35(CommandPalette, { ...palette ?? { groups: [{ items: sections.map((section) => ({ id: section.href, ...section })) }] }, open: search, onClose: () => setSearch(false) })
3800
3862
  ] });
3801
3863
  }
3802
3864
 
3803
3865
  // src/EmptyState.tsx
3804
3866
  import { icon as kitIcon2, illo } from "@apliteni/apliteni-ui";
3805
- import { jsx as jsx35, jsxs as jsxs30 } from "react/jsx-runtime";
3867
+ import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
3806
3868
  var cases = {
3807
3869
  "first-run": { title: "Nothing needs you yet", sub: "New items will appear here when they need your attention.", icon: "check" },
3808
3870
  "no-matches": { title: "No results in this view", sub: "Try another search or clear your filters.", icon: "search" },
3809
3871
  "not-found": { title: "We can't find that page", sub: "Check the address or return to the home page.", icon: "folder" },
3810
3872
  "not-yet-built": { title: "This ships later", sub: "This screen is not available yet. Check back later.", icon: "clock" }
3811
3873
  };
3812
- function EmptyState({ variant = "first-run", title, sub, icon: icon7, art, actions }) {
3874
+ function EmptyState({ variant = "first-run", title, sub, icon: icon6, art, actions }) {
3813
3875
  const preset = cases[variant];
3814
3876
  const heading = title ?? preset.title;
3815
3877
  const guidance = sub ?? preset.sub;
3816
- const glyph = icon7 ?? preset.icon;
3817
- return /* @__PURE__ */ jsxs30("div", { className: "ui-empty", children: [
3818
- art ? typeof art === "string" ? /* @__PURE__ */ jsx35("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx35("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx35("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
3819
- heading && /* @__PURE__ */ jsx35("div", { className: "ui-empty__title", children: heading }),
3820
- guidance && /* @__PURE__ */ jsx35("div", { className: "ui-empty__sub", children: guidance }),
3821
- actions && /* @__PURE__ */ jsx35("div", { className: "ui-empty__actions", children: actions })
3878
+ const glyph = icon6 ?? preset.icon;
3879
+ return /* @__PURE__ */ jsxs31("div", { className: "ui-empty", children: [
3880
+ art ? typeof art === "string" ? /* @__PURE__ */ jsx36("div", { className: "ui-empty__art", dangerouslySetInnerHTML: { __html: /<svg/.test(art) ? art : illo(art) } }) : /* @__PURE__ */ jsx36("div", { className: "ui-empty__art", "aria-hidden": "true", children: art }) : glyph ? /* @__PURE__ */ jsx36("div", { className: "ui-empty__icon", dangerouslySetInnerHTML: { __html: kitIcon2(glyph) } }) : null,
3881
+ heading && /* @__PURE__ */ jsx36("div", { className: "ui-empty__title", children: heading }),
3882
+ guidance && /* @__PURE__ */ jsx36("div", { className: "ui-empty__sub", children: guidance }),
3883
+ actions && /* @__PURE__ */ jsx36("div", { className: "ui-empty__actions", children: actions })
3822
3884
  ] });
3823
3885
  }
3824
3886
 
3825
- // src/ThemeToggle.tsx
3826
- import { useSyncExternalStore } from "react";
3827
- import { icon as icon6 } from "@apliteni/apliteni-ui";
3828
- import { jsx as jsx36, jsxs as jsxs31 } from "react/jsx-runtime";
3829
- var STORAGE_KEY = "apliteni-strategy-theme";
3830
- var CHOICE_EVENT = "apliteni-theme-choice";
3831
- var THEME_INIT_SCRIPT = `(()=>{let t;try{t=localStorage.getItem('apliteni-strategy-theme')}catch{}document.documentElement.setAttribute('data-theme',t==='dark'||t==='light'?t:window.matchMedia?.('(prefers-color-scheme: light)').matches?'light':'dark')})()`;
3832
- function storedTheme() {
3833
- try {
3834
- const value = localStorage.getItem(STORAGE_KEY);
3835
- return value === "dark" || value === "light" || value === "auto" ? value : null;
3836
- } catch {
3837
- return snapshot();
3838
- }
3839
- }
3840
- function snapshot() {
3841
- const choice = document.documentElement.getAttribute("data-theme-choice");
3842
- return choice === "dark" || choice === "light" ? choice : "auto";
3843
- }
3844
- function subscribe(notify) {
3845
- const root = document.documentElement;
3846
- const media = window.matchMedia?.("(prefers-color-scheme: light)");
3847
- const applyPreference = (choice) => {
3848
- root.setAttribute("data-theme-choice", choice);
3849
- root.setAttribute("data-theme", choice === "auto" ? media?.matches ? "light" : "dark" : choice);
3850
- };
3851
- const onSystem = () => {
3852
- if (snapshot() === "auto") applyPreference("auto");
3853
- };
3854
- const onChoice = () => notify();
3855
- const onStorage = (event) => {
3856
- if (event.key !== null && event.key !== STORAGE_KEY) return;
3857
- applyPreference(storedTheme() ?? "auto");
3858
- };
3859
- const observer = new MutationObserver(notify);
3860
- observer.observe(root, { attributes: true, attributeFilter: ["data-theme", "data-theme-choice"] });
3861
- applyPreference(root.hasAttribute("data-theme-choice") ? snapshot() : storedTheme() ?? "auto");
3862
- media?.addEventListener("change", onSystem);
3863
- window.addEventListener(CHOICE_EVENT, onChoice);
3864
- window.addEventListener("storage", onStorage);
3865
- return () => {
3866
- observer.disconnect();
3867
- media?.removeEventListener("change", onSystem);
3868
- window.removeEventListener(CHOICE_EVENT, onChoice);
3869
- window.removeEventListener("storage", onStorage);
3870
- };
3871
- }
3872
- function ThemeToggle({ labelled = false }) {
3873
- const theme = useSyncExternalStore(subscribe, snapshot, () => "auto");
3874
- const next = theme === "dark" ? "light" : theme === "light" ? "auto" : "dark";
3875
- const label = theme === "auto" ? "Auto" : theme === "light" ? "Light" : "Dark";
3876
- const name = `Theme: ${label}. Switch to ${next}.`;
3877
- const toggle = () => {
3878
- document.documentElement.setAttribute("data-theme-choice", next);
3879
- document.documentElement.setAttribute("data-theme", next === "auto" ? window.matchMedia?.("(prefers-color-scheme: light)").matches ? "light" : "dark" : next);
3880
- try {
3881
- localStorage.setItem(STORAGE_KEY, next);
3882
- } catch {
3883
- }
3884
- window.dispatchEvent(new Event(CHOICE_EVENT));
3885
- };
3886
- const button = /* @__PURE__ */ jsx36("button", { type: "button", className: "toggle", "aria-label": name, title: name, onClick: toggle, children: /* @__PURE__ */ jsx36("span", { className: "ic", "aria-hidden": "true", dangerouslySetInnerHTML: { __html: icon6(theme === "auto" ? "monitor" : theme === "light" ? "sun" : "moon") } }) });
3887
- return labelled ? /* @__PURE__ */ jsxs31("span", { className: "ui-theme-toggle", children: [
3888
- button,
3889
- /* @__PURE__ */ jsxs31("span", { children: [
3890
- label,
3891
- " theme"
3892
- ] })
3893
- ] }) : button;
3894
- }
3895
-
3896
3887
  // src/Success.tsx
3897
3888
  import { forwardRef as forwardRef9, useEffect as useEffect15, useRef as useRef19, useState as useState15 } from "react";
3898
3889
  import { jsx as jsx37, jsxs as jsxs32 } from "react/jsx-runtime";