@stll/ui 0.45.0 → 0.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +2 -1
  2. package/dist/components/ai-label.d.ts +33 -0
  3. package/dist/components/ai-label.js +42 -0
  4. package/dist/components/alert-dialog.js +3 -2
  5. package/dist/components/animated-count.d.ts +13 -0
  6. package/dist/components/animated-count.js +63 -0
  7. package/dist/components/application-rail.js +1 -1
  8. package/dist/components/brand-icons.d.ts +1 -0
  9. package/dist/components/brand-icons.js +8 -1
  10. package/dist/components/button.d.ts +2 -2
  11. package/dist/components/button.js +12 -2
  12. package/dist/components/command.js +1 -1
  13. package/dist/components/dialog.js +3 -2
  14. package/dist/components/document-identity-badge.d.ts +1 -1
  15. package/dist/components/input-group.d.ts +1 -1
  16. package/dist/components/input-otp.js +1 -1
  17. package/dist/components/landing.js +1 -1
  18. package/dist/components/list.d.ts +8 -2
  19. package/dist/components/list.js +10 -4
  20. package/dist/components/menu.js +1 -1
  21. package/dist/components/popover.js +1 -1
  22. package/dist/components/select.js +1 -1
  23. package/dist/components/sheet.js +19 -2
  24. package/dist/components/sidebar.d.ts +1 -1
  25. package/dist/components/sidebar.js +8 -8
  26. package/dist/components/status-mark.d.ts +17 -0
  27. package/dist/components/status-mark.js +74 -0
  28. package/dist/components/swap-text.d.ts +16 -0
  29. package/dist/components/swap-text.js +52 -0
  30. package/dist/components/table-chart.d.ts +20 -0
  31. package/dist/components/table-chart.js +274 -0
  32. package/dist/components/tabs.js +1 -1
  33. package/dist/components/toast.js +3 -2
  34. package/dist/components/workspace-shell.js +5 -4
  35. package/dist/icons.js +1 -2
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.js +2 -2
  38. package/dist/inspector/dock.js +1 -1
  39. package/dist/kanban/virtual-cell.js +1 -1
  40. package/dist/lib/safe-area.d.ts +11 -0
  41. package/dist/lib/safe-area.js +12 -0
  42. package/dist/styles/theme.css +79 -0
  43. package/package.json +28 -4
package/README.md CHANGED
@@ -213,7 +213,8 @@ column and keep their trailing column, so they never scroll sideways.
213
213
  <ListGroupHeader>
214
214
  <ListGroupHeading>
215
215
  <ListGroupTitle id="apps">
216
- Apps with access<ListGroupCount>3</ListGroupCount>
216
+ Apps with access
217
+ <ListGroupCount count="3" />
217
218
  </ListGroupTitle>
218
219
  <ListGroupDescription>Apps you authorized.</ListGroupDescription>
219
220
  </ListGroupHeading>
@@ -0,0 +1,33 @@
1
+ //#region src/components/ai-label.d.ts
2
+ type AiLabelProps = {
3
+ /**
4
+ * The words the mark shows ("AI", "AI topics"), already translated. They
5
+ * are also the control's accessible name, so what a sighted reader sees is
6
+ * what a screen-reader user hears.
7
+ */
8
+ label: string;
9
+ /**
10
+ * What the reader is told about the content the mark stands beside, already
11
+ * translated. Hosts pass the sentence for one closed disclosure context
12
+ * (`@stll/api-contract/ai-disclosure`), never free text.
13
+ */
14
+ disclosure: string;
15
+ className?: string | undefined;
16
+ };
17
+ /**
18
+ * The mark on content a model wrote, and the one place a reader learns what
19
+ * that means.
20
+ *
21
+ * It keeps the weight of the plain label it replaces: the surrounding text's
22
+ * size, colour, case and tracking, a small sparkle, no chip and no tint. The
23
+ * only addition is that it is a button: a click, a tap, Enter or Space opens
24
+ * a small popover with the disclosure, and Escape or a click outside closes
25
+ * it. Nothing depends on hover.
26
+ *
27
+ * The trigger stays inline, so it can sit inside a heading or a line of
28
+ * text. Never inside another control (a `<summary>`, a link, a button): it is
29
+ * one itself, and nested interactive content is unreachable to assistive
30
+ * technology.
31
+ */
32
+ export declare const AiLabel: ({ label, disclosure, className }: AiLabelProps) => import("react").JSX.Element;
33
+ //#endregion
@@ -0,0 +1,42 @@
1
+ "use client";
2
+ import { SparklesIcon } from "../icons.js";
3
+ import { cn } from "../lib/utils.js";
4
+ import { Popover, PopoverContent as PopoverPopup, PopoverDescription, PopoverTrigger } from "./popover.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/ai-label.tsx
7
+ /**
8
+ * The mark on content a model wrote, and the one place a reader learns what
9
+ * that means.
10
+ *
11
+ * It keeps the weight of the plain label it replaces: the surrounding text's
12
+ * size, colour, case and tracking, a small sparkle, no chip and no tint. The
13
+ * only addition is that it is a button: a click, a tap, Enter or Space opens
14
+ * a small popover with the disclosure, and Escape or a click outside closes
15
+ * it. Nothing depends on hover.
16
+ *
17
+ * The trigger stays inline, so it can sit inside a heading or a line of
18
+ * text. Never inside another control (a `<summary>`, a link, a button): it is
19
+ * one itself, and nested interactive content is unreachable to assistive
20
+ * technology.
21
+ */
22
+ const AiLabel = ({ label, disclosure, className }) => /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
23
+ className: cn("focus-visible:ring-ring relative inline-flex cursor-pointer items-center justify-center gap-1 rounded-sm align-middle tracking-[inherit] text-inherit [text-transform:inherit] decoration-dotted underline-offset-2 outline-none [font:inherit] hover:underline focus-visible:ring-2 data-popup-open:underline pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11", className),
24
+ "data-slot": "ai-label",
25
+ type: "button",
26
+ children: [/* @__PURE__ */ jsx(SparklesIcon, {
27
+ "aria-hidden": true,
28
+ className: "size-3 shrink-0"
29
+ }), label]
30
+ }), /* @__PURE__ */ jsx(PopoverPopup, {
31
+ "aria-label": label,
32
+ className: "max-w-72",
33
+ "data-slot": "ai-label-popup",
34
+ padding: "sm",
35
+ side: "bottom",
36
+ children: /* @__PURE__ */ jsx(PopoverDescription, {
37
+ className: "text-xs leading-relaxed text-pretty",
38
+ children: disclosure
39
+ })
40
+ })] });
41
+ //#endregion
42
+ export { AiLabel };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
4
+ import { SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME } from "../lib/safe-area.js";
4
5
  import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { AlertDialog as AlertDialog$1 } from "@base-ui/react/alert-dialog";
@@ -24,7 +25,7 @@ const AlertDialogBackdrop = ({ className, layer = "default", ...props }) => /* @
24
25
  ...props
25
26
  });
26
27
  const AlertDialogViewport = ({ className, layer = "default", ...props }) => /* @__PURE__ */ jsx(AlertDialog$1.Viewport, {
27
- className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center p-4", OVERLAY_LAYER_CLASS_NAMES[layer], className),
28
+ className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center", SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME, OVERLAY_LAYER_CLASS_NAMES[layer], className),
28
29
  "data-slot": "alert-dialog-viewport",
29
30
  ...props
30
31
  });
@@ -32,7 +33,7 @@ const AlertDialogPopup = ({ className, bottomStickOnMobile = true, layer = "defa
32
33
  className: cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12"),
33
34
  layer,
34
35
  children: /* @__PURE__ */ jsx(AlertDialog$1.Popup, {
35
- className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
36
+ className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-ending-style:duration-150 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:pb-(--safe-area-bottom) max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
36
37
  "data-slot": "alert-dialog-popup",
37
38
  ...props
38
39
  })
@@ -0,0 +1,13 @@
1
+ //#region src/components/animated-count.d.ts
2
+ /**
3
+ * A formatted number whose changed characters fade in. The caller formats
4
+ * (locale digits, units); the glyphs stay plain inline text so bidirectional
5
+ * ordering is the browser's, and only opacity moves. The first render is
6
+ * still: a count that loads does not announce itself.
7
+ */
8
+ declare const AnimatedCount: ({ className, value }: {
9
+ className?: string;
10
+ value: string;
11
+ }) => import("react").JSX.Element;
12
+ //#endregion
13
+ export { AnimatedCount };
@@ -0,0 +1,63 @@
1
+ "use client";
2
+ import { cn } from "../lib/utils.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useState } from "react";
5
+ //#region src/components/animated-count.tsx
6
+ /**
7
+ * Aligns characters from the end, where digits keep their place value, so
8
+ * 99 → 100 changes every glyph but 120 → 121 only the last one. An unchanged
9
+ * glyph keeps its key and therefore its DOM node: it never replays its fade.
10
+ */
11
+ const toGlyphs = (value, previous, generation) => {
12
+ const chars = Array.from(value);
13
+ return chars.map((char, index) => {
14
+ const fromEnd = chars.length - index;
15
+ const priorIndex = previous === null ? -1 : previous.length - fromEnd;
16
+ const prior = priorIndex >= 0 ? previous?.[priorIndex] : void 0;
17
+ if (prior?.char === char) return prior;
18
+ return {
19
+ char,
20
+ enters: previous !== null,
21
+ key: `${generation}:${fromEnd}`
22
+ };
23
+ });
24
+ };
25
+ /**
26
+ * A formatted number whose changed characters fade in. The caller formats
27
+ * (locale digits, units); the glyphs stay plain inline text so bidirectional
28
+ * ordering is the browser's, and only opacity moves. The first render is
29
+ * still: a count that loads does not announce itself.
30
+ */
31
+ const AnimatedCount = ({ className, value }) => {
32
+ const [state, setState] = useState(() => ({
33
+ generation: 0,
34
+ glyphs: toGlyphs(value, null, 0),
35
+ value
36
+ }));
37
+ let { glyphs } = state;
38
+ if (state.value !== value) {
39
+ const generation = state.generation + 1;
40
+ glyphs = toGlyphs(value, state.glyphs, generation);
41
+ setState({
42
+ generation,
43
+ glyphs,
44
+ value
45
+ });
46
+ }
47
+ return /* @__PURE__ */ jsxs("span", {
48
+ className: cn("tabular-nums", className),
49
+ "data-slot": "animated-count",
50
+ children: [/* @__PURE__ */ jsx("span", {
51
+ className: "sr-only",
52
+ children: value
53
+ }), /* @__PURE__ */ jsx("span", {
54
+ "aria-hidden": "true",
55
+ children: glyphs.map((glyph) => /* @__PURE__ */ jsx("span", {
56
+ className: cn(glyph.enters && "motion-safe:animate-text-fade"),
57
+ children: glyph.char
58
+ }, glyph.key))
59
+ })]
60
+ });
61
+ };
62
+ //#endregion
63
+ export { AnimatedCount };
@@ -22,7 +22,7 @@ const ApplicationRailHeader = ({ className, ...props }) => /* @__PURE__ */ jsx("
22
22
  ...props
23
23
  });
24
24
  const ApplicationRailContent = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
25
- className: cn("flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-2 overflow-x-hidden overflow-y-auto [&::-webkit-scrollbar]:hidden", className),
25
+ className: cn("flex min-h-0 flex-1 [scrollbar-width:none] flex-col gap-2 overflow-x-hidden overflow-y-auto overscroll-contain [&::-webkit-scrollbar]:hidden", className),
26
26
  "data-slot": "application-rail-content",
27
27
  ...props
28
28
  });
@@ -3,4 +3,5 @@ import * as React$1 from "react";
3
3
  type BrandIconProps = React$1.ComponentProps<"svg">;
4
4
  export declare const DiscordLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
5
5
  export declare const GitHubLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
6
+ export declare const XLogoIcon: (props: BrandIconProps) => React$1.JSX.Element;
6
7
  //#endregion
@@ -14,5 +14,12 @@ const GitHubLogoIcon = (props) => /* @__PURE__ */ jsx("svg", {
14
14
  ...props,
15
15
  children: /* @__PURE__ */ jsx("path", { d: "M6.766 11.328c-2.063-.25-3.516-1.734-3.516-3.656 0-.781.281-1.625.75-2.188-.203-.515-.172-1.609.063-2.062.625-.078 1.468.25 1.968.703.594-.187 1.219-.281 1.985-.281.765 0 1.39.094 1.953.265.484-.437 1.344-.765 1.969-.687.218.422.25 1.515.046 2.047.5.593.766 1.39.766 2.203 0 1.922-1.453 3.375-3.547 3.64.531.344.89 1.094.89 1.954v1.625c0 .468.391.734.86.547C13.781 14.359 16 11.53 16 8.03 16 3.61 12.406 0 7.984 0 3.563 0 0 3.61 0 8.031a7.88 7.88 0 0 0 5.172 7.422c.422.156.828-.125.828-.547v-1.25c-.219.094-.5.156-.75.156-1.031 0-1.64-.562-2.078-1.609-.172-.422-.36-.672-.719-.719-.187-.015-.25-.093-.25-.187 0-.188.313-.328.625-.328.453 0 .844.281 1.25.86.313.452.64.655 1.031.655s.641-.14 1-.5c.266-.265.47-.5.657-.656" })
16
16
  });
17
+ const XLogoIcon = (props) => /* @__PURE__ */ jsx("svg", {
18
+ "aria-hidden": "true",
19
+ fill: "currentColor",
20
+ viewBox: "0 0 24 24",
21
+ ...props,
22
+ children: /* @__PURE__ */ jsx("path", { d: "M18.901 1.153h3.68l-8.04 9.19L24 22.846h-7.406l-5.8-7.584-6.64 7.584H.47l8.6-9.835L0 1.153h7.594l5.243 6.932 6.064-6.932Zm-1.29 19.49h2.039L6.486 3.24H4.298l13.313 17.403Z" })
23
+ });
17
24
  //#endregion
18
- export { DiscordLogoIcon, GitHubLogoIcon };
25
+ export { DiscordLogoIcon, GitHubLogoIcon, XLogoIcon };
@@ -13,8 +13,8 @@ import { VariantProps } from "class-variance-authority";
13
13
  */
14
14
  declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
15
15
  declare const buttonVariants: (props?: ({
16
- size?: "default" | "chip" | "icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "row" | "sm" | "xl" | "xs" | null | undefined;
17
- variant?: "link" | "default" | "destructive" | "destructive-outline" | "destructive-ghost" | "ghost" | "muted" | "outline" | "secondary" | null | undefined;
16
+ size?: "default" | "row" | "icon" | "chip" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "sm" | "xl" | "xs" | null | undefined;
17
+ variant?: "link" | "default" | "muted" | "destructive" | "destructive-outline" | "destructive-ghost" | "ghost" | "outline" | "secondary" | null | undefined;
18
18
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
19
19
  type ButtonProps = {
20
20
  variant?: VariantProps<typeof buttonVariants>["variant"];
@@ -19,7 +19,17 @@ import { cva } from "class-variance-authority";
19
19
  * swaps the cursor so the control still reads as unavailable on hover.
20
20
  */
21
21
  const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
22
- const buttonVariants = cva("focus-visible:ring-ring focus-visible:ring-offset-background relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-lg border text-base font-medium whitespace-nowrap outline-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] focus-visible:ring-2 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-64 sm:text-sm pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", {
22
+ /**
23
+ * Press feedback: the button gives slightly under the pointer.
24
+ *
25
+ * Applied by `Button` only while it can act, so a disabled or loading button
26
+ * never appears to respond. `scale` is its own property in Tailwind v4, so it
27
+ * composes with a caller's `translate` (a vertically centred icon button) and
28
+ * the transition names only the transform family. Menu rows and link-styled
29
+ * buttons keep the treatment of their surface instead.
30
+ */
31
+ const buttonPressClass = "motion-safe:transition-transform motion-safe:duration-150 ease-out motion-safe:active:scale-[0.98]";
32
+ const buttonVariants = cva("focus-visible:ring-ring focus-visible:ring-offset-background relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-lg border text-base font-medium whitespace-nowrap outline-none select-none before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] focus-visible:ring-2 focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-64 sm:text-sm pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11 [&_svg]:pointer-events-none [&_svg]:-mx-0.5 [&_svg]:shrink-0 [&_svg:not([class*='opacity-'])]:opacity-80 [&_svg:not([class*='size-'])]:size-4.5 sm:[&_svg:not([class*='size-'])]:size-4", {
23
33
  defaultVariants: {
24
34
  size: "default",
25
35
  variant: "default"
@@ -68,7 +78,7 @@ function Button({ className, variant, size, render, loading, children, disabled,
68
78
  size: "sm",
69
79
  variant: "decorative"
70
80
  }), children] }) : children,
71
- className: cn(buttonVariants({
81
+ className: cn(disposition === BUTTON_DISPOSITION.enabled && size !== "row" && variant !== "link" && buttonPressClass, buttonVariants({
72
82
  className,
73
83
  size,
74
84
  variant
@@ -66,7 +66,7 @@ const CommandEmpty = ({ className, ...props }) => /* @__PURE__ */ jsx(Autocomple
66
66
  ...props
67
67
  });
68
68
  const CommandList = ({ className, ...props }) => /* @__PURE__ */ jsx(Autocomplete.List, {
69
- className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto", className),
69
+ className: cn("max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto overscroll-contain", className),
70
70
  "data-slot": "command-list",
71
71
  ...props
72
72
  });
@@ -2,6 +2,7 @@
2
2
  import { XIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
5
+ import { SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME } from "../lib/safe-area.js";
5
6
  import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
6
7
  import { Button } from "./button.js";
7
8
  import { ScrollArea } from "./scroll-area.js";
@@ -86,7 +87,7 @@ const DialogBackdrop = ({ className, layer = "default", ...props }) => /* @__PUR
86
87
  ...props
87
88
  });
88
89
  const DialogViewport = ({ className, layer = "default", ...props }) => /* @__PURE__ */ jsx(Dialog$1.Viewport, {
89
- className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center p-4", OVERLAY_LAYER_CLASS_NAMES[layer], className),
90
+ className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center", SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME, OVERLAY_LAYER_CLASS_NAMES[layer], className),
90
91
  "data-slot": "dialog-viewport",
91
92
  ...props
92
93
  });
@@ -100,7 +101,7 @@ const DialogPopup = ({ backdropClassName, className, children, showCloseButton =
100
101
  className: cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12", viewportClassName),
101
102
  layer,
102
103
  children: /* @__PURE__ */ jsxs(Dialog$1.Popup, {
103
- className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
104
+ className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-ending-style:duration-150 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:pb-(--safe-area-bottom) max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
104
105
  "data-slot": "dialog-popup",
105
106
  ...props,
106
107
  children: [
@@ -4,7 +4,7 @@ import { DocumentIdentity } from "./document-identity-badge.logic.js";
4
4
  export declare const DocumentIdentityBadge: ({ identity, size, title }: DocumentIdentityBadgeProps) => import("react").JSX.Element;
5
5
  type DocumentIdentityBadgeProps = {
6
6
  identity: DocumentIdentity;
7
- /** Decision chips can fit an inline metadata row. */
7
+ /** Court chip size; statute labels follow their container width. */
8
8
  size?: CourtBadgeSize | undefined;
9
9
  /** Omit when the enclosing tab already supplies the full-title tooltip. */
10
10
  title?: string | undefined;
@@ -5,7 +5,7 @@ import { VariantProps } from "class-variance-authority";
5
5
  //#region src/components/input-group.d.ts
6
6
  declare const InputGroup: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
7
7
  declare const inputGroupAddonVariants: (props?: ({
8
- align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
8
+ align?: "inline-end" | "inline-start" | "block-start" | "block-end" | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
10
  declare const InputGroupAddon: ({ className, align, ...props }: React$1.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) => React$1.JSX.Element;
11
11
  declare const InputGroupText: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
@@ -25,7 +25,7 @@ const InputOTPSlot = ({ index, className, ...props }) => {
25
25
  ...props,
26
26
  children: [char, hasFakeCaret && /* @__PURE__ */ jsx("div", {
27
27
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
28
- children: /* @__PURE__ */ jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px duration-1000" })
28
+ children: /* @__PURE__ */ jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px" })
29
29
  })]
30
30
  });
31
31
  };
@@ -22,7 +22,7 @@ const LandingLayout = ({ actions, hero, children, footer }) => /* @__PURE__ */ j
22
22
  children
23
23
  }),
24
24
  footer !== void 0 && /* @__PURE__ */ jsx(ScrollArea, {
25
- className: "h-auto max-h-[40vh] w-full shrink-0",
25
+ className: "h-auto max-h-[40dvh] w-full shrink-0",
26
26
  scrollFade: true,
27
27
  children: footer
28
28
  })
@@ -15,8 +15,14 @@ declare const ListGroupHeader: ({ className, ...props }: React$1.ComponentProps<
15
15
  /** Title and description stack; takes the space the header action leaves. */
16
16
  declare const ListGroupHeading: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
17
17
  declare const ListGroupTitle: ({ className, children, ...props }: React$1.ComponentProps<"h2">) => React$1.JSX.Element;
18
- /** Quiet item count after the title; shows matches while a search filters. */
19
- declare const ListGroupCount: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
18
+ /**
19
+ * Quiet item count after the title; shows matches while a search filters,
20
+ * and a changed digit fades in rather than snapping. `count` arrives
21
+ * formatted in the caller's locale.
22
+ */
23
+ declare const ListGroupCount: ({ className, count, ...props }: Omit<React$1.ComponentProps<"span">, "children"> & {
24
+ count: string;
25
+ }) => React$1.JSX.Element;
20
26
  declare const ListGroupDescription: ({ className, ...props }: React$1.ComponentProps<"p">) => React$1.JSX.Element;
21
27
  /** Quiet trailing affordance on the header row (a link or ghost button). */
22
28
  declare const ListGroupAction: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
+ import { AnimatedCount } from "./animated-count.js";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  import { mergeProps } from "@base-ui/react/merge-props";
5
6
  import { useRender } from "@base-ui/react/use-render";
@@ -35,11 +36,16 @@ const ListGroupTitle = ({ className, children, ...props }) => /* @__PURE__ */ js
35
36
  ...props,
36
37
  children
37
38
  });
38
- /** Quiet item count after the title; shows matches while a search filters. */
39
- const ListGroupCount = ({ className, ...props }) => /* @__PURE__ */ jsx("span", {
40
- className: cn("text-muted-foreground ms-1.5 font-normal tabular-nums", className),
39
+ /**
40
+ * Quiet item count after the title; shows matches while a search filters,
41
+ * and a changed digit fades in rather than snapping. `count` arrives
42
+ * formatted in the caller's locale.
43
+ */
44
+ const ListGroupCount = ({ className, count, ...props }) => /* @__PURE__ */ jsx("span", {
45
+ className: cn("text-muted-foreground ms-1.5 font-normal", className),
41
46
  "data-slot": "list-group-count",
42
- ...props
47
+ ...props,
48
+ children: /* @__PURE__ */ jsx(AnimatedCount, { value: count })
43
49
  });
44
50
  const ListGroupDescription = ({ className, ...props }) => /* @__PURE__ */ jsx("p", {
45
51
  className: cn("text-muted-foreground text-xs text-pretty", className),
@@ -34,7 +34,7 @@ const MenuPopup = ({ children, className, sideOffset = 4, align, alignOffset, si
34
34
  "data-slot": "menu-popup",
35
35
  ...props,
36
36
  children: [header, /* @__PURE__ */ jsx("div", {
37
- className: cn("min-h-0 w-full flex-1 overflow-y-auto p-1", viewportClassName),
37
+ className: cn("min-h-0 w-full flex-1 overflow-y-auto overscroll-contain p-1", viewportClassName),
38
38
  children
39
39
  })]
40
40
  })
@@ -42,7 +42,7 @@ const PopoverPopup = ({ children, className, side = "bottom", align = "center",
42
42
  "data-slot": "popover-popup",
43
43
  ...props,
44
44
  children: /* @__PURE__ */ jsx(Popover$1.Viewport, {
45
- className: cn("relative size-full max-h-(--available-height) overflow-clip px-(--viewport-inline-padding) outline-none **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-current:opacity-100 **:data-current:transition-opacity **:data-current:data-ending-style:opacity-0 data-instant:transition-none **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:opacity-100 **:data-previous:transition-opacity **:data-previous:data-ending-style:opacity-0 **:data-current:data-starting-style:opacity-0 **:data-previous:data-starting-style:opacity-0", tooltipStyle ? "py-1 [--viewport-inline-padding:--spacing(2)]" : [POPOVER_PADDING[padding], "not-data-transitioning:overflow-y-auto"]),
45
+ className: cn("relative size-full max-h-(--available-height) overflow-clip px-(--viewport-inline-padding) outline-none **:data-current:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-current:opacity-100 **:data-current:transition-opacity **:data-current:data-ending-style:opacity-0 data-instant:transition-none **:data-previous:w-[calc(var(--popup-width)-2*var(--viewport-inline-padding)-2px)] **:data-previous:opacity-100 **:data-previous:transition-opacity **:data-previous:data-ending-style:opacity-0 **:data-current:data-starting-style:opacity-0 **:data-previous:data-starting-style:opacity-0", tooltipStyle ? "py-1 [--viewport-inline-padding:--spacing(2)]" : [POPOVER_PADDING[padding], "overscroll-contain not-data-transitioning:overflow-y-auto"]),
46
46
  "data-slot": "popover-viewport",
47
47
  children
48
48
  })
@@ -83,7 +83,7 @@ const SelectPopup = ({ className, children, side = "bottom", sideOffset = 4, ali
83
83
  /* @__PURE__ */ jsx("div", {
84
84
  className: "bg-popover text-popover-foreground relative h-full min-w-(--anchor-width) rounded-lg border shadow-lg/5 not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] dark:before:shadow-[0_-1px_--theme(--color-white/6%)]",
85
85
  children: /* @__PURE__ */ jsx(Select$1.List, {
86
- className: cn("max-h-(--available-height) overflow-y-auto p-1", className),
86
+ className: cn("max-h-(--available-height) overflow-y-auto overscroll-contain p-1", className),
87
87
  "data-slot": "select-list",
88
88
  children
89
89
  })
@@ -2,6 +2,7 @@
2
2
  import { XIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
5
+ import "../lib/safe-area.js";
5
6
  import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
6
7
  import { Button } from "./button.js";
7
8
  import { ScrollArea } from "./scroll-area.js";
@@ -42,12 +43,28 @@ const SheetPopup = ({ className, children, showCloseButton = true, side = "inlin
42
43
  side,
43
44
  variant,
44
45
  children: /* @__PURE__ */ jsxs(Dialog.Popup, {
45
- className: cn("bg-popover text-popover-foreground relative flex max-h-full min-h-0 w-full min-w-0 flex-col shadow-lg/5 transition-[opacity,translate] duration-200 ease-in-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:opacity-0 data-starting-style:opacity-0 max-sm:before:hidden dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", side === "block-end" && "row-start-2 border-t data-ending-style:translate-y-8 data-starting-style:translate-y-8", side === "block-start" && "border-b data-ending-style:-translate-y-8 data-starting-style:-translate-y-8", side === "inline-start" && "w-[calc(100%-(--spacing(12)))] max-w-md border-e data-ending-style:-translate-x-8 data-starting-style:-translate-x-8 rtl:data-ending-style:translate-x-8 rtl:data-starting-style:translate-x-8", side === "inline-end" && "w-[calc(100%-(--spacing(12)))] max-w-md border-s data-ending-style:translate-x-8 data-starting-style:translate-x-8 rtl:data-ending-style:-translate-x-8 rtl:data-starting-style:-translate-x-8", variant === "inset" && "before:hidden sm:rounded-2xl sm:border sm:before:rounded-[calc(var(--radius-2xl)-1px)] sm:**:data-[slot=sheet-footer]:rounded-b-[calc(var(--radius-2xl)-1px)]", className),
46
+ className: cn("bg-popover text-popover-foreground relative flex max-h-full min-h-0 w-full min-w-0 flex-col shadow-lg/5 transition-[opacity,translate] duration-200 ease-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:opacity-0 data-ending-style:duration-150 data-starting-style:opacity-0 max-sm:before:hidden dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", side === "block-end" && [
47
+ "row-start-2 border-t data-ending-style:translate-y-8 data-starting-style:translate-y-8",
48
+ "pb-(--safe-area-bottom)",
49
+ "ps-(--safe-area-inline-start) pe-(--safe-area-inline-end)"
50
+ ], side === "block-start" && [
51
+ "border-b data-ending-style:-translate-y-8 data-starting-style:-translate-y-8",
52
+ "pt-(--safe-area-top)",
53
+ "ps-(--safe-area-inline-start) pe-(--safe-area-inline-end)"
54
+ ], side === "inline-start" && [
55
+ "w-[calc(100%-(--spacing(12)))] max-w-md border-e ps-(--safe-area-inline-start) data-ending-style:-translate-x-8 data-starting-style:-translate-x-8 rtl:data-ending-style:translate-x-8 rtl:data-starting-style:translate-x-8 [&.w-full.max-w-none]:pe-(--safe-area-inline-end)",
56
+ "pt-(--safe-area-top)",
57
+ "pb-(--safe-area-bottom)"
58
+ ], side === "inline-end" && [
59
+ "w-[calc(100%-(--spacing(12)))] max-w-md border-s pe-(--safe-area-inline-end) data-ending-style:translate-x-8 data-starting-style:translate-x-8 rtl:data-ending-style:-translate-x-8 rtl:data-starting-style:-translate-x-8 [&.w-full.max-w-none]:ps-(--safe-area-inline-start)",
60
+ "pt-(--safe-area-top)",
61
+ "pb-(--safe-area-bottom)"
62
+ ], variant === "inset" && "before:hidden sm:rounded-2xl sm:border sm:before:rounded-[calc(var(--radius-2xl)-1px)] sm:**:data-[slot=sheet-footer]:rounded-b-[calc(var(--radius-2xl)-1px)]", className),
46
63
  "data-slot": "sheet-popup",
47
64
  ...props,
48
65
  children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog.Close, {
49
66
  "aria-label": "Close",
50
- className: "absolute end-2 top-2",
67
+ className: cn("absolute end-[calc(--spacing(2)+var(--safe-area-inline-end))] top-2", side !== "block-end" && "top-[calc(--spacing(2)+var(--safe-area-top))]"),
51
68
  render: /* @__PURE__ */ jsx(Button, {
52
69
  size: "icon",
53
70
  variant: "ghost"
@@ -33,7 +33,7 @@ declare function useSidebarLayout(): SidebarLayout;
33
33
  * left for the content column. Mobile reports 0: there the sidebar is an
34
34
  * overlay sheet and occupies no layout width.
35
35
  */
36
- declare function useSidebarInlineSize(): 0 | 48 | 256;
36
+ declare function useSidebarInlineSize(): 0 | 256 | 48;
37
37
  declare const SidebarProvider: ({ defaultOpen, forceCollapsed, open: openProp, onOpenChange: setOpenProp, className, style, children, ...props }: React.ComponentProps<"div"> & {
38
38
  defaultOpen?: boolean;
39
39
  forceCollapsed?: boolean;
@@ -118,7 +118,7 @@ const Sidebar = ({ side = "left", variant = "sidebar", collapsible = "offcanvas"
118
118
  open: openMobile,
119
119
  ...props,
120
120
  children: /* @__PURE__ */ jsxs(SheetPopup, {
121
- className: "bg-sidebar text-sidebar-foreground w-(--sidebar-width) p-0 [&>button]:hidden",
121
+ className: "bg-sidebar text-sidebar-foreground w-(--sidebar-width) [&>button]:hidden",
122
122
  "data-mobile": "true",
123
123
  "data-sidebar": "sidebar",
124
124
  "data-slot": "sidebar",
@@ -135,17 +135,17 @@ const Sidebar = ({ side = "left", variant = "sidebar", collapsible = "offcanvas"
135
135
  });
136
136
  }
137
137
  return /* @__PURE__ */ jsxs("div", {
138
- className: "group peer text-sidebar-foreground hidden md:block",
138
+ className: cn("group peer text-sidebar-foreground hidden [--sidebar-inline-size:calc(var(--sidebar-width)+var(--sidebar-safe-area-inline))] md:block", side === "left" ? "[--sidebar-safe-area-inline:var(--safe-area-inline-start)]" : "[--sidebar-safe-area-inline:var(--safe-area-inline-end)]", variant === "floating" || variant === "inset" ? "data-[collapsible=icon]:[--sidebar-inline-size:calc(var(--sidebar-width-icon)+--spacing(4)+var(--sidebar-safe-area-inline))]" : "data-[collapsible=icon]:[--sidebar-inline-size:calc(var(--sidebar-width-icon)+var(--sidebar-safe-area-inline))]"),
139
139
  "data-collapsible": state === "collapsed" ? collapsible : "",
140
140
  "data-side": side,
141
141
  "data-slot": "sidebar",
142
142
  "data-state": state,
143
143
  "data-variant": variant,
144
144
  children: [/* @__PURE__ */ jsx("div", {
145
- className: cn("relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180", variant === "floating" || variant === "inset" ? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon)"),
145
+ className: cn("relative w-(--sidebar-inline-size) bg-transparent transition-[width] duration-200 ease-in-out", "group-data-[collapsible=offcanvas]:w-0", "group-data-[side=right]:rotate-180"),
146
146
  "data-slot": "sidebar-gap"
147
147
  }), /* @__PURE__ */ jsx("div", {
148
- className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[inset-inline-start,inset-inline-end,width] duration-200 ease-linear md:flex", side === "left" ? "start-0 group-data-[collapsible=offcanvas]:start-[calc(var(--sidebar-width)*-1)]" : "end-0 group-data-[collapsible=offcanvas]:end-[calc(var(--sidebar-width)*-1)]", variant === "floating" || variant === "inset" ? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]" : "group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-e group-data-[side=right]:border-s", className),
148
+ className: cn("fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-inline-size) transition-[inset-inline-start,inset-inline-end,width] duration-200 ease-in-out md:flex", "pt-(--safe-area-top) pb-(--safe-area-bottom)", side === "left" ? "start-0 ps-(--safe-area-inline-start) group-data-[collapsible=offcanvas]:start-[calc(var(--sidebar-inline-size)*-1)]" : "end-0 pe-(--safe-area-inline-end) group-data-[collapsible=offcanvas]:end-[calc(var(--sidebar-inline-size)*-1)]", variant === "floating" || variant === "inset" ? ["p-2 pt-[calc(var(--safe-area-top)+--spacing(2))] pb-[calc(var(--safe-area-bottom)+--spacing(2))] group-data-[collapsible=icon]:w-[calc(var(--sidebar-inline-size)+2px)]", side === "left" ? "ps-[calc(var(--safe-area-inline-start)+--spacing(2))]" : "pe-[calc(var(--safe-area-inline-end)+--spacing(2))]"] : "group-data-[side=left]:border-e group-data-[side=right]:border-s", className),
149
149
  "data-slot": "sidebar-container",
150
150
  ...props,
151
151
  children: /* @__PURE__ */ jsx("div", {
@@ -182,7 +182,7 @@ const SidebarRail = ({ className, label = DEFAULT_TOGGLE_LABEL, "aria-label": ar
182
182
  return /* @__PURE__ */ jsx("button", {
183
183
  "aria-label": ariaLabel ?? label,
184
184
  type: "button",
185
- className: cn("hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 transition-transform ease-linear group-data-[side=left]:-end-4 group-data-[side=right]:start-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] sm:flex", "ltr:-translate-x-1/2 rtl:translate-x-1/2", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "hover:group-data-[collapsible=offcanvas]:bg-sidebar", "group-data-[collapsible=offcanvas]:ltr:translate-x-0 group-data-[collapsible=offcanvas]:rtl:translate-x-0", "group-data-[collapsible=offcanvas]:group-data-[side=left]:after:end-full group-data-[collapsible=offcanvas]:group-data-[side=right]:after:start-full", "[[data-side=left][data-collapsible=offcanvas]_&]:-end-2", "[[data-side=right][data-collapsible=offcanvas]_&]:-start-2", className),
185
+ className: cn("hover:after:bg-sidebar-border absolute inset-y-0 z-20 hidden w-4 transition-transform ease-in-out group-data-[side=left]:-end-4 group-data-[side=right]:start-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] sm:flex", "ltr:-translate-x-1/2 rtl:translate-x-1/2", "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize", "[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize", "hover:group-data-[collapsible=offcanvas]:bg-sidebar", "group-data-[collapsible=offcanvas]:ltr:translate-x-0 group-data-[collapsible=offcanvas]:rtl:translate-x-0", "group-data-[collapsible=offcanvas]:group-data-[side=left]:after:end-full group-data-[collapsible=offcanvas]:group-data-[side=right]:after:start-full", "[[data-side=left][data-collapsible=offcanvas]_&]:-end-2", "[[data-side=right][data-collapsible=offcanvas]_&]:-start-2", className),
186
186
  "data-sidebar": "rail",
187
187
  "data-slot": "sidebar-rail",
188
188
  onClick: toggleSidebar,
@@ -220,7 +220,7 @@ const SidebarSeparator = ({ className, ...props }) => /* @__PURE__ */ jsx(Separa
220
220
  ...props
221
221
  });
222
222
  const SidebarContent = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
223
- className: cn("flex min-h-0 flex-1 flex-col gap-2 overflow-x-hidden overflow-y-auto group-data-[collapsible=icon]:[scrollbar-width:none] group-data-[collapsible=icon]:[&::-webkit-scrollbar]:hidden", className),
223
+ className: cn("flex min-h-0 flex-1 flex-col gap-2 overflow-x-hidden overflow-y-auto overscroll-contain group-data-[collapsible=icon]:[scrollbar-width:none] group-data-[collapsible=icon]:[&::-webkit-scrollbar]:hidden", className),
224
224
  "data-sidebar": "content",
225
225
  "data-slot": "sidebar-content",
226
226
  ...props
@@ -233,7 +233,7 @@ const SidebarGroup = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
233
233
  });
234
234
  const SidebarGroupLabel = ({ className, asChild = false, ...props }) => {
235
235
  return /* @__PURE__ */ jsx(asChild ? Slot : "div", {
236
- className: cn("text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "group-data-[collapsible=icon]:hidden", className),
236
+ className: cn("text-sidebar-foreground/70 ring-sidebar-ring flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-in-out focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "group-data-[collapsible=icon]:hidden", className),
237
237
  "data-sidebar": "group-label",
238
238
  "data-slot": "sidebar-group-label",
239
239
  ...props
@@ -265,7 +265,7 @@ const SidebarMenuItem = ({ className, ...props }) => /* @__PURE__ */ jsx("li", {
265
265
  "data-slot": "sidebar-menu-item",
266
266
  ...props
267
267
  });
268
- const sidebarMenuButtonVariants = cva("peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-accent data-[active=true]:text-sidebar-accent-foreground data-[active=true]:hover:bg-sidebar-accent data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-start text-sm outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pe-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>span:last-child]:transition-opacity [&>span:last-child]:duration-200 group-data-[collapsible=icon]:[&>span:last-child]:opacity-0 [&>svg]:size-4 [&>svg]:shrink-0", {
268
+ const sidebarMenuButtonVariants = cva("peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-accent data-[active=true]:text-sidebar-accent-foreground data-[active=true]:hover:bg-sidebar-accent data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-start text-sm outline-hidden transition-[width,height,padding] select-none group-has-data-[sidebar=menu-action]/menu-item:pe-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>span:last-child]:transition-opacity [&>span:last-child]:duration-200 group-data-[collapsible=icon]:[&>span:last-child]:opacity-0 [&>svg]:size-4 [&>svg]:shrink-0", {
269
269
  variants: {
270
270
  variant: {
271
271
  default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
@@ -0,0 +1,17 @@
1
+ //#region src/components/status-mark.d.ts
2
+ type StatusMarkStatus = "pending" | "running" | "done" | "failed";
3
+ type StatusMarkProps = {
4
+ status: StatusMarkStatus;
5
+ /** Accessible name; omit when adjacent text already states the status. */
6
+ label?: string | undefined;
7
+ className?: string;
8
+ };
9
+ /**
10
+ * One glyph for a unit of work across its life: dashes while it waits, a
11
+ * turning arc while it runs, then a closed ring with a check or a cross
12
+ * drawn in. Monochrome by design: it takes the text colour, and only a
13
+ * failure eases to destructive.
14
+ */
15
+ declare const StatusMark: ({ className, label, status }: StatusMarkProps) => import("react").JSX.Element;
16
+ //#endregion
17
+ export { StatusMark, type StatusMarkStatus };