@upbound/monarch-blocks 0.7.3 → 0.8.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.js CHANGED
@@ -2863,21 +2863,74 @@ function ChatInterface(_a) {
2863
2863
  }
2864
2864
 
2865
2865
  // src/code-block.tsx
2866
- import { useEffect as useEffect3, useState as useState5 } from "react";
2866
+ import { useCallback as useCallback3, useEffect as useEffect3, useId as useId2, useRef as useRef3, useState as useState5 } from "react";
2867
2867
  import { Icon as Icon3 } from "@upbound/monarch-core";
2868
2868
  import {
2869
2869
  Card,
2870
2870
  CardContent,
2871
2871
  CardFooter,
2872
2872
  CardHeader,
2873
+ InputGroup as InputGroup3,
2874
+ InputGroupAddon as InputGroupAddon3,
2875
+ InputGroupButton,
2876
+ InputGroupInput as InputGroupInput2,
2877
+ InputGroupText as InputGroupText2,
2873
2878
  Tooltip as Tooltip3,
2874
2879
  TooltipContent as TooltipContent2,
2875
2880
  TooltipTrigger as TooltipTrigger2
2876
2881
  } from "@upbound/monarch-core";
2877
2882
 
2883
+ // src/lib/find-text.ts
2884
+ function findTextRanges(root, query) {
2885
+ var _a, _b;
2886
+ const needle = query.toLowerCase();
2887
+ if (!needle) return [];
2888
+ const doc = (_a = root.ownerDocument) != null ? _a : document;
2889
+ const walker = doc.createTreeWalker(root, NodeFilter.SHOW_TEXT);
2890
+ const nodes = [];
2891
+ const starts = [];
2892
+ let text = "";
2893
+ for (let node = walker.nextNode(); node; node = walker.nextNode()) {
2894
+ nodes.push(node);
2895
+ starts.push(text.length);
2896
+ text += (_b = node.nodeValue) != null ? _b : "";
2897
+ }
2898
+ let nodeIndex = 0;
2899
+ const locate = (offset, isEnd) => {
2900
+ while (nodeIndex < nodes.length - 1 && (isEnd ? starts[nodeIndex + 1] < offset : starts[nodeIndex + 1] <= offset)) {
2901
+ nodeIndex++;
2902
+ }
2903
+ return [nodes[nodeIndex], offset - starts[nodeIndex]];
2904
+ };
2905
+ const haystack = text.toLowerCase();
2906
+ const ranges = [];
2907
+ for (let at = haystack.indexOf(needle); at !== -1; at = haystack.indexOf(needle, at + needle.length)) {
2908
+ const range = doc.createRange();
2909
+ range.setStart(...locate(at, false));
2910
+ range.setEnd(...locate(at + needle.length, true));
2911
+ ranges.push(range);
2912
+ }
2913
+ return ranges;
2914
+ }
2915
+
2916
+ // src/lib/search-input.ts
2917
+ var SEARCH_INPUT_PROPS = {
2918
+ type: "search",
2919
+ autoComplete: "off",
2920
+ "data-1p-ignore": true,
2921
+ // 1Password
2922
+ "data-lpignore": "true",
2923
+ // LastPass
2924
+ "data-bwignore": true,
2925
+ // Bitwarden
2926
+ "data-form-type": "other"
2927
+ // Dashlane
2928
+ };
2929
+ var SEARCH_INPUT_CLASS = "[&::-webkit-search-cancel-button]:appearance-none";
2930
+
2878
2931
  // src/lib/highlight.ts
2879
2932
  import { createHighlighter } from "shiki";
2880
- var CODE_BLOCK_LANGUAGES = ["tsx", "typescript", "bash", "shell", "json", "css", "yaml"];
2933
+ var CODE_BLOCK_LANGUAGES = ["tsx", "typescript", "bash", "shell", "json", "css", "yaml", "python"];
2881
2934
  var HIGHLIGHTER_KEY = "__monarch_shiki_highlighter__";
2882
2935
  function getHighlighter() {
2883
2936
  const g = globalThis;
@@ -2956,23 +3009,84 @@ var LINE_NUMBER_GUTTER = cn(
2956
3009
  "[&_.line]:before:text-[0.75rem]",
2957
3010
  "[&_.line]:before:select-none"
2958
3011
  );
3012
+ var SHIKI_DARK_THEME = [
3013
+ "dark:[&_.shiki]:text-(--shiki-dark)!",
3014
+ "dark:[&_.shiki_span]:text-(--shiki-dark)!",
3015
+ "dark:[&_.shiki_span]:[font-style:var(--shiki-dark-font-style)]!",
3016
+ "dark:[&_.shiki_span]:[font-weight:var(--shiki-dark-font-weight)]!",
3017
+ "dark:[&_.shiki_span]:[text-decoration:var(--shiki-dark-text-decoration)]!"
3018
+ ];
3019
+ var CODE_BLOCK_ICON_BUTTON = cn(
3020
+ "border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
3021
+ "text-muted-foreground hover:bg-accent hover:text-foreground",
3022
+ "focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
3023
+ "group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg"
3024
+ );
3025
+ function supportsHighlights() {
3026
+ return typeof CSS !== "undefined" && "highlights" in CSS && typeof Highlight !== "undefined";
3027
+ }
3028
+ function useCodeSearch(codeRef, query, renderedCode) {
3029
+ const id = useId2().replace(/[^a-zA-Z0-9_-]/g, "");
3030
+ const names = { all: `code-find-${id}`, current: `code-find-${id}-current` };
3031
+ const [ranges, setRanges] = useState5([]);
3032
+ const [current, setCurrent] = useState5(0);
3033
+ useEffect3(() => {
3034
+ const root = codeRef.current;
3035
+ const next = root ? findTextRanges(root, query) : [];
3036
+ setRanges(next);
3037
+ setCurrent(0);
3038
+ if (!supportsHighlights() || next.length === 0) return;
3039
+ CSS.highlights.set(names.all, new Highlight(...next));
3040
+ return () => {
3041
+ CSS.highlights.delete(names.all);
3042
+ };
3043
+ }, [codeRef, query, renderedCode, names.all]);
3044
+ useEffect3(() => {
3045
+ var _a, _b;
3046
+ const range = ranges[current];
3047
+ if (!range) return;
3048
+ (_b = (_a = range.startContainer.parentElement) == null ? void 0 : _a.scrollIntoView) == null ? void 0 : _b.call(_a, { block: "nearest", inline: "nearest" });
3049
+ if (!supportsHighlights()) return;
3050
+ CSS.highlights.set(names.current, new Highlight(range));
3051
+ return () => {
3052
+ CSS.highlights.delete(names.current);
3053
+ };
3054
+ }, [ranges, current, names.current]);
3055
+ const step = (by) => setCurrent((i) => ranges.length ? (i + by + ranges.length) % ranges.length : 0);
3056
+ return {
3057
+ count: ranges.length,
3058
+ current,
3059
+ next: () => step(1),
3060
+ previous: () => step(-1),
3061
+ highlightCss: `::highlight(${names.all}){background-color:color-mix(in oklch,var(--status-warning) 30%,transparent)}::highlight(${names.current}){background-color:color-mix(in oklch,var(--status-warning) 65%,transparent)}`
3062
+ };
3063
+ }
2959
3064
  function CodeBlockBody(_a) {
2960
3065
  var _b = _a, {
2961
3066
  className,
2962
3067
  code,
2963
3068
  maxHeight,
3069
+ searchable = false,
2964
3070
  copyButton,
2965
3071
  downloadButton
2966
3072
  } = _b, props = __objRest(_b, [
2967
3073
  "className",
2968
3074
  "code",
2969
3075
  "maxHeight",
3076
+ "searchable",
2970
3077
  "copyButton",
2971
3078
  "downloadButton"
2972
3079
  ]);
2973
3080
  const { content, lineNumbers } = code;
2974
3081
  const [html, setHtml] = useState5(null);
2975
3082
  const language = code.type === "parsed" ? code.language : null;
3083
+ const [searchOpen, setSearchOpen] = useState5(false);
3084
+ const [query, setQuery] = useState5("");
3085
+ const codeRef = useRef3(null);
3086
+ const searchInputRef = useRef3(null);
3087
+ const searchInputId = `code-search-${useId2()}`;
3088
+ const searchButtonRef = useRef3(null);
3089
+ const search = useCodeSearch(codeRef, searchOpen ? query : "", html != null ? html : content);
2976
3090
  useEffect3(() => {
2977
3091
  if (!language) {
2978
3092
  setHtml(null);
@@ -2986,32 +3100,127 @@ function CodeBlockBody(_a) {
2986
3100
  cancelled = true;
2987
3101
  };
2988
3102
  }, [content, language]);
2989
- return /* @__PURE__ */ jsxs4(CardContent, __spreadProps(__spreadValues({ "data-line-numbers": lineNumbers || void 0, className: cn("relative p-0!", className) }, props), { children: [
2990
- (copyButton || downloadButton) && /* @__PURE__ */ jsxs4("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-hover/code-block:opacity-100", children: [
2991
- copyButton,
2992
- downloadButton
2993
- ] }),
2994
- /* @__PURE__ */ jsx6(
2995
- "div",
3103
+ const openSearch = () => {
3104
+ var _a2, _b2;
3105
+ setSearchOpen(true);
3106
+ (_a2 = searchInputRef.current) == null ? void 0 : _a2.focus();
3107
+ (_b2 = searchInputRef.current) == null ? void 0 : _b2.select();
3108
+ };
3109
+ const closeSearch = useCallback3(() => {
3110
+ var _a2;
3111
+ setSearchOpen(false);
3112
+ setQuery("");
3113
+ (_a2 = searchButtonRef.current) == null ? void 0 : _a2.focus();
3114
+ }, []);
3115
+ useEffect3(() => {
3116
+ if (!searchOpen) return;
3117
+ const onKeyDown = (event) => {
3118
+ if (event.key !== "Escape" || event.target !== searchInputRef.current) return;
3119
+ event.preventDefault();
3120
+ event.stopPropagation();
3121
+ closeSearch();
3122
+ };
3123
+ window.addEventListener("keydown", onKeyDown, { capture: true });
3124
+ return () => window.removeEventListener("keydown", onKeyDown, { capture: true });
3125
+ }, [searchOpen, closeSearch]);
3126
+ const searchButton = searchable && /* @__PURE__ */ jsxs4(Tooltip3, { children: [
3127
+ /* @__PURE__ */ jsx6(TooltipTrigger2, { asChild: true, children: /* @__PURE__ */ jsx6(
3128
+ "button",
2996
3129
  {
2997
- className: cn("overflow-x-auto", maxHeight && "overflow-y-auto"),
2998
- style: maxHeight ? { maxHeight } : void 0,
2999
- children: /* @__PURE__ */ jsx6(
3000
- "div",
3001
- {
3002
- className: cn(
3003
- "[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3",
3004
- html && "[&_pre]:bg-transparent!",
3005
- lineNumbers && LINE_NUMBER_GUTTER
3006
- ),
3007
- children: html ? /* @__PURE__ */ jsx6("div", { dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx6("pre", { children: /* @__PURE__ */ jsx6("code", { className: lineNumbers ? void 0 : "whitespace-pre", children: lineNumbers ? content.split("\n").map((line, i) => /* @__PURE__ */ jsxs4("span", { className: "line", children: [
3008
- line,
3009
- "\n"
3010
- ] }, i)) : content }) })
3011
- }
3012
- )
3130
+ ref: searchButtonRef,
3131
+ type: "button",
3132
+ "data-slot": "code-block-search-button",
3133
+ "aria-label": "Search",
3134
+ "aria-expanded": searchOpen,
3135
+ onClick: openSearch,
3136
+ className: CODE_BLOCK_ICON_BUTTON,
3137
+ children: /* @__PURE__ */ jsx6(Icon3, { name: "magnifying-glass", size: "sm" })
3013
3138
  }
3014
- )
3139
+ ) }),
3140
+ /* @__PURE__ */ jsx6(TooltipContent2, { children: "Search" })
3141
+ ] });
3142
+ return /* @__PURE__ */ jsxs4(CardContent, __spreadProps(__spreadValues({ "data-line-numbers": lineNumbers || void 0, className: cn("relative p-0!", className) }, props), { children: [
3143
+ searchable && searchOpen && /* @__PURE__ */ jsxs4("div", { "data-slot": "code-block-search", className: "p-2", children: [
3144
+ /* @__PURE__ */ jsx6("style", { children: search.highlightCss }),
3145
+ /* @__PURE__ */ jsxs4(InputGroup3, { children: [
3146
+ /* @__PURE__ */ jsx6(InputGroupAddon3, { children: /* @__PURE__ */ jsx6(Icon3, { name: "magnifying-glass" }) }),
3147
+ /* @__PURE__ */ jsx6(
3148
+ InputGroupInput2,
3149
+ __spreadProps(__spreadValues({
3150
+ ref: searchInputRef,
3151
+ autoFocus: true
3152
+ }, SEARCH_INPUT_PROPS), {
3153
+ id: searchInputId,
3154
+ className: SEARCH_INPUT_CLASS,
3155
+ placeholder: "Find\u2026",
3156
+ "aria-label": "Find in code",
3157
+ value: query,
3158
+ onChange: (e) => setQuery(e.target.value),
3159
+ onKeyDown: (e) => {
3160
+ if (e.key === "Enter") {
3161
+ e.preventDefault();
3162
+ if (e.shiftKey) search.previous();
3163
+ else search.next();
3164
+ }
3165
+ }
3166
+ })
3167
+ ),
3168
+ /* @__PURE__ */ jsxs4(InputGroupAddon3, { align: "inline-end", children: [
3169
+ /* @__PURE__ */ jsx6(InputGroupText2, { "aria-live": "polite", className: "text-caption tabular-nums", children: query ? search.count ? `${search.current + 1} of ${search.count}` : "No results" : null }),
3170
+ /* @__PURE__ */ jsx6(
3171
+ InputGroupButton,
3172
+ {
3173
+ size: "icon-xs",
3174
+ "aria-label": "Previous match",
3175
+ disabled: search.count === 0,
3176
+ onClick: search.previous,
3177
+ children: /* @__PURE__ */ jsx6(Icon3, { name: "chevron-up" })
3178
+ }
3179
+ ),
3180
+ /* @__PURE__ */ jsx6(
3181
+ InputGroupButton,
3182
+ {
3183
+ size: "icon-xs",
3184
+ "aria-label": "Next match",
3185
+ disabled: search.count === 0,
3186
+ onClick: search.next,
3187
+ children: /* @__PURE__ */ jsx6(Icon3, { name: "chevron-down" })
3188
+ }
3189
+ ),
3190
+ /* @__PURE__ */ jsx6(InputGroupButton, { size: "icon-xs", "aria-label": "Close search", onClick: closeSearch, children: /* @__PURE__ */ jsx6(Icon3, { name: "xmark" }) })
3191
+ ] })
3192
+ ] })
3193
+ ] }),
3194
+ /* @__PURE__ */ jsxs4("div", { className: "relative", children: [
3195
+ (searchButton || copyButton || downloadButton) && /* @__PURE__ */ jsxs4("div", { className: "absolute top-2 right-2 z-10 flex items-center gap-1 opacity-0 transition-opacity group-focus-within/code-block:opacity-100 group-hover/code-block:opacity-100", children: [
3196
+ searchButton,
3197
+ copyButton,
3198
+ downloadButton
3199
+ ] }),
3200
+ /* @__PURE__ */ jsx6(
3201
+ "div",
3202
+ {
3203
+ className: cn("overflow-x-auto", maxHeight && "overflow-y-auto"),
3204
+ style: maxHeight ? { maxHeight } : void 0,
3205
+ children: /* @__PURE__ */ jsx6(
3206
+ "div",
3207
+ {
3208
+ ref: codeRef,
3209
+ className: cn(
3210
+ "[&_code]:font-mono [&_pre]:m-0 [&_pre]:p-4 group-data-[size=sm]/code-block:[&_pre]:p-3",
3211
+ html && "[&_pre]:bg-transparent!",
3212
+ html && SHIKI_DARK_THEME,
3213
+ lineNumbers && LINE_NUMBER_GUTTER
3214
+ ),
3215
+ children: html ? /* @__PURE__ */ jsx6("div", { dangerouslySetInnerHTML: { __html: html } }) : /* @__PURE__ */ jsx6("pre", { children: /* @__PURE__ */ jsx6("code", { className: lineNumbers ? void 0 : "whitespace-pre", children: lineNumbers ? content.split("\n").map((line, i) => /* @__PURE__ */ jsxs4("span", { className: "line", children: [
3216
+ line,
3217
+ "\n"
3218
+ ] }, i)) : content }) })
3219
+ }
3220
+ )
3221
+ }
3222
+ )
3223
+ ] })
3015
3224
  ] }));
3016
3225
  }
3017
3226
  function CodeBlockFooter(_a) {
@@ -3046,13 +3255,7 @@ function CodeBlockCopyButton(_a) {
3046
3255
  __spreadProps(__spreadValues({
3047
3256
  "data-slot": "code-block-copy-button",
3048
3257
  onClick: handleCopy,
3049
- className: cn(
3050
- "border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
3051
- "text-muted-foreground hover:bg-accent hover:text-foreground",
3052
- "focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
3053
- "group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg",
3054
- className
3055
- ),
3258
+ className: cn(CODE_BLOCK_ICON_BUTTON, className),
3056
3259
  "aria-label": copied ? "Copied" : "Copy code"
3057
3260
  }, props), {
3058
3261
  children: copied ? /* @__PURE__ */ jsx6(Icon3, { name: "check", size: "sm", className: "text-green-500" }) : /* @__PURE__ */ jsx6(Icon3, { name: "copy", size: "sm" })
@@ -3093,13 +3296,7 @@ function CodeBlockDownloadButton(_a) {
3093
3296
  __spreadProps(__spreadValues({
3094
3297
  "data-slot": "code-block-download-button",
3095
3298
  onClick: handleDownload,
3096
- className: cn(
3097
- "border-border bg-background inline-flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border motion-safe:transition-colors",
3098
- "text-muted-foreground hover:bg-accent hover:text-foreground",
3099
- "focus-visible:ring-ring/30 focus-visible:ring-2 focus-visible:outline-none",
3100
- "group-data-[variant=terminal]/code-block:border-terminal-border group-data-[variant=terminal]/code-block:bg-terminal-bg group-data-[variant=terminal]/code-block:text-terminal-muted group-data-[variant=terminal]/code-block:hover:bg-terminal-muted-hover-bg group-data-[variant=terminal]/code-block:hover:text-terminal-muted-hover-fg",
3101
- className
3102
- ),
3299
+ className: cn(CODE_BLOCK_ICON_BUTTON, className),
3103
3300
  "aria-label": downloaded ? "Downloaded" : "Download code"
3104
3301
  }, props), {
3105
3302
  children: downloaded ? /* @__PURE__ */ jsx6(Icon3, { name: "check", size: "sm", className: "text-green-500" }) : /* @__PURE__ */ jsx6(Icon3, { name: "download", size: "sm" })
@@ -3645,7 +3842,7 @@ import {
3645
3842
  useReactTable
3646
3843
  } from "@tanstack/react-table";
3647
3844
  import { Icon as Icon11 } from "@upbound/monarch-core";
3648
- import { InputGroup as InputGroup3, InputGroupAddon as InputGroupAddon3, InputGroupInput as InputGroupInput2 } from "@upbound/monarch-core";
3845
+ import { InputGroup as InputGroup4, InputGroupAddon as InputGroupAddon4, InputGroupInput as InputGroupInput3 } from "@upbound/monarch-core";
3649
3846
 
3650
3847
  // src/lib/data-table/contextual-filters.ts
3651
3848
  function valueToString(value) {
@@ -3741,7 +3938,7 @@ function getColumnFilterCount(filterValue, filter) {
3741
3938
  }
3742
3939
 
3743
3940
  // src/hooks/use-table-column-resizing.ts
3744
- import { useCallback as useCallback4, useEffect as useEffect4, useRef as useRef3, useState as useState7 } from "react";
3941
+ import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState7 } from "react";
3745
3942
  var RESIZE_MIN = 40;
3746
3943
  var RESIZE_MAX = 500;
3747
3944
  var RESIZE_MAX_UNBOUNDED = Number.MAX_SAFE_INTEGER;
@@ -3829,7 +4026,7 @@ function useTableColumnResizing({
3829
4026
  maxSize
3830
4027
  } = {}) {
3831
4028
  const [columnSizing, setColumnSizing] = useState7(() => clampSizing(initialSizing != null ? initialSizing : {}, maxSize));
3832
- const persistTimerRef = useRef3(null);
4029
+ const persistTimerRef = useRef4(null);
3833
4030
  useEffect4(() => {
3834
4031
  if (!initialSizing) return;
3835
4032
  const next = clampSizing(initialSizing, maxSize);
@@ -3844,14 +4041,14 @@ function useTableColumnResizing({
3844
4041
  if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
3845
4042
  };
3846
4043
  }, []);
3847
- const debouncedPersist = useCallback4(
4044
+ const debouncedPersist = useCallback5(
3848
4045
  (sizing) => {
3849
4046
  if (persistTimerRef.current) clearTimeout(persistTimerRef.current);
3850
4047
  persistTimerRef.current = setTimeout(() => onColumnSizingPersist == null ? void 0 : onColumnSizingPersist(sizing), PERSIST_DEBOUNCE_MS);
3851
4048
  },
3852
4049
  [onColumnSizingPersist]
3853
4050
  );
3854
- const handleColumnSizingChange = useCallback4(
4051
+ const handleColumnSizingChange = useCallback5(
3855
4052
  (updaterOrValue) => {
3856
4053
  setColumnSizing((prev) => {
3857
4054
  const raw = typeof updaterOrValue === "function" ? updaterOrValue(prev) : updaterOrValue;
@@ -3862,18 +4059,18 @@ function useTableColumnResizing({
3862
4059
  },
3863
4060
  [debouncedPersist, maxSize]
3864
4061
  );
3865
- const cancelPendingPersist = useCallback4(() => {
4062
+ const cancelPendingPersist = useCallback5(() => {
3866
4063
  if (persistTimerRef.current) {
3867
4064
  clearTimeout(persistTimerRef.current);
3868
4065
  persistTimerRef.current = null;
3869
4066
  }
3870
4067
  }, []);
3871
- const handleAutosizeAll = useCallback4(() => {
4068
+ const handleAutosizeAll = useCallback5(() => {
3872
4069
  cancelPendingPersist();
3873
4070
  setColumnSizing({});
3874
4071
  onColumnSizingPersist == null ? void 0 : onColumnSizingPersist({});
3875
4072
  }, [cancelPendingPersist, onColumnSizingPersist]);
3876
- const handleAutosizeColumn = useCallback4(
4073
+ const handleAutosizeColumn = useCallback5(
3877
4074
  (columnId) => {
3878
4075
  cancelPendingPersist();
3879
4076
  setColumnSizing((prev) => {
@@ -3887,7 +4084,7 @@ function useTableColumnResizing({
3887
4084
  },
3888
4085
  [cancelPendingPersist, onColumnSizingPersist]
3889
4086
  );
3890
- const handleResetColumnSize = useCallback4(
4087
+ const handleResetColumnSize = useCallback5(
3891
4088
  (columnId) => {
3892
4089
  cancelPendingPersist();
3893
4090
  setColumnSizing((prev) => {
@@ -4280,8 +4477,7 @@ function DataTableEmptyView({
4280
4477
  emptyView,
4281
4478
  tableState,
4282
4479
  filterCount,
4283
- resetFilters,
4284
- className
4480
+ resetFilters
4285
4481
  }) {
4286
4482
  var _a, _b, _c, _d, _e, _f, _g;
4287
4483
  const isFilteredEmpty = tableState === "empty" && filterCount > 0;
@@ -4291,9 +4487,7 @@ function DataTableEmptyView({
4291
4487
  /* @__PURE__ */ jsx10(Icon6, { name: "xmark" }),
4292
4488
  "Clear filters"
4293
4489
  ] }) : null;
4294
- return /* @__PURE__ */ jsx10("div", { className: cn("flex size-full flex-col justify-center p-6", wrapperClassName, className), children: config && // The error state ignores its own config's `className` for outer
4295
- // padding, matching the source's forced `pt-0` on that branch.
4296
- /* @__PURE__ */ jsxs8(Empty, { className: tableState === "error" ? "pt-0" : config.className, children: [
4490
+ return /* @__PURE__ */ jsx10("div", { className: cn("flex size-full flex-col justify-center", wrapperClassName), children: config && /* @__PURE__ */ jsxs8(Empty, { className: cn("border border-dashed", config.className), children: [
4297
4491
  /* @__PURE__ */ jsxs8(EmptyHeader, { children: [
4298
4492
  config.icon && /* @__PURE__ */ jsx10(EmptyMedia, { children: /* @__PURE__ */ jsx10(IconTile, { variant: "branded", children: /* @__PURE__ */ jsx10(Icon6, { name: config.icon }) }) }),
4299
4493
  config.header && /* @__PURE__ */ jsx10(EmptyTitle, { children: config.header }),
@@ -4504,7 +4698,7 @@ import { Fragment as Fragment7, jsx as jsx13, jsxs as jsxs11 } from "react/jsx-r
4504
4698
  var HEADER_SURFACE_BG = "bg-[color-mix(in_oklch,var(--muted)_50%,var(--card))]";
4505
4699
  var TABLE_HEAD_CLASSES = "text-body-sm text-muted-foreground h-10 px-4 text-left align-middle font-medium whitespace-nowrap has-[[role=checkbox]]:pr-0";
4506
4700
  var TABLE_CELL_CLASSES = "px-4 py-2 align-middle whitespace-nowrap has-[[role=checkbox]]:pr-0";
4507
- var TABLE_ROW_CLASSES = "hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-muted border-b motion-safe:transition-colors";
4701
+ var TABLE_ROW_CLASSES = "hover:bg-muted/50 has-[[aria-haspopup][aria-expanded=true]]:bg-muted/50 data-[state=selected]:bg-muted border-b motion-safe:transition-colors";
4508
4702
  function DataTableRoot({
4509
4703
  layout = "auto",
4510
4704
  gridTemplateColumns,
@@ -4968,18 +5162,19 @@ function DataTableBody({
4968
5162
  );
4969
5163
  }
4970
5164
  const rows = tableRows.map((row) => {
4971
- var _a, _b, _c, _d;
5165
+ var _a, _b, _c, _d, _e;
4972
5166
  if ((_a = row.getIsGrouped) == null ? void 0 : _a.call(row)) {
4973
5167
  const groupLabel = (_c = row.groupingValue) != null ? _c : row.groupingColumnId ? (_b = row.getGroupingValue) == null ? void 0 : _b.call(row, row.groupingColumnId) : void 0;
4974
5168
  return /* @__PURE__ */ jsx13(DataTableGroupRow, { groupLabel: String(groupLabel != null ? groupLabel : ""), colSpan, layout }, row.id);
4975
5169
  }
4976
- const isExpanded = !!(renderExpandedRow && ((_d = row.getIsExpanded) == null ? void 0 : _d.call(row)));
4977
- const expandedRow = renderExpandedRow ? renderExpandedRow(row.original) : null;
5170
+ const isExpanded = !!(((_d = row.getCanExpand) == null ? void 0 : _d.call(row)) && ((_e = row.getIsExpanded) == null ? void 0 : _e.call(row)));
5171
+ const expandedRow = renderExpandedRow && isExpanded ? renderExpandedRow(row.original) : null;
4978
5172
  return /* @__PURE__ */ jsxs11(React8.Fragment, { children: [
4979
5173
  /* @__PURE__ */ jsx13(
4980
5174
  DataTableRow,
4981
- __spreadProps(__spreadValues({}, onRowRender ? onRowRender(row.original) : {}), {
5175
+ __spreadProps(__spreadValues({}, onRowRender ? onRowRender(row.original, row) : {}), {
4982
5176
  "data-expanded": isExpanded || void 0,
5177
+ "data-depth": row.depth || void 0,
4983
5178
  layout,
4984
5179
  children: row.getVisibleCells().filter((cell) => !isFilterOnlyColumn(cell.column)).map((cell) => /* @__PURE__ */ jsx13(
4985
5180
  DataTableCell,
@@ -4995,7 +5190,7 @@ function DataTableBody({
4995
5190
  ))
4996
5191
  })
4997
5192
  ),
4998
- /* @__PURE__ */ jsx13(DataTableExpandedRow, { colSpan, show: isExpanded, layout, children: expandedRow })
5193
+ /* @__PURE__ */ jsx13(DataTableExpandedRow, { colSpan, show: expandedRow != null, layout, children: expandedRow })
4999
5194
  ] }, row.id);
5000
5195
  });
5001
5196
  if (isGrid) {
@@ -5730,7 +5925,8 @@ function DataTable({
5730
5925
  renderExpandedRow,
5731
5926
  expanded: parentExpanded,
5732
5927
  onExpandedChange: parentOnExpandedChange,
5733
- disableSearch = false,
5928
+ disableSearch: deprecatedDisableSearch = false,
5929
+ search,
5734
5930
  disableFilters = false,
5735
5931
  disableSorting = false,
5736
5932
  paginationConfig = true,
@@ -5765,8 +5961,9 @@ function DataTable({
5765
5961
  celFilterConfig,
5766
5962
  filterVisibility
5767
5963
  }) {
5768
- var _a, _b, _c, _d, _e, _f;
5964
+ var _a, _b, _c, _d, _e, _f, _g, _h;
5769
5965
  const isGrid = layout === "grid";
5966
+ const searchInputId = `table-search-${React11.useId()}`;
5770
5967
  const tableBodyScrollRef = React11.useRef(null);
5771
5968
  const [innerGlobalFilter, setInnerGlobalFilter] = React11.useState("");
5772
5969
  const [innerColumnOrdering, setInnerColumnOrdering] = React11.useState([]);
@@ -5839,23 +6036,24 @@ function DataTable({
5839
6036
  const grouping = parentGrouping != null ? parentGrouping : innerGrouping;
5840
6037
  const setGrouping = parentSetGrouping != null ? parentSetGrouping : setInnerGrouping;
5841
6038
  const hasGrouping = grouping.length > 0;
6039
+ const hasSubRows = !!config.getSubRows;
6040
+ const hasExpansion = !!renderExpandedRow || hasSubRows;
6041
+ const disableSearch = (_f = search == null ? void 0 : search.disabled) != null ? _f : deprecatedDisableSearch;
5842
6042
  const hasExternalSearch = parentGlobalFilter !== void 0;
5843
6043
  const hasExternalFilters = parentColumnFilters !== void 0;
5844
6044
  const shouldDisableFiltersModels = !!disableFilters && !!disableSearch && !hasExternalSearch && !hasExternalFilters;
5845
- const table = useReactTable(__spreadProps(__spreadValues(__spreadProps(__spreadValues(__spreadProps(__spreadValues({}, config), {
6045
+ const table = useReactTable(__spreadProps(__spreadValues(__spreadProps(__spreadValues(__spreadValues(__spreadProps(__spreadValues({}, config), {
5846
6046
  getCoreRowModel: getCoreRowModel(),
5847
6047
  getSortedRowModel: !disableSorting ? getSortedRowModel() : void 0,
5848
6048
  getPaginationRowModel: !shouldDisablePagination ? getPaginationRowModel() : void 0,
5849
6049
  getFilteredRowModel: !shouldDisableFiltersModels ? getFilteredRowModel() : void 0,
5850
6050
  getFacetedRowModel: !shouldDisableFiltersModels ? getFacetedRowModel() : void 0,
5851
6051
  getFacetedUniqueValues: !shouldDisableFiltersModels ? getFacetedUniqueValues() : void 0
5852
- }), !!renderExpandedRow && {
5853
- getExpandedRowModel: getExpandedRowModel(),
5854
- getRowCanExpand: () => true,
5855
- paginateExpandedRows: false
5856
- }), {
6052
+ }), !!renderExpandedRow && { getRowCanExpand: () => true }), hasExpansion && { paginateExpandedRows: false }), {
5857
6053
  getGroupedRowModel: hasGrouping ? getGroupedRowModel() : void 0,
5858
- getExpandedRowModel: hasGrouping ? getExpandedRowModel() : void 0,
6054
+ // Only grouping and `getSubRows` put extra rows in the row model; `renderExpandedRow`
6055
+ // content renders outside it (see `DataTableBody`).
6056
+ getExpandedRowModel: hasGrouping || hasSubRows ? getExpandedRowModel() : void 0,
5859
6057
  groupedColumnMode: hasGrouping ? "reorder" : void 0,
5860
6058
  enableFilters: !shouldDisableFiltersModels,
5861
6059
  enableGlobalFilter: !shouldDisableFiltersModels,
@@ -5878,7 +6076,7 @@ function DataTable({
5878
6076
  columnVisibility: disableColumnVisibility ? {} : columnVisibility,
5879
6077
  columnSizing: disableColumnResizing ? {} : columnSizing,
5880
6078
  columnPinning: disableColumnPinning ? { left: [], right: [] } : columnPinning
5881
- }, !!renderExpandedRow && { expanded }), {
6079
+ }, hasExpansion && { expanded }), {
5882
6080
  grouping
5883
6081
  }), hasGrouping && { expanded: true }),
5884
6082
  manualSorting: serverSidePagination,
@@ -5892,7 +6090,7 @@ function DataTable({
5892
6090
  onColumnVisibilityChange: disableColumnVisibility ? noop : setColumnVisibility,
5893
6091
  onColumnSizingChange: disableColumnResizing ? noop : handleColumnSizingChange,
5894
6092
  onColumnPinningChange: disableColumnPinning ? noop : setColumnPinning
5895
- }), !!renderExpandedRow && {
6093
+ }), hasExpansion && {
5896
6094
  onExpandedChange: (updater) => setExpanded((prev) => typeof updater === "function" ? updater(prev) : updater)
5897
6095
  }), {
5898
6096
  onGroupingChange: setGrouping
@@ -5981,17 +6179,19 @@ function DataTable({
5981
6179
  }, [columnOrdering, disableColumnReordering, flatColumns, setColumnOrdering]);
5982
6180
  const showViewHeader = !!title || !!Actions;
5983
6181
  const showColumnOptions = !disableColumnReordering || !disableColumnVisibility;
5984
- const toolbarSearch = !disableSearch && /* @__PURE__ */ jsxs13(InputGroup3, { className: "w-70", children: [
5985
- /* @__PURE__ */ jsx17(InputGroupAddon3, { align: "inline-start", children: /* @__PURE__ */ jsx17(Icon11, { name: "magnifying-glass" }) }),
6182
+ const toolbarSearch = !disableSearch && /* @__PURE__ */ jsxs13(InputGroup4, { className: cn("w-70", search == null ? void 0 : search.className), children: [
6183
+ /* @__PURE__ */ jsx17(InputGroupAddon4, { align: "inline-start", children: /* @__PURE__ */ jsx17(Icon11, { name: "magnifying-glass" }) }),
5986
6184
  /* @__PURE__ */ jsx17(
5987
- InputGroupInput2,
5988
- {
6185
+ InputGroupInput3,
6186
+ __spreadProps(__spreadValues({}, SEARCH_INPUT_PROPS), {
6187
+ id: searchInputId,
6188
+ className: SEARCH_INPUT_CLASS,
5989
6189
  "aria-label": "Search",
5990
- placeholder: "Search\u2026",
6190
+ placeholder: (_g = search == null ? void 0 : search.placeholder) != null ? _g : "Search\u2026",
5991
6191
  disabled: shouldDisable,
5992
6192
  value: globalFilter,
5993
6193
  onChange: (e) => setGlobalFilter(e.target.value)
5994
- }
6194
+ })
5995
6195
  )
5996
6196
  ] });
5997
6197
  const toolbarColumns = !!showColumnOptions && /* @__PURE__ */ jsx17(
@@ -6100,7 +6300,7 @@ function DataTable({
6100
6300
  DataTablePagination,
6101
6301
  {
6102
6302
  rowCount,
6103
- rowCountRelation: (_f = config.rowCountRelation) != null ? _f : "eq",
6303
+ rowCountRelation: (_h = config.rowCountRelation) != null ? _h : "eq",
6104
6304
  paginationConfig,
6105
6305
  pageSize: pagination.pageSize,
6106
6306
  pageIndex: pagination.pageIndex,
@@ -6313,7 +6513,7 @@ var useGraphActionsContext = () => {
6313
6513
  };
6314
6514
 
6315
6515
  // src/graph/hooks/useGraphActions.ts
6316
- import { useCallback as useCallback9, useState as useState12 } from "react";
6516
+ import { useCallback as useCallback10, useState as useState12 } from "react";
6317
6517
 
6318
6518
  // src/graph/graph-node.tsx
6319
6519
  import * as React13 from "react";
@@ -6608,37 +6808,37 @@ function GraphNodeSkeleton(_props) {
6608
6808
  var useGraphActions = (initialNodes = [], initialEdges = []) => {
6609
6809
  const [nodes, setNodes] = useState12(initialNodes);
6610
6810
  const [edges, setEdges] = useState12(initialEdges);
6611
- const addNodes = useCallback9((newNodes) => {
6811
+ const addNodes = useCallback10((newNodes) => {
6612
6812
  setNodes((prevNodes) => {
6613
6813
  const existingIds = new Set(prevNodes.map((node) => node.id));
6614
6814
  const uniqueNewNodes = newNodes.filter((node) => !existingIds.has(node.id));
6615
6815
  return [...prevNodes, ...uniqueNewNodes];
6616
6816
  });
6617
6817
  }, []);
6618
- const addEdges = useCallback9((newEdges) => {
6818
+ const addEdges = useCallback10((newEdges) => {
6619
6819
  setEdges((prevEdges) => {
6620
6820
  const existingIds = new Set(prevEdges.map((edge) => edge.id));
6621
6821
  const uniqueNewEdges = newEdges.filter((edge) => !existingIds.has(edge.id));
6622
6822
  return [...prevEdges, ...uniqueNewEdges];
6623
6823
  });
6624
6824
  }, []);
6625
- const removeNodes = useCallback9((nodeIds) => {
6825
+ const removeNodes = useCallback10((nodeIds) => {
6626
6826
  const nodeIdSet = new Set(nodeIds);
6627
6827
  setNodes((prevNodes) => prevNodes.filter((node) => !nodeIdSet.has(node.id)));
6628
6828
  setEdges((prevEdges) => prevEdges.filter((edge) => !nodeIdSet.has(edge.source) && !nodeIdSet.has(edge.target)));
6629
6829
  }, []);
6630
- const removeEdges = useCallback9((edgeIds) => {
6830
+ const removeEdges = useCallback10((edgeIds) => {
6631
6831
  const edgeIdSet = new Set(edgeIds);
6632
6832
  setEdges((prevEdges) => prevEdges.filter((edge) => !edgeIdSet.has(edge.id)));
6633
6833
  }, []);
6634
- const resetGraph = useCallback9(() => {
6834
+ const resetGraph = useCallback10(() => {
6635
6835
  setNodes(initialNodes);
6636
6836
  setEdges(initialEdges);
6637
6837
  }, [initialNodes, initialEdges]);
6638
- const updateNode = useCallback9((nodeId, node) => {
6838
+ const updateNode = useCallback10((nodeId, node) => {
6639
6839
  setNodes((prevNodes) => prevNodes.map((n) => n.id === nodeId ? node : n));
6640
6840
  }, []);
6641
- const toggleNodeExpansion = useCallback9((nodeId) => {
6841
+ const toggleNodeExpansion = useCallback10((nodeId) => {
6642
6842
  setNodes(
6643
6843
  (prevNodes) => prevNodes.map((node) => {
6644
6844
  if (node.id === nodeId && isDefaultNode(node)) {
@@ -6699,7 +6899,7 @@ import {
6699
6899
  getFilteredRowModel as getFilteredRowModel2,
6700
6900
  useReactTable as useReactTable2
6701
6901
  } from "@tanstack/react-table";
6702
- import { Icon as Icon14, InputGroup as InputGroup4, InputGroupAddon as InputGroupAddon4, InputGroupInput as InputGroupInput3 } from "@upbound/monarch-core";
6902
+ import { Icon as Icon14, InputGroup as InputGroup5, InputGroupAddon as InputGroupAddon5, InputGroupInput as InputGroupInput4 } from "@upbound/monarch-core";
6703
6903
 
6704
6904
  // src/graph/graph-view.tsx
6705
6905
  import * as React16 from "react";
@@ -6842,9 +7042,9 @@ var useAutoLayout = (nodes, edges, { viewportFocusNodeId }) => {
6842
7042
  };
6843
7043
 
6844
7044
  // src/graph/hooks/useNodeVisibility.ts
6845
- import { useCallback as useCallback10, useMemo as useMemo11 } from "react";
7045
+ import { useCallback as useCallback11, useMemo as useMemo11 } from "react";
6846
7046
  var useNodeVisibility = (nodes, edges) => {
6847
- const hasRealChildren = useCallback10(
7047
+ const hasRealChildren = useCallback11(
6848
7048
  (nodeId) => {
6849
7049
  const childEdges = edges.filter((edge) => edge.source === nodeId);
6850
7050
  const childNodes = childEdges.map((edge) => nodes.find((node) => node.id === edge.target)).filter(Boolean);
@@ -6852,7 +7052,7 @@ var useNodeVisibility = (nodes, edges) => {
6852
7052
  },
6853
7053
  [nodes, edges]
6854
7054
  );
6855
- const areAllAncestorsExpanded = useCallback10(
7055
+ const areAllAncestorsExpanded = useCallback11(
6856
7056
  (nodeId) => {
6857
7057
  const parentEdges = edges.filter((edge) => edge.target === nodeId);
6858
7058
  if (parentEdges.length === 0) {
@@ -7042,6 +7242,7 @@ function Graph({
7042
7242
  }) {
7043
7243
  var _a, _b, _c;
7044
7244
  const graphRef = React17.useRef(null);
7245
+ const searchInputId = `graph-search-${React17.useId()}`;
7045
7246
  const [innerGlobalFilter, setInnerGlobalFilter] = React17.useState("");
7046
7247
  const [innerColumnFilters, setInnerColumnFilters] = React17.useState([]);
7047
7248
  const globalFilter = parentGlobalFilter != null ? parentGlobalFilter : innerGlobalFilter;
@@ -7107,17 +7308,19 @@ function Graph({
7107
7308
  return /* @__PURE__ */ jsxs18("div", { id: `graph-${id}`, className: cn("flex size-full flex-1 flex-col", className), children: [
7108
7309
  !!title && /* @__PURE__ */ jsx23(SectionHeader, { className: "mb-4", children: /* @__PURE__ */ jsx23(SectionHeaderContent, { children: /* @__PURE__ */ jsx23(SectionHeaderTitle, { children: title }) }) }),
7109
7310
  showToolbar && /* @__PURE__ */ jsxs18("div", { className: "mb-4 flex flex-row gap-2", children: [
7110
- !disableSearch && /* @__PURE__ */ jsxs18(InputGroup4, { className: "w-70", children: [
7111
- /* @__PURE__ */ jsx23(InputGroupAddon4, { align: "inline-start", children: /* @__PURE__ */ jsx23(Icon14, { name: "magnifying-glass" }) }),
7311
+ !disableSearch && /* @__PURE__ */ jsxs18(InputGroup5, { className: "w-70", children: [
7312
+ /* @__PURE__ */ jsx23(InputGroupAddon5, { align: "inline-start", children: /* @__PURE__ */ jsx23(Icon14, { name: "magnifying-glass" }) }),
7112
7313
  /* @__PURE__ */ jsx23(
7113
- InputGroupInput3,
7114
- {
7314
+ InputGroupInput4,
7315
+ __spreadProps(__spreadValues({}, SEARCH_INPUT_PROPS), {
7316
+ id: searchInputId,
7317
+ className: SEARCH_INPUT_CLASS,
7115
7318
  "aria-label": "Search",
7116
7319
  placeholder: "Search\u2026",
7117
7320
  disabled: shouldDisable,
7118
7321
  value: globalFilter,
7119
7322
  onChange: (e) => setGlobalFilter(e.target.value)
7120
- }
7323
+ })
7121
7324
  )
7122
7325
  ] }),
7123
7326
  !!Actions && /* @__PURE__ */ jsx23("div", { className: "ml-auto flex items-center gap-2", children: /* @__PURE__ */ jsx23(Actions, { state: graphState }) })
@@ -7156,10 +7359,10 @@ function Graph({
7156
7359
  }
7157
7360
 
7158
7361
  // src/graph/hooks/useGetViewportNodeIds.ts
7159
- import { useEffect as useEffect10, useMemo as useMemo14, useRef as useRef9, useState as useState14 } from "react";
7362
+ import { useEffect as useEffect10, useMemo as useMemo14, useRef as useRef10, useState as useState14 } from "react";
7160
7363
  import { useStore as useStore2, useViewport } from "reactflow";
7161
7364
  var useGetDebouncedViewport = (debounceDelay = 1e3) => {
7162
- const timerRef = useRef9(null);
7365
+ const timerRef = useRef10(null);
7163
7366
  const [debouncedValues, setDebouncedValues] = useState14({ x: 0, y: 0, zoom: 1, width: 0, height: 0, nodes: [] });
7164
7367
  const { x, y, zoom } = useViewport();
7165
7368
  const { width, height, nodes } = useStore2((state) => ({