@bysages/react 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Accordion as Accordion$1 } from "@ark-ui/react/accordion";
2
- import { AnchorHTMLAttributes, ButtonHTMLAttributes, CSSProperties, HTMLAttributes, InputHTMLAttributes, ReactNode, Ref, TextareaHTMLAttributes } from "react";
2
+ import { AnchorHTMLAttributes, ButtonHTMLAttributes, CSSProperties, ComponentProps, HTMLAttributes, InputHTMLAttributes, ReactNode, Ref, TextareaHTMLAttributes } from "react";
3
3
  import { Field as Field$1 } from "@ark-ui/react/field";
4
4
  import { Popover as Popover$1 } from "@ark-ui/react/popover";
5
5
  import { Collapsible as Collapsible$1 } from "@ark-ui/react/collapsible";
@@ -278,8 +278,12 @@ export interface ResponseProps extends HTMLAttributes<HTMLDivElement> {
278
278
  /** Optional code-highlighting function re-inking fenced blocks; the
279
279
  * component stays agnostic about which engine provides it. */
280
280
  highlighter?: (code: string, lang?: string) => string;
281
+ /** The copy stamp's accessible name before the copy lands. */
282
+ copyLabel?: string;
283
+ /** The copy stamp's accessible name once the text has landed. */
284
+ copiedLabel?: string;
281
285
  }
282
- declare function Response$1({ content, highlighter, ...rest }: ResponseProps): import("react").JSX.Element;
286
+ declare function Response$1({ content, highlighter, copyLabel, copiedLabel, ...rest }: ResponseProps): import("react").JSX.Element;
283
287
  //#endregion
284
288
  //#region src/components/ai-source/index.d.ts
285
289
  /** One place the ink came from; href and the rest ride the anchor. */
@@ -326,7 +330,7 @@ export interface ToolProps extends HTMLAttributes<HTMLDivElement> {
326
330
  }
327
331
  export declare function Tool({ name, label, status, input, output, ...rest }: ToolProps): import("react").JSX.Element;
328
332
  //#endregion
329
- //#region ../../node_modules/.pnpm/@ai-sdk+provider@4.0.14/node_modules/@ai-sdk/provider/dist/index.d.ts
333
+ //#region ../../node_modules/.pnpm/@ai-sdk+provider@4.0.17/node_modules/@ai-sdk/provider/dist/index.d.ts
330
334
  /**
331
335
  * Audio format configuration shared by provider V4 model specifications.
332
336
  */
@@ -406,7 +410,7 @@ type SharedV4Headers = Record<string, string>;
406
410
  * A JSON value can be a string, number, boolean, object, array, or null.
407
411
  * JSON values can be serialized and deserialized by the JSON.stringify and JSON.parse methods.
408
412
  */
409
- type JSONValue = null | string | number | boolean | JSONObject | JSONArray;
413
+ type JSONValue = null | string | number | boolean | Readonly<JSONObject> | readonly JSONValue[];
410
414
  type JSONObject = {
411
415
  [key: string]: JSONValue | undefined;
412
416
  };
@@ -9853,7 +9857,7 @@ interface $ZodConfig {
9853
9857
  declare const globalConfig: $ZodConfig;
9854
9858
  declare function config(newConfig?: Partial<$ZodConfig>): $ZodConfig;
9855
9859
  //#endregion
9856
- //#region ../../node_modules/.pnpm/@ai-sdk+provider-utils@5.0.40_zod@4.6.5/node_modules/@ai-sdk/provider-utils/dist/index.d.ts
9860
+ //#region ../../node_modules/.pnpm/@ai-sdk+provider-utils@5.0.45_zod@4.6.5/node_modules/@ai-sdk/provider-utils/dist/index.d.ts
9857
9861
  /**
9858
9862
  * A value that can be provided either as a single item, an array of items,
9859
9863
  * or be left undefined.
@@ -10814,6 +10818,13 @@ type ToolOutputProperties<INPUT, OUTPUT, CONTEXT extends Context | unknown | nev
10814
10818
  * Common properties shared by all tool kinds.
10815
10819
  */
10816
10820
  type BaseTool<INPUT extends JSONValue | unknown | never = any, OUTPUT extends JSONValue | unknown | never = any, CONTEXT extends Context | unknown | never = any> = {
10821
+ /**
10822
+ * Defer exposing this tool until it is discovered by `toolSearch`.
10823
+ * Supports direct calls and local callers that announce tools in conversation
10824
+ * messages (code mode with `toolDiscovery: 'conversation'`). Discovered tools
10825
+ * become available on the next model step.
10826
+ */
10827
+ deferLoading?: boolean;
10817
10828
  /**
10818
10829
  * An optional title of the tool.
10819
10830
  *
@@ -11070,7 +11081,7 @@ type Normalize<OBJECT> = { [KEY in keyof OBJECT]: OBJECT[KEY]; };
11070
11081
  */
11071
11082
  type InferToolSetContext<TOOLS extends ToolSet> = Normalize<RequiredToolSetContext<TOOLS> & OptionalToolSetContext<TOOLS>>;
11072
11083
  //#endregion
11073
- //#region ../../node_modules/.pnpm/ai@7.0.99_zod@4.6.5/node_modules/ai/dist/index.d.ts
11084
+ //#region ../../node_modules/.pnpm/ai@7.0.107_zod@4.6.5/node_modules/ai/dist/index.d.ts
11074
11085
  /**
11075
11086
  * Embedding.
11076
11087
  */
@@ -12559,6 +12570,10 @@ type UIToolInvocation<TOOL extends UITool | Tool$1> = {
12559
12570
  } | {
12560
12571
  state: 'output-error';
12561
12572
  input: asUITool<TOOL>['input'] | undefined;
12573
+ /**
12574
+ * @deprecated Use `input` instead. This field will be removed in the next
12575
+ * major version.
12576
+ */
12562
12577
  rawInput?: unknown;
12563
12578
  output?: never;
12564
12579
  errorText: string;
@@ -13430,6 +13445,7 @@ declare global {
13430
13445
  * String model ids are resolved to the default provider and model id.
13431
13446
  *
13432
13447
  * If not set, the default provider is the Vercel AI gateway provider.
13448
+ * Experimental evaluation requires an explicitly configured evaluation-capable provider.
13433
13449
  *
13434
13450
  * @see https://ai-sdk.dev/docs/ai-sdk-core/provider-management#global-provider-configuration
13435
13451
  */
@@ -13562,10 +13578,22 @@ export interface AutoCompleteProps extends Omit<HTMLAttributes<HTMLDivElement>,
13562
13578
  export declare function AutoComplete({ value, items, placeholder, filter, onValueChange, ...rest }: AutoCompleteProps): import("react").JSX.Element;
13563
13579
  //#endregion
13564
13580
  //#region src/components/avatar/index.d.ts
13565
- /** Ark's Avatar, dressed in the paper-and-ink system: a circular seal on
13566
- * inset paper that holds initials until the image loads over them. The
13567
- * API is Ark's own — Root, Image, Fallback. */
13568
- export declare const Avatar: typeof Avatar$1;
13581
+ /** Avatar, dressed in the paper-and-ink system: a circular seal on
13582
+ * inset paper that holds initials until the image loads over them.
13583
+ * `size` picks a control-height rung for the seal — the core styles
13584
+ * re-point `--bs-avatar-size` per rung, and scenes can still retune the
13585
+ * variable directly. */
13586
+ export type AvatarSize = "sm" | "md" | "lg";
13587
+ export interface AvatarProps extends ComponentProps<typeof Avatar$1.Root> {
13588
+ /** Seal diameter: the small, medium, or large control rung. The
13589
+ * default (large) stands as tall as the biggest control so an avatar
13590
+ * rides a row without stretching it. */
13591
+ size?: AvatarSize;
13592
+ }
13593
+ declare function AvatarRoot({ size, ...rest }: AvatarProps): import("react").JSX.Element;
13594
+ export declare const Avatar: typeof Avatar$1 & {
13595
+ Root: typeof AvatarRoot;
13596
+ };
13569
13597
  //#endregion
13570
13598
  //#region src/components/avatar-group/index.d.ts
13571
13599
  /** Avatars overlapping one row, each rimmed in the ground so the pile
@@ -13580,20 +13608,26 @@ export interface BackTopProps extends HTMLAttributes<HTMLButtonElement> {
13580
13608
  threshold?: number;
13581
13609
  /** The accessible name; the control is icon-only by default. */
13582
13610
  label?: string;
13611
+ /** Ride a scrolling element of your own instead of the page: the
13612
+ * control moors inside that element's corner and watches its
13613
+ * scrollTop. The host supplies the positioning context. */
13614
+ scrollEl?: () => HTMLElement | null;
13583
13615
  }
13584
13616
  /**
13585
13617
  * A way home: after the page has scrolled past `threshold`, a small
13586
13618
  * floating control rises at the page's corner and returns the reader to
13587
13619
  * the top. The scroll itself stays native — `window.scrollTo` defers to
13588
13620
  * the stylesheet's `scroll-behavior: smooth`, which reduced motion turns
13589
- * back into an instant jump. The button stays mounted either way so the
13590
- * entrance is a transition, never a pop.
13621
+ * back into an instant jump (a moored control asks the media query
13622
+ * directly, since a scroller of its own has no stylesheet to defer to).
13623
+ * The button stays mounted either way so the entrance is a transition,
13624
+ * never a pop.
13591
13625
  *
13592
13626
  * The control itself is the shared `Button` (outline, square) — the
13593
13627
  * paper, hairline and halo are its; this family owns only the floating
13594
13628
  * and the entrance.
13595
13629
  */
13596
- export declare function BackTop({ threshold, label, ...rest }: BackTopProps): import("react").JSX.Element;
13630
+ export declare function BackTop({ threshold, label, scrollEl, ...rest }: BackTopProps): import("react").JSX.Element;
13597
13631
  //#endregion
13598
13632
  //#region src/components/badge/index.d.ts
13599
13633
  /** A small seal of state. Ink is the neutral tone; the four semantic
@@ -13778,18 +13812,21 @@ export interface CascadeSelectNode {
13778
13812
  * dissolves open beside it, until a leaf click settles the whole path.
13779
13813
  * `value` is the selected path (or paths, when `multiple`) — the joined
13780
13814
  * labels ride the trigger. `highlightTrigger: "hover"` turns the
13781
- * classic cascading menu: pointing is enough to unfold.
13815
+ * classic cascading menu: pointing is enough to unfold. `filterable`
13816
+ * swaps the corridor for a flat list of matching paths while a query
13817
+ * runs — each hit still reads as its full route.
13782
13818
  */
13783
13819
  export interface CascadeSelectProps extends HTMLAttributes<HTMLDivElement> {
13784
13820
  value?: string[][];
13785
13821
  data: CascadeSelectNode[];
13786
13822
  placeholder?: string;
13787
13823
  highlightTrigger?: "click" | "hover";
13824
+ filterable?: boolean;
13788
13825
  multiple?: boolean;
13789
13826
  disabled?: boolean;
13790
13827
  onValueChange?: (value: string[][]) => void;
13791
13828
  }
13792
- export declare function CascadeSelect({ value, data, placeholder, highlightTrigger, multiple, disabled, onValueChange, ...rest }: CascadeSelectProps): import("react").JSX.Element;
13829
+ export declare function CascadeSelect({ value, data, placeholder, highlightTrigger, filterable, multiple, disabled, onValueChange, ...rest }: CascadeSelectProps): import("react").JSX.Element;
13793
13830
  //#endregion
13794
13831
  //#region src/components/carousel/index.d.ts
13795
13832
  /** Ark's Carousel, dressed in the paper-and-ink system: slides ride in one
@@ -13816,6 +13853,7 @@ export interface CheckboxGroupProps extends HTMLAttributes<HTMLDivElement> {
13816
13853
  value?: string[];
13817
13854
  options: CheckboxOption[];
13818
13855
  layout?: "vertical" | "horizontal";
13856
+ invalid?: boolean;
13819
13857
  disabled?: boolean;
13820
13858
  onValueChange?: (value: string[]) => void;
13821
13859
  }
@@ -13823,9 +13861,11 @@ export interface CheckboxGroupProps extends HTMLAttributes<HTMLDivElement> {
13823
13861
  * One question, many answers: a labelled stack (or row) of the seal-cut
13824
13862
  * checkboxes bound to a single array. Toggling a box adds or removes its
13825
13863
  * value; the group itself is semantics (`role="group"`), the boxes stay
13826
- * the machine-driven originals.
13864
+ * the machine-driven originals. Inside a `Field.Root` the group picks up
13865
+ * the field context, so the invalid and disabled states a Form routes to
13866
+ * its name dress every box at once.
13827
13867
  */
13828
- export declare function CheckboxGroup({ value, options, layout, disabled, onValueChange, ...rest }: CheckboxGroupProps): import("react").JSX.Element;
13868
+ export declare function CheckboxGroup({ value, options, layout, invalid, disabled, onValueChange, ...rest }: CheckboxGroupProps): import("react").JSX.Element;
13829
13869
  //#endregion
13830
13870
  //#region src/components/chip/index.d.ts
13831
13871
  /** A counting coin: the numeric value, capped at `max` with an ellipsis
@@ -14411,10 +14451,9 @@ export interface LayoutContextValue {
14411
14451
  }
14412
14452
  /**
14413
14453
  * The application skeleton: Root, Header, Sider, Content, Footer —
14414
- * the admin arrangement (Element Plus / AntD keep the same anatomy)
14415
- * dressed in the paper-and-ink surfaces. The root is a full-height
14416
- * grid; a declared `sider` turns the columns around to meet whichever
14417
- * edge the rail stands on.
14454
+ * the admin arrangement dressed in the paper-and-ink surfaces. The
14455
+ * root is a full-height grid; a declared `sider` turns the columns
14456
+ * around to meet whichever edge the rail stands on.
14418
14457
  */
14419
14458
  declare function Root$1({ sider, children, ...rest }: LayoutRootProps): import("react").JSX.Element;
14420
14459
  export interface LayoutSiderProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
@@ -14946,6 +14985,11 @@ export interface DataTableProps {
14946
14985
  sortable?: boolean;
14947
14986
  /** Render a global-filter toolbar and per-column filter inputs. */
14948
14987
  filterable?: boolean;
14988
+ /** Keep the built-in toolbar row above the table (default true when
14989
+ * `filterable`). Turn it off to host the global filter in your own
14990
+ * toolbar — the feature stays registered and the instance methods
14991
+ * keep working. */
14992
+ showToolbar?: boolean;
14949
14993
  /** Enable tree expansion over `subRows`; the first column hosts the
14950
14994
  * expander and depth indent. */
14951
14995
  tree?: boolean;
@@ -15134,16 +15178,18 @@ export interface TreeSelectNode {
15134
15178
  * A tree behind a field: the control reads like an input, the vessel
15135
15179
  * below walks the hierarchy, and one leaf click closes the deal. Single
15136
15180
  * selection — the chosen label rides on the control, its value rides in
15137
- * `value`.
15181
+ * `value`. `filterable` puts a filter line at the top of the vessel;
15182
+ * matches keep their ancestors and the branches fan open.
15138
15183
  */
15139
15184
  export interface TreeSelectProps extends HTMLAttributes<HTMLDivElement> {
15140
15185
  value?: string;
15141
15186
  data: TreeSelectNode[];
15142
15187
  placeholder?: string;
15188
+ filterable?: boolean;
15143
15189
  disabled?: boolean;
15144
15190
  onValueChange?: (value: string) => void;
15145
15191
  }
15146
- export declare function TreeSelect({ value, data, placeholder, disabled, onValueChange, ...rest }: TreeSelectProps): import("react").JSX.Element;
15192
+ export declare function TreeSelect({ value, data, placeholder, filterable, disabled, onValueChange, ...rest }: TreeSelectProps): import("react").JSX.Element;
15147
15193
  //#endregion
15148
15194
  //#region src/components/tree-view/index.d.ts
15149
15195
  /** Ark's TreeView, dressed in the paper-and-ink system: quiet rows where
package/dist/index.mjs CHANGED
@@ -13,7 +13,7 @@ import { Combobox as Combobox$1 } from "@ark-ui/react/combobox";
13
13
  import { Avatar as Avatar$1 } from "@ark-ui/react/avatar";
14
14
  import { DatePicker as DatePicker$1 } from "@ark-ui/react/date-picker";
15
15
  import { useEnvironmentContext } from "@ark-ui/react/environment";
16
- import { useLocaleContext } from "@ark-ui/react/locale";
16
+ import { useFilter, useLocaleContext } from "@ark-ui/react/locale";
17
17
  import * as cascade from "@zag-js/cascade-select";
18
18
  import { normalizeProps, useMachine } from "@zag-js/react";
19
19
  import { Carousel as Carousel$1 } from "@ark-ui/react/carousel";
@@ -51,7 +51,7 @@ import { RadioGroup as RadioGroup$1 } from "@ark-ui/react/radio-group";
51
51
  import { RatingGroup as RatingGroup$1 } from "@ark-ui/react/rating-group";
52
52
  import { ScrollArea as ScrollArea$1 } from "@ark-ui/react/scroll-area";
53
53
  import { SegmentGroup as SegmentGroup$1 } from "@ark-ui/react/segment-group";
54
- import { Select as Select$1 } from "@ark-ui/react/select";
54
+ import { Select as Select$1, createListCollection } from "@ark-ui/react/select";
55
55
  import { SignaturePad as SignaturePad$1 } from "@ark-ui/react/signature-pad";
56
56
  import { Slider as Slider$1 } from "@ark-ui/react/slider";
57
57
  import { Splitter as Splitter$1 } from "@ark-ui/react/splitter";
@@ -487,12 +487,83 @@ function Reasoning({ label = "Thinking", children, ...rest }) {
487
487
  }
488
488
  injectComponentStyle("ai");
489
489
  //#endregion
490
+ //#region src/components/ai-response/code-copy.ts
491
+ /** The two glyphs a copy affordance needs: the stamp and its
492
+ * confirmation. Inline SVG keeps the enhancement framework-agnostic —
493
+ * it lives beside the renderer's generated code HTML. */
494
+ const COPY_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`;
495
+ const COPIED_CODE_ICON = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg>`;
496
+ /** How long the confirmation stays before the stamp returns. */
497
+ const REVERT_MS = 1500;
498
+ /** Make sure every fenced block under `host` carries its copy stamp.
499
+ * The stamp anchors to a wrapper outside the scrolling `pre` — inside,
500
+ * it would fix to the overflow area and travel out of view with the
501
+ * code. The renderer rebuilds the markdown as one HTML string on every
502
+ * stream tick, so this stays idempotent and cheap enough to re-run
503
+ * after each patch. */
504
+ function decorateCodeCopy(host, copyLabel) {
505
+ for (const pre of host.querySelectorAll("pre")) {
506
+ let body = pre.parentElement;
507
+ if (!body || body.getAttribute("data-part") !== "code-body") {
508
+ body = document.createElement("div");
509
+ body.setAttribute("data-scope", "ai");
510
+ body.setAttribute("data-part", "code-body");
511
+ pre.replaceWith(body);
512
+ body.appendChild(pre);
513
+ }
514
+ let button = body.querySelector("[data-part=\"code-copy\"]");
515
+ if (!button) {
516
+ button = document.createElement("button");
517
+ button.type = "button";
518
+ button.setAttribute("data-scope", "ai");
519
+ button.setAttribute("data-part", "code-copy");
520
+ button.innerHTML = COPY_CODE_ICON;
521
+ body.appendChild(button);
522
+ }
523
+ button.title = copyLabel;
524
+ button.setAttribute("aria-label", copyLabel);
525
+ }
526
+ }
527
+ /** The one delegated click for the whole response: copy the block's
528
+ * text and confirm in place, then return the stamp after a beat. */
529
+ async function clickCodeCopy(event, copyLabel, copiedLabel) {
530
+ const button = event.target?.closest("[data-part=\"code-copy\"]");
531
+ const pre = button?.closest("[data-part=\"code-body\"]")?.querySelector("pre");
532
+ if (!button || !pre) return;
533
+ await navigator.clipboard.writeText(pre.textContent ?? "");
534
+ button.innerHTML = COPIED_CODE_ICON;
535
+ button.setAttribute("data-state", "copied");
536
+ button.title = copiedLabel;
537
+ button.setAttribute("aria-label", copiedLabel);
538
+ setTimeout(() => {
539
+ if (!button.isConnected) return;
540
+ button.innerHTML = COPY_CODE_ICON;
541
+ button.removeAttribute("data-state");
542
+ button.title = copyLabel;
543
+ button.setAttribute("aria-label", copyLabel);
544
+ }, REVERT_MS);
545
+ }
546
+ //#endregion
490
547
  //#region src/components/ai-response/index.tsx
491
- function Response({ content, highlighter, ...rest }) {
548
+ function Response({ content, highlighter, copyLabel = "Copy code", copiedLabel = "Copied", ...rest }) {
549
+ const root = useRef(null);
550
+ useEffect(() => {
551
+ const host = root.current;
552
+ if (host) decorateCodeCopy(host, copyLabel);
553
+ }, [
554
+ content,
555
+ highlighter,
556
+ copyLabel
557
+ ]);
558
+ const onClick = (event) => {
559
+ clickCodeCopy(event.nativeEvent, copyLabel, copiedLabel);
560
+ };
492
561
  return /* @__PURE__ */ jsx("div", {
493
562
  ...rest,
563
+ ref: root,
494
564
  "data-scope": "ai",
495
565
  "data-part": "response",
566
+ onClick,
496
567
  dangerouslySetInnerHTML: { __html: renderHtml(content, highlighter ? { highlighter } : void 0) }
497
568
  });
498
569
  }
@@ -849,10 +920,13 @@ function AutoComplete({ value = "", items = [], placeholder, filter, onValueChan
849
920
  injectComponentStyle("combobox");
850
921
  //#endregion
851
922
  //#region src/components/avatar/index.tsx
852
- /** Ark's Avatar, dressed in the paper-and-ink system: a circular seal on
853
- * inset paper that holds initials until the image loads over them. The
854
- * API is Ark's own — Root, Image, Fallback. */
855
- const Avatar = Avatar$1;
923
+ function AvatarRoot({ size, ...rest }) {
924
+ return /* @__PURE__ */ jsx(Avatar$1.Root, {
925
+ ...rest,
926
+ "data-size": size
927
+ });
928
+ }
929
+ const Avatar = Object.assign({}, Avatar$1, { Root: AvatarRoot });
856
930
  injectComponentStyle("avatar");
857
931
  //#endregion
858
932
  //#region src/components/avatar-group/index.tsx
@@ -887,26 +961,46 @@ function chevronUp() {
887
961
  * floating control rises at the page's corner and returns the reader to
888
962
  * the top. The scroll itself stays native — `window.scrollTo` defers to
889
963
  * the stylesheet's `scroll-behavior: smooth`, which reduced motion turns
890
- * back into an instant jump. The button stays mounted either way so the
891
- * entrance is a transition, never a pop.
964
+ * back into an instant jump (a moored control asks the media query
965
+ * directly, since a scroller of its own has no stylesheet to defer to).
966
+ * The button stays mounted either way so the entrance is a transition,
967
+ * never a pop.
892
968
  *
893
969
  * The control itself is the shared `Button` (outline, square) — the
894
970
  * paper, hairline and halo are its; this family owns only the floating
895
971
  * and the entrance.
896
972
  */
897
- function BackTop({ threshold = 400, label = "Back to top", ...rest }) {
973
+ function BackTop({ threshold = 400, label = "Back to top", scrollEl, ...rest }) {
898
974
  const [visible, setVisible] = useState(false);
899
975
  useEffect(() => {
900
976
  if (typeof window === "undefined") return;
901
- const onScroll = () => setVisible(window.scrollY > threshold);
977
+ const el = scrollEl?.() ?? null;
978
+ const onScroll = () => setVisible((el ? el.scrollTop : window.scrollY) > threshold);
902
979
  onScroll();
903
- window.addEventListener("scroll", onScroll, { passive: true });
904
- return () => window.removeEventListener("scroll", onScroll);
905
- }, [threshold]);
980
+ if (el) el.addEventListener("scroll", onScroll, { passive: true });
981
+ else window.addEventListener("scroll", onScroll, { passive: true });
982
+ return () => {
983
+ if (el) el.removeEventListener("scroll", onScroll);
984
+ else window.removeEventListener("scroll", onScroll);
985
+ };
986
+ }, [threshold, scrollEl]);
987
+ const toTop = () => {
988
+ const el = scrollEl?.();
989
+ if (!el) {
990
+ window.scrollTo({ top: 0 });
991
+ return;
992
+ }
993
+ const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
994
+ el.scrollTo({
995
+ top: 0,
996
+ behavior: reduce ? "auto" : "smooth"
997
+ });
998
+ };
906
999
  return /* @__PURE__ */ jsx("div", {
907
1000
  "data-scope": "back-top",
908
1001
  "data-part": "root",
909
1002
  "data-state": visible ? "shown" : "hidden",
1003
+ ...scrollEl ? { "data-container": "" } : {},
910
1004
  children: /* @__PURE__ */ jsx(Button, {
911
1005
  ...rest,
912
1006
  variant: "outline",
@@ -916,7 +1010,7 @@ function BackTop({ threshold = 400, label = "Back to top", ...rest }) {
916
1010
  "aria-label": label,
917
1011
  "aria-hidden": visible ? void 0 : "true",
918
1012
  tabIndex: visible ? 0 : -1,
919
- onClick: () => window.scrollTo({ top: 0 }),
1013
+ onClick: toTop,
920
1014
  children: rest.children ?? chevronUp()
921
1015
  })
922
1016
  });
@@ -1181,6 +1275,23 @@ const Card = Object.assign(Root$9, {
1181
1275
  });
1182
1276
  injectComponentStyle("card");
1183
1277
  //#endregion
1278
+ //#region src/components/input/index.tsx
1279
+ function Input({ value, size = "md", invalid = false, onValueChange, ...rest }) {
1280
+ const field = useFieldContext();
1281
+ const fieldProps = field?.getInputProps() ?? {};
1282
+ return /* @__PURE__ */ jsx("input", {
1283
+ ...fieldProps,
1284
+ ...rest,
1285
+ ...value !== void 0 ? { value } : {},
1286
+ "data-scope": "input",
1287
+ "data-part": "root",
1288
+ "data-size": size,
1289
+ "data-invalid": invalid || field?.invalid ? "" : void 0,
1290
+ onChange: (event) => onValueChange?.(event.target.value)
1291
+ });
1292
+ }
1293
+ injectComponentStyle("input");
1294
+ //#endregion
1184
1295
  //#region src/components/cascade-select/index.tsx
1185
1296
  const chevronDown$2 = /* @__PURE__ */ jsx("svg", {
1186
1297
  viewBox: "0 0 24 24",
@@ -1212,7 +1323,7 @@ const checkGlyph$2 = /* @__PURE__ */ jsx("svg", {
1212
1323
  "aria-hidden": "true",
1213
1324
  children: /* @__PURE__ */ jsx("path", { d: "m5 12.5 5 5L19 7" })
1214
1325
  });
1215
- function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigger, multiple = false, disabled = false, onValueChange, ...rest }) {
1326
+ function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigger, filterable = false, multiple = false, disabled = false, onValueChange, ...rest }) {
1216
1327
  const id = useId();
1217
1328
  const locale = useLocaleContext();
1218
1329
  const env = useEnvironmentContext();
@@ -1240,6 +1351,47 @@ function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigge
1240
1351
  }
1241
1352
  });
1242
1353
  const api = cascade.connect(service, normalizeProps);
1354
+ const [query, setQuery] = useState("");
1355
+ const filterFns = useFilter({ sensitivity: "base" });
1356
+ const filtering = filterable && query.trim().length > 0;
1357
+ /** The corridor's answer to a query: every leaf whose route matches —
1358
+ * the query may land on any hop, and the whole route still shows. */
1359
+ const matchPaths = useMemo(() => {
1360
+ if (!filtering) return [];
1361
+ const q = query.trim();
1362
+ const hits = [];
1363
+ const walk = (nodes, path, labels, matched) => {
1364
+ for (const node of nodes ?? []) {
1365
+ const hit = matched || filterFns.contains(node.label, q);
1366
+ const nextPath = [...path, node.value];
1367
+ const nextLabels = [...labels, node.label];
1368
+ if (node.children?.length) walk(node.children, nextPath, nextLabels, hit);
1369
+ else if (hit) hits.push({
1370
+ path: nextPath,
1371
+ labels: nextLabels
1372
+ });
1373
+ }
1374
+ };
1375
+ walk(data, [], [], false);
1376
+ return hits;
1377
+ }, [
1378
+ filtering,
1379
+ query,
1380
+ filterFns,
1381
+ data
1382
+ ]);
1383
+ const isSelected = (path) => (value ?? []).some((p) => p.join("/") === path.join("/"));
1384
+ function pickMatch(path) {
1385
+ if (multiple) {
1386
+ const current = value ?? [];
1387
+ const key = path.join("/");
1388
+ const next = current.some((p) => p.join("/") === key) ? current.filter((p) => p.join("/") !== key) : [...current, path];
1389
+ api.setValue(next);
1390
+ } else {
1391
+ api.selectValue(path);
1392
+ api.setOpen(false);
1393
+ }
1394
+ }
1243
1395
  /** The machine's `valueAsString` only updates through its select
1244
1396
  * event, so an externally-set `value` would render the placeholder
1245
1397
  * forever — resolve the labels from the data instead. */
@@ -1305,6 +1457,10 @@ function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigge
1305
1457
  ...api.getControlProps(),
1306
1458
  children: /* @__PURE__ */ jsxs("button", {
1307
1459
  ...api.getTriggerProps(),
1460
+ onClick: (event) => {
1461
+ api.getTriggerProps().onClick?.(event);
1462
+ setQuery("");
1463
+ },
1308
1464
  disabled: disabled || void 0,
1309
1465
  children: [/* @__PURE__ */ jsx("span", {
1310
1466
  ...api.getValueTextProps(),
@@ -1316,9 +1472,27 @@ function CascadeSelect({ value, data, placeholder = "Select…", highlightTrigge
1316
1472
  })
1317
1473
  }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx("div", {
1318
1474
  ...api.getPositionerProps(),
1319
- children: /* @__PURE__ */ jsx("div", {
1475
+ children: /* @__PURE__ */ jsxs("div", {
1320
1476
  ...api.getContentProps(),
1321
- children: renderColumn(collection.rootNode, [], [])
1477
+ children: [filterable ? /* @__PURE__ */ jsx("div", {
1478
+ "data-part": "search",
1479
+ children: /* @__PURE__ */ jsx(Input, {
1480
+ size: "sm",
1481
+ value: query,
1482
+ onValueChange: setQuery,
1483
+ placeholder: "Filter…",
1484
+ "aria-label": "Filter options"
1485
+ })
1486
+ }) : null, filtering ? matchPaths.length === 0 ? /* @__PURE__ */ jsx("p", {
1487
+ "data-part": "empty",
1488
+ children: "Nothing matches"
1489
+ }) : matchPaths.map((hit) => /* @__PURE__ */ jsx("button", {
1490
+ type: "button",
1491
+ "data-part": "match",
1492
+ "data-selected": isSelected(hit.path) || void 0,
1493
+ onClick: () => pickMatch(hit.path),
1494
+ children: hit.labels.join(" / ")
1495
+ }, hit.path.join("/"))) : renderColumn(collection.rootNode, [], [])]
1322
1496
  })
1323
1497
  }) })]
1324
1498
  });
@@ -1357,10 +1531,15 @@ const checkGlyph$1 = /* @__PURE__ */ jsx("svg", {
1357
1531
  * One question, many answers: a labelled stack (or row) of the seal-cut
1358
1532
  * checkboxes bound to a single array. Toggling a box adds or removes its
1359
1533
  * value; the group itself is semantics (`role="group"`), the boxes stay
1360
- * the machine-driven originals.
1534
+ * the machine-driven originals. Inside a `Field.Root` the group picks up
1535
+ * the field context, so the invalid and disabled states a Form routes to
1536
+ * its name dress every box at once.
1361
1537
  */
1362
- function CheckboxGroup({ value = [], options, layout = "vertical", disabled = false, onValueChange, ...rest }) {
1538
+ function CheckboxGroup({ value = [], options, layout = "vertical", invalid = false, disabled = false, onValueChange, ...rest }) {
1539
+ const field = useFieldContext();
1363
1540
  const selected = new Set(value);
1541
+ const isInvalid = invalid || field?.invalid === true;
1542
+ const isDisabled = disabled || field?.disabled === true;
1364
1543
  function toggle(option) {
1365
1544
  const next = new Set(selected);
1366
1545
  if (next.has(option)) next.delete(option);
@@ -1373,9 +1552,11 @@ function CheckboxGroup({ value = [], options, layout = "vertical", disabled = fa
1373
1552
  "data-scope": "checkbox-group",
1374
1553
  "data-part": "root",
1375
1554
  "data-layout": layout,
1555
+ "data-invalid": isInvalid ? "" : void 0,
1376
1556
  children: options.map((option) => /* @__PURE__ */ jsxs(Checkbox$1.Root, {
1377
1557
  checked: selected.has(option.value),
1378
- disabled: disabled || option.disabled === true,
1558
+ invalid: isInvalid,
1559
+ disabled: isDisabled || option.disabled === true,
1379
1560
  onCheckedChange: () => toggle(option.value),
1380
1561
  children: [
1381
1562
  /* @__PURE__ */ jsx(Checkbox$1.Control, { children: /* @__PURE__ */ jsx(Checkbox$1.Indicator, { children: checkGlyph$1 }) }),
@@ -1716,23 +1897,6 @@ injectComponentStyle("dialog");
1716
1897
  const Drawer = Drawer$1;
1717
1898
  injectComponentStyle("drawer");
1718
1899
  //#endregion
1719
- //#region src/components/input/index.tsx
1720
- function Input({ value, size = "md", invalid = false, onValueChange, ...rest }) {
1721
- const field = useFieldContext();
1722
- const fieldProps = field?.getInputProps() ?? {};
1723
- return /* @__PURE__ */ jsx("input", {
1724
- ...fieldProps,
1725
- ...rest,
1726
- ...value !== void 0 ? { value } : {},
1727
- "data-scope": "input",
1728
- "data-part": "root",
1729
- "data-size": size,
1730
- "data-invalid": invalid || field?.invalid ? "" : void 0,
1731
- onChange: (event) => onValueChange?.(event.target.value)
1732
- });
1733
- }
1734
- injectComponentStyle("input");
1735
- //#endregion
1736
1900
  //#region src/components/dynamic-input/index.tsx
1737
1901
  /**
1738
1902
  * A column of entry rows: one Input per line, each with a quiet remove
@@ -2383,10 +2547,9 @@ injectComponentStyle("kbd");
2383
2547
  const LAYOUT_CONTEXT = createContext(null);
2384
2548
  /**
2385
2549
  * The application skeleton: Root, Header, Sider, Content, Footer —
2386
- * the admin arrangement (Element Plus / AntD keep the same anatomy)
2387
- * dressed in the paper-and-ink surfaces. The root is a full-height
2388
- * grid; a declared `sider` turns the columns around to meet whichever
2389
- * edge the rail stands on.
2550
+ * the admin arrangement dressed in the paper-and-ink surfaces. The
2551
+ * root is a full-height grid; a declared `sider` turns the columns
2552
+ * around to meet whichever edge the rail stands on.
2390
2553
  */
2391
2554
  function Root$5({ sider, children, ...rest }) {
2392
2555
  return /* @__PURE__ */ jsx(LAYOUT_CONTEXT.Provider, {
@@ -3203,6 +3366,36 @@ const TagsInput = TagsInput$1;
3203
3366
  injectComponentStyle("tags-input");
3204
3367
  //#endregion
3205
3368
  //#region src/components/table/index.tsx
3369
+ /** The pagination bar's arrows — thin chevrons for the row of seals. */
3370
+ function pageGlyph(direction) {
3371
+ return /* @__PURE__ */ jsx("svg", {
3372
+ viewBox: "0 0 24 24",
3373
+ width: 14,
3374
+ height: 14,
3375
+ fill: "none",
3376
+ stroke: "currentColor",
3377
+ strokeWidth: 2,
3378
+ strokeLinecap: "round",
3379
+ strokeLinejoin: "round",
3380
+ "aria-hidden": "true",
3381
+ children: /* @__PURE__ */ jsx("path", { d: direction === "start" ? "m15 18-6-6 6-6" : "m9 18 6-6-6-6" })
3382
+ });
3383
+ }
3384
+ /** The page-size select's pointing chevron. */
3385
+ function chevronDownGlyph() {
3386
+ return /* @__PURE__ */ jsx("svg", {
3387
+ viewBox: "0 0 24 24",
3388
+ width: 14,
3389
+ height: 14,
3390
+ fill: "none",
3391
+ stroke: "currentColor",
3392
+ strokeWidth: 2.5,
3393
+ strokeLinecap: "round",
3394
+ strokeLinejoin: "round",
3395
+ "aria-hidden": "true",
3396
+ children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" })
3397
+ });
3398
+ }
3206
3399
  const SELECT_COL_ID = "__select";
3207
3400
  const SELECT_COL_WIDTH = 48;
3208
3401
  /** The list holding `id` and the index inside it, or null when absent. */
@@ -3282,7 +3475,7 @@ function SelectBox(props) {
3282
3475
  }
3283
3476
  function DataTable(rawProps) {
3284
3477
  const props = rawProps;
3285
- const { selectable = false, sortable = true, filterable = false, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
3478
+ const { selectable = false, sortable = true, filterable = false, showToolbar = true, tree = false, virtual: virtualProp = false, rowHeight = 40, paginated = false, pageSize = 10, pageSizeOptions = [
3286
3479
  10,
3287
3480
  20,
3288
3481
  50
@@ -3377,21 +3570,24 @@ function DataTable(rawProps) {
3377
3570
  const rows = table.getRowModel().rows;
3378
3571
  /** The declared row height is the density-scale baseline; the live
3379
3572
  * scale comes off the document so the virtual window matches what CSS
3380
- * actually renders. */
3381
- const densityScale = () => {
3573
+ * actually renders. It is read once and re-read only when the density
3574
+ * or scene attribute moves — estimateSize runs on every scroll frame. */
3575
+ let densityScale = 1;
3576
+ const readDensityScale = () => {
3382
3577
  const value = Number(getComputedStyle(document.documentElement).getPropertyValue("--bs-density-scale"));
3383
- return Number.isFinite(value) && value > 0 ? value : 1;
3578
+ densityScale = Number.isFinite(value) && value > 0 ? value : 1;
3384
3579
  };
3385
3580
  const virtualizer = useVirtualizer({
3386
3581
  count: rows.length,
3387
3582
  getScrollElement: () => viewport.current,
3388
- estimateSize: () => rowHeight * densityScale(),
3583
+ estimateSize: () => rowHeight * densityScale,
3389
3584
  getItemKey: (index) => rows[index]?.id ?? index,
3390
3585
  overscan: 8
3391
3586
  });
3392
3587
  const [, rerender] = useReducer((count) => count + 1, 0);
3393
3588
  useEffect(() => {
3394
3589
  if (!virtual) return;
3590
+ readDensityScale();
3395
3591
  const observer = new MutationObserver(rerender);
3396
3592
  observer.observe(document.documentElement, {
3397
3593
  attributes: true,
@@ -3650,7 +3846,7 @@ function DataTable(rawProps) {
3650
3846
  onDragLeave: canDrag ? onColDragLeave : void 0,
3651
3847
  onDrop: canDrag ? (e) => onColDrop(column, e) : void 0,
3652
3848
  onDragEnd: canDrag ? onColDragEnd : void 0,
3653
- children: [header.isPlaceholder ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
3849
+ children: [header.isPlaceholder || header.column.columnDef.header === "" ? null : /* @__PURE__ */ jsx(FlexRender, { header }), canFilter ? /* @__PURE__ */ jsx("input", {
3654
3850
  type: "text",
3655
3851
  "data-scope": "table",
3656
3852
  "data-part": "header-filter",
@@ -3757,55 +3953,53 @@ function DataTable(rawProps) {
3757
3953
  const paginationBar = paginated ? (() => {
3758
3954
  const pagination = table.atoms.pagination.get();
3759
3955
  const rowCount = table.getRowCount();
3760
- const pageCount = table.getPageCount();
3956
+ const sizeItems = createListCollection({ items: pageSizeOptions.map((size) => ({
3957
+ label: `${size} / page`,
3958
+ value: String(size)
3959
+ })) });
3960
+ const pageSize = /* @__PURE__ */ jsxs(Select.Root, {
3961
+ collection: sizeItems,
3962
+ value: [String(pagination.pageSize)],
3963
+ onValueChange: (details) => table.setPageSize(Number(details.value[0])),
3964
+ positioning: { placement: "top-start" },
3965
+ children: [/* @__PURE__ */ jsx(Select.Control, { children: /* @__PURE__ */ jsxs(Select.Trigger, {
3966
+ "aria-label": "Rows per page",
3967
+ children: [/* @__PURE__ */ jsx(Select.ValueText, {}), /* @__PURE__ */ jsx(Select.Indicator, { children: chevronDownGlyph() })]
3968
+ }) }), /* @__PURE__ */ jsx(Select.Positioner, { children: /* @__PURE__ */ jsx(Select.Content, { children: sizeItems.items.map((item) => /* @__PURE__ */ jsxs(Select.Item, {
3969
+ item,
3970
+ children: [/* @__PURE__ */ jsx(Select.ItemText, { children: item.label }), /* @__PURE__ */ jsx(Select.ItemIndicator, { children: "✓" })]
3971
+ }, item.value)) }) })]
3972
+ });
3973
+ const pager = /* @__PURE__ */ jsxs(Pagination.Root, {
3974
+ count: rowCount,
3975
+ pageSize: pagination.pageSize,
3976
+ page: pagination.pageIndex + 1,
3977
+ onPageChange: (details) => table.setPageIndex(details.page - 1),
3978
+ children: [
3979
+ /* @__PURE__ */ jsx(Pagination.PrevTrigger, { children: pageGlyph("start") }),
3980
+ /* @__PURE__ */ jsx(Pagination.Context, { children: (scope) => scope.pages.map((page, index) => page.type === "ellipsis" ? /* @__PURE__ */ jsx(Pagination.Ellipsis, { index }, `e${index}`) : /* @__PURE__ */ jsx(Pagination.Item, {
3981
+ type: "page",
3982
+ value: page.value,
3983
+ children: String(page.value)
3984
+ }, page.value)) }),
3985
+ /* @__PURE__ */ jsx(Pagination.NextTrigger, { children: pageGlyph("end") })
3986
+ ]
3987
+ });
3761
3988
  return /* @__PURE__ */ jsxs("div", {
3762
3989
  "data-scope": "table",
3763
3990
  "data-part": "pagination",
3764
- children: [
3765
- /* @__PURE__ */ jsx("button", {
3766
- type: "button",
3767
- "data-scope": "table",
3768
- "data-part": "page-button",
3769
- disabled: !table.getCanPreviousPage(),
3770
- onClick: () => table.previousPage(),
3771
- children: "Prev"
3772
- }),
3773
- /* @__PURE__ */ jsx("button", {
3774
- type: "button",
3775
- "data-scope": "table",
3776
- "data-part": "page-button",
3777
- disabled: !table.getCanNextPage(),
3778
- onClick: () => table.nextPage(),
3779
- children: "Next"
3780
- }),
3781
- /* @__PURE__ */ jsx("select", {
3782
- "data-scope": "table",
3783
- "data-part": "page-size",
3784
- "aria-label": "Rows per page",
3785
- value: pagination.pageSize,
3786
- onChange: (e) => table.setPageSize(Number(e.target.value)),
3787
- children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxs("option", {
3788
- value: size,
3789
- children: [size, " / page"]
3790
- }, size))
3791
- }),
3792
- /* @__PURE__ */ jsxs("span", {
3793
- "data-scope": "table",
3794
- "data-part": "page-status",
3795
- children: [
3796
- "Page ",
3797
- pagination.pageIndex + 1,
3798
- " of ",
3799
- pageCount,
3800
- " · ",
3801
- rowCount,
3802
- " rows"
3803
- ]
3804
- })
3805
- ]
3991
+ children: [/* @__PURE__ */ jsxs("span", {
3992
+ "data-scope": "table",
3993
+ "data-part": "page-status",
3994
+ children: [rowCount, " rows"]
3995
+ }), /* @__PURE__ */ jsxs("div", {
3996
+ "data-scope": "table",
3997
+ "data-part": "page-nav",
3998
+ children: [pageSize, pager]
3999
+ })]
3806
4000
  });
3807
4001
  })() : null;
3808
- const toolbar = filterable ? /* @__PURE__ */ jsx("div", {
4002
+ const toolbar = filterable && showToolbar ? /* @__PURE__ */ jsx("div", {
3809
4003
  "data-scope": "table",
3810
4004
  "data-part": "toolbar",
3811
4005
  children: /* @__PURE__ */ jsx("input", {
@@ -4162,9 +4356,11 @@ function TreeSelectRow({ node, indexPath }) {
4162
4356
  })
4163
4357
  });
4164
4358
  }
4165
- function TreeSelect({ value, data, placeholder = "Select…", disabled = false, onValueChange, ...rest }) {
4359
+ function TreeSelect({ value, data, placeholder = "Select…", filterable = false, disabled = false, onValueChange, ...rest }) {
4166
4360
  const [open, setOpen] = useState(false);
4167
- const collection = createTreeCollection$1({
4361
+ const [query, setQuery] = useState("");
4362
+ const filterFns = useFilter({ sensitivity: "base" });
4363
+ const collection = useMemo(() => createTreeCollection$1({
4168
4364
  nodeToValue: (node) => node.value,
4169
4365
  nodeToString: (node) => node.label,
4170
4366
  rootNode: {
@@ -4172,8 +4368,29 @@ function TreeSelect({ value, data, placeholder = "Select…", disabled = false,
4172
4368
  label: "",
4173
4369
  children: data
4174
4370
  }
4175
- });
4371
+ }), [data]);
4372
+ const filtering = filterable && query.trim().length > 0;
4373
+ /** The collection pruned to the matches — each hit keeping its
4374
+ * ancestors, so a deep match still reads inside its hierarchy. */
4375
+ const visibleCollection = useMemo(() => filtering ? collection.filter((node) => filterFns.contains(node.label, query.trim())) : collection, [
4376
+ filtering,
4377
+ collection,
4378
+ filterFns,
4379
+ query
4380
+ ]);
4176
4381
  const firstLevel = data.map((node) => node.value);
4382
+ const expandedWhileFiltering = useMemo(() => {
4383
+ if (!filtering) return void 0;
4384
+ const values = [];
4385
+ const walk = (nodes) => {
4386
+ for (const node of nodes) if (node.children?.length) {
4387
+ values.push(node.value);
4388
+ walk(node.children);
4389
+ }
4390
+ };
4391
+ walk(visibleCollection.rootNode.children ?? []);
4392
+ return values;
4393
+ }, [filtering, visibleCollection]);
4177
4394
  let found;
4178
4395
  const walk = (nodes) => {
4179
4396
  for (const node of nodes ?? []) {
@@ -4192,7 +4409,10 @@ function TreeSelect({ value, data, placeholder = "Select…", disabled = false,
4192
4409
  return /* @__PURE__ */ jsxs(Popover$1.Root, {
4193
4410
  ...rest,
4194
4411
  open,
4195
- onOpenChange: (details) => setOpen(details.open),
4412
+ onOpenChange: (details) => {
4413
+ setOpen(details.open);
4414
+ if (!details.open) setQuery("");
4415
+ },
4196
4416
  positioning: {
4197
4417
  sameWidth: true,
4198
4418
  placement: "bottom-start"
@@ -4209,20 +4429,38 @@ function TreeSelect({ value, data, placeholder = "Select…", disabled = false,
4209
4429
  disabled,
4210
4430
  children: [label ?? placeholder, chevronDown]
4211
4431
  })
4212
- }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsx(Popover$1.Content, {
4432
+ }), /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Popover$1.Positioner, { children: /* @__PURE__ */ jsxs(Popover$1.Content, {
4213
4433
  "data-scope": "tree-select",
4214
4434
  "data-part": "content",
4215
- children: /* @__PURE__ */ jsx(TreeView$1.Root, {
4216
- collection,
4217
- selectionMode: "single",
4218
- selectedValue: value ? [value] : [],
4219
- defaultExpandedValue: firstLevel,
4220
- onSelectionChange: pick,
4221
- children: /* @__PURE__ */ jsx(TreeView$1.Tree, { children: collection.rootNode.children?.map((node, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
4222
- node,
4223
- indexPath: [index]
4224
- }, node.value)) })
4225
- })
4435
+ children: [filterable ? /* @__PURE__ */ jsx("div", {
4436
+ "data-scope": "tree-select",
4437
+ "data-part": "search",
4438
+ children: /* @__PURE__ */ jsx(Input, {
4439
+ size: "sm",
4440
+ value: query,
4441
+ onValueChange: setQuery,
4442
+ placeholder: "Filter…",
4443
+ "aria-label": "Filter options"
4444
+ })
4445
+ }) : null, /* @__PURE__ */ jsx("div", {
4446
+ "data-scope": "tree-select",
4447
+ "data-part": "body",
4448
+ children: (visibleCollection.rootNode.children ?? []).length === 0 ? /* @__PURE__ */ jsx("p", {
4449
+ "data-scope": "tree-select",
4450
+ "data-part": "empty",
4451
+ children: "Nothing matches"
4452
+ }) : /* @__PURE__ */ jsx(TreeView$1.Root, {
4453
+ collection: visibleCollection,
4454
+ selectionMode: "single",
4455
+ selectedValue: value ? [value] : [],
4456
+ ...filtering ? { expandedValue: expandedWhileFiltering } : { defaultExpandedValue: firstLevel },
4457
+ onSelectionChange: pick,
4458
+ children: /* @__PURE__ */ jsx(TreeView$1.Tree, { children: visibleCollection.rootNode.children?.map((node, index) => /* @__PURE__ */ jsx(TreeSelectRow, {
4459
+ node,
4460
+ indexPath: [index]
4461
+ }, node.value)) })
4462
+ })
4463
+ })]
4226
4464
  }) }) })]
4227
4465
  });
4228
4466
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bysages/react",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "By Sages Elements components for React — styled by the paper-and-ink design tokens",
5
5
  "keywords": [
6
6
  "accessible",
@@ -41,23 +41,24 @@
41
41
  }
42
42
  },
43
43
  "dependencies": {
44
- "@ark-ui/react": "5.39.1",
44
+ "@ark-ui/react": "5.39.2",
45
45
  "@tanstack/markdown": "0.0.15",
46
46
  "@tanstack/react-table": "9.2.4",
47
- "@tanstack/react-virtual": "3.14.12",
48
- "@zag-js/cascade-select": "1.43.3",
49
- "@zag-js/react": "1.43.3",
50
- "@bysages/core": "0.1.1"
47
+ "@tanstack/react-virtual": "3.14.13",
48
+ "@zag-js/cascade-select": "1.44.0",
49
+ "@zag-js/react": "1.44.0",
50
+ "@bysages/core": "0.2.1"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@storybook/addon-a11y": "10.6.0",
54
54
  "@storybook/react-vite": "10.6.0",
55
55
  "@types/react": "19.3.0",
56
56
  "@types/react-dom": "19.3.0",
57
- "ai": "7.0.99",
57
+ "ai": "7.0.107",
58
58
  "react": "19.3.0",
59
59
  "react-dom": "19.3.0",
60
- "storybook": "10.6.0"
60
+ "storybook": "10.6.0",
61
+ "zod": "4.6.5"
61
62
  },
62
63
  "peerDependencies": {
63
64
  "react": ">=18",