@alexgorbatchev/shadcn 0.0.16 → 0.0.18

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/README.md CHANGED
@@ -52,6 +52,7 @@ Import shared controls and providers from `@alexgorbatchev/shadcn`. Import the f
52
52
  | `/fonts.css` | JetBrains Mono font stylesheet | `bun add @fontsource-variable/jetbrains-mono@^5.3.0` |
53
53
  | `/sonner` | Sonner, SonnerToaster, Toaster, toast, useSonner | No additional install; patched implementation is bundled |
54
54
  | `/input-otp` | InputOTP components | No additional install; patched implementation is bundled |
55
+ | `/virtual-table` | VirtualTable and row measurement props | `@tanstack/react-virtual@3.14.13` |
55
56
  | `/message-scroller` | MessageScroller components, hooks, and types | No additional install; patched implementation is bundled |
56
57
  | `/questionnaire` | Questionnaire components and types | No additional install; patched implementation is bundled |
57
58
 
@@ -268,6 +269,8 @@ Compose `QuestionnaireItem`, `QuestionnaireTitle`, and answer controls inside `Q
268
269
 
269
270
  `InlineEdit` mounts an edit session with a controlled `value`, `onValueChange`, `onCommit`, and `onCancel`. Mount it in place of the displayed label. Its input focuses and selects its text by default; `shouldSelectOnMount={false}` preserves the initial caret. Enter or blur commits once, Escape cancels without committing, and asynchronous saves disable the input until completion. Rejected saves display an error and allow retry.
270
271
 
272
+ `VirtualTable` renders a bounded, dynamically measured set of native table rows using TanStack Virtual. Supply `items`, stable `getItemKey`, `header`, `columnCount`, `label`, and `renderRow`; spread the supplied measurement props onto each native row. Optional `columns` accepts a colgroup. `maxHeight` defaults to `65vh`, `estimateRowHeight` to 80 pixels, and `overscan` to five rows. `retainedKeys` keeps editing rows mounted; the focused row is retained automatically. Filtering and selection belong to the consumer.
273
+
271
274
  `StarRating` takes a controlled numeric `value` from 0 to 5, `onValueChange`, and an accessible `label`. Its native radio buttons support keyboard selection and an Unrated option to clear the rating. Set `disabled` while saving. `Avatar shape="square"` displays square artwork with matching image, fallback, and border corners.
272
275
 
273
276
  `DialogContent size="wide"` uses a desktop maximum width of 576px; `size="narrow"` uses 192px, and the default uses 384px. Dialogs remain bounded by the viewport. `FieldGroup columns={2}` arranges fields in two columns on desktop and one on mobile.
@@ -157,7 +157,9 @@ var ThreadHistorySkeleton = () => /* @__PURE__ */ jsxs("div", {
157
157
  function Thread2({
158
158
  components = EMPTY_COMPONENTS,
159
159
  autoFocus = true,
160
- emptyComposerPosition = "center"
160
+ emptyComposerPosition = "center",
161
+ viewportProps,
162
+ composerInputRef
161
163
  }) {
162
164
  const isEmpty = useAuiState(isNewChatView);
163
165
  return /* @__PURE__ */ jsx(ThreadComponentsContext.Provider, {
@@ -165,11 +167,19 @@ function Thread2({
165
167
  children: /* @__PURE__ */ jsx(ThreadRoot, {
166
168
  isEmpty,
167
169
  autoFocus,
168
- emptyComposerPosition
170
+ emptyComposerPosition,
171
+ viewportProps,
172
+ composerInputRef
169
173
  })
170
174
  });
171
175
  }
172
- var ThreadRoot = ({ isEmpty, autoFocus, emptyComposerPosition }) => {
176
+ var ThreadRoot = ({
177
+ isEmpty,
178
+ autoFocus,
179
+ emptyComposerPosition,
180
+ viewportProps,
181
+ composerInputRef
182
+ }) => {
173
183
  const { Welcome = ThreadWelcome } = useContext(ThreadComponentsContext);
174
184
  const isCentered = isEmpty && emptyComposerPosition === "center";
175
185
  return /* @__PURE__ */ jsx(ThreadPrimitive.Root, {
@@ -178,6 +188,7 @@ var ThreadRoot = ({ isEmpty, autoFocus, emptyComposerPosition }) => {
178
188
  }),
179
189
  children: /* @__PURE__ */ jsx(ThreadPrimitive.Viewport, {
180
190
  turnAnchor: "top",
191
+ ...viewportProps,
181
192
  "data-slot": "aui_thread-viewport",
182
193
  render: /* @__PURE__ */ jsx("div", {
183
194
  className: "relative flex flex-1 flex-col overflow-x-auto overflow-y-scroll scroll-smooth"
@@ -201,15 +212,20 @@ var ThreadRoot = ({ isEmpty, autoFocus, emptyComposerPosition }) => {
201
212
  children: () => /* @__PURE__ */ jsx(ThreadMessage, {})
202
213
  })
203
214
  }),
215
+ /* @__PURE__ */ jsx("div", {
216
+ "aria-hidden": "true",
217
+ className: cn2(!isCentered && "flex-1")
218
+ }),
204
219
  /* @__PURE__ */ jsxs(ThreadPrimitive.ViewportFooter, {
205
220
  render: /* @__PURE__ */ jsx("div", {
206
- className: cn2("aui-thread-viewport-footer bg-background sticky bottom-0 flex flex-col gap-4 overflow-visible pb-4 md:pb-6", !isCentered && "mt-auto rounded-t-(--composer-radius)")
221
+ className: cn2("aui-thread-viewport-footer bg-background sticky bottom-0 flex flex-col gap-4 overflow-visible pb-4 md:pb-6", !isCentered && "rounded-t-(--composer-radius)")
207
222
  }),
208
223
  children: [
209
224
  /* @__PURE__ */ jsx(ThreadScrollToBottom, {}),
210
225
  /* @__PURE__ */ jsx(ThreadFollowupSuggestions2, {}),
211
226
  /* @__PURE__ */ jsx(Composer, {
212
- autoFocus
227
+ autoFocus,
228
+ inputRef: composerInputRef
213
229
  }),
214
230
  /* @__PURE__ */ jsx(AuiIf, {
215
231
  condition: (s) => isNewChatView(s) && s.composer.isEmpty,
@@ -460,7 +476,7 @@ var ThreadSuggestionItem = () => {
460
476
  })
461
477
  });
462
478
  };
463
- var Composer = ({ autoFocus }) => {
479
+ var Composer = ({ autoFocus, inputRef }) => {
464
480
  return /* @__PURE__ */ jsx(ComposerPrimitive.Root, {
465
481
  render: /* @__PURE__ */ jsx("form", {
466
482
  className: "aui-composer-root relative flex w-full flex-col"
@@ -473,6 +489,7 @@ var Composer = ({ autoFocus }) => {
473
489
  children: [
474
490
  /* @__PURE__ */ jsx(ComposerAttachments3, {}),
475
491
  /* @__PURE__ */ jsx(ComposerPrimitive.Input, {
492
+ ref: inputRef,
476
493
  placeholder: "Send a message...",
477
494
  rows: 1,
478
495
  autoFocus,
@@ -488,6 +505,7 @@ var Composer = ({ autoFocus }) => {
488
505
  });
489
506
  };
490
507
  var ComposerAction = () => {
508
+ const { ComposerActions } = useContext(ThreadComponentsContext);
491
509
  const isSending = useAuiState((s) => s.composer.submission !== undefined && !(s.thread.isRunning && s.thread.capabilities.cancel));
492
510
  return /* @__PURE__ */ jsxs("div", {
493
511
  className: "aui-composer-action-wrapper relative flex items-center justify-between",
@@ -496,6 +514,7 @@ var ComposerAction = () => {
496
514
  /* @__PURE__ */ jsxs("div", {
497
515
  className: "flex items-center gap-1.5",
498
516
  children: [
517
+ ComposerActions && /* @__PURE__ */ jsx(ComposerActions, {}),
499
518
  /* @__PURE__ */ jsxs(AuiIf, {
500
519
  condition: (s) => s.thread.capabilities.dictation,
501
520
  children: [
@@ -504,11 +523,11 @@ var ComposerAction = () => {
504
523
  children: /* @__PURE__ */ jsx(ComposerPrimitive.Dictate, {
505
524
  asChild: true,
506
525
  children: /* @__PURE__ */ jsx(TooltipIconButton2, {
507
- tooltip: "Voice input",
526
+ tooltip: "Voice Input",
508
527
  side: "bottom",
509
528
  type: "button",
510
529
  variant: "ghost",
511
- "aria-label": "Start voice input",
530
+ "aria-label": "Start Voice Input",
512
531
  children: /* @__PURE__ */ jsx(MicIcon, {
513
532
  "aria-hidden": "true",
514
533
  className: "size-4 shrink-0"
@@ -521,11 +540,11 @@ var ComposerAction = () => {
521
540
  children: /* @__PURE__ */ jsx(ComposerPrimitive.StopDictation, {
522
541
  asChild: true,
523
542
  children: /* @__PURE__ */ jsx(TooltipIconButton2, {
524
- tooltip: "Stop dictation",
543
+ tooltip: "Stop Dictation",
525
544
  side: "bottom",
526
545
  type: "button",
527
546
  variant: "ghost",
528
- "aria-label": "Stop voice input",
547
+ "aria-label": "Stop Voice Input",
529
548
  children: /* @__PURE__ */ jsx(SquareIcon, {
530
549
  "aria-hidden": "true",
531
550
  className: "size-3.5 shrink-0 animate-pulse motion-reduce:animate-none"
@@ -540,11 +559,11 @@ var ComposerAction = () => {
540
559
  children: /* @__PURE__ */ jsx(ComposerPrimitive.Send, {
541
560
  asChild: true,
542
561
  children: /* @__PURE__ */ jsx(TooltipIconButton2, {
543
- tooltip: "Send message",
562
+ tooltip: "Send Message",
544
563
  side: "bottom",
545
564
  type: "button",
546
565
  variant: "default",
547
- "aria-label": "Send message",
566
+ "aria-label": "Send Message",
548
567
  children: /* @__PURE__ */ jsx(ArrowUpIcon, {
549
568
  "aria-hidden": "true",
550
569
  className: "size-4 shrink-0"
@@ -557,11 +576,11 @@ var ComposerAction = () => {
557
576
  children: /* @__PURE__ */ jsx(ComposerPrimitive.Resume, {
558
577
  asChild: true,
559
578
  children: /* @__PURE__ */ jsx(TooltipIconButton2, {
560
- tooltip: "Resume generating",
579
+ tooltip: "Resume Generating",
561
580
  side: "bottom",
562
581
  type: "button",
563
582
  variant: "default",
564
- "aria-label": "Resume generating",
583
+ "aria-label": "Resume Generating",
565
584
  children: /* @__PURE__ */ jsx(PlayIcon, {
566
585
  "aria-hidden": "true",
567
586
  className: "size-4 shrink-0"
@@ -576,7 +595,7 @@ var ComposerAction = () => {
576
595
  children: /* @__PURE__ */ jsx(Button2, {
577
596
  type: "button",
578
597
  variant: "default",
579
- "aria-label": isSending ? "Cancel sending" : "Stop generating",
598
+ "aria-label": isSending ? "Cancel Sending" : "Stop Generating",
580
599
  children: /* @__PURE__ */ jsx(SquareIcon, {
581
600
  "aria-hidden": "true",
582
601
  className: "size-3.5 shrink-0"
@@ -874,6 +893,7 @@ var UserImagePart = (part) => /* @__PURE__ */ jsx("div", {
874
893
  })
875
894
  });
876
895
  var UserMessage = () => {
896
+ const { UserText } = useContext(ThreadComponentsContext);
877
897
  return /* @__PURE__ */ jsxs(MessagePrimitive.Root, {
878
898
  "data-slot": "aui_user-message-root",
879
899
  "data-role": "user",
@@ -888,7 +908,7 @@ var UserMessage = () => {
888
908
  /* @__PURE__ */ jsx("div", {
889
909
  className: "aui-user-message-content peer bg-muted text-foreground rounded-(--composer-radius) px-4 py-2 wrap-break-word empty:hidden",
890
910
  children: /* @__PURE__ */ jsx(MessagePrimitive.Parts, {
891
- components: { File: UserFilePart, Image: UserImagePart }
911
+ components: { Text: UserText, File: UserFilePart, Image: UserImagePart }
892
912
  })
893
913
  }),
894
914
  /* @__PURE__ */ jsx("div", {
@@ -9,7 +9,7 @@ import {
9
9
  } from "./chunk-q40gqpwh.js";
10
10
  import {
11
11
  Thread2
12
- } from "./chunk-5ntb7p4q.js";
12
+ } from "./chunk-0qcknj5n.js";
13
13
  import {
14
14
  ThreadListSearch2,
15
15
  ThreadListRoot2,
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-1x2ch0xc.js";
6
6
  import {
7
7
  Thread2
8
- } from "./chunk-5ntb7p4q.js";
8
+ } from "./chunk-0qcknj5n.js";
9
9
 
10
10
  // src/components/assistant-ui/native/AssistantSidebar.tsx
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  AssistantModal2
3
- } from "../../../chunk-299wvwy8.js";
3
+ } from "../../../chunk-6aqm34nb.js";
4
4
  import"../../../chunk-tsekdy3w.js";
5
- import"../../../chunk-5ntb7p4q.js";
5
+ import"../../../chunk-0qcknj5n.js";
6
6
  import"../../../chunk-9msk1t9e.js";
7
7
  import"../../../chunk-c2vcrwjp.js";
8
8
  import"../../../chunk-sbrpnv8s.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  AssistantSidebar2
3
- } from "../../../chunk-r9mng3ws.js";
4
- import"../../../chunk-5ntb7p4q.js";
3
+ } from "../../../chunk-c6nvc3ft.js";
4
+ import"../../../chunk-0qcknj5n.js";
5
5
  import"../../../chunk-9msk1t9e.js";
6
6
  import"../../../chunk-c2vcrwjp.js";
7
7
  import"../../../chunk-sbrpnv8s.js";
@@ -1,5 +1,5 @@
1
- import type { ReactElement } from "react";
2
- import { MessagePrimitive, type ToolCallMessagePartComponent } from "@assistant-ui/react";
1
+ import type { ComponentPropsWithRef, ReactElement, Ref } from "react";
2
+ import { MessagePrimitive, ThreadPrimitive, type TextMessagePartComponent, type ToolCallMessagePartComponent } from "@assistant-ui/react";
3
3
  import { type ComponentType, type PropsWithChildren } from "react";
4
4
  export type ThreadGroupPart = MessagePrimitive.GroupedParts.GroupPart;
5
5
  /**
@@ -12,6 +12,8 @@ export type ThreadGroupPart = MessagePrimitive.GroupedParts.GroupPart;
12
12
  * tool group; without it they render like any other tool call.
13
13
  */
14
14
  export type ThreadComponents = {
15
+ UserText?: TextMessagePartComponent | undefined;
16
+ ComposerActions?: ComponentType | undefined;
15
17
  AssistantMessage?: ComponentType | undefined;
16
18
  Welcome?: ComponentType | undefined;
17
19
  ToolFallback?: ToolCallMessagePartComponent | undefined;
@@ -26,9 +28,13 @@ export type ThreadComponents = {
26
28
  }> | undefined;
27
29
  };
28
30
  export type ThreadProps = {
31
+ /** Native viewport behavior and ref, including scroll restoration. */
32
+ viewportProps?: Omit<ComponentPropsWithRef<typeof ThreadPrimitive.Viewport>, "children" | "render"> | undefined;
33
+ /** Ref to the thread composer's native input. */
34
+ composerInputRef?: Ref<HTMLTextAreaElement> | undefined;
29
35
  components?: ThreadComponents | undefined;
30
36
  autoFocus?: boolean | undefined;
31
37
  /** Place the composer at the bottom even before the first message. */
32
38
  emptyComposerPosition?: "center" | "bottom" | undefined;
33
39
  };
34
- export declare function Thread({ components, autoFocus, emptyComposerPosition, }: ThreadProps): ReactElement;
40
+ export declare function Thread({ components, autoFocus, emptyComposerPosition, viewportProps, composerInputRef, }: ThreadProps): ReactElement;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Thread2
3
- } from "../../../chunk-5ntb7p4q.js";
3
+ } from "../../../chunk-0qcknj5n.js";
4
4
  import"../../../chunk-9msk1t9e.js";
5
5
  import"../../../chunk-c2vcrwjp.js";
6
6
  import"../../../chunk-sbrpnv8s.js";
@@ -64,7 +64,7 @@ import {
64
64
  import"../../../chunk-d70rcwk7.js";
65
65
  import {
66
66
  Thread2
67
- } from "../../../chunk-5ntb7p4q.js";
67
+ } from "../../../chunk-0qcknj5n.js";
68
68
  import {
69
69
  useThreadListGroups2,
70
70
  ThreadList3,
@@ -77,11 +77,11 @@ import {
77
77
  import"../../../chunk-59p19t57.js";
78
78
  import {
79
79
  AssistantModal2
80
- } from "../../../chunk-299wvwy8.js";
80
+ } from "../../../chunk-6aqm34nb.js";
81
81
  import"../../../chunk-1x2ch0xc.js";
82
82
  import {
83
83
  AssistantSidebar2
84
- } from "../../../chunk-r9mng3ws.js";
84
+ } from "../../../chunk-c6nvc3ft.js";
85
85
  import {
86
86
  ComposerQuotePreviewRoot2,
87
87
  ComposerQuotePreviewIcon2,
@@ -0,0 +1,23 @@
1
+ import { type ComponentProps, type Key, type ReactElement, type ReactNode } from "react";
2
+ import { Table } from "../Table.js";
3
+ export interface IVirtualTableRowProps {
4
+ ref: (element: HTMLTableRowElement | null) => void;
5
+ "data-index": number;
6
+ "aria-rowindex": number;
7
+ }
8
+ interface IVirtualTableProps<T> {
9
+ items: readonly T[];
10
+ getItemKey: (item: T) => Key;
11
+ renderRow: (item: T, props: IVirtualTableRowProps) => ReactNode;
12
+ header: ReactNode;
13
+ columns?: ReactNode;
14
+ columnCount: number;
15
+ label: string;
16
+ maxHeight?: number | string;
17
+ estimateRowHeight?: number;
18
+ overscan?: number;
19
+ retainedKeys?: readonly Key[];
20
+ layout?: ComponentProps<typeof Table>["layout"];
21
+ }
22
+ export declare function VirtualTable<T>({ items, getItemKey, renderRow, header, columns, columnCount, label, maxHeight, estimateRowHeight, overscan, retainedKeys, layout, }: IVirtualTableProps<T>): ReactElement;
23
+ export {};
@@ -0,0 +1,2 @@
1
+ export { VirtualTable } from "./VirtualTable.js";
2
+ export type { IVirtualTableRowProps } from "./VirtualTable.js";
@@ -0,0 +1,103 @@
1
+ import"../../chunk-cp2m7y7v.js";
2
+ import {
3
+ Table2,
4
+ TableHeader2,
5
+ TableBody2
6
+ } from "../../chunk-0y0kqaqj.js";
7
+
8
+ // src/components/virtual-table/VirtualTable.tsx
9
+ import {
10
+ Fragment,
11
+ useCallback,
12
+ useMemo,
13
+ useRef,
14
+ useState
15
+ } from "react";
16
+ import { defaultRangeExtractor, useVirtualizer } from "@tanstack/react-virtual";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ function VirtualTable({
19
+ items,
20
+ getItemKey,
21
+ renderRow,
22
+ header,
23
+ columns,
24
+ columnCount,
25
+ label,
26
+ maxHeight = "65vh",
27
+ estimateRowHeight = 80,
28
+ overscan = 5,
29
+ retainedKeys,
30
+ layout = "fixed"
31
+ }) {
32
+ const viewport = useRef(null);
33
+ const [focusedKey, setFocusedKey] = useState();
34
+ const retained = useMemo(() => items.flatMap((item, index) => retainedKeys?.includes(getItemKey(item)) || getItemKey(item) === focusedKey ? [index] : []), [items, retainedKeys, getItemKey, focusedKey]);
35
+ const rangeExtractor = useCallback((range) => [...new Set([...defaultRangeExtractor(range), ...retained])].sort((a, b) => a - b), [retained]);
36
+ const itemKey = useCallback((index) => getItemKey(items[index]), [items, getItemKey]);
37
+ const virtualizer = useVirtualizer({
38
+ count: items.length,
39
+ getScrollElement: () => viewport.current,
40
+ getItemKey: itemKey,
41
+ estimateSize: () => estimateRowHeight,
42
+ overscan,
43
+ rangeExtractor
44
+ });
45
+ const rows = virtualizer.getVirtualItems();
46
+ const last = rows.at(-1);
47
+ return /* @__PURE__ */ jsx("div", {
48
+ ref: viewport,
49
+ "data-testid": "VirtualTable",
50
+ role: "region",
51
+ "aria-label": label,
52
+ tabIndex: 0,
53
+ className: "min-w-0 overflow-auto overscroll-contain",
54
+ style: { maxHeight },
55
+ onFocusCapture: (event) => {
56
+ const row = event.target.closest("tr[data-index]");
57
+ const item = items[Number(row?.dataset.index)];
58
+ if (row && item)
59
+ setFocusedKey(getItemKey(item));
60
+ },
61
+ children: /* @__PURE__ */ jsxs(Table2, {
62
+ layout,
63
+ "aria-label": label,
64
+ "aria-rowcount": items.length + 1,
65
+ children: [
66
+ columns,
67
+ /* @__PURE__ */ jsx(TableHeader2, {
68
+ children: header
69
+ }),
70
+ /* @__PURE__ */ jsxs(TableBody2, {
71
+ children: [
72
+ rows.map((row, index) => /* @__PURE__ */ jsxs(Fragment, {
73
+ children: [
74
+ row.start > (rows[index - 1]?.end ?? 0) && /* @__PURE__ */ jsx("tr", {
75
+ "aria-hidden": "true",
76
+ children: /* @__PURE__ */ jsx("td", {
77
+ colSpan: columnCount,
78
+ style: { height: row.start - (rows[index - 1]?.end ?? 0), padding: 0, border: 0 }
79
+ })
80
+ }),
81
+ renderRow(items[row.index], {
82
+ ref: virtualizer.measureElement,
83
+ "data-index": row.index,
84
+ "aria-rowindex": row.index + 2
85
+ })
86
+ ]
87
+ }, row.key)),
88
+ last && virtualizer.getTotalSize() > last.end && /* @__PURE__ */ jsx("tr", {
89
+ "aria-hidden": "true",
90
+ children: /* @__PURE__ */ jsx("td", {
91
+ colSpan: columnCount,
92
+ style: { height: virtualizer.getTotalSize() - last.end, padding: 0, border: 0 }
93
+ })
94
+ })
95
+ ]
96
+ })
97
+ ]
98
+ })
99
+ });
100
+ }
101
+ export {
102
+ VirtualTable
103
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alexgorbatchev/shadcn",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "Sharp, monospace React components with compact and comfortable densities.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -988,6 +988,10 @@
988
988
  "./assistant-ui/native/task-tray": {
989
989
  "types": "./dist/components/assistant-ui/native/TaskTray.d.ts",
990
990
  "import": "./dist/components/assistant-ui/native/TaskTray.js"
991
+ },
992
+ "./virtual-table": {
993
+ "types": "./dist/components/virtual-table/index.d.ts",
994
+ "import": "./dist/components/virtual-table/index.js"
991
995
  }
992
996
  },
993
997
  "publishConfig": {
@@ -1105,6 +1109,7 @@
1105
1109
  "@daypicker/persian": "^10.0.2",
1106
1110
  "@daypicker/react": "^10.0.2",
1107
1111
  "@fontsource-variable/jetbrains-mono": "^5.3.0",
1112
+ "@tanstack/react-virtual": "^3.14.13",
1108
1113
  "anser": "2.3.5",
1109
1114
  "beautiful-mermaid": "^1.1.3",
1110
1115
  "cmdk": "^1.1.1",
@@ -1218,6 +1223,9 @@
1218
1223
  },
1219
1224
  "colorjs.io": {
1220
1225
  "optional": true
1226
+ },
1227
+ "@tanstack/react-virtual": {
1228
+ "optional": true
1221
1229
  }
1222
1230
  },
1223
1231
  "packageManager": "bun@1.4.2"