@orangecheck/app-design 1.4.1 → 1.5.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.
package/dist/index.mjs CHANGED
@@ -13,7 +13,7 @@ import * as LabelPrimitive from '@radix-ui/react-label';
13
13
  import * as RadioGroupPrimitive from '@radix-ui/react-radio-group';
14
14
  import * as SelectPrimitive from '@radix-ui/react-select';
15
15
  import * as SwitchPrimitive from '@radix-ui/react-switch';
16
- import { ChevronDown, Check, X, Search, ArrowUpRight, ArrowRight, Copy, ChevronLeft, ChevronRight, CornerDownLeft, Download, FileText, Upload, Menu, Sun, Moon, SunMoon, PanelLeft, CircleUser, LogOut, ArrowLeft, Lock, CircleDashed, CircleMinus, CircleSlash, Clock, CircleX, CircleCheck } from 'lucide-react';
16
+ import { ChevronDown, Check, X, Search, ArrowUpRight, ArrowRight, Copy, ChevronLeft, ChevronRight, Lock, CornerDownLeft, Download, FileText, Upload, Menu, Sun, Moon, SunMoon, PanelLeft, CircleUser, LogOut, ArrowLeft, CircleDashed, CircleMinus, CircleSlash, Clock, CircleX, CircleCheck, Ellipsis } from 'lucide-react';
17
17
  import * as DialogPrimitive from '@radix-ui/react-dialog';
18
18
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
19
19
  import * as PopoverPrimitive from '@radix-ui/react-popover';
@@ -931,7 +931,9 @@ var none = () => null;
931
931
  function PromptBody({ ask: a }) {
932
932
  const o = a.options;
933
933
  const [value, setValue] = useState(o.defaultValue ?? "");
934
+ const [again, setAgain] = useState("");
934
935
  const [error, setError] = useState(null);
936
+ const [mismatch, setMismatch] = useState(false);
935
937
  const submit = (e) => {
936
938
  e.preventDefault();
937
939
  if (o.minLength && value.length < o.minLength) {
@@ -943,6 +945,10 @@ function PromptBody({ ask: a }) {
943
945
  setError(refused);
944
946
  return;
945
947
  }
948
+ if (o.confirmField && again !== value) {
949
+ setMismatch(true);
950
+ return;
951
+ }
946
952
  a.settle(value);
947
953
  done();
948
954
  };
@@ -958,9 +964,30 @@ function PromptBody({ ask: a }) {
958
964
  onChange: (e) => {
959
965
  setValue(e.target.value);
960
966
  setError(null);
967
+ setMismatch(false);
961
968
  }
962
969
  }
963
970
  ) }),
971
+ o.confirmField ? /* @__PURE__ */ jsx(
972
+ Field,
973
+ {
974
+ label: o.confirmField === true ? "Type it again" : o.confirmField,
975
+ error: mismatch ? "The two do not match." : null,
976
+ className: "mt-4",
977
+ children: /* @__PURE__ */ jsx(
978
+ Input,
979
+ {
980
+ type: o.secret ? "password" : "text",
981
+ autoComplete: o.secret ? "new-password" : "off",
982
+ value: again,
983
+ onChange: (e) => {
984
+ setAgain(e.target.value);
985
+ setMismatch(false);
986
+ }
987
+ }
988
+ )
989
+ }
990
+ ) : null,
964
991
  /* @__PURE__ */ jsxs(DialogFooter, { children: [
965
992
  /* @__PURE__ */ jsx(
966
993
  Button,
@@ -2001,7 +2028,7 @@ var JS_TAG = /<\/?(?=[A-Za-z])/y;
2001
2028
  var JS_TAG_NAME = /[A-Za-z][\w$.:-]*/y;
2002
2029
  var JS_PUNCT = /=>|\.\.\.|[{}()[\];,.<>=+\-*/%!?:&|^~#]/y;
2003
2030
  var JSX_BEFORE = /* @__PURE__ */ new Set(["(", ",", "=", "?", ":", "{", "[", ";", ">", "&&", "||", "=>", "return"]);
2004
- function scanJs(src, emit2, jsx50) {
2031
+ function scanJs(src, emit2, jsx51) {
2005
2032
  let i = 0;
2006
2033
  let prev = "";
2007
2034
  let tagDepth = 0;
@@ -2027,7 +2054,7 @@ function scanJs(src, emit2, jsx50) {
2027
2054
  sig('"');
2028
2055
  continue;
2029
2056
  }
2030
- if (jsx50 && tagDepth === 0 && (m = at(JS_TAG, src, i)) && (prev === "" || JSX_BEFORE.has(prev))) {
2057
+ if (jsx51 && tagDepth === 0 && (m = at(JS_TAG, src, i)) && (prev === "" || JSX_BEFORE.has(prev))) {
2031
2058
  const name = at(JS_TAG_NAME, src, i + m.length);
2032
2059
  if (name) {
2033
2060
  emit2(m);
@@ -2039,7 +2066,7 @@ function scanJs(src, emit2, jsx50) {
2039
2066
  continue;
2040
2067
  }
2041
2068
  }
2042
- if (jsx50 && tagDepth > 0 && braceInTag === 0) {
2069
+ if (jsx51 && tagDepth > 0 && braceInTag === 0) {
2043
2070
  if (src.startsWith("/>", i) || src[i] === ">") {
2044
2071
  const close = src[i] === ">" ? ">" : "/>";
2045
2072
  emit2(close);
@@ -2059,7 +2086,7 @@ function scanJs(src, emit2, jsx50) {
2059
2086
  continue;
2060
2087
  }
2061
2088
  if (src[i] === "{") braceInTag = 1;
2062
- } else if (jsx50 && tagDepth > 0 && braceInTag > 0) {
2089
+ } else if (jsx51 && tagDepth > 0 && braceInTag > 0) {
2063
2090
  if (src[i] === "{") braceInTag++;
2064
2091
  else if (src[i] === "}") braceInTag--;
2065
2092
  }
@@ -3774,6 +3801,131 @@ function ErrorPage({
3774
3801
  art !== null ? /* @__PURE__ */ jsx("div", { "aria-hidden": true, className: "oca-enter hidden justify-center md:col-span-5 md:flex [--enter-delay:120ms]", children: art ?? /* @__PURE__ */ jsx(MarkOutline, { className: "w-full max-w-[20rem] text-oca-hairline-strong" }) }) : null
3775
3802
  ] }) });
3776
3803
  }
3804
+ function onSecond(tick) {
3805
+ const id = setInterval(tick, 1e3);
3806
+ return () => clearInterval(id);
3807
+ }
3808
+ var nowSeconds = () => Math.floor(Date.now() / 1e3);
3809
+ function CountdownRing({
3810
+ period,
3811
+ remaining,
3812
+ warnAt = 5,
3813
+ size = 18,
3814
+ showSeconds = true,
3815
+ className
3816
+ }) {
3817
+ const now = useSyncExternalStore(onSecond, nowSeconds, () => null);
3818
+ const left = remaining ?? (now === null ? null : period - now % period);
3819
+ const share = left === null ? 1 : Math.max(0, Math.min(1, left / period));
3820
+ const warn = left !== null && left <= warnAt;
3821
+ const r = size / 2 - 1.5;
3822
+ const c = 2 * Math.PI * r;
3823
+ return /* @__PURE__ */ jsxs(
3824
+ "span",
3825
+ {
3826
+ role: "timer",
3827
+ "aria-label": left === null ? void 0 : `${left} second${left === 1 ? "" : "s"} left`,
3828
+ className: cn("inline-flex items-center gap-1.5", warn ? "text-destructive" : "text-muted-foreground", className),
3829
+ children: [
3830
+ /* @__PURE__ */ jsxs("svg", { "aria-hidden": true, width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "-rotate-90", children: [
3831
+ /* @__PURE__ */ jsx("circle", { cx: size / 2, cy: size / 2, r, fill: "none", stroke: "var(--border)", strokeWidth: 2 }),
3832
+ /* @__PURE__ */ jsx(
3833
+ "circle",
3834
+ {
3835
+ cx: size / 2,
3836
+ cy: size / 2,
3837
+ r,
3838
+ fill: "none",
3839
+ stroke: "currentColor",
3840
+ strokeWidth: 2,
3841
+ strokeLinecap: "round",
3842
+ strokeDasharray: c,
3843
+ strokeDashoffset: c * (1 - share),
3844
+ className: cn(left !== null && left < period && "transition-[stroke-dashoffset] duration-1000 ease-linear motion-reduce:transition-none")
3845
+ }
3846
+ )
3847
+ ] }),
3848
+ showSeconds && left !== null ? /* @__PURE__ */ jsxs("span", { className: "text-[0.8125rem] oca-tabular", children: [
3849
+ left,
3850
+ "s"
3851
+ ] }) : null
3852
+ ]
3853
+ }
3854
+ );
3855
+ }
3856
+ function PlanCard({
3857
+ name,
3858
+ subtitle,
3859
+ price,
3860
+ period,
3861
+ footnote,
3862
+ features = [],
3863
+ action,
3864
+ badge,
3865
+ badgeTone = "neutral",
3866
+ highlight: highlight2 = false,
3867
+ className
3868
+ }) {
3869
+ return /* @__PURE__ */ jsxs(
3870
+ "section",
3871
+ {
3872
+ className: cn(
3873
+ "flex h-full flex-col gap-5 rounded-[1.25rem] border bg-card p-6",
3874
+ highlight2 ? "border-foreground shadow-sm ring-1 ring-foreground" : "border-border",
3875
+ className
3876
+ ),
3877
+ children: [
3878
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3879
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3", children: [
3880
+ /* @__PURE__ */ jsx("h3", { className: "text-[1.0625rem] font-medium leading-snug", children: name }),
3881
+ badge ? /* @__PURE__ */ jsx(Badge, { tone: badgeTone, children: badge }) : null
3882
+ ] }),
3883
+ subtitle ? /* @__PURE__ */ jsx("p", { className: "text-[0.875rem] leading-snug text-muted-foreground", children: subtitle }) : null
3884
+ ] }),
3885
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
3886
+ /* @__PURE__ */ jsxs("p", { className: "flex flex-wrap items-baseline gap-x-1.5", children: [
3887
+ /* @__PURE__ */ jsx("span", { className: "oca-display-3 oca-tabular", children: price }),
3888
+ period ? /* @__PURE__ */ jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: period }) : null
3889
+ ] }),
3890
+ footnote ? /* @__PURE__ */ jsx("p", { className: "text-[0.8125rem] text-muted-foreground", children: footnote }) : null
3891
+ ] }),
3892
+ features.length > 0 ? /* @__PURE__ */ jsx("ul", { className: "flex flex-col gap-2.5 text-[0.9375rem]", children: features.map((f, i) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2.5", children: [
3893
+ /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "mt-1 size-4 shrink-0 text-muted-foreground" }),
3894
+ /* @__PURE__ */ jsx("span", { className: "min-w-0", children: f })
3895
+ ] }, i)) }) : null,
3896
+ action ? /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-col pt-1 [&>*]:w-full", children: action }) : null
3897
+ ]
3898
+ }
3899
+ );
3900
+ }
3901
+ function LockedPreview({
3902
+ title,
3903
+ description,
3904
+ action,
3905
+ children,
3906
+ className
3907
+ }) {
3908
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative", className), children: [
3909
+ children ? /* @__PURE__ */ jsx(
3910
+ "div",
3911
+ {
3912
+ inert: true,
3913
+ "aria-hidden": true,
3914
+ "data-oca-locked": "",
3915
+ className: "pointer-events-none max-h-[28rem] select-none overflow-hidden [mask-image:linear-gradient(to_bottom,#000_25%,transparent_85%)]",
3916
+ children
3917
+ }
3918
+ ) : null,
3919
+ /* @__PURE__ */ jsxs("div", { className: cn("relative mx-auto flex max-w-md flex-col items-center gap-4 rounded-[1.25rem] border border-border bg-card px-6 py-7 text-center shadow-sm", children && "-mt-40"), children: [
3920
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "flex size-10 items-center justify-center rounded-full bg-muted text-foreground", children: /* @__PURE__ */ jsx(Lock, { className: "size-[1.125rem]" }) }),
3921
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1.5", children: [
3922
+ /* @__PURE__ */ jsx("p", { className: "text-[1.0625rem] font-medium leading-snug", children: title }),
3923
+ description ? /* @__PURE__ */ jsx("p", { className: "text-[0.9375rem] leading-snug text-muted-foreground", children: description }) : null
3924
+ ] }),
3925
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-2", children: action })
3926
+ ] })
3927
+ ] });
3928
+ }
3777
3929
  function SectionHeading({
3778
3930
  eyebrow,
3779
3931
  title,
@@ -4931,6 +5083,7 @@ function SearchDialog({
4931
5083
  idle,
4932
5084
  defaultQuery = "",
4933
5085
  debounceMs = 120,
5086
+ browse = false,
4934
5087
  className
4935
5088
  }) {
4936
5089
  const [own, setOwn] = useState(false);
@@ -4967,7 +5120,7 @@ function SearchDialog({
4967
5120
  }, [hotkey]);
4968
5121
  useEffect(() => {
4969
5122
  const q = query.trim();
4970
- if (!q) {
5123
+ if (!q && !(browse && open)) {
4971
5124
  setGroups([]);
4972
5125
  setState("idle");
4973
5126
  return;
@@ -4989,14 +5142,15 @@ function SearchDialog({
4989
5142
  clearTimeout(t);
4990
5143
  ctl.abort();
4991
5144
  };
4992
- }, [query, debounceMs]);
5145
+ }, [query, debounceMs, browse, open]);
4993
5146
  useEffect(() => {
4994
5147
  listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView({ block: "nearest" });
4995
5148
  }, [active]);
4996
5149
  const choose = (r) => {
4997
5150
  setOpen(false);
4998
- if (onSelect) onSelect(r);
4999
- else window.location.assign(r.href);
5151
+ if (r.run) r.run();
5152
+ else if (onSelect) onSelect(r);
5153
+ else if (r.href) window.location.assign(r.href);
5000
5154
  };
5001
5155
  const optionId = (i) => `${listId}-o${i}`;
5002
5156
  const showList = state === "done" && flat.length > 0;
@@ -5067,25 +5221,28 @@ function SearchDialog({
5067
5221
  index++;
5068
5222
  const i = index;
5069
5223
  const on = i === active;
5224
+ const Option = r.href ? "a" : "div";
5070
5225
  return /* @__PURE__ */ jsxs(
5071
- "a",
5226
+ Option,
5072
5227
  {
5073
5228
  id: optionId(i),
5074
- href: r.href,
5229
+ ...r.href ? { href: r.href } : {},
5075
5230
  role: "option",
5076
5231
  "aria-selected": on,
5077
5232
  tabIndex: -1,
5078
5233
  onMouseMove: () => setActive(i),
5079
5234
  onClick: (e) => {
5080
- if (e.metaKey || e.ctrlKey || e.shiftKey) return;
5235
+ if (r.href && (e.metaKey || e.ctrlKey || e.shiftKey)) return;
5081
5236
  e.preventDefault();
5082
5237
  choose(r);
5083
5238
  },
5084
5239
  className: cn(
5085
5240
  "flex items-start gap-3 rounded-xl px-3 py-2.5 transition-colors",
5086
- on ? "bg-accent" : "hover:bg-accent"
5241
+ on ? "bg-accent" : "hover:bg-accent",
5242
+ !r.href && "cursor-pointer"
5087
5243
  ),
5088
5244
  children: [
5245
+ r.icon ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "mt-0.5 flex size-5 shrink-0 items-center justify-center text-muted-foreground [&_svg]:size-[1.0625rem]", children: r.icon }) : null,
5089
5246
  /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5090
5247
  /* @__PURE__ */ jsx("span", { className: "truncate text-[0.9375rem] font-medium text-foreground", children: r.title }),
5091
5248
  r.section ? /* @__PURE__ */ jsx("span", { className: "truncate text-[0.8125rem] text-muted-foreground", children: r.section }) : null,
@@ -5097,6 +5254,7 @@ function SearchDialog({
5097
5254
  }
5098
5255
  ) : r.excerpt ? /* @__PURE__ */ jsx("span", { className: "line-clamp-2 text-[0.875rem] leading-snug text-muted-foreground", children: r.excerpt }) : null
5099
5256
  ] }),
5257
+ r.meta ? /* @__PURE__ */ jsx("span", { className: "mt-0.5 shrink-0 text-[0.8125rem] text-muted-foreground", children: r.meta }) : null,
5100
5258
  /* @__PURE__ */ jsx(CornerDownLeft, { "aria-hidden": true, className: cn("mt-1 size-4 shrink-0 text-muted-foreground", !on && "invisible") })
5101
5259
  ]
5102
5260
  },
@@ -5105,11 +5263,11 @@ function SearchDialog({
5105
5263
  })
5106
5264
  ] }, gi)) }),
5107
5265
  state === "idle" ? /* @__PURE__ */ jsx("div", { className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: idle ?? "Type a word: a protocol, a function, an error." }) : null,
5108
- state === "done" && flat.length === 0 ? /* @__PURE__ */ jsxs("p", { role: "status", className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: [
5266
+ state === "done" && flat.length === 0 ? /* @__PURE__ */ jsx("p", { role: "status", className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: query.trim() ? /* @__PURE__ */ jsxs(Fragment, { children: [
5109
5267
  "No results for \u201C",
5110
5268
  query.trim(),
5111
5269
  "\u201D."
5112
- ] }) : null,
5270
+ ] }) : "Nothing here yet." }) : null,
5113
5271
  state === "error" ? /* @__PURE__ */ jsx("p", { role: "status", className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: "Search is not available right now. Try again in a moment." }) : null
5114
5272
  ] }),
5115
5273
  /* @__PURE__ */ jsxs("div", { "aria-hidden": true, className: "hidden shrink-0 items-center gap-4 border-t border-border px-5 py-2.5 text-[0.75rem] text-muted-foreground sm:flex", children: [
@@ -5134,6 +5292,13 @@ function SearchDialog({
5134
5292
  ] }) })
5135
5293
  ] });
5136
5294
  }
5295
+ function CommandDialog({
5296
+ label = "Commands",
5297
+ placeholder = "Search, or run a command",
5298
+ ...props
5299
+ }) {
5300
+ return /* @__PURE__ */ jsx(SearchDialog, { ...props, label, placeholder, button: false, browse: true });
5301
+ }
5137
5302
  function pretty(content) {
5138
5303
  try {
5139
5304
  return JSON.stringify(JSON.parse(content), null, 2);
@@ -5562,6 +5727,13 @@ function BlockCountdown({
5562
5727
  ] }) : null
5563
5728
  ] });
5564
5729
  }
5730
+ var METER_FILL = {
5731
+ ink: "bg-foreground",
5732
+ brand: "bg-oca-orange-deep",
5733
+ good: "bg-success",
5734
+ caution: "bg-warning",
5735
+ bad: "bg-destructive"
5736
+ };
5565
5737
  function Meter({
5566
5738
  value,
5567
5739
  max,
@@ -5596,7 +5768,7 @@ function Meter({
5596
5768
  /* @__PURE__ */ jsx(
5597
5769
  "div",
5598
5770
  {
5599
- className: cn("h-full shrink-0 rounded-full", tone === "brand" ? "bg-oca-orange-deep" : "bg-foreground"),
5771
+ className: cn("h-full shrink-0 rounded-full", METER_FILL[tone]),
5600
5772
  style: { width: `${v * 100}%` }
5601
5773
  }
5602
5774
  ),
@@ -5843,48 +6015,106 @@ function SectionSwitch({
5843
6015
  }
5844
6016
  );
5845
6017
  }
6018
+ function NavBadge({ badge, className }) {
6019
+ if (typeof badge === "number") {
6020
+ return /* @__PURE__ */ jsx(
6021
+ "span",
6022
+ {
6023
+ "aria-hidden": true,
6024
+ className: cn(
6025
+ "absolute flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 text-[0.625rem] font-medium leading-none text-primary-foreground ring-2 ring-background oca-tabular",
6026
+ className
6027
+ ),
6028
+ children: badge > 99 ? "99+" : badge
6029
+ }
6030
+ );
6031
+ }
6032
+ return /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: cn("absolute size-2 rounded-full bg-oca-orange-deep ring-2 ring-background", className) });
6033
+ }
6034
+ var TAB = "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring";
6035
+ function TabFace({ icon, label, badge, active }) {
6036
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6037
+ /* @__PURE__ */ jsxs(
6038
+ "span",
6039
+ {
6040
+ className: cn(
6041
+ "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
6042
+ "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
6043
+ active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
6044
+ ),
6045
+ children: [
6046
+ icon,
6047
+ badge ? /* @__PURE__ */ jsx(NavBadge, { badge, className: typeof badge === "number" ? "-top-1 right-0.5" : "right-2.5 top-0.5" }) : null
6048
+ ]
6049
+ }
6050
+ ),
6051
+ /* @__PURE__ */ jsx("span", { className: "max-w-full truncate px-0.5", children: label }),
6052
+ badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: badge }) : null
6053
+ ] });
6054
+ }
5846
6055
  function TabBar({ items }) {
6056
+ const [more, setMore] = useState(false);
6057
+ const overflow = items.length > 5;
6058
+ const shown = overflow ? items.slice(0, 4) : items;
6059
+ const rest = overflow ? items.slice(4) : [];
6060
+ const restBadge = rest.find((it) => it.badge)?.badge;
5847
6061
  return /* @__PURE__ */ jsx(
5848
6062
  "nav",
5849
6063
  {
5850
6064
  "aria-label": "Pages",
5851
6065
  "data-oca-tabbar": "",
5852
6066
  className: "fixed inset-x-0 bottom-0 z-40 border-t border-border bg-background/90 pb-[env(safe-area-inset-bottom)] backdrop-blur-md md:hidden",
5853
- children: /* @__PURE__ */ jsx(
6067
+ children: /* @__PURE__ */ jsxs(
5854
6068
  "div",
5855
6069
  {
5856
6070
  className: "mx-auto grid h-14 max-w-[32rem] px-1.5",
5857
- style: { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` },
5858
- children: items.map((it) => /* @__PURE__ */ jsxs(
5859
- Link6,
5860
- {
5861
- href: it.href,
5862
- "aria-current": it.active ? "page" : void 0,
5863
- className: cn(
5864
- "flex min-w-0 flex-col items-center justify-center gap-1 rounded-2xl text-[0.6875rem] leading-none outline-none transition-colors [-webkit-tap-highlight-color:transparent] focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
5865
- it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
6071
+ style: { gridTemplateColumns: `repeat(${shown.length + (overflow ? 1 : 0)}, minmax(0, 1fr))` },
6072
+ children: [
6073
+ shown.map((it) => /* @__PURE__ */ jsx(
6074
+ Link6,
6075
+ {
6076
+ href: it.href,
6077
+ "aria-current": it.active ? "page" : void 0,
6078
+ className: cn(TAB, it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
6079
+ children: /* @__PURE__ */ jsx(TabFace, { icon: it.icon, label: it.label, badge: it.badge, active: it.active })
6080
+ },
6081
+ it.href
6082
+ )),
6083
+ overflow ? /* @__PURE__ */ jsxs(Sheet, { open: more, onOpenChange: setMore, children: [
6084
+ /* @__PURE__ */ jsx(
6085
+ SheetTrigger,
6086
+ {
6087
+ className: cn(TAB, rest.some((it) => it.active) ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
6088
+ children: /* @__PURE__ */ jsx(TabFace, { icon: /* @__PURE__ */ jsx(Ellipsis, {}), label: "More", badge: restBadge ? true : void 0, active: rest.some((it) => it.active) })
6089
+ }
5866
6090
  ),
5867
- children: [
5868
- /* @__PURE__ */ jsxs(
5869
- "span",
6091
+ /* @__PURE__ */ jsxs(SheetContent, { side: "bottom", className: "pb-[calc(1.5rem+env(safe-area-inset-bottom))]", children: [
6092
+ /* @__PURE__ */ jsx(SheetTitle, { className: "px-1 text-[1.0625rem] font-medium", children: "More" }),
6093
+ /* @__PURE__ */ jsx("div", { className: "mt-4 grid grid-cols-3 gap-2", children: rest.map((it) => /* @__PURE__ */ jsxs(
6094
+ Link6,
5870
6095
  {
6096
+ href: it.href,
6097
+ onClick: () => setMore(false),
6098
+ "aria-current": it.active ? "page" : void 0,
5871
6099
  className: cn(
5872
- "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
5873
- "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
5874
- it.active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
6100
+ "flex min-h-20 flex-col items-center justify-center gap-2 rounded-2xl border px-2 text-center text-[0.8125rem] transition-colors",
6101
+ "[&_svg]:size-5",
6102
+ it.active ? "border-foreground bg-card text-foreground" : "border-border text-muted-foreground hover:text-foreground"
5875
6103
  ),
5876
6104
  children: [
5877
- it.icon,
5878
- it.badge ? /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "absolute right-2.5 top-0.5 size-2 rounded-full bg-oca-orange ring-2 ring-background" }) : null
6105
+ /* @__PURE__ */ jsxs("span", { className: "relative", children: [
6106
+ it.icon,
6107
+ it.badge ? /* @__PURE__ */ jsx(NavBadge, { badge: it.badge, className: typeof it.badge === "number" ? "-right-3 -top-2" : "-right-1 -top-0.5" }) : null
6108
+ ] }),
6109
+ /* @__PURE__ */ jsx("span", { className: "max-w-full truncate", children: it.label }),
6110
+ it.badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: it.badge }) : null
5879
6111
  ]
5880
- }
5881
- ),
5882
- /* @__PURE__ */ jsx("span", { className: "max-w-full truncate px-0.5", children: it.label }),
5883
- it.badge ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: it.badge }) : null
5884
- ]
5885
- },
5886
- it.href
5887
- ))
6112
+ },
6113
+ it.href
6114
+ )) })
6115
+ ] })
6116
+ ] }) : null
6117
+ ]
5888
6118
  }
5889
6119
  )
5890
6120
  }
@@ -5892,7 +6122,11 @@ function TabBar({ items }) {
5892
6122
  }
5893
6123
  function tabsFor(groups) {
5894
6124
  const items = groups.flatMap((g) => g.items);
5895
- return items.length >= 2 && items.length <= 5 && items.every((it) => it.icon) ? items : null;
6125
+ return items.length >= 2 && items.every((it) => it.icon) ? items : null;
6126
+ }
6127
+ function sectionTabs(sections, current2) {
6128
+ if (!sections || !current2 || sections.length < 2 || !sections.every((sec) => sec.icon)) return null;
6129
+ return sections.map((sec) => ({ href: sec.href, label: sec.label, icon: sec.icon, active: sec.id === current2.id }));
5896
6130
  }
5897
6131
  var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
5898
6132
  var warned = /* @__PURE__ */ new Set();
@@ -5907,7 +6141,10 @@ function warnDuplicateLabels(groups) {
5907
6141
  seen.add(it.label);
5908
6142
  }
5909
6143
  }
5910
- function AppShell({
6144
+ function AppShell(props) {
6145
+ return props.variant === "rail" ? /* @__PURE__ */ jsx(RailShell, { ...props }) : /* @__PURE__ */ jsx(SidebarShell, { ...props });
6146
+ }
6147
+ function SidebarShell({
5911
6148
  nav,
5912
6149
  sections,
5913
6150
  activeSection,
@@ -5925,7 +6162,8 @@ function AppShell({
5925
6162
  const current2 = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
5926
6163
  const groups = current2 ? [...current2.groups, ...persistent] : [...nav ?? [], ...persistent];
5927
6164
  warnDuplicateLabels(groups);
5928
- const tabs = current2 ? null : tabsFor(groups);
6165
+ const sectionTabsFor = sectionTabs(sections, current2);
6166
+ const tabs = current2 ? sectionTabsFor : tabsFor(groups);
5929
6167
  const page = activeItem(groups);
5930
6168
  const place = useMemo(
5931
6169
  () => ({
@@ -5953,8 +6191,8 @@ function AppShell({
5953
6191
  {
5954
6192
  className: cn(
5955
6193
  "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
5956
- // The tab bar holds every page; the sheet is left for a footer.
5957
- tabs && !footer && "max-md:hidden"
6194
+ // The tab bar holds every page; the sheet is left for a footer. Section tabs leave the pages here.
6195
+ tabs && !sectionTabsFor && !footer && "max-md:hidden"
5958
6196
  ),
5959
6197
  "aria-label": "Open navigation",
5960
6198
  children: /* @__PURE__ */ jsx(Menu, { className: "size-5" })
@@ -5962,8 +6200,8 @@ function AppShell({
5962
6200
  ),
5963
6201
  /* @__PURE__ */ jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
5964
6202
  /* @__PURE__ */ jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
5965
- switcher,
5966
- /* @__PURE__ */ jsx("div", { className: tabs ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
6203
+ switcher ? /* @__PURE__ */ jsx("div", { className: sectionTabsFor ? "max-md:hidden" : void 0, children: switcher }) : null,
6204
+ /* @__PURE__ */ jsx("div", { className: tabs && !sectionTabsFor ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
5967
6205
  footer ? /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
5968
6206
  ] })
5969
6207
  ] }),
@@ -5977,7 +6215,7 @@ function AppShell({
5977
6215
  className: cn(
5978
6216
  "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]",
5979
6217
  // The active tab already names the page.
5980
- tabs && !pageOverride && "max-md:hidden"
6218
+ tabs && !sectionTabsFor && !pageOverride && "max-md:hidden"
5981
6219
  ),
5982
6220
  children: [
5983
6221
  place.section ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -6010,6 +6248,64 @@ function AppShell({
6010
6248
  }
6011
6249
  ) });
6012
6250
  }
6251
+ function RailItem({ item }) {
6252
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
6253
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
6254
+ Link6,
6255
+ {
6256
+ href: item.href,
6257
+ "aria-current": item.active ? "page" : void 0,
6258
+ className: cn(
6259
+ "relative flex size-10 items-center justify-center rounded-xl outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
6260
+ "[&_svg]:size-5 [&_svg]:shrink-0",
6261
+ item.active ? "bg-card text-foreground shadow-sm ring-1 ring-border" : "text-muted-foreground hover:bg-accent hover:text-foreground"
6262
+ ),
6263
+ children: [
6264
+ item.icon,
6265
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
6266
+ item.label,
6267
+ item.badge ? /* @__PURE__ */ jsxs(Fragment, { children: [
6268
+ ", ",
6269
+ item.badge
6270
+ ] }) : null
6271
+ ] }),
6272
+ item.badge ? /* @__PURE__ */ jsx(NavBadge, { badge: item.badge, className: typeof item.badge === "number" ? "-right-1 -top-1" : "right-1 top-1" }) : null
6273
+ ]
6274
+ }
6275
+ ) }),
6276
+ /* @__PURE__ */ jsx(TooltipContent, { side: "right", children: item.label })
6277
+ ] });
6278
+ }
6279
+ function RailShell({ nav = [], persistent = [], page: pageOverride, header, account, banner, children, className }) {
6280
+ const main = nav.flatMap((g) => g.items);
6281
+ const foot = persistent.flatMap((g) => g.items);
6282
+ warnDuplicateLabels([...nav, ...persistent]);
6283
+ const tabs = tabsFor([{ items: [...main, ...foot] }]);
6284
+ const active = activeItem([...nav, ...persistent]);
6285
+ const place = useMemo(() => ({ page: pageOverride ?? active?.label }), [pageOverride, active?.label]);
6286
+ return /* @__PURE__ */ jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs("div", { className: cn("flex h-dvh flex-col overflow-hidden bg-background", className), children: [
6287
+ header,
6288
+ banner,
6289
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
6290
+ /* @__PURE__ */ jsxs("nav", { "aria-label": "Pages", className: "hidden w-14 shrink-0 flex-col items-center gap-1 border-r border-border py-3 md:flex", children: [
6291
+ main.map((it) => /* @__PURE__ */ jsx(RailItem, { item: it }, it.href)),
6292
+ /* @__PURE__ */ jsxs("div", { className: "mt-auto flex flex-col items-center gap-1", children: [
6293
+ foot.map((it) => /* @__PURE__ */ jsx(RailItem, { item: it }, it.href)),
6294
+ account
6295
+ ] })
6296
+ ] }),
6297
+ /* @__PURE__ */ jsx(
6298
+ "main",
6299
+ {
6300
+ id: "main-content",
6301
+ className: cn("flex min-h-0 min-w-0 flex-1 flex-col", tabs && "max-md:pb-[calc(3.5rem_+_env(safe-area-inset-bottom))]"),
6302
+ children
6303
+ }
6304
+ )
6305
+ ] }),
6306
+ tabs ? /* @__PURE__ */ jsx(TabBar, { items: tabs }) : null
6307
+ ] }) }) });
6308
+ }
6013
6309
  function AccountMenu({
6014
6310
  name,
6015
6311
  detail,
@@ -6916,6 +7212,6 @@ function createMdxComponents({ mermaid } = {}) {
6916
7212
  }
6917
7213
  var mdxComponents = createMdxComponents();
6918
7214
 
6919
- export { APP_ATTRIBUTE, APP_ID, Accent, Accordion, AccordionItem, AccountMenu, ActivityList, AnchorStatus, ApiTable, AppCard, AppGrid, AppIcon, AppShell, ArtifactView, Badge, BlockCountdown, BlockHeight, Breadcrumbs, BrowserFrame, Button, CODE_LANGS, Callout, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardNumeral, CardTitle, Checkbox, Closing, CodeBlock, CodeTabs, ComparisonTable, Container, CopyButton, Crowd, DOCS_HEADER_NAV, DOCS_HREF, DataTable, DensityProvider, Diagram, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogHost, DialogTitle, DialogTrigger, Display, DocHeader, DocLayout, DocsHeading, DocsNav, DocsPager, DocsShell, DropZone, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EmptyLine, EmptyState, EndpointList, ErrorBoundary, ErrorPage, Eyebrow, FAMILY, FAMILY_GROUPS, FactBlock, FactRows, FamilyMenu, Faq, Field, FilterChips, FloatChip, Footnote, FormSection, HalftoneCanvas, HalftoneSphere, HashChip, Input, InstallTabs, IsoCheck, Kbd, KeyValueList, LEGAL_LINKS, Label, Lead, LegalDocument, LinkCard, LoadingBlock, Lockup, MARK_FACES, Mark, MdxTable, Meter, Monogram, Notice, OCHK_APP_ACCOUNT, OCHK_IO, OCHK_IO_FOOTER, OCHK_IO_NAV, OchkIoFooter, OnThisPage, PROTOCOLS, PROTOCOL_ORDER, PackageCard, PageHeader, PageHero, Pagination, PhoneFrame, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProgressSteps, ProofStrip, Prose, ProtocolGrid, ProtocolHero, ProtocolNav, QrCode, RadioGroup, RadioGroupItem, Reveal, SELF_HOSTED_LEGAL_LINKS, SPHERE_GRADIENT, Sats, SatsFigure, ScrollFade, ScrollStory, SearchButton, SearchDialog, Section, SectionHeading, SectionIntro, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Separator, SessionAccountView, Sheet, SheetClose, SheetContent, SheetDescription, SheetTitle, SheetTrigger, ShellContext, SignInFrame, SignMessage, SiteBanner, SiteFooter, SiteHeader, Skeleton, SkipLink, SpecCard, Spinner, SplitBar, StatCard, StatGrid, StateBadge, StatusBadge, StatusDot, StatusPill, Step, StepGrid, Steps, Switch, TOKEN_CLASS, Tabs, TabsContent, TabsList, TabsTrigger, TextLink, Textarea, ThemeToggle, Ticker, Toaster, ToolGrid, ToolShell, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TruthTable, UsdApprox, VerifyBox, VerifyReport, appColors, appFonts, badgeVariants, blocksToDuration, buttonVariants, cardVariants, cn, confirm, createMdxComponents, createStateBadge, escapeHtml, explorerUrl, familyByGroup, familySite, formatBlockHeight, formatBytes, formatUtc, grammarOf, highlight, highlightToHtml, languageName, linesToHtml, mdxComponents, monogramGround, monogramLetter, parseCodeMeta, parseLineRanges, portraitTone, prompt, protocolSite, readDiff, rehypeCode, satsText, sectionVariants, shortenMiddle, statusWord, useActiveHeading, useCopy, useDensity, useHeadings, useInView, useShellPlace, useSignInHref, useTimedOut };
7215
+ export { APP_ATTRIBUTE, APP_ID, Accent, Accordion, AccordionItem, AccountMenu, ActivityList, AnchorStatus, ApiTable, AppCard, AppGrid, AppIcon, AppShell, ArtifactView, Badge, BlockCountdown, BlockHeight, Breadcrumbs, BrowserFrame, Button, CODE_LANGS, Callout, Card, CardContent, CardDescription, CardFooter, CardGrid, CardHeader, CardNumeral, CardTitle, Checkbox, Closing, CodeBlock, CodeTabs, CommandDialog, ComparisonTable, Container, CopyButton, CountdownRing, Crowd, DOCS_HEADER_NAV, DOCS_HREF, DataTable, DensityProvider, Diagram, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogHost, DialogTitle, DialogTrigger, Display, DocHeader, DocLayout, DocsHeading, DocsNav, DocsPager, DocsShell, DropZone, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, EmptyLine, EmptyState, EndpointList, ErrorBoundary, ErrorPage, Eyebrow, FAMILY, FAMILY_GROUPS, FactBlock, FactRows, FamilyMenu, Faq, Field, FilterChips, FloatChip, Footnote, FormSection, HalftoneCanvas, HalftoneSphere, HashChip, Input, InstallTabs, IsoCheck, Kbd, KeyValueList, LEGAL_LINKS, Label, Lead, LegalDocument, LinkCard, LoadingBlock, LockedPreview, Lockup, MARK_FACES, Mark, MdxTable, Meter, Monogram, Notice, OCHK_APP_ACCOUNT, OCHK_IO, OCHK_IO_FOOTER, OCHK_IO_NAV, OchkIoFooter, OnThisPage, PROTOCOLS, PROTOCOL_ORDER, PackageCard, PageHeader, PageHero, Pagination, PhoneFrame, PlanCard, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProgressSteps, ProofStrip, Prose, ProtocolGrid, ProtocolHero, ProtocolNav, QrCode, RadioGroup, RadioGroupItem, Reveal, SELF_HOSTED_LEGAL_LINKS, SPHERE_GRADIENT, Sats, SatsFigure, ScrollFade, ScrollStory, SearchButton, SearchDialog, Section, SectionHeading, SectionIntro, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Separator, SessionAccountView, Sheet, SheetClose, SheetContent, SheetDescription, SheetTitle, SheetTrigger, ShellContext, SignInFrame, SignMessage, SiteBanner, SiteFooter, SiteHeader, Skeleton, SkipLink, SpecCard, Spinner, SplitBar, StatCard, StatGrid, StateBadge, StatusBadge, StatusDot, StatusPill, Step, StepGrid, Steps, Switch, TOKEN_CLASS, Tabs, TabsContent, TabsList, TabsTrigger, TextLink, Textarea, ThemeToggle, Ticker, Toaster, ToolGrid, ToolShell, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TruthTable, UsdApprox, VerifyBox, VerifyReport, appColors, appFonts, badgeVariants, blocksToDuration, buttonVariants, cardVariants, cn, confirm, createMdxComponents, createStateBadge, escapeHtml, explorerUrl, familyByGroup, familySite, formatBlockHeight, formatBytes, formatUtc, grammarOf, highlight, highlightToHtml, languageName, linesToHtml, mdxComponents, monogramGround, monogramLetter, parseCodeMeta, parseLineRanges, portraitTone, prompt, protocolSite, readDiff, rehypeCode, satsText, sectionVariants, shortenMiddle, statusWord, useActiveHeading, useCopy, useDensity, useHeadings, useInView, useShellPlace, useSignInHref, useTimedOut };
6920
7216
  //# sourceMappingURL=index.mjs.map
6921
7217
  //# sourceMappingURL=index.mjs.map