@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.js CHANGED
@@ -966,7 +966,9 @@ var none = () => null;
966
966
  function PromptBody({ ask: a }) {
967
967
  const o = a.options;
968
968
  const [value, setValue] = react.useState(o.defaultValue ?? "");
969
+ const [again, setAgain] = react.useState("");
969
970
  const [error, setError] = react.useState(null);
971
+ const [mismatch, setMismatch] = react.useState(false);
970
972
  const submit = (e) => {
971
973
  e.preventDefault();
972
974
  if (o.minLength && value.length < o.minLength) {
@@ -978,6 +980,10 @@ function PromptBody({ ask: a }) {
978
980
  setError(refused);
979
981
  return;
980
982
  }
983
+ if (o.confirmField && again !== value) {
984
+ setMismatch(true);
985
+ return;
986
+ }
981
987
  a.settle(value);
982
988
  done();
983
989
  };
@@ -993,9 +999,30 @@ function PromptBody({ ask: a }) {
993
999
  onChange: (e) => {
994
1000
  setValue(e.target.value);
995
1001
  setError(null);
1002
+ setMismatch(false);
996
1003
  }
997
1004
  }
998
1005
  ) }),
1006
+ o.confirmField ? /* @__PURE__ */ jsxRuntime.jsx(
1007
+ Field,
1008
+ {
1009
+ label: o.confirmField === true ? "Type it again" : o.confirmField,
1010
+ error: mismatch ? "The two do not match." : null,
1011
+ className: "mt-4",
1012
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1013
+ Input,
1014
+ {
1015
+ type: o.secret ? "password" : "text",
1016
+ autoComplete: o.secret ? "new-password" : "off",
1017
+ value: again,
1018
+ onChange: (e) => {
1019
+ setAgain(e.target.value);
1020
+ setMismatch(false);
1021
+ }
1022
+ }
1023
+ )
1024
+ }
1025
+ ) : null,
999
1026
  /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
1000
1027
  /* @__PURE__ */ jsxRuntime.jsx(
1001
1028
  Button,
@@ -2036,7 +2063,7 @@ var JS_TAG = /<\/?(?=[A-Za-z])/y;
2036
2063
  var JS_TAG_NAME = /[A-Za-z][\w$.:-]*/y;
2037
2064
  var JS_PUNCT = /=>|\.\.\.|[{}()[\];,.<>=+\-*/%!?:&|^~#]/y;
2038
2065
  var JSX_BEFORE = /* @__PURE__ */ new Set(["(", ",", "=", "?", ":", "{", "[", ";", ">", "&&", "||", "=>", "return"]);
2039
- function scanJs(src, emit2, jsx50) {
2066
+ function scanJs(src, emit2, jsx51) {
2040
2067
  let i = 0;
2041
2068
  let prev = "";
2042
2069
  let tagDepth = 0;
@@ -2062,7 +2089,7 @@ function scanJs(src, emit2, jsx50) {
2062
2089
  sig('"');
2063
2090
  continue;
2064
2091
  }
2065
- if (jsx50 && tagDepth === 0 && (m = at(JS_TAG, src, i)) && (prev === "" || JSX_BEFORE.has(prev))) {
2092
+ if (jsx51 && tagDepth === 0 && (m = at(JS_TAG, src, i)) && (prev === "" || JSX_BEFORE.has(prev))) {
2066
2093
  const name = at(JS_TAG_NAME, src, i + m.length);
2067
2094
  if (name) {
2068
2095
  emit2(m);
@@ -2074,7 +2101,7 @@ function scanJs(src, emit2, jsx50) {
2074
2101
  continue;
2075
2102
  }
2076
2103
  }
2077
- if (jsx50 && tagDepth > 0 && braceInTag === 0) {
2104
+ if (jsx51 && tagDepth > 0 && braceInTag === 0) {
2078
2105
  if (src.startsWith("/>", i) || src[i] === ">") {
2079
2106
  const close = src[i] === ">" ? ">" : "/>";
2080
2107
  emit2(close);
@@ -2094,7 +2121,7 @@ function scanJs(src, emit2, jsx50) {
2094
2121
  continue;
2095
2122
  }
2096
2123
  if (src[i] === "{") braceInTag = 1;
2097
- } else if (jsx50 && tagDepth > 0 && braceInTag > 0) {
2124
+ } else if (jsx51 && tagDepth > 0 && braceInTag > 0) {
2098
2125
  if (src[i] === "{") braceInTag++;
2099
2126
  else if (src[i] === "}") braceInTag--;
2100
2127
  }
@@ -3809,6 +3836,131 @@ function ErrorPage({
3809
3836
  art !== null ? /* @__PURE__ */ jsxRuntime.jsx("div", { "aria-hidden": true, className: "oca-enter hidden justify-center md:col-span-5 md:flex [--enter-delay:120ms]", children: art ?? /* @__PURE__ */ jsxRuntime.jsx(MarkOutline, { className: "w-full max-w-[20rem] text-oca-hairline-strong" }) }) : null
3810
3837
  ] }) });
3811
3838
  }
3839
+ function onSecond(tick) {
3840
+ const id = setInterval(tick, 1e3);
3841
+ return () => clearInterval(id);
3842
+ }
3843
+ var nowSeconds = () => Math.floor(Date.now() / 1e3);
3844
+ function CountdownRing({
3845
+ period,
3846
+ remaining,
3847
+ warnAt = 5,
3848
+ size = 18,
3849
+ showSeconds = true,
3850
+ className
3851
+ }) {
3852
+ const now = react.useSyncExternalStore(onSecond, nowSeconds, () => null);
3853
+ const left = remaining ?? (now === null ? null : period - now % period);
3854
+ const share = left === null ? 1 : Math.max(0, Math.min(1, left / period));
3855
+ const warn = left !== null && left <= warnAt;
3856
+ const r = size / 2 - 1.5;
3857
+ const c = 2 * Math.PI * r;
3858
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3859
+ "span",
3860
+ {
3861
+ role: "timer",
3862
+ "aria-label": left === null ? void 0 : `${left} second${left === 1 ? "" : "s"} left`,
3863
+ className: cn("inline-flex items-center gap-1.5", warn ? "text-destructive" : "text-muted-foreground", className),
3864
+ children: [
3865
+ /* @__PURE__ */ jsxRuntime.jsxs("svg", { "aria-hidden": true, width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "-rotate-90", children: [
3866
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: size / 2, cy: size / 2, r, fill: "none", stroke: "var(--border)", strokeWidth: 2 }),
3867
+ /* @__PURE__ */ jsxRuntime.jsx(
3868
+ "circle",
3869
+ {
3870
+ cx: size / 2,
3871
+ cy: size / 2,
3872
+ r,
3873
+ fill: "none",
3874
+ stroke: "currentColor",
3875
+ strokeWidth: 2,
3876
+ strokeLinecap: "round",
3877
+ strokeDasharray: c,
3878
+ strokeDashoffset: c * (1 - share),
3879
+ className: cn(left !== null && left < period && "transition-[stroke-dashoffset] duration-1000 ease-linear motion-reduce:transition-none")
3880
+ }
3881
+ )
3882
+ ] }),
3883
+ showSeconds && left !== null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[0.8125rem] oca-tabular", children: [
3884
+ left,
3885
+ "s"
3886
+ ] }) : null
3887
+ ]
3888
+ }
3889
+ );
3890
+ }
3891
+ function PlanCard({
3892
+ name,
3893
+ subtitle,
3894
+ price,
3895
+ period,
3896
+ footnote,
3897
+ features = [],
3898
+ action,
3899
+ badge,
3900
+ badgeTone = "neutral",
3901
+ highlight: highlight2 = false,
3902
+ className
3903
+ }) {
3904
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3905
+ "section",
3906
+ {
3907
+ className: cn(
3908
+ "flex h-full flex-col gap-5 rounded-[1.25rem] border bg-card p-6",
3909
+ highlight2 ? "border-foreground shadow-sm ring-1 ring-foreground" : "border-border",
3910
+ className
3911
+ ),
3912
+ children: [
3913
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3914
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
3915
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-[1.0625rem] font-medium leading-snug", children: name }),
3916
+ badge ? /* @__PURE__ */ jsxRuntime.jsx(Badge, { tone: badgeTone, children: badge }) : null
3917
+ ] }),
3918
+ subtitle ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.875rem] leading-snug text-muted-foreground", children: subtitle }) : null
3919
+ ] }),
3920
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
3921
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "flex flex-wrap items-baseline gap-x-1.5", children: [
3922
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "oca-display-3 oca-tabular", children: price }),
3923
+ period ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[0.9375rem] text-muted-foreground", children: period }) : null
3924
+ ] }),
3925
+ footnote ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.8125rem] text-muted-foreground", children: footnote }) : null
3926
+ ] }),
3927
+ features.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-2.5 text-[0.9375rem]", children: features.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-start gap-2.5", children: [
3928
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { "aria-hidden": true, className: "mt-1 size-4 shrink-0 text-muted-foreground" }),
3929
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0", children: f })
3930
+ ] }, i)) }) : null,
3931
+ action ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col pt-1 [&>*]:w-full", children: action }) : null
3932
+ ]
3933
+ }
3934
+ );
3935
+ }
3936
+ function LockedPreview({
3937
+ title,
3938
+ description,
3939
+ action,
3940
+ children,
3941
+ className
3942
+ }) {
3943
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", className), children: [
3944
+ children ? /* @__PURE__ */ jsxRuntime.jsx(
3945
+ "div",
3946
+ {
3947
+ inert: true,
3948
+ "aria-hidden": true,
3949
+ "data-oca-locked": "",
3950
+ className: "pointer-events-none max-h-[28rem] select-none overflow-hidden [mask-image:linear-gradient(to_bottom,#000_25%,transparent_85%)]",
3951
+ children
3952
+ }
3953
+ ) : null,
3954
+ /* @__PURE__ */ jsxRuntime.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: [
3955
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "flex size-10 items-center justify-center rounded-full bg-muted text-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Lock, { className: "size-[1.125rem]" }) }),
3956
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
3957
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[1.0625rem] font-medium leading-snug", children: title }),
3958
+ description ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[0.9375rem] leading-snug text-muted-foreground", children: description }) : null
3959
+ ] }),
3960
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap justify-center gap-2", children: action })
3961
+ ] })
3962
+ ] });
3963
+ }
3812
3964
  function SectionHeading({
3813
3965
  eyebrow,
3814
3966
  title,
@@ -4966,6 +5118,7 @@ function SearchDialog({
4966
5118
  idle,
4967
5119
  defaultQuery = "",
4968
5120
  debounceMs = 120,
5121
+ browse = false,
4969
5122
  className
4970
5123
  }) {
4971
5124
  const [own, setOwn] = react.useState(false);
@@ -5002,7 +5155,7 @@ function SearchDialog({
5002
5155
  }, [hotkey]);
5003
5156
  react.useEffect(() => {
5004
5157
  const q = query.trim();
5005
- if (!q) {
5158
+ if (!q && !(browse && open)) {
5006
5159
  setGroups([]);
5007
5160
  setState("idle");
5008
5161
  return;
@@ -5024,14 +5177,15 @@ function SearchDialog({
5024
5177
  clearTimeout(t);
5025
5178
  ctl.abort();
5026
5179
  };
5027
- }, [query, debounceMs]);
5180
+ }, [query, debounceMs, browse, open]);
5028
5181
  react.useEffect(() => {
5029
5182
  listRef.current?.querySelector('[aria-selected="true"]')?.scrollIntoView({ block: "nearest" });
5030
5183
  }, [active]);
5031
5184
  const choose = (r) => {
5032
5185
  setOpen(false);
5033
- if (onSelect) onSelect(r);
5034
- else window.location.assign(r.href);
5186
+ if (r.run) r.run();
5187
+ else if (onSelect) onSelect(r);
5188
+ else if (r.href) window.location.assign(r.href);
5035
5189
  };
5036
5190
  const optionId = (i) => `${listId}-o${i}`;
5037
5191
  const showList = state === "done" && flat.length > 0;
@@ -5102,25 +5256,28 @@ function SearchDialog({
5102
5256
  index++;
5103
5257
  const i = index;
5104
5258
  const on = i === active;
5259
+ const Option = r.href ? "a" : "div";
5105
5260
  return /* @__PURE__ */ jsxRuntime.jsxs(
5106
- "a",
5261
+ Option,
5107
5262
  {
5108
5263
  id: optionId(i),
5109
- href: r.href,
5264
+ ...r.href ? { href: r.href } : {},
5110
5265
  role: "option",
5111
5266
  "aria-selected": on,
5112
5267
  tabIndex: -1,
5113
5268
  onMouseMove: () => setActive(i),
5114
5269
  onClick: (e) => {
5115
- if (e.metaKey || e.ctrlKey || e.shiftKey) return;
5270
+ if (r.href && (e.metaKey || e.ctrlKey || e.shiftKey)) return;
5116
5271
  e.preventDefault();
5117
5272
  choose(r);
5118
5273
  },
5119
5274
  className: cn(
5120
5275
  "flex items-start gap-3 rounded-xl px-3 py-2.5 transition-colors",
5121
- on ? "bg-accent" : "hover:bg-accent"
5276
+ on ? "bg-accent" : "hover:bg-accent",
5277
+ !r.href && "cursor-pointer"
5122
5278
  ),
5123
5279
  children: [
5280
+ r.icon ? /* @__PURE__ */ jsxRuntime.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,
5124
5281
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
5125
5282
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[0.9375rem] font-medium text-foreground", children: r.title }),
5126
5283
  r.section ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[0.8125rem] text-muted-foreground", children: r.section }) : null,
@@ -5132,6 +5289,7 @@ function SearchDialog({
5132
5289
  }
5133
5290
  ) : r.excerpt ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "line-clamp-2 text-[0.875rem] leading-snug text-muted-foreground", children: r.excerpt }) : null
5134
5291
  ] }),
5292
+ r.meta ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 shrink-0 text-[0.8125rem] text-muted-foreground", children: r.meta }) : null,
5135
5293
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CornerDownLeft, { "aria-hidden": true, className: cn("mt-1 size-4 shrink-0 text-muted-foreground", !on && "invisible") })
5136
5294
  ]
5137
5295
  },
@@ -5140,11 +5298,11 @@ function SearchDialog({
5140
5298
  })
5141
5299
  ] }, gi)) }),
5142
5300
  state === "idle" ? /* @__PURE__ */ jsxRuntime.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,
5143
- state === "done" && flat.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("p", { role: "status", className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: [
5301
+ state === "done" && flat.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("p", { role: "status", className: "px-3 py-6 text-[0.9375rem] text-muted-foreground", children: query.trim() ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5144
5302
  "No results for \u201C",
5145
5303
  query.trim(),
5146
5304
  "\u201D."
5147
- ] }) : null,
5305
+ ] }) : "Nothing here yet." }) : null,
5148
5306
  state === "error" ? /* @__PURE__ */ jsxRuntime.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
5149
5307
  ] }),
5150
5308
  /* @__PURE__ */ jsxRuntime.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: [
@@ -5169,6 +5327,13 @@ function SearchDialog({
5169
5327
  ] }) })
5170
5328
  ] });
5171
5329
  }
5330
+ function CommandDialog({
5331
+ label = "Commands",
5332
+ placeholder = "Search, or run a command",
5333
+ ...props
5334
+ }) {
5335
+ return /* @__PURE__ */ jsxRuntime.jsx(SearchDialog, { ...props, label, placeholder, button: false, browse: true });
5336
+ }
5172
5337
  function pretty(content) {
5173
5338
  try {
5174
5339
  return JSON.stringify(JSON.parse(content), null, 2);
@@ -5597,6 +5762,13 @@ function BlockCountdown({
5597
5762
  ] }) : null
5598
5763
  ] });
5599
5764
  }
5765
+ var METER_FILL = {
5766
+ ink: "bg-foreground",
5767
+ brand: "bg-oca-orange-deep",
5768
+ good: "bg-success",
5769
+ caution: "bg-warning",
5770
+ bad: "bg-destructive"
5771
+ };
5600
5772
  function Meter({
5601
5773
  value,
5602
5774
  max,
@@ -5631,7 +5803,7 @@ function Meter({
5631
5803
  /* @__PURE__ */ jsxRuntime.jsx(
5632
5804
  "div",
5633
5805
  {
5634
- className: cn("h-full shrink-0 rounded-full", tone === "brand" ? "bg-oca-orange-deep" : "bg-foreground"),
5806
+ className: cn("h-full shrink-0 rounded-full", METER_FILL[tone]),
5635
5807
  style: { width: `${v * 100}%` }
5636
5808
  }
5637
5809
  ),
@@ -5878,48 +6050,106 @@ function SectionSwitch({
5878
6050
  }
5879
6051
  );
5880
6052
  }
6053
+ function NavBadge({ badge, className }) {
6054
+ if (typeof badge === "number") {
6055
+ return /* @__PURE__ */ jsxRuntime.jsx(
6056
+ "span",
6057
+ {
6058
+ "aria-hidden": true,
6059
+ className: cn(
6060
+ "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",
6061
+ className
6062
+ ),
6063
+ children: badge > 99 ? "99+" : badge
6064
+ }
6065
+ );
6066
+ }
6067
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: cn("absolute size-2 rounded-full bg-oca-orange-deep ring-2 ring-background", className) });
6068
+ }
6069
+ 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";
6070
+ function TabFace({ icon, label, badge, active }) {
6071
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6072
+ /* @__PURE__ */ jsxRuntime.jsxs(
6073
+ "span",
6074
+ {
6075
+ className: cn(
6076
+ "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
6077
+ "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
6078
+ active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
6079
+ ),
6080
+ children: [
6081
+ icon,
6082
+ badge ? /* @__PURE__ */ jsxRuntime.jsx(NavBadge, { badge, className: typeof badge === "number" ? "-top-1 right-0.5" : "right-2.5 top-0.5" }) : null
6083
+ ]
6084
+ }
6085
+ ),
6086
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate px-0.5", children: label }),
6087
+ badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: badge }) : null
6088
+ ] });
6089
+ }
5881
6090
  function TabBar({ items }) {
6091
+ const [more, setMore] = react.useState(false);
6092
+ const overflow = items.length > 5;
6093
+ const shown = overflow ? items.slice(0, 4) : items;
6094
+ const rest = overflow ? items.slice(4) : [];
6095
+ const restBadge = rest.find((it) => it.badge)?.badge;
5882
6096
  return /* @__PURE__ */ jsxRuntime.jsx(
5883
6097
  "nav",
5884
6098
  {
5885
6099
  "aria-label": "Pages",
5886
6100
  "data-oca-tabbar": "",
5887
6101
  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",
5888
- children: /* @__PURE__ */ jsxRuntime.jsx(
6102
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
5889
6103
  "div",
5890
6104
  {
5891
6105
  className: "mx-auto grid h-14 max-w-[32rem] px-1.5",
5892
- style: { gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` },
5893
- children: items.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
5894
- Link6__default.default,
5895
- {
5896
- href: it.href,
5897
- "aria-current": it.active ? "page" : void 0,
5898
- className: cn(
5899
- "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",
5900
- it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"
6106
+ style: { gridTemplateColumns: `repeat(${shown.length + (overflow ? 1 : 0)}, minmax(0, 1fr))` },
6107
+ children: [
6108
+ shown.map((it) => /* @__PURE__ */ jsxRuntime.jsx(
6109
+ Link6__default.default,
6110
+ {
6111
+ href: it.href,
6112
+ "aria-current": it.active ? "page" : void 0,
6113
+ className: cn(TAB, it.active ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
6114
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabFace, { icon: it.icon, label: it.label, badge: it.badge, active: it.active })
6115
+ },
6116
+ it.href
6117
+ )),
6118
+ overflow ? /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open: more, onOpenChange: setMore, children: [
6119
+ /* @__PURE__ */ jsxRuntime.jsx(
6120
+ SheetTrigger,
6121
+ {
6122
+ className: cn(TAB, rest.some((it) => it.active) ? "text-foreground" : "text-muted-foreground hover:text-foreground"),
6123
+ children: /* @__PURE__ */ jsxRuntime.jsx(TabFace, { icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ellipsis, {}), label: "More", badge: restBadge ? true : void 0, active: rest.some((it) => it.active) })
6124
+ }
5901
6125
  ),
5902
- children: [
5903
- /* @__PURE__ */ jsxRuntime.jsxs(
5904
- "span",
6126
+ /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "bottom", className: "pb-[calc(1.5rem+env(safe-area-inset-bottom))]", children: [
6127
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "px-1 text-[1.0625rem] font-medium", children: "More" }),
6128
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 grid grid-cols-3 gap-2", children: rest.map((it) => /* @__PURE__ */ jsxRuntime.jsxs(
6129
+ Link6__default.default,
5905
6130
  {
6131
+ href: it.href,
6132
+ onClick: () => setMore(false),
6133
+ "aria-current": it.active ? "page" : void 0,
5906
6134
  className: cn(
5907
- "relative flex h-7 w-12 items-center justify-center rounded-full transition-[background-color,box-shadow] duration-200",
5908
- "[&_svg]:size-[1.125rem] [&_svg]:shrink-0",
5909
- it.active ? "bg-card shadow-sm ring-1 ring-border [&_svg]:stroke-2" : "[&_svg]:stroke-[1.75]"
6135
+ "flex min-h-20 flex-col items-center justify-center gap-2 rounded-2xl border px-2 text-center text-[0.8125rem] transition-colors",
6136
+ "[&_svg]:size-5",
6137
+ it.active ? "border-foreground bg-card text-foreground" : "border-border text-muted-foreground hover:text-foreground"
5910
6138
  ),
5911
6139
  children: [
5912
- it.icon,
5913
- it.badge ? /* @__PURE__ */ jsxRuntime.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
6140
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative", children: [
6141
+ it.icon,
6142
+ it.badge ? /* @__PURE__ */ jsxRuntime.jsx(NavBadge, { badge: it.badge, className: typeof it.badge === "number" ? "-right-3 -top-2" : "-right-1 -top-0.5" }) : null
6143
+ ] }),
6144
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate", children: it.label }),
6145
+ it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: it.badge }) : null
5914
6146
  ]
5915
- }
5916
- ),
5917
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-full truncate px-0.5", children: it.label }),
5918
- it.badge ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: it.badge }) : null
5919
- ]
5920
- },
5921
- it.href
5922
- ))
6147
+ },
6148
+ it.href
6149
+ )) })
6150
+ ] })
6151
+ ] }) : null
6152
+ ]
5923
6153
  }
5924
6154
  )
5925
6155
  }
@@ -5927,7 +6157,11 @@ function TabBar({ items }) {
5927
6157
  }
5928
6158
  function tabsFor(groups) {
5929
6159
  const items = groups.flatMap((g) => g.items);
5930
- return items.length >= 2 && items.length <= 5 && items.every((it) => it.icon) ? items : null;
6160
+ return items.length >= 2 && items.every((it) => it.icon) ? items : null;
6161
+ }
6162
+ function sectionTabs(sections, current2) {
6163
+ if (!sections || !current2 || sections.length < 2 || !sections.every((sec) => sec.icon)) return null;
6164
+ return sections.map((sec) => ({ href: sec.href, label: sec.label, icon: sec.icon, active: sec.id === current2.id }));
5931
6165
  }
5932
6166
  var activeItem = (groups) => groups.flatMap((g) => g.items).find((it) => it.active);
5933
6167
  var warned = /* @__PURE__ */ new Set();
@@ -5942,7 +6176,10 @@ function warnDuplicateLabels(groups) {
5942
6176
  seen.add(it.label);
5943
6177
  }
5944
6178
  }
5945
- function AppShell({
6179
+ function AppShell(props) {
6180
+ return props.variant === "rail" ? /* @__PURE__ */ jsxRuntime.jsx(RailShell, { ...props }) : /* @__PURE__ */ jsxRuntime.jsx(SidebarShell, { ...props });
6181
+ }
6182
+ function SidebarShell({
5946
6183
  nav,
5947
6184
  sections,
5948
6185
  activeSection,
@@ -5960,7 +6197,8 @@ function AppShell({
5960
6197
  const current2 = sections && sections.length > 0 ? sections.find((sec) => sec.id === activeSection) ?? sections.find((sec) => activeItem(sec.groups)) ?? sections[0] : void 0;
5961
6198
  const groups = current2 ? [...current2.groups, ...persistent] : [...nav ?? [], ...persistent];
5962
6199
  warnDuplicateLabels(groups);
5963
- const tabs = current2 ? null : tabsFor(groups);
6200
+ const sectionTabsFor = sectionTabs(sections, current2);
6201
+ const tabs = current2 ? sectionTabsFor : tabsFor(groups);
5964
6202
  const page = activeItem(groups);
5965
6203
  const place = react.useMemo(
5966
6204
  () => ({
@@ -5988,8 +6226,8 @@ function AppShell({
5988
6226
  {
5989
6227
  className: cn(
5990
6228
  "inline-flex size-10 items-center justify-center rounded-full hover:bg-accent",
5991
- // The tab bar holds every page; the sheet is left for a footer.
5992
- tabs && !footer && "max-md:hidden"
6229
+ // The tab bar holds every page; the sheet is left for a footer. Section tabs leave the pages here.
6230
+ tabs && !sectionTabsFor && !footer && "max-md:hidden"
5993
6231
  ),
5994
6232
  "aria-label": "Open navigation",
5995
6233
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { className: "size-5" })
@@ -5997,8 +6235,8 @@ function AppShell({
5997
6235
  ),
5998
6236
  /* @__PURE__ */ jsxRuntime.jsxs(SheetContent, { side: "left", className: "flex flex-col gap-6 overflow-y-auto p-4 pt-16", children: [
5999
6237
  /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: "Navigation" }),
6000
- switcher,
6001
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: tabs ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
6238
+ switcher ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: sectionTabsFor ? "max-md:hidden" : void 0, children: switcher }) : null,
6239
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: tabs && !sectionTabsFor ? "max-md:hidden" : void 0, children: /* @__PURE__ */ jsxRuntime.jsx(SideNav, { groups, onNavigate: () => setOpen(false) }) }),
6002
6240
  footer ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-auto flex flex-col gap-3", children: footer }) : null
6003
6241
  ] })
6004
6242
  ] }),
@@ -6012,7 +6250,7 @@ function AppShell({
6012
6250
  className: cn(
6013
6251
  "flex h-9 items-center gap-1.5 border-t border-border px-4 text-[0.875rem]",
6014
6252
  // The active tab already names the page.
6015
- tabs && !pageOverride && "max-md:hidden"
6253
+ tabs && !sectionTabsFor && !pageOverride && "max-md:hidden"
6016
6254
  ),
6017
6255
  children: [
6018
6256
  place.section ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -6045,6 +6283,64 @@ function AppShell({
6045
6283
  }
6046
6284
  ) });
6047
6285
  }
6286
+ function RailItem({ item }) {
6287
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
6288
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
6289
+ Link6__default.default,
6290
+ {
6291
+ href: item.href,
6292
+ "aria-current": item.active ? "page" : void 0,
6293
+ className: cn(
6294
+ "relative flex size-10 items-center justify-center rounded-xl outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring",
6295
+ "[&_svg]:size-5 [&_svg]:shrink-0",
6296
+ item.active ? "bg-card text-foreground shadow-sm ring-1 ring-border" : "text-muted-foreground hover:bg-accent hover:text-foreground"
6297
+ ),
6298
+ children: [
6299
+ item.icon,
6300
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "sr-only", children: [
6301
+ item.label,
6302
+ item.badge ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
6303
+ ", ",
6304
+ item.badge
6305
+ ] }) : null
6306
+ ] }),
6307
+ item.badge ? /* @__PURE__ */ jsxRuntime.jsx(NavBadge, { badge: item.badge, className: typeof item.badge === "number" ? "-right-1 -top-1" : "right-1 top-1" }) : null
6308
+ ]
6309
+ }
6310
+ ) }),
6311
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: "right", children: item.label })
6312
+ ] });
6313
+ }
6314
+ function RailShell({ nav = [], persistent = [], page: pageOverride, header, account, banner, children, className }) {
6315
+ const main = nav.flatMap((g) => g.items);
6316
+ const foot = persistent.flatMap((g) => g.items);
6317
+ warnDuplicateLabels([...nav, ...persistent]);
6318
+ const tabs = tabsFor([{ items: [...main, ...foot] }]);
6319
+ const active = activeItem([...nav, ...persistent]);
6320
+ const place = react.useMemo(() => ({ page: pageOverride ?? active?.label }), [pageOverride, active?.label]);
6321
+ return /* @__PURE__ */ jsxRuntime.jsx(ShellContext.Provider, { value: place, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex h-dvh flex-col overflow-hidden bg-background", className), children: [
6322
+ header,
6323
+ banner,
6324
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1", children: [
6325
+ /* @__PURE__ */ jsxRuntime.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: [
6326
+ main.map((it) => /* @__PURE__ */ jsxRuntime.jsx(RailItem, { item: it }, it.href)),
6327
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-auto flex flex-col items-center gap-1", children: [
6328
+ foot.map((it) => /* @__PURE__ */ jsxRuntime.jsx(RailItem, { item: it }, it.href)),
6329
+ account
6330
+ ] })
6331
+ ] }),
6332
+ /* @__PURE__ */ jsxRuntime.jsx(
6333
+ "main",
6334
+ {
6335
+ id: "main-content",
6336
+ 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))]"),
6337
+ children
6338
+ }
6339
+ )
6340
+ ] }),
6341
+ tabs ? /* @__PURE__ */ jsxRuntime.jsx(TabBar, { items: tabs }) : null
6342
+ ] }) }) });
6343
+ }
6048
6344
  function AccountMenu({
6049
6345
  name,
6050
6346
  detail,
@@ -6989,9 +7285,11 @@ exports.Checkbox = Checkbox;
6989
7285
  exports.Closing = Closing;
6990
7286
  exports.CodeBlock = CodeBlock;
6991
7287
  exports.CodeTabs = CodeTabs;
7288
+ exports.CommandDialog = CommandDialog;
6992
7289
  exports.ComparisonTable = ComparisonTable;
6993
7290
  exports.Container = Container;
6994
7291
  exports.CopyButton = CopyButton;
7292
+ exports.CountdownRing = CountdownRing;
6995
7293
  exports.Crowd = Crowd;
6996
7294
  exports.DOCS_HEADER_NAV = DOCS_HEADER_NAV;
6997
7295
  exports.DOCS_HREF = DOCS_HREF;
@@ -7053,6 +7351,7 @@ exports.Lead = Lead;
7053
7351
  exports.LegalDocument = LegalDocument;
7054
7352
  exports.LinkCard = LinkCard;
7055
7353
  exports.LoadingBlock = LoadingBlock;
7354
+ exports.LockedPreview = LockedPreview;
7056
7355
  exports.Lockup = Lockup;
7057
7356
  exports.MARK_FACES = MARK_FACES;
7058
7357
  exports.Mark = Mark;
@@ -7073,6 +7372,7 @@ exports.PageHeader = PageHeader;
7073
7372
  exports.PageHero = PageHero;
7074
7373
  exports.Pagination = Pagination;
7075
7374
  exports.PhoneFrame = PhoneFrame;
7375
+ exports.PlanCard = PlanCard;
7076
7376
  exports.Popover = Popover;
7077
7377
  exports.PopoverAnchor = PopoverAnchor;
7078
7378
  exports.PopoverContent = PopoverContent;