{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "data-table",
  "title": "Data Table with Filters",
  "description": "Data table with search, checkbox, slider, date range filters, sorting, infinite scroll, virtualization, and in-memory state management.",
  "dependencies": [
    "@tanstack/react-table",
    "@tanstack/react-query",
    "@tanstack/react-virtual",
    "lucide-react",
    "zod",
    "date-fns",
    "clsx",
    "tailwind-merge",
    "class-variance-authority",
    "react-day-picker",
    "@radix-ui/react-slider",
    "@radix-ui/react-visually-hidden",
    "@radix-ui/react-slot",
    "@dnd-kit/core",
    "@dnd-kit/modifiers",
    "@dnd-kit/sortable",
    "@dnd-kit/utilities"
  ],
  "registryDependencies": [
    "button",
    "input",
    "input-group",
    "kbd",
    "table",
    "checkbox",
    "tooltip",
    "dropdown-menu",
    "separator",
    "label",
    "accordion",
    "command",
    "popover",
    "skeleton",
    "drawer",
    "calendar",
    "select"
  ],
  "files": [
    {
      "path": "src/components/data-table/types.ts",
      "content": "import type { SerializableDisplayConfig } from \"@/lib/table-schema/types\";\nimport type { JSX } from \"react\";\n\nexport type SearchParams = {\n  [key: string]: string | string[] | undefined;\n};\n\nexport type DatePreset = {\n  label: string;\n  from: Date;\n  to: Date;\n  shortcut: string;\n};\n\n// TODO: we could type the value(!) especially when using enums\nexport type Option = {\n  label: string;\n  value: string | boolean | number | undefined;\n};\n\nexport type Input = {\n  type: \"input\";\n  options?: Option[];\n};\n\nexport type Checkbox = {\n  type: \"checkbox\";\n  component?: (props: Option) => JSX.Element | null;\n  options?: Option[];\n};\n\nexport type Slider = {\n  type: \"slider\";\n  min: number;\n  max: number;\n  // if options is undefined, faceted unique values from the data are used in the command\n  options?: Option[];\n  unit?: string;\n};\n\nexport type Timerange = {\n  type: \"timerange\";\n  options?: Option[]; // required for TS\n  presets?: DatePreset[];\n};\n\nexport type Base<TData> = {\n  label: string;\n  value: keyof TData;\n  /**\n   * Defines if the accordion in the filter bar is open by default\n   */\n  defaultOpen?: boolean;\n  /**\n   * Defines if the command input is disabled for this field\n   */\n  commandDisabled?: boolean;\n};\n\nexport type DataTableCheckboxFilterField<TData> = Base<TData> & Checkbox;\nexport type DataTableSliderFilterField<TData> = Base<TData> & Slider;\nexport type DataTableInputFilterField<TData> = Base<TData> & Input;\nexport type DataTableTimerangeFilterField<TData> = Base<TData> & Timerange;\n\nexport type DataTableFilterField<TData> =\n  | DataTableCheckboxFilterField<TData>\n  | DataTableSliderFilterField<TData>\n  | DataTableInputFilterField<TData>\n  | DataTableTimerangeFilterField<TData>;\n\n/** ----------------------------------------- */\n\nexport type SheetField<TData, TMeta = Record<string, unknown>> = {\n  id: keyof TData;\n  label: string;\n  // FIXME: rethink that! I dont think we need this as there is no input type\n  // REMINDER: readonly if we only want to copy the value (e.g. uuid)\n  // TODO: we might have some values that are not in the data but can be computed\n  type: \"readonly\" | \"input\" | \"checkbox\" | \"slider\" | \"timerange\";\n  display?: SerializableDisplayConfig;\n  component?: (\n    // REMINDER: this is used to pass additional data like the `InfiniteQueryMeta`\n    props: TData & {\n      metadata?: {\n        totalRows: number;\n        filterRows: number;\n        totalRowsFetched: number;\n      } & TMeta;\n    },\n  ) => JSX.Element | null | string;\n  condition?: (props: TData) => boolean;\n  className?: string;\n  skeletonClassName?: string;\n};\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/utils.ts",
      "content": "// TODO: check if we can move to /data-table-filter-command/utils.ts\nimport {\n  ARRAY_DELIMITER,\n  RANGE_DELIMITER,\n  SLIDER_DELIMITER,\n} from \"@/lib/delimiters\";\nimport type { ColumnFiltersState } from \"@tanstack/react-table\";\nimport { z } from \"zod\";\nimport type { DataTableFilterField } from \"./types\";\n\nexport function deserialize<T extends z.ZodObject>(schema: T) {\n  const castToSchema = z.preprocess((val) => {\n    if (typeof val !== \"string\") return val;\n    return val\n      .trim()\n      .split(\" \")\n      .reduce(\n        (prev, curr) => {\n          const [name, value] = curr.split(\":\");\n          if (!value || !name) return prev;\n          prev[name] = value;\n          return prev;\n        },\n        {} as Record<string, unknown>,\n      );\n  }, schema);\n  return (value: string) => castToSchema.safeParse(value);\n}\n\nexport function serializeColumnFilters<TData>(\n  columnFilters: ColumnFiltersState,\n  filterFields?: DataTableFilterField<TData>[],\n) {\n  return columnFilters.reduce((prev, curr) => {\n    const { type, commandDisabled } = filterFields?.find(\n      (field) => curr.id === field.value,\n    ) || { commandDisabled: true }; // if column filter is not found, disable the command by default\n\n    if (commandDisabled) return prev;\n\n    if (Array.isArray(curr.value)) {\n      if (type === \"slider\") {\n        return `${prev}${curr.id}:${curr.value.join(SLIDER_DELIMITER)} `;\n      }\n      if (type === \"checkbox\") {\n        return `${prev}${curr.id}:${curr.value.join(ARRAY_DELIMITER)} `;\n      }\n      if (type === \"timerange\") {\n        return `${prev}${curr.id}:${curr.value.join(RANGE_DELIMITER)} `;\n      }\n    }\n\n    return `${prev}${curr.id}:${curr.value} `;\n  }, \"\");\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-provider.tsx",
      "content": "import { DataTableFilterField } from \"@/components/data-table/types\";\nimport { ControlsProvider } from \"@/providers/controls\";\nimport type {\n  ColumnDef,\n  ColumnFiltersState,\n  PaginationState,\n  RowSelectionState,\n  SortingState,\n  Table,\n  VisibilityState,\n} from \"@tanstack/react-table\";\nimport { createContext, useContext, useMemo } from \"react\";\nimport { DataTableStoreSync } from \"./data-table-store-sync\";\n\n// REMINDER: read about how to move controlled state out of the useReactTable hook\n// https://github.com/TanStack/table/discussions/4005#discussioncomment-7303569\n\ninterface DataTableStateContextType {\n  columnFilters: ColumnFiltersState;\n  sorting: SortingState;\n  rowSelection: RowSelectionState;\n  columnOrder: string[];\n  columnVisibility: VisibilityState;\n  pagination: PaginationState;\n  enableColumnOrdering: boolean;\n}\n\ninterface DataTableBaseContextType<TData = unknown, TValue = unknown> {\n  table: Table<TData>;\n  filterFields: DataTableFilterField<TData>[];\n  columns: ColumnDef<TData, TValue>[];\n  isLoading?: boolean;\n  totalRows?: number;\n  filterRows?: number;\n  getFacetedUniqueValues?: (\n    table: Table<TData>,\n    columnId: string,\n  ) => Map<string, number>;\n  getFacetedMinMaxValues?: (\n    table: Table<TData>,\n    columnId: string,\n  ) => undefined | [number, number];\n}\n\ninterface DataTableContextType<TData = unknown, TValue = unknown>\n  extends DataTableStateContextType,\n    DataTableBaseContextType<TData, TValue> {}\n\nexport const DataTableContext = createContext<DataTableContextType<\n  any,\n  any\n> | null>(null);\n\nexport function DataTableProvider<TData, TValue>({\n  children,\n  ...props\n}: Partial<DataTableStateContextType> &\n  DataTableBaseContextType<TData, TValue> & {\n    children: React.ReactNode;\n  }) {\n  const value = useMemo(\n    // eslint-disable-next-line react-hooks/preserve-manual-memoization\n    () => ({\n      ...props,\n      columnFilters: props.columnFilters ?? [],\n      sorting: props.sorting ?? [],\n      rowSelection: props.rowSelection ?? {},\n      columnOrder: props.columnOrder ?? [],\n      columnVisibility: props.columnVisibility ?? {},\n      pagination: props.pagination ?? { pageIndex: 0, pageSize: 10 },\n      enableColumnOrdering: props.enableColumnOrdering ?? false,\n    }),\n    [\n      props.columnFilters,\n      props.sorting,\n      props.rowSelection,\n      props.columnOrder,\n      props.columnVisibility,\n      props.pagination,\n      props.table,\n      props.filterFields,\n      props.columns,\n      props.enableColumnOrdering,\n      props.isLoading,\n      props.totalRows,\n      props.filterRows,\n      props.getFacetedUniqueValues,\n      props.getFacetedMinMaxValues,\n    ],\n  );\n\n  return (\n    <DataTableContext.Provider value={value}>\n      <ControlsProvider>\n        <DataTableStoreSync />\n        {children}\n      </ControlsProvider>\n    </DataTableContext.Provider>\n  );\n}\n\nexport function useDataTable<TData, TValue>() {\n  const context = useContext(DataTableContext);\n\n  if (!context) {\n    throw new Error(\"useDataTable must be used within a DataTableProvider\");\n  }\n\n  return context as DataTableContextType<TData, TValue>;\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-store-sync.tsx",
      "content": "\"use client\";\n\n/**\n * DataTableStoreSync - Syncs React Table state to/from BYOS adapter\n *\n * On initial mount: seeds the table's columnFilters from BYOS state (BYOS → Table).\n * This handles the case where URL params are available in BYOS but useState missed\n * them during SSR/hydration (e.g. nuqs reads URL after initial render).\n *\n * After mount: syncs changes from React Table's state (columnFilters, sorting,\n * rowSelection) to the BYOS adapter (Table → BYOS). Filter components update\n * the table directly via column.setFilterValue(), and this component propagates\n * those changes to the BYOS adapter for URL sync (nuqs) or state persistence.\n */\nimport { useStoreContext } from \"@/lib/store/context\";\nimport { useFilterActions } from \"@/lib/store/hooks/useFilterActions\";\nimport { isStateEqual } from \"@/lib/store/schema\";\nimport { useEffect, useRef } from \"react\";\nimport { useDataTable } from \"./data-table-provider\";\n\nexport function DataTableStoreSync() {\n  useDataTableStoreSync();\n  return null;\n}\n\n/**\n * Hook version for more control - syncs filters, sorting, and selection\n */\nexport function useDataTableStoreSync() {\n  const context = useStoreContext();\n  const { table, filterFields, sorting, rowSelection } = useDataTable();\n  const { setFilters } = useFilterActions();\n\n  const lastSentFiltersRef = useRef<Record<string, unknown>>({});\n  const lastSentSortRef = useRef<{ id: string; desc: boolean } | null>(null);\n  const lastSentUuidRef = useRef<string>(\"\");\n  const isInitialMount = useRef(true);\n\n  const columnFilters = table.getState().columnFilters;\n\n  // Sync column filters\n  useEffect(() => {\n    if (!context) return;\n    if (isInitialMount.current) {\n      isInitialMount.current = false;\n      const filterFieldKeys = new Set(\n        filterFields?.map((f) => f.value as string) || [],\n      );\n      const currentFilters: Record<string, unknown> = {};\n      for (const filter of columnFilters) {\n        if (filterFieldKeys.has(filter.id)) {\n          currentFilters[filter.id] = filter.value;\n        }\n      }\n\n      // Seed table from BYOS on initial mount (handles URL hydration where\n      // useState(defaultColumnFilters) missed the URL-derived values)\n      const byosState = context.adapter.getSnapshot().state as Record<\n        string,\n        unknown\n      >;\n      for (const key of filterFieldKeys) {\n        if (key in currentFilters) continue;\n        const byosValue = byosState[key];\n        if (byosValue == null) continue;\n        if (Array.isArray(byosValue) && byosValue.length === 0) continue;\n        table.getColumn(key)?.setFilterValue(byosValue);\n        currentFilters[key] = byosValue;\n      }\n\n      lastSentFiltersRef.current = { ...currentFilters };\n      lastSentSortRef.current = sorting?.[0] || null;\n      const selectedKeys = Object.keys(rowSelection || {});\n      lastSentUuidRef.current = selectedKeys.length > 0 ? selectedKeys[0] : \"\";\n      return;\n    }\n\n    const filterFieldKeys = new Set(\n      filterFields?.map((f) => f.value as string) || [],\n    );\n\n    const currentFilters: Record<string, unknown> = {};\n    for (const filter of columnFilters) {\n      if (filterFieldKeys.has(filter.id)) {\n        currentFilters[filter.id] = filter.value;\n      }\n    }\n\n    if (isStateEqual(currentFilters, lastSentFiltersRef.current)) {\n      return;\n    }\n\n    const updates: Record<string, unknown> = { ...currentFilters };\n\n    // Only null out keys that we previously sent — never clear BYOS filters\n    // that were set externally (e.g. from URL) and not managed by this sync.\n    for (const key of Object.keys(lastSentFiltersRef.current)) {\n      if (!(key in currentFilters)) {\n        updates[key] = null;\n      }\n    }\n\n    lastSentFiltersRef.current = { ...currentFilters };\n    setFilters(updates);\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [columnFilters, context, filterFields, setFilters]);\n\n  // Sync sorting\n  useEffect(() => {\n    if (!context) return;\n    if (isInitialMount.current) return;\n\n    const newSort = sorting?.[0] || null;\n    const lastSort = lastSentSortRef.current;\n\n    if (newSort?.id === lastSort?.id && newSort?.desc === lastSort?.desc) {\n      return;\n    }\n\n    lastSentSortRef.current = newSort;\n    setFilters({ sort: newSort });\n  }, [sorting, context, setFilters]);\n\n  // Sync row selection (single-select only — skip when multi-row selection is\n  // enabled because rowSelection is used for checkbox bulk actions, not sheet)\n  useEffect(() => {\n    if (!context) return;\n    if (isInitialMount.current) return;\n    if (table.options.enableMultiRowSelection) return;\n\n    const selectedKeys = Object.keys(rowSelection || {});\n    const newUuid = selectedKeys.length > 0 ? selectedKeys[0] : null;\n    const newUuidStr = newUuid || \"\";\n\n    if (newUuidStr === lastSentUuidRef.current) {\n      return;\n    }\n\n    lastSentUuidRef.current = newUuidStr;\n    setFilters({ uuid: newUuid });\n  }, [\n    rowSelection,\n    context,\n    setFilters,\n    table.options.enableMultiRowSelection,\n  ]);\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-infinite.tsx",
      "content": "\"use client\";\n\n// REMINDER: React Compiler is not compatible with Tanstack Table v8 https://github.com/TanStack/table/issues/5567\n\"use no memo\";\n\nimport {\n  Table,\n  TableBody,\n  TableCell,\n  TableHead,\n  TableHeader,\n  TableRow,\n} from \"@/components/custom/table\";\nimport { DataTableFilterControls } from \"@/components/data-table/data-table-filter-controls\";\nimport { DataTableProvider } from \"@/components/data-table/data-table-provider\";\nimport { DataTableResetButton } from \"@/components/data-table/data-table-reset-button\";\nimport { DataTableToolbar } from \"@/components/data-table/data-table-toolbar\"; // TODO: check where to put this\nimport type { DataTableFilterField } from \"@/components/data-table/types\";\nimport { Button } from \"@/components/ui/button\";\nimport { useHotKey } from \"@/hooks/use-hot-key\";\nimport { useLocalStorage } from \"@/hooks/use-local-storage\";\nimport {\n  getColumnOrderKey,\n  getColumnVisibilityKey,\n} from \"@/lib/constants/local-storage\";\nimport { getFacetedUniqueValuesFlattened } from \"@/lib/data-table/faceted\";\nimport { formatCompactNumber } from \"@/lib/format\";\nimport { useFilterActions } from \"@/lib/store/hooks/useFilterActions\";\nimport { useFilterState } from \"@/lib/store/hooks/useFilterState\";\nimport { arrSome, inDateRange } from \"@/lib/table/filterfns\";\nimport { cn } from \"@/lib/utils\";\nimport {\n  type FetchNextPageOptions,\n  type FetchPreviousPageOptions,\n  type RefetchOptions,\n} from \"@tanstack/react-query\";\nimport type {\n  ColumnDef,\n  ColumnFiltersState,\n  Row,\n  RowSelectionState,\n  SortingState,\n  TableOptions,\n  Table as TTable,\n  VisibilityState,\n} from \"@tanstack/react-table\";\nimport {\n  flexRender,\n  getCoreRowModel,\n  getFacetedRowModel,\n  getFilteredRowModel,\n  getSortedRowModel,\n  getFacetedMinMaxValues as getTTableFacetedMinMaxValues,\n  useReactTable,\n} from \"@tanstack/react-table\";\nimport { LoaderCircle } from \"lucide-react\";\nimport * as React from \"react\";\n\n// TODO: add a possible chartGroupBy\nexport interface DataTableInfiniteProps<TData, TValue> {\n  columns: ColumnDef<TData, TValue>[];\n  getRowClassName?: (row: Row<TData>) => string;\n  // REMINDER: make sure to pass the correct id to access the rows\n  getRowId?: TableOptions<TData>[\"getRowId\"];\n  data: TData[];\n  defaultColumnFilters?: ColumnFiltersState;\n  defaultColumnSorting?: SortingState;\n  defaultRowSelection?: RowSelectionState;\n  defaultColumnVisibility?: VisibilityState;\n  filterFields?: DataTableFilterField<TData>[];\n  // REMINDER: close to the same signature as the `getFacetedUniqueValues` of the `useReactTable`\n  getFacetedUniqueValues?: (\n    table: TTable<TData>,\n    columnId: string,\n  ) => Map<string, number>;\n  getFacetedMinMaxValues?: (\n    table: TTable<TData>,\n    columnId: string,\n  ) => undefined | [number, number];\n  totalRows?: number;\n  filterRows?: number;\n  totalRowsFetched?: number;\n  isFetching?: boolean;\n  isLoading?: boolean;\n  hasNextPage?: boolean;\n  fetchNextPage: (\n    options?: FetchNextPageOptions | undefined,\n  ) => Promise<unknown>;\n  fetchPreviousPage?: (\n    options?: FetchPreviousPageOptions | undefined,\n  ) => Promise<unknown>;\n  refetch: (options?: RefetchOptions | undefined) => void;\n  renderLiveRow?: (props?: { row: Row<TData> }) => React.ReactNode;\n  // Used to store column order and visibility in local storage for specific data-table namespace\n  tableId?: string;\n  // Optional slots for extensibility\n  commandSlot?: React.ReactNode;\n  sheetSlot?: React.ReactNode;\n  toolbarActions?: React.ReactNode;\n  chartSlot?: React.ReactNode;\n  footerSlot?: React.ReactNode;\n  floatingBarSlot?: React.ReactNode;\n  className?: string;\n}\n\nexport function DataTableInfinite<TData, TValue>({\n  columns,\n  getRowClassName,\n  getRowId,\n  data,\n  defaultColumnFilters = [],\n  defaultColumnSorting = [],\n  defaultRowSelection = {},\n  defaultColumnVisibility = {},\n  filterFields = [],\n  isFetching,\n  isLoading,\n  fetchNextPage,\n  hasNextPage,\n  fetchPreviousPage,\n  refetch,\n  totalRows,\n  filterRows,\n  totalRowsFetched = 0,\n  getFacetedUniqueValues,\n  getFacetedMinMaxValues,\n  renderLiveRow,\n  tableId = \"infinite\",\n  commandSlot,\n  sheetSlot,\n  toolbarActions,\n  chartSlot,\n  footerSlot,\n  floatingBarSlot,\n  className,\n}: DataTableInfiniteProps<TData, TValue>) {\n  const [columnFilters, setColumnFilters] =\n    React.useState<ColumnFiltersState>(defaultColumnFilters);\n  const [sorting, setSorting] =\n    React.useState<SortingState>(defaultColumnSorting);\n  const [rowSelection, setRowSelection] =\n    React.useState<RowSelectionState>(defaultRowSelection);\n  const [columnOrder, setColumnOrder] = useLocalStorage<string[]>(\n    getColumnOrderKey(tableId),\n    [],\n  );\n  const [columnVisibility, setColumnVisibility] =\n    useLocalStorage<VisibilityState>(\n      getColumnVisibilityKey(tableId),\n      defaultColumnVisibility,\n    );\n  const topBarRef = React.useRef<HTMLDivElement>(null);\n  const tableRef = React.useRef<HTMLTableElement>(null);\n  const [topBarHeight, setTopBarHeight] = React.useState(0);\n\n  // Detect if a select column exists to enable multi-row selection\n  const hasSelectColumn = React.useMemo(\n    () => columns.some((col) => col.meta?.kind === \"select\"),\n    [columns],\n  );\n\n  const onScroll = React.useCallback(\n    (e: React.UIEvent<HTMLElement>) => {\n      const onPageBottom =\n        Math.ceil(e.currentTarget.scrollTop + e.currentTarget.clientHeight) >=\n        e.currentTarget.scrollHeight;\n\n      if (onPageBottom && !isFetching && totalRowsFetched < (filterRows ?? 0)) {\n        fetchNextPage();\n      }\n    },\n    [fetchNextPage, isFetching, filterRows, totalRowsFetched],\n  );\n\n  React.useEffect(() => {\n    const observer = new ResizeObserver(() => {\n      const rect = topBarRef.current?.getBoundingClientRect();\n      if (rect) {\n        setTopBarHeight(rect.height);\n      }\n    });\n\n    const topBar = topBarRef.current;\n    if (!topBar) return;\n\n    observer.observe(topBar);\n    return () => observer.unobserve(topBar);\n  }, [topBarRef]);\n\n  const table = useReactTable({\n    data,\n    columns,\n    state: {\n      columnFilters,\n      sorting,\n      columnVisibility,\n      rowSelection,\n      columnOrder,\n    },\n    enableMultiRowSelection: hasSelectColumn,\n    columnResizeMode: \"onChange\",\n    getRowId,\n    onColumnVisibilityChange: setColumnVisibility,\n    onColumnFiltersChange: setColumnFilters,\n    onRowSelectionChange: setRowSelection,\n    onSortingChange: setSorting,\n    onColumnOrderChange: setColumnOrder,\n    getSortedRowModel: getSortedRowModel(),\n    getCoreRowModel: getCoreRowModel(),\n    getFilteredRowModel: getFilteredRowModel(),\n    getFacetedRowModel: getFacetedRowModel(),\n    getFacetedUniqueValues: getFacetedUniqueValuesFlattened(),\n    getFacetedMinMaxValues: getTTableFacetedMinMaxValues(),\n    filterFns: { inDateRange, arrSome },\n    debugAll: process.env.NEXT_PUBLIC_TABLE_DEBUG === \"true\",\n    meta: { getRowClassName },\n  });\n\n  // Clear multi-select when filters or sorting change\n  React.useEffect(() => {\n    if (hasSelectColumn) {\n      setRowSelection({});\n    }\n  }, [columnFilters, sorting, hasSelectColumn, setRowSelection]);\n\n  // NOTE: Filter, sort, and selection syncing is now handled by DataTableStoreSync\n\n  /**\n   * https://tanstack.com/table/v8/docs/guide/column-sizing#advanced-column-resizing-performance\n   * Instead of calling `column.getSize()` on every render for every header\n   * and especially every data cell (very expensive),\n   * we will calculate all column sizes at once at the root table level in a useMemo\n   * and pass the column sizes down as CSS variables to the <table> element.\n   */\n  const columnSizeVars = React.useMemo(() => {\n    const headers = table.getFlatHeaders();\n    const colSizes: { [key: string]: string } = {};\n    for (let i = 0; i < headers.length; i++) {\n      const header = headers[i]!;\n      // REMINDER: replace \".\" with \"-\" to avoid invalid CSS variable name (e.g. \"timing.dns\" -> \"timing-dns\")\n      colSizes[`--header-${header.id.replace(\".\", \"-\")}-size`] =\n        `${header.getSize()}px`;\n      colSizes[`--col-${header.column.id.replace(\".\", \"-\")}-size`] =\n        `${header.column.getSize()}px`;\n    }\n    return colSizes;\n  }, [\n    table.getState().columnSizingInfo,\n    table.getState().columnSizing,\n    table.getState().columnVisibility,\n  ]);\n\n  useHotKey(() => {\n    setColumnOrder([]);\n    setColumnVisibility(defaultColumnVisibility);\n  }, \"u\");\n\n  // Memoize column state as strings for efficient row memoization comparison\n  const visibleColumnIds = React.useMemo(\n    () =>\n      table\n        .getVisibleLeafColumns()\n        .map((c) => c.id)\n        .join(\",\"),\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    [table.getState().columnVisibility],\n  );\n  const columnOrderString = React.useMemo(\n    () => columnOrder.join(\",\"),\n    [columnOrder],\n  );\n\n  // Read BYOS uuid for detail row visual state (only used in multi-select mode)\n  const detailRowId = useFilterState((s) => s.uuid) as\n    | string\n    | null\n    | undefined;\n  const { setFilters } = useFilterActions();\n\n  // Stable callback for row clicks — lifted here so Row doesn't need useFilterActions\n  const onRowClick = React.useCallback(\n    (row: Row<TData>) => {\n      if (hasSelectColumn) {\n        setFilters({ uuid: detailRowId === row.id ? null : row.id });\n      } else {\n        row.toggleSelected();\n      }\n    },\n    [hasSelectColumn, detailRowId, setFilters],\n  );\n\n  return (\n    <DataTableProvider\n      table={table}\n      columns={columns}\n      filterFields={filterFields}\n      columnFilters={columnFilters}\n      sorting={sorting}\n      rowSelection={rowSelection}\n      columnOrder={columnOrder}\n      columnVisibility={columnVisibility}\n      enableColumnOrdering={true}\n      isLoading={isFetching || isLoading}\n      totalRows={totalRows}\n      filterRows={filterRows ?? table.getFilteredRowModel().rows.length}\n      getFacetedUniqueValues={getFacetedUniqueValues}\n      getFacetedMinMaxValues={getFacetedMinMaxValues}\n    >\n      <div\n        className=\"flex h-full min-h-screen w-full flex-col sm:flex-row\"\n        style={\n          {\n            \"--top-bar-height\": `${topBarHeight}px`,\n            ...columnSizeVars,\n          } as React.CSSProperties\n        }\n      >\n        <div\n          className={cn(\n            \"h-full w-full flex-col sm:sticky sm:top-0 sm:max-h-screen sm:min-h-screen sm:max-w-52 sm:min-w-52 sm:self-start md:max-w-72 md:min-w-72\",\n            \"group-data-[expanded=false]/controls:hidden\",\n            \"hidden sm:flex\",\n          )}\n        >\n          <div className=\"border-border bg-background border-b p-2 md:sticky md:top-0\">\n            <div className=\"flex h-[46px] items-center justify-between gap-3\">\n              <p className=\"text-foreground px-2 font-medium\">Filters</p>\n              <div>\n                {table.getState().columnFilters.length ? (\n                  <DataTableResetButton />\n                ) : null}\n              </div>\n            </div>\n          </div>\n          <div className=\"flex-1 p-2 sm:overflow-y-scroll\">\n            <DataTableFilterControls />\n          </div>\n          {footerSlot ? (\n            <div className=\"border-border bg-background border-t p-4 md:sticky md:bottom-0\">\n              {footerSlot}\n            </div>\n          ) : null}\n        </div>\n        <div\n          className={cn(\n            \"border-border flex max-w-full flex-1 flex-col sm:border-l\",\n            // Chrome issue\n            \"sm:group-data-[expanded=true]/controls:max-w-[calc(100%-208px)] md:group-data-[expanded=true]/controls:max-w-[calc(100%-288px)]\",\n          )}\n        >\n          <div\n            ref={topBarRef}\n            className={cn(\n              \"bg-background flex flex-col gap-4 p-2\",\n              \"sticky top-0 z-10 pb-4\",\n            )}\n          >\n            {commandSlot}\n            {/* TBD: better flexibility with compound components? */}\n            <DataTableToolbar\n              renderActions={toolbarActions ? () => toolbarActions : undefined}\n            />\n            {chartSlot}\n          </div>\n          <div className=\"z-0\">\n            <Table\n              ref={tableRef}\n              onScroll={onScroll}\n              // REMINDER: https://stackoverflow.com/questions/50361698/border-style-do-not-work-with-sticky-position-element\n              className=\"border-separate border-spacing-0\"\n              containerClassName=\"max-h-[calc(100vh-var(--top-bar-height))]\"\n            >\n              <TableHeader className={cn(\"bg-background sticky top-0 z-20\")}>\n                {table.getHeaderGroups().map((headerGroup) => (\n                  <TableRow\n                    key={headerGroup.id}\n                    className={cn(\n                      \"bg-muted/50 hover:bg-muted/50\",\n                      \"*:border-t [&>:not(:last-child)]:border-r\",\n                    )}\n                  >\n                    {headerGroup.headers.map((header) => {\n                      return (\n                        <TableHead\n                          key={header.id}\n                          style={\n                            header.column.getCanResize()\n                              ? {\n                                  width: `var(--header-${header.id.replace(\".\", \"-\")}-size)`,\n                                  minWidth: `var(--header-${header.id.replace(\".\", \"-\")}-size)`,\n                                }\n                              : header.column.columnDef.maxSize\n                                ? {\n                                    width: `var(--header-${header.id.replace(\".\", \"-\")}-size)`,\n                                    minWidth: `var(--header-${header.id.replace(\".\", \"-\")}-size)`,\n                                    maxWidth: `var(--header-${header.id.replace(\".\", \"-\")}-size)`,\n                                  }\n                                : undefined\n                          }\n                          className={cn(\n                            \"border-border relative truncate border-b select-none last:[&>.cursor-col-resize]:opacity-0\",\n                            header.column.columnDef.meta?.headerClassName,\n                          )}\n                          aria-sort={\n                            header.column.getIsSorted() === \"asc\"\n                              ? \"ascending\"\n                              : header.column.getIsSorted() === \"desc\"\n                                ? \"descending\"\n                                : \"none\"\n                          }\n                        >\n                          {header.isPlaceholder\n                            ? null\n                            : flexRender(\n                                header.column.columnDef.header,\n                                header.getContext(),\n                              )}\n                          {header.column.getCanResize() && (\n                            <div\n                              onDoubleClick={() => header.column.resetSize()}\n                              onMouseDown={header.getResizeHandler()}\n                              onTouchStart={header.getResizeHandler()}\n                              className={cn(\n                                \"user-select-none absolute top-0 -right-2 z-10 flex h-full w-4 cursor-col-resize touch-none justify-center\",\n                                \"before:bg-border before:absolute before:inset-y-0 before:w-px before:translate-x-px\",\n                              )}\n                            />\n                          )}\n                        </TableHead>\n                      );\n                    })}\n                  </TableRow>\n                ))}\n              </TableHeader>\n              <TableBody\n                id=\"content\"\n                tabIndex={-1}\n                className=\"outline-primary outline-1 -outline-offset-1 transition-colors outline-none focus-visible:outline-solid\"\n                // REMINDER: avoids scroll (skipping the table header) when using skip to content\n                style={{\n                  scrollMarginTop: \"calc(var(--top-bar-height) + 40px)\",\n                }}\n              >\n                {table.getRowModel().rows?.length ? (\n                  table.getRowModel().rows.map((row) => (\n                    // REMINDER: if we want to add arrow navigation https://github.com/TanStack/table/discussions/2752#discussioncomment-192558\n                    <React.Fragment key={row.id}>\n                      {renderLiveRow?.({ row })}\n                      <MemoizedRow\n                        row={row}\n                        table={table}\n                        selected={row.getIsSelected()}\n                        detailRowId={hasSelectColumn ? detailRowId : undefined}\n                        onRowClick={onRowClick}\n                        visibleColumnIds={visibleColumnIds}\n                        columnOrder={columnOrderString}\n                      />\n                    </React.Fragment>\n                  ))\n                ) : (\n                  <React.Fragment>\n                    {renderLiveRow?.()}\n                    <TableRow>\n                      <TableCell\n                        colSpan={columns.length}\n                        className=\"h-24 text-center\"\n                      >\n                        No results.\n                      </TableCell>\n                    </TableRow>\n                  </React.Fragment>\n                )}\n                <TableRow className=\"hover:bg-transparent data-[state=selected]:bg-transparent\">\n                  <TableCell colSpan={columns.length} className=\"text-center\">\n                    {hasNextPage || isFetching || isLoading ? (\n                      <Button\n                        disabled={isFetching || isLoading}\n                        onClick={() => fetchNextPage()}\n                        variant=\"outline\"\n                      >\n                        {isFetching ? (\n                          <LoaderCircle className=\"mr-2 h-4 w-4 animate-spin\" />\n                        ) : null}\n                        Load More\n                      </Button>\n                    ) : (\n                      <p className=\"text-muted-foreground text-sm\">\n                        No more data to load (\n                        <span className=\"font-mono font-medium\">\n                          {formatCompactNumber(filterRows ?? 0)}\n                        </span>{\" \"}\n                        of{\" \"}\n                        <span className=\"font-mono font-medium\">\n                          {formatCompactNumber(totalRows ?? 0)}\n                        </span>{\" \"}\n                        rows)\n                      </p>\n                    )}\n                  </TableCell>\n                </TableRow>\n              </TableBody>\n            </Table>\n          </div>\n        </div>\n      </div>\n      {sheetSlot}\n      {floatingBarSlot}\n    </DataTableProvider>\n  );\n}\n\n/**\n * REMINDER: this is the heaviest component in the table if lots of rows\n * Some other components are rendered more often necessary, but are fixed size (not like rows that can grow in height)\n * e.g. DataTableFilterControls, DataTableFilterCommand, DataTableToolbar, DataTableHeader\n */\n\nfunction Row<TData>({\n  row,\n  table,\n  selected,\n  detailRowId,\n  onRowClick,\n  visibleColumnIds,\n  columnOrder,\n}: {\n  row: Row<TData>;\n  table: TTable<TData>;\n  // REMINDER: row.getIsSelected(); - just for memoization\n  selected?: boolean;\n  // When multi-select is enabled, the BYOS uuid for detail sheet (undefined = single-select mode)\n  detailRowId?: string | null;\n  onRowClick: (row: Row<TData>) => void;\n  // REMINDER: for memoization - triggers re-render when columns change\n  visibleColumnIds: string;\n  columnOrder: string;\n}) {\n  // REMINDER: rerender the row when live mode is toggled - used to opacity the row\n  // via the `getRowClassName` prop - but for some reasons it wil render the row on data fetch\n  useFilterState((s) => s.live);\n\n  const isMultiSelect = detailRowId !== undefined;\n  const isDetail = isMultiSelect && detailRowId === row.id;\n\n  const handleClick = React.useCallback(() => {\n    onRowClick(row);\n  }, [onRowClick, row]);\n\n  return (\n    <TableRow\n      id={row.id}\n      tabIndex={0}\n      // Single-select: data-state=\"selected\" for the selected row\n      // Multi-select: data-detail + data-checked are independent (a row can be both)\n      data-state={!isMultiSelect && selected ? \"selected\" : undefined}\n      data-detail={isDetail ? \"\" : undefined}\n      data-checked={isMultiSelect && selected ? \"\" : undefined}\n      onClick={handleClick}\n      onKeyDown={(event) => {\n        if (event.key === \"Enter\") {\n          event.preventDefault();\n          handleClick();\n        }\n      }}\n      className={cn(\n        \"[&>:not(:last-child)]:border-r\",\n        \"outline-primary focus-visible:bg-muted/50 outline-1 -outline-offset-1 transition-colors outline-none focus-visible:outline-solid\",\n        \"data-[state=selected]:outline-solid\",\n        \"data-detail:outline-solid\",\n        \"data-checked:bg-muted/50\",\n        table.options.meta?.getRowClassName?.(row),\n      )}\n    >\n      {row.getVisibleCells().map((cell) => (\n        <TableCell\n          key={cell.id}\n          style={\n            cell.column.getCanResize()\n              ? {\n                  width: `var(--col-${cell.column.id.replace(\".\", \"-\")}-size)`,\n                  maxWidth: `var(--col-${cell.column.id.replace(\".\", \"-\")}-size)`,\n                }\n              : cell.column.columnDef.maxSize\n                ? {\n                    width: `var(--col-${cell.column.id.replace(\".\", \"-\")}-size)`,\n                    minWidth: `var(--col-${cell.column.id.replace(\".\", \"-\")}-size)`,\n                    maxWidth: `var(--col-${cell.column.id.replace(\".\", \"-\")}-size)`,\n                  }\n                : undefined\n          }\n          className={cn(\n            \"border-border truncate border-b\",\n            cell.column.columnDef.meta?.cellClassName,\n          )}\n        >\n          {flexRender(cell.column.columnDef.cell, cell.getContext())}\n        </TableCell>\n      ))}\n    </TableRow>\n  );\n}\n\nconst MemoizedRow = React.memo(\n  Row,\n  (prev, next) =>\n    prev.row.id === next.row.id &&\n    prev.selected === next.selected &&\n    prev.detailRowId === next.detailRowId &&\n    prev.onRowClick === next.onRowClick &&\n    prev.visibleColumnIds === next.visibleColumnIds &&\n    prev.columnOrder === next.columnOrder,\n) as typeof Row;\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-floating-bar.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { useHotKey } from \"@/hooks/use-hot-key\";\nimport type { Row, Table as TTable } from \"@tanstack/react-table\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useDataTable } from \"./data-table-provider\";\n\ninterface DataTableFloatingBarProps<TData> {\n  children: (props: {\n    rows: Row<TData>[];\n    table: TTable<TData>;\n  }) => React.ReactNode;\n}\n\nexport function DataTableFloatingBar<TData>({\n  children,\n}: DataTableFloatingBarProps<TData>) {\n  const { table, rowSelection } = useDataTable<TData, unknown>();\n  const selectedRowCount = Object.keys(rowSelection).length;\n\n  const selectedRows = React.useMemo(() => {\n    return table.getFilteredSelectedRowModel().rows;\n    // rowSelection is not used directly but serves as an invalidation trigger\n  }, [table, rowSelection]);\n\n  useHotKey(() => table.resetRowSelection(), \"x\", { shift: true });\n\n  if (selectedRowCount === 0) return null;\n\n  return (\n    <div className=\"fixed inset-x-0 bottom-4 z-50 mx-auto w-fit\">\n      <div className=\"bg-background border-border flex items-center gap-2 rounded-lg border px-4 py-2.5 shadow-lg\">\n        <div className=\"flex items-center gap-1\">\n          <span className=\"text-muted-foreground text-sm whitespace-nowrap\">\n            {selectedRowCount} selected\n          </span>\n          <TooltipProvider>\n            <Tooltip delayDuration={100}>\n              <TooltipTrigger asChild>\n                <Button\n                  variant=\"ghost\"\n                  size=\"icon-xs\"\n                  onClick={() => table.resetRowSelection()}\n                  className=\"text-muted-foreground hover:text-foreground rounded-sm p-0.5 transition-colors\"\n                  aria-label=\"Deselect all\"\n                >\n                  <X className=\"size-4\" />\n                </Button>\n              </TooltipTrigger>\n              <TooltipContent side=\"top\">\n                <p>\n                  Deselect all <Kbd>⌘ ⇧ X</Kbd>\n                </p>\n              </TooltipContent>\n            </Tooltip>\n          </TooltipProvider>\n        </div>\n        <div className=\"bg-border mr-1 h-5 w-px\" />\n        <div className=\"flex items-center gap-2\">\n          {children({ rows: selectedRows, table })}\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-column-header.tsx",
      "content": "import { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\nimport type { Column } from \"@tanstack/react-table\";\nimport { ChevronDown, ChevronUp } from \"lucide-react\";\nimport type React from \"react\";\n\ninterface DataTableColumnHeaderProps<TData, TValue>\n  extends React.ComponentProps<typeof Button> {\n  column: Column<TData, TValue>;\n  title: string;\n}\n\nexport function DataTableColumnHeader<TData, TValue>({\n  column,\n  title,\n  className,\n  ...props\n}: DataTableColumnHeaderProps<TData, TValue>) {\n  if (!column.getCanSort()) {\n    return <div className={cn(className)}>{title}</div>;\n  }\n\n  return (\n    <Button\n      variant=\"ghost\"\n      onClick={() => {\n        column.toggleSorting(undefined);\n      }}\n      className={cn(\n        \"flex h-7 w-full items-center justify-between gap-2 px-0 py-0 hover:bg-transparent\",\n        className,\n      )}\n      {...props}\n    >\n      <span>{title}</span>\n      <span className=\"flex flex-col\">\n        <ChevronUp\n          className={cn(\n            \"-mb-0.5! size-3!\",\n            column.getIsSorted() === \"asc\"\n              ? \"text-accent-foreground\"\n              : \"text-muted-foreground\",\n          )}\n        />\n        <ChevronDown\n          className={cn(\n            \"-mt-0.5! size-3!\",\n            column.getIsSorted() === \"desc\"\n              ? \"text-accent-foreground\"\n              : \"text-muted-foreground\",\n          )}\n        />\n      </span>\n    </Button>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-toolbar.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { Button } from \"@/components/ui/button\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { useHotKey } from \"@/hooks/use-hot-key\";\nimport { formatCompactNumber } from \"@/lib/format\";\nimport { useControls } from \"@/providers/controls\";\nimport { PanelLeftClose, PanelLeftOpen } from \"lucide-react\";\nimport { DataTableFilterControlsDrawer } from \"./data-table-filter-controls-drawer\";\nimport { DataTableResetButton } from \"./data-table-reset-button\";\nimport { DataTableViewOptions } from \"./data-table-view-options\";\n\ninterface DataTableToolbarProps {\n  renderActions?: () => React.ReactNode;\n}\n\nexport function DataTableToolbar({ renderActions }: DataTableToolbarProps) {\n  const { table, isLoading, columnFilters, totalRows, filterRows } =\n    useDataTable();\n  const { open, setOpen } = useControls();\n  useHotKey(() => setOpen((prev) => !prev), \"b\");\n  const rows = {\n    total: totalRows ?? table.getCoreRowModel().rows.length,\n    filtered: filterRows ?? table.getFilteredRowModel().rows.length,\n  };\n\n  return (\n    <div className=\"flex flex-wrap items-center justify-between gap-4\">\n      <div className=\"flex flex-wrap items-center gap-2\">\n        <TooltipProvider>\n          <Tooltip delayDuration={100}>\n            <TooltipTrigger asChild>\n              <Button\n                variant=\"ghost\"\n                onClick={() => setOpen((prev) => !prev)}\n                className=\"hidden gap-2 sm:flex\"\n              >\n                {open ? (\n                  <>\n                    <PanelLeftClose className=\"h-4 w-4\" />\n                    <span className=\"hidden md:block\">Hide Controls</span>\n                  </>\n                ) : (\n                  <>\n                    <PanelLeftOpen className=\"h-4 w-4\" />\n                    <span className=\"hidden md:block\">Show Controls</span>\n                  </>\n                )}\n              </Button>\n            </TooltipTrigger>\n            <TooltipContent side=\"right\">\n              <p className=\"text-nowrap\">\n                Toggle controls with{\" \"}\n                <Kbd className=\"text-muted-foreground group-hover:text-accent-foreground ml-1\">\n                  <span className=\"mr-1\">⌘</span>\n                  <span>B</span>\n                </Kbd>\n              </p>\n            </TooltipContent>\n          </Tooltip>\n        </TooltipProvider>\n        <div className=\"block sm:hidden\">\n          <DataTableFilterControlsDrawer />\n        </div>\n        <div>\n          <p className=\"text-muted-foreground hidden text-sm sm:block\">\n            <span className=\"font-mono font-medium\">\n              {formatCompactNumber(rows.filtered)}\n            </span>{\" \"}\n            of{\" \"}\n            <span className=\"font-mono font-medium\">\n              {formatCompactNumber(rows.total)}\n            </span>{\" \"}\n            row(s) <span className=\"sr-only sm:not-sr-only\">filtered</span>\n          </p>\n          <p className=\"text-muted-foreground block text-sm sm:hidden\">\n            <span className=\"font-mono font-medium\">\n              {formatCompactNumber(rows.filtered)}\n            </span>{\" \"}\n            row(s)\n          </p>\n        </div>\n      </div>\n      <div className=\"ml-auto flex items-center gap-2\">\n        {columnFilters.length ? <DataTableResetButton /> : null}\n        {renderActions?.()}\n        <DataTableViewOptions />\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-view-options.tsx",
      "content": "\"use client\";\n\nimport {\n  Sortable,\n  SortableDragHandle,\n  SortableItem,\n} from \"@/components/custom/sortable\";\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Command,\n  CommandEmpty,\n  CommandGroup,\n  CommandInput,\n  CommandItem,\n  CommandList,\n} from \"@/components/ui/command\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport { cn } from \"@/lib/utils\";\nimport { Check, GripVertical, Settings2 } from \"lucide-react\";\nimport { useMemo, useState } from \"react\";\n\nexport function DataTableViewOptions() {\n  const { table, enableColumnOrdering } = useDataTable();\n  const [open, setOpen] = useState(false);\n  const [drag, setDrag] = useState(false);\n  const [search, setSearch] = useState(\"\");\n\n  const columnOrder = table.getState().columnOrder;\n\n  const sortedColumns = useMemo(\n    () =>\n      table.getAllColumns().sort((a, b) => {\n        return columnOrder.indexOf(a.id) - columnOrder.indexOf(b.id);\n      }),\n    [columnOrder, table],\n  );\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger asChild>\n        <Button\n          variant=\"outline\"\n          size=\"icon\"\n          role=\"combobox\"\n          aria-expanded={open}\n          className=\"shadow-none\"\n        >\n          <Settings2 className=\"h-4 w-4\" />\n          <span className=\"sr-only\">View</span>\n        </Button>\n      </PopoverTrigger>\n      <PopoverContent side=\"bottom\" align=\"end\" className=\"w-[200px] p-0\">\n        <Command>\n          <CommandInput\n            value={search}\n            onValueChange={setSearch}\n            placeholder=\"Search options...\"\n          />\n          <CommandList>\n            <CommandEmpty>No option found.</CommandEmpty>\n            <CommandGroup>\n              <Sortable\n                value={sortedColumns.map((c) => ({ id: c.id }))}\n                onValueChange={(items) =>\n                  table.setColumnOrder(items.map((c) => c.id))\n                }\n                overlay={<div className=\"bg-muted/60 h-8 w-full rounded-md\" />}\n                onDragStart={() => setDrag(true)}\n                onDragEnd={() => setDrag(false)}\n                onDragCancel={() => setDrag(false)}\n              >\n                {sortedColumns\n                  .filter(\n                    (column) =>\n                      typeof column.accessorFn !== \"undefined\" &&\n                      column.getCanHide(),\n                  )\n                  .map((column) => (\n                    <SortableItem key={column.id} value={column.id} asChild>\n                      <CommandItem\n                        value={column.id}\n                        onSelect={() =>\n                          column.toggleVisibility(!column.getIsVisible())\n                        }\n                        className={\"capitalize\"}\n                        disabled={drag}\n                      >\n                        <div\n                          className={cn(\n                            \"border-foreground! flex h-4 w-4 items-center justify-center rounded-sm border\",\n                            column.getIsVisible()\n                              ? \"bg-primary text-primary-foreground\"\n                              : \"opacity-50 [&_svg]:invisible\",\n                          )}\n                        >\n                          <Check className={cn(\"text-background size-3\")} />\n                        </div>\n                        <span>{column.columnDef.meta?.label || column.id}</span>\n                        <span data-slot=\"command-shortcut\" className=\"hidden\" />\n                        {enableColumnOrdering && !search ? (\n                          <SortableDragHandle\n                            variant=\"ghost\"\n                            size=\"icon\"\n                            className=\"text-muted-foreground hover:text-foreground focus:bg-muted focus:text-foreground ml-auto size-5\"\n                          >\n                            <GripVertical\n                              className=\"size-4\"\n                              aria-hidden=\"true\"\n                            />\n                          </SortableDragHandle>\n                        ) : null}\n                      </CommandItem>\n                    </SortableItem>\n                  ))}\n              </Sortable>\n            </CommandGroup>\n          </CommandList>\n        </Command>\n      </PopoverContent>\n    </Popover>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-reset-button.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { Button } from \"@/components/ui/button\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { useHotKey } from \"@/hooks/use-hot-key\";\nimport { X } from \"lucide-react\";\n\nexport function DataTableResetButton() {\n  const { table } = useDataTable();\n  useHotKey(table.resetColumnFilters, \"Escape\");\n\n  return (\n    <TooltipProvider>\n      <Tooltip delayDuration={100}>\n        <TooltipTrigger asChild>\n          <Button variant=\"ghost\" onClick={() => table.resetColumnFilters()}>\n            <X className=\"mr-2 h-4 w-4\" />\n            Reset\n          </Button>\n        </TooltipTrigger>\n        <TooltipContent side=\"left\">\n          <p className=\"text-nowrap\">\n            Reset filters with{\" \"}\n            <Kbd className=\"text-muted-foreground group-hover:text-accent-foreground ml-1\">\n              <span className=\"mr-1\">⌘</span>\n              <span>Esc</span>\n            </Kbd>\n          </p>\n        </TooltipContent>\n      </Tooltip>\n    </TooltipProvider>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-checkbox.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { Checkbox } from \"@/components/ui/checkbox\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/components/ui/input-group\";\nimport { Label } from \"@/components/ui/label\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { formatCompactNumber } from \"@/lib/format\";\nimport { cn } from \"@/lib/utils\";\nimport { Search } from \"lucide-react\";\nimport { useState } from \"react\";\nimport type { DataTableCheckboxFilterField } from \"./types\";\n\nexport function DataTableFilterCheckbox<TData>({\n  value: _value,\n  options,\n  component,\n}: DataTableCheckboxFilterField<TData>) {\n  const value = _value as string;\n  const [inputValue, setInputValue] = useState(\"\");\n  const { table, columnFilters, isLoading, getFacetedUniqueValues } =\n    useDataTable();\n  const column = table.getColumn(value);\n  // REMINDER: avoid using column?.getFilterValue()\n  const filterValue = columnFilters.find((i) => i.id === value)?.value;\n  const facetedValue =\n    getFacetedUniqueValues?.(table, value) || column?.getFacetedUniqueValues();\n\n  const Component = component;\n\n  // filter out the options based on the input value\n  const filterOptions = options?.filter(\n    (option) =>\n      inputValue === \"\" ||\n      option.label.toLowerCase().includes(inputValue.toLowerCase()),\n  );\n\n  // CHECK: it could be filterValue or searchValue\n  const filters = filterValue\n    ? Array.isArray(filterValue)\n      ? filterValue\n      : [filterValue]\n    : [];\n\n  // REMINDER: if no options are defined, while fetching data, we should show a skeleton\n  if (isLoading && !filterOptions?.length)\n    return (\n      <div className=\"border-border grid divide-y rounded-lg border\">\n        {Array.from({ length: 3 }).map((_, index) => (\n          <div\n            key={index}\n            className=\"flex items-center justify-between gap-2 px-2 py-2.5\"\n          >\n            <Skeleton className=\"h-4 w-4 rounded-sm\" />\n            <Skeleton className=\"h-4 w-full rounded-sm\" />\n          </div>\n        ))}\n      </div>\n    );\n\n  return (\n    <div className=\"grid gap-2\">\n      {options && options.length > 4 ? (\n        <InputGroup className=\"h-9 rounded-lg shadow-none\">\n          <InputGroupAddon>\n            <Search className=\"mt-0.5 h-4 w-4\" />\n          </InputGroupAddon>\n          <InputGroupInput\n            placeholder=\"Search\"\n            value={inputValue}\n            onChange={(e) => setInputValue(e.target.value)}\n          />\n        </InputGroup>\n      ) : null}\n      {/* FIXME: due to the added max-h and overflow-y-auto, the hover state and border is laying on top of the scroll bar */}\n      <div className=\"border-border max-h-[200px] overflow-y-auto rounded-lg border empty:border-none\">\n        {filterOptions\n          // TODO: we shoudn't sort the options here, instead filterOptions should be sorted by default\n          // .sort((a, b) => a.label.localeCompare(b.label))\n          ?.map((option, index) => {\n            const checked = filters.includes(option.value);\n\n            return (\n              <div\n                key={String(option.value)}\n                className={cn(\n                  \"group hover:bg-accent/50 relative flex items-center space-x-2 px-2 py-2.5\",\n                  index !== filterOptions.length - 1 ? \"border-b\" : undefined,\n                )}\n              >\n                <Checkbox\n                  id={`${value}-${option.value}`}\n                  checked={checked}\n                  onCheckedChange={(checked) => {\n                    const newValue = checked\n                      ? [...(filters || []), option.value]\n                      : filters?.filter((value) => option.value !== value);\n                    column?.setFilterValue(\n                      newValue?.length ? newValue : undefined,\n                    );\n                  }}\n                  className=\"border-foreground! shadow-none\"\n                />\n                <Label\n                  htmlFor={`${value}-${option.value}`}\n                  className=\"text-foreground/70 group-hover:text-accent-foreground flex w-full items-center justify-center gap-1 truncate\"\n                >\n                  {Component ? (\n                    <Component {...option} />\n                  ) : (\n                    <span className=\"truncate font-normal\">{option.label}</span>\n                  )}\n                  <span className=\"ml-auto flex items-center justify-center font-mono text-xs\">\n                    {isLoading ? (\n                      <Skeleton className=\"h-4 w-4\" />\n                    ) : facetedValue?.has(option.value) ? (\n                      formatCompactNumber(facetedValue.get(option.value) || 0)\n                    ) : null}\n                  </span>\n                  <button\n                    type=\"button\"\n                    onClick={() => column?.setFilterValue([option.value])}\n                    className={cn(\n                      \"text-muted-foreground hover:text-foreground absolute inset-y-0 right-0 hidden font-normal backdrop-blur-xs group-hover:block\",\n                      \"focus-visible:border-ring focus-visible:ring-ring/50 rounded-md transition-all outline-none focus-visible:ring-[3px]\",\n                    )}\n                  >\n                    <span className=\"px-2\">only</span>\n                  </button>\n                </Label>\n              </div>\n            );\n          })}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-input.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/components/ui/input-group\";\nimport { Label } from \"@/components/ui/label\";\nimport { useDebounce } from \"@/hooks/use-debounce\";\nimport { Search } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport type { DataTableInputFilterField } from \"./types\";\n\nfunction getFilter(filterValue: unknown) {\n  return typeof filterValue === \"string\" ? filterValue : null;\n}\n\nexport function DataTableFilterInput<TData>({\n  value: _value,\n}: DataTableInputFilterField<TData>) {\n  const value = _value as string;\n  const { table, columnFilters } = useDataTable();\n  const column = table.getColumn(value);\n  const filterValue = columnFilters.find((i) => i.id === value)?.value;\n  const filters = getFilter(filterValue);\n  const [input, setInput] = useState<string | null>(filters);\n\n  const debouncedInput = useDebounce(input, 500);\n\n  useEffect(() => {\n    const newValue = debouncedInput?.trim() === \"\" ? null : debouncedInput;\n    if (debouncedInput === null) return;\n    column?.setFilterValue(newValue);\n  }, [debouncedInput]);\n\n  useEffect(() => {\n    if (debouncedInput?.trim() !== filters) {\n      setInput(filters);\n    }\n  }, [filters]);\n\n  return (\n    <div className=\"grid w-full gap-1.5\">\n      <Label htmlFor={value} className=\"text-muted-foreground sr-only px-2\">\n        {value}\n      </Label>\n      <InputGroup className=\"h-9 rounded-lg shadow-none\">\n        <InputGroupAddon>\n          <Search className=\"mt-0.5 h-4 w-4\" />\n        </InputGroupAddon>\n        <InputGroupInput\n          placeholder=\"Search\"\n          name={value}\n          id={value}\n          value={input || \"\"}\n          onChange={(e) => setInput(e.target.value)}\n        />\n      </InputGroup>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-slider.tsx",
      "content": "\"use client\";\n\nimport { Slider } from \"@/components/custom/slider\";\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupInput,\n} from \"@/components/ui/input-group\";\nimport { Label } from \"@/components/ui/label\";\nimport { useDebounce } from \"@/hooks/use-debounce\";\nimport { isArrayOfNumbers } from \"@/lib/is-array\";\nimport { useEffect, useState } from \"react\";\nimport type { DataTableSliderFilterField } from \"./types\";\n\nfunction getFilter(filterValue: unknown) {\n  return typeof filterValue === \"number\"\n    ? [filterValue, filterValue]\n    : Array.isArray(filterValue) && isArrayOfNumbers(filterValue)\n      ? filterValue.length === 1\n        ? [filterValue[0], filterValue[0]]\n        : filterValue\n      : null;\n}\n\n// TODO: discuss if we even need the `defaultMin` and `defaultMax`\nexport function DataTableFilterSlider<TData>({\n  value: _value,\n  min: defaultMin,\n  max: defaultMax,\n  unit,\n}: DataTableSliderFilterField<TData>) {\n  const value = _value as string;\n  const { table, columnFilters, getFacetedMinMaxValues } = useDataTable();\n  const column = table.getColumn(value);\n  const filterValue = columnFilters.find((i) => i.id === value)?.value;\n  const filters = getFilter(filterValue);\n  const [input, setInput] = useState<number[] | null>(filters);\n  const [min, max] = getFacetedMinMaxValues?.(table, value) ||\n    column?.getFacetedMinMaxValues() || [defaultMin, defaultMax];\n\n  const debouncedInput = useDebounce(input, 500);\n\n  useEffect(() => {\n    if (debouncedInput?.length === 2) {\n      column?.setFilterValue(debouncedInput);\n    }\n  }, [debouncedInput]);\n\n  useEffect(() => {\n    if (debouncedInput?.length !== 2) {\n    } else if (!filters) {\n      setInput(null);\n    } else if (\n      debouncedInput[0] !== filters[0] ||\n      debouncedInput[1] !== filters[1]\n    ) {\n      setInput(filters);\n    }\n  }, [filters]);\n\n  return (\n    <div className=\"grid gap-2\">\n      <div className=\"flex items-center gap-4\">\n        <div className=\"grid w-full gap-1.5\">\n          <Label\n            htmlFor={`min-${value}`}\n            className=\"text-muted-foreground px-2\"\n          >\n            Min.\n          </Label>\n          <InputGroup className=\"mb-2 h-9 rounded-lg font-mono shadow-none\">\n            <InputGroupInput\n              placeholder=\"from\"\n              type=\"number\"\n              name={`min-${value}`}\n              id={`min-${value}`}\n              value={`${input?.[0] ?? min}`}\n              min={min}\n              max={max}\n              onChange={(e) =>\n                setInput((prev) => [Number(e.target.value), prev?.[1] || max])\n              }\n            />\n            {unit ? (\n              <InputGroupAddon align=\"inline-end\">{unit}</InputGroupAddon>\n            ) : null}\n          </InputGroup>\n        </div>\n        <div className=\"grid w-full gap-1.5\">\n          <Label\n            htmlFor={`max-${value}`}\n            className=\"text-muted-foreground px-2\"\n          >\n            Max.\n          </Label>\n          <InputGroup className=\"mb-2 h-9 rounded-lg font-mono shadow-none\">\n            <InputGroupInput\n              placeholder=\"to\"\n              type=\"number\"\n              name={`max-${value}`}\n              id={`max-${value}`}\n              value={`${input?.[1] ?? max}`}\n              min={min}\n              max={max}\n              onChange={(e) =>\n                setInput((prev) => [prev?.[0] || min, Number(e.target.value)])\n              }\n            />\n            {unit ? (\n              <InputGroupAddon align=\"inline-end\">{unit}</InputGroupAddon>\n            ) : null}\n          </InputGroup>\n        </div>\n      </div>\n      <Slider\n        min={min}\n        max={max}\n        value={input?.length === 2 ? input : [min, max]}\n        onValueChange={(values) => setInput(values)}\n      />\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-timerange.tsx",
      "content": "\"use client\";\n\nimport { DatePickerWithRange } from \"@/components/custom/date-picker-with-range\";\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { isArrayOfDates } from \"@/lib/is-array\";\nimport { useMemo } from \"react\";\nimport type { DateRange } from \"react-day-picker\";\nimport type { DataTableTimerangeFilterField } from \"./types\";\n\nexport function DataTableFilterTimerange<TData>({\n  value: _value,\n  presets,\n}: DataTableTimerangeFilterField<TData>) {\n  const value = _value as string;\n  const { table, columnFilters } = useDataTable();\n  const column = table.getColumn(value);\n  const filterValue = columnFilters.find((i) => i.id === value)?.value;\n\n  const date: DateRange | undefined = useMemo(\n    () =>\n      filterValue instanceof Date\n        ? { from: filterValue, to: undefined }\n        : Array.isArray(filterValue) && isArrayOfDates(filterValue)\n          ? { from: filterValue?.[0], to: filterValue?.[1] }\n          : undefined,\n    [filterValue],\n  );\n\n  const setDate = (date: DateRange | undefined) => {\n    if (!date) {\n      column?.setFilterValue(undefined);\n      return;\n    }\n    if (date.from && !date.to) {\n      column?.setFilterValue([date.from]);\n    }\n    if (date.to && date.from) {\n      column?.setFilterValue([date.from, date.to]);\n    }\n  };\n\n  return <DatePickerWithRange {...{ date, setDate, presets }} />;\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-reset-button.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport { Button } from \"@/components/ui/button\";\nimport { X } from \"lucide-react\";\nimport type { DataTableFilterField } from \"./types\";\n\nexport function DataTableFilterResetButton<TData>({\n  value: _value,\n}: DataTableFilterField<TData>) {\n  const { columnFilters, table } = useDataTable();\n  const value = _value as string;\n  const column = table.getColumn(value);\n  const filterValue = columnFilters.find((f) => f.id === value)?.value;\n\n  // TODO: check if we could useMemo\n  const filters = filterValue\n    ? Array.isArray(filterValue)\n      ? filterValue\n      : [filterValue]\n    : [];\n\n  if (filters.length === 0) return null;\n\n  return (\n    <Button\n      variant=\"outline\"\n      className=\"h-5 gap-1 rounded-full px-1.5! py-1! font-mono text-[10px] shadow-none\"\n      onClick={(e) => {\n        e.stopPropagation();\n        column?.setFilterValue(undefined);\n      }}\n      onKeyDown={(e) => {\n        e.stopPropagation();\n        if (e.code === \"Enter\") {\n          column?.setFilterValue(undefined);\n        }\n      }}\n      asChild\n    >\n      {/* REMINDER: `AccordionTrigger` is also a button(!) and we get Hydration error when rendering button within button */}\n      <div role=\"button\" tabIndex={0}>\n        <span>{filters.length}</span>\n        <X className=\"text-muted-foreground ml-1! size-2.5!\" />\n      </div>\n    </Button>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-controls.tsx",
      "content": "\"use client\";\n\nimport { useDataTable } from \"@/components/data-table/data-table-provider\";\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from \"@/components/ui/accordion\";\nimport * as React from \"react\";\nimport { DataTableFilterCheckbox } from \"./data-table-filter-checkbox\";\nimport { DataTableFilterInput } from \"./data-table-filter-input\";\nimport { DataTableFilterResetButton } from \"./data-table-filter-reset-button\";\nimport { DataTableFilterSlider } from \"./data-table-filter-slider\";\nimport { DataTableFilterTimerange } from \"./data-table-filter-timerange\";\n\n// FIXME: use @container (especially for the slider element) to restructure elements\n\n// TODO: only pass the columns to generate the filters!\n// https://tanstack.com/table/v8/docs/framework/react/examples/filters\n\n// Pluggable filter registry — extend by adding entries\nexport const FILTER_COMPONENTS: Record<string, React.ComponentType<any>> = {\n  checkbox: DataTableFilterCheckbox,\n  input: DataTableFilterInput,\n  slider: DataTableFilterSlider,\n  timerange: DataTableFilterTimerange,\n};\n\nexport function DataTableFilterControls() {\n  const { filterFields } = useDataTable();\n  const [isMounted, setIsMounted] = React.useState(false);\n\n  React.useEffect(() => {\n    const timer = setTimeout(() => setIsMounted(true), 0);\n    return () => clearTimeout(timer);\n  }, []);\n\n  return (\n    <Accordion\n      type=\"multiple\"\n      className={\n        isMounted\n          ? undefined\n          : \"[&_[data-slot=accordion-content]]:!animate-none\"\n      }\n      defaultValue={filterFields\n        ?.filter(({ defaultOpen }) => defaultOpen)\n        ?.map(({ value }) => value as string)}\n    >\n      {filterFields?.map((field) => {\n        const value = field.value as string;\n        const FilterComponent = FILTER_COMPONENTS[field.type];\n        if (!FilterComponent) return null;\n        return (\n          <AccordionItem key={value} value={value} className=\"border-none\">\n            <AccordionTrigger className=\"data-[state=closed]:text-muted-foreground data-[state=open]:text-foreground focus-within:data-[state=closed]:text-foreground hover:data-[state=closed]:text-foreground w-full items-center px-2 py-0 hover:no-underline\">\n              <div className=\"flex w-full items-center justify-between gap-2 truncate py-2\">\n                <div className=\"flex items-center gap-2 truncate\">\n                  <p className=\"text-sm font-medium\">{field.label}</p>\n                  {value !== field.label.toLowerCase() &&\n                  !field.commandDisabled ? (\n                    <p className=\"text-muted-foreground mt-px truncate font-mono text-[10px]\">\n                      {value}\n                    </p>\n                  ) : null}\n                </div>\n                <DataTableFilterResetButton {...field} />\n              </div>\n            </AccordionTrigger>\n            <AccordionContent>\n              {/* REMINDER: avoid the focus state to be cut due to overflow-hidden */}\n              {/* REMINDER: need to move within here because of accordion height animation */}\n              <div className=\"p-1\">\n                <FilterComponent {...field} />\n              </div>\n            </AccordionContent>\n          </AccordionItem>\n        );\n      })}\n    </Accordion>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/data-table/data-table-filter-controls-drawer.tsx",
      "content": "import { Button } from \"@/components/ui/button\";\nimport {\n  Drawer,\n  DrawerClose,\n  DrawerContent,\n  DrawerDescription,\n  DrawerFooter,\n  DrawerHeader,\n  DrawerTitle,\n  DrawerTrigger,\n} from \"@/components/ui/drawer\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipProvider,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { useHotKey } from \"@/hooks/use-hot-key\";\nimport { useMediaQuery } from \"@/hooks/use-media-query\";\nimport { VisuallyHidden } from \"@radix-ui/react-visually-hidden\";\nimport { FilterIcon } from \"lucide-react\";\nimport React from \"react\";\nimport { DataTableFilterControls } from \"./data-table-filter-controls\";\n\nexport function DataTableFilterControlsDrawer() {\n  const triggerButtonRef = React.useRef<HTMLButtonElement>(null);\n  const isMobile = useMediaQuery(\"(max-width: 640px)\");\n\n  useHotKey(() => {\n    triggerButtonRef.current?.click();\n  }, \"b\");\n\n  return (\n    <Drawer>\n      <TooltipProvider>\n        <Tooltip delayDuration={100}>\n          <TooltipTrigger asChild>\n            <DrawerTrigger asChild>\n              <Button\n                ref={isMobile ? triggerButtonRef : null}\n                variant=\"ghost\"\n                size=\"icon\"\n                className=\"h-9 w-9\"\n              >\n                <FilterIcon className=\"h-4 w-4\" />\n              </Button>\n            </DrawerTrigger>\n          </TooltipTrigger>\n          <TooltipContent side=\"right\">\n            <p className=\"text-nowrap\">\n              Toggle controls with{\" \"}\n              <Kbd className=\"text-muted-foreground group-hover:text-accent-foreground ml-1\">\n                <span className=\"mr-1\">⌘</span>\n                <span>B</span>\n              </Kbd>\n            </p>\n          </TooltipContent>\n        </Tooltip>\n      </TooltipProvider>\n      <DrawerContent className=\"max-h-[calc(100dvh-4rem)]\">\n        <VisuallyHidden>\n          <DrawerHeader>\n            <DrawerTitle>Filters</DrawerTitle>\n            <DrawerDescription>Adjust your table filters</DrawerDescription>\n          </DrawerHeader>\n        </VisuallyHidden>\n        <div className=\"flex-1 overflow-y-auto px-4\">\n          <DataTableFilterControls />\n        </div>\n        <DrawerFooter>\n          <DrawerClose asChild>\n            <Button variant=\"outline\" className=\"w-full\">\n              Close\n            </Button>\n          </DrawerClose>\n        </DrawerFooter>\n      </DrawerContent>\n    </Drawer>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/custom/table.tsx",
      "content": "import { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\n\ninterface TableProps extends React.ComponentProps<\"table\"> {\n  containerClassName?: string;\n}\n\nfunction Table({\n  className,\n  containerClassName,\n  onScroll,\n  ...props\n}: TableProps) {\n  return (\n    <div\n      className={cn(\"w-full overflow-auto\", containerClassName)}\n      // REMINDER: we are not scrolling the table, but the container\n      {...{ onScroll }}\n    >\n      <table\n        className={cn(\"w-full caption-bottom text-sm\", className)}\n        {...props}\n      />\n    </div>\n  );\n}\n\nfunction TableHeader({ className, ...props }: React.ComponentProps<\"thead\">) {\n  return <thead className={cn(\"[&_tr]:border-b\", className)} {...props} />;\n}\n\nfunction TableBody({ className, ...props }: React.ComponentProps<\"tbody\">) {\n  return (\n    <tbody className={cn(\"[&_tr:last-child]:border-0\", className)} {...props} />\n  );\n}\n\nfunction TableFooter({ className, ...props }: React.ComponentProps<\"tfoot\">) {\n  return (\n    <tfoot\n      className={cn(\n        \"bg-primary text-primary-foreground font-medium\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction TableRow({ className, ...props }: React.ComponentProps<\"tr\">) {\n  return (\n    <tr\n      className={cn(\n        \"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction TableHead({ className, ...props }: React.ComponentProps<\"th\">) {\n  return (\n    <th\n      className={cn(\n        \"text-muted-foreground h-10 px-2 text-left align-middle font-medium\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n\nfunction TableCell({ className, ...props }: React.ComponentProps<\"td\">) {\n  return <td className={cn(\"p-2 align-middle\", className)} {...props} />;\n}\n\nfunction TableCaption({\n  className,\n  ...props\n}: React.ComponentProps<\"caption\">) {\n  return (\n    <caption\n      className={cn(\"text-muted-foreground mt-4 text-sm\", className)}\n      {...props}\n    />\n  );\n}\n\nexport {\n  Table,\n  TableHeader,\n  TableBody,\n  TableFooter,\n  TableHead,\n  TableRow,\n  TableCell,\n  TableCaption,\n};\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/custom/sortable.tsx",
      "content": "\"use client\";\n\nimport type {\n  DndContextProps,\n  DraggableSyntheticListeners,\n  DropAnimation,\n  UniqueIdentifier,\n} from \"@dnd-kit/core\";\nimport {\n  closestCenter,\n  defaultDropAnimationSideEffects,\n  DndContext,\n  DragOverlay,\n  KeyboardSensor,\n  MouseSensor,\n  TouchSensor,\n  useSensor,\n  useSensors,\n} from \"@dnd-kit/core\";\nimport {\n  restrictToHorizontalAxis,\n  restrictToParentElement,\n  restrictToVerticalAxis,\n} from \"@dnd-kit/modifiers\";\nimport {\n  arrayMove,\n  horizontalListSortingStrategy,\n  SortableContext,\n  useSortable,\n  verticalListSortingStrategy,\n  type SortableContextProps,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { Button } from \"@/components/ui/button\";\nimport { composeRefs } from \"@/lib/compose-refs\";\nimport { cn } from \"@/lib/utils\";\nimport { Slot, type SlotProps } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nconst orientationConfig = {\n  vertical: {\n    modifiers: [restrictToVerticalAxis, restrictToParentElement],\n    strategy: verticalListSortingStrategy,\n  },\n  horizontal: {\n    modifiers: [restrictToHorizontalAxis, restrictToParentElement],\n    strategy: horizontalListSortingStrategy,\n  },\n  mixed: {\n    modifiers: [restrictToParentElement],\n    strategy: undefined,\n  },\n};\n\ninterface SortableProps<TData extends { id: UniqueIdentifier }>\n  extends DndContextProps {\n  /**\n   * An array of data items that the sortable component will render.\n   * @example\n   * value={[\n   *   { id: 1, name: 'Item 1' },\n   *   { id: 2, name: 'Item 2' },\n   * ]}\n   */\n  value: TData[];\n\n  /**\n   * An optional callback function that is called when the order of the data items changes.\n   * It receives the new array of items as its argument.\n   * @example\n   * onValueChange={(items) => console.log(items)}\n   */\n  onValueChange?: (items: TData[]) => void;\n\n  /**\n   * An optional callback function that is called when an item is moved.\n   * It receives an event object with `activeIndex` and `overIndex` properties, representing the original and new positions of the moved item.\n   * This will override the default behavior of updating the order of the data items.\n   * @type (event: { activeIndex: number; overIndex: number }) => void\n   * @example\n   * onMove={(event) => console.log(`Item moved from index ${event.activeIndex} to index ${event.overIndex}`)}\n   */\n  onMove?: (event: { activeIndex: number; overIndex: number }) => void;\n\n  /**\n   * A collision detection strategy that will be used to determine the closest sortable item.\n   * @default closestCenter\n   * @type DndContextProps[\"collisionDetection\"]\n   */\n  collisionDetection?: DndContextProps[\"collisionDetection\"];\n\n  /**\n   * An array of modifiers that will be used to modify the behavior of the sortable component.\n   * @default\n   * [restrictToVerticalAxis, restrictToParentElement]\n   * @type Modifier[]\n   */\n  modifiers?: DndContextProps[\"modifiers\"];\n\n  /**\n   * A sorting strategy that will be used to determine the new order of the data items.\n   * @default verticalListSortingStrategy\n   * @type SortableContextProps[\"strategy\"]\n   */\n  strategy?: SortableContextProps[\"strategy\"];\n\n  /**\n   * Specifies the axis for the drag-and-drop operation. It can be \"vertical\", \"horizontal\", or \"both\".\n   * @default \"vertical\"\n   * @type \"vertical\" | \"horizontal\" | \"mixed\"\n   */\n  orientation?: \"vertical\" | \"horizontal\" | \"mixed\";\n\n  /**\n   * An optional React node that is rendered on top of the sortable component.\n   * It can be used to display additional information or controls.\n   * @default null\n   * @type React.ReactNode | null\n   * @example\n   * overlay={<Skeleton className=\"w-full h-8\" />}\n   */\n  overlay?: React.ReactNode | null;\n}\n\nfunction Sortable<TData extends { id: UniqueIdentifier }>({\n  value,\n  onValueChange,\n  onDragStart,\n  onDragEnd,\n  onDragCancel,\n  collisionDetection = closestCenter,\n  modifiers,\n  strategy,\n  onMove,\n  orientation = \"vertical\",\n  overlay,\n  children,\n  ...props\n}: SortableProps<TData>) {\n  const [activeId, setActiveId] = React.useState<UniqueIdentifier | null>(null);\n  const sensors = useSensors(\n    useSensor(MouseSensor),\n    useSensor(TouchSensor),\n    useSensor(KeyboardSensor),\n  );\n\n  const config = orientationConfig[orientation];\n\n  return (\n    <DndContext\n      modifiers={modifiers ?? config.modifiers}\n      sensors={sensors}\n      onDragStart={(event) => {\n        setActiveId(event.active.id);\n        onDragStart?.(event);\n      }}\n      onDragEnd={(event) => {\n        const { active, over } = event;\n        if (over && active.id !== over?.id) {\n          const activeIndex = value.findIndex((item) => item.id === active.id);\n          const overIndex = value.findIndex((item) => item.id === over.id);\n\n          if (onMove) {\n            onMove({ activeIndex, overIndex });\n          } else {\n            onValueChange?.(arrayMove(value, activeIndex, overIndex));\n          }\n        }\n        setActiveId(null);\n        onDragEnd?.(event);\n      }}\n      onDragCancel={(event) => {\n        setActiveId?.(null);\n        onDragCancel?.(event);\n      }}\n      collisionDetection={collisionDetection}\n      {...props}\n    >\n      <SortableContext items={value} strategy={strategy ?? config.strategy}>\n        {children}\n      </SortableContext>\n      {overlay\n        ? // https://docs.dndkit.com/api-documentation/draggable/drag-overlay#portals\n          createPortal(\n            <SortableOverlay activeId={activeId}>{overlay}</SortableOverlay>,\n            document.body,\n          )\n        : null}\n    </DndContext>\n  );\n}\n\nconst dropAnimationOpts: DropAnimation = {\n  sideEffects: defaultDropAnimationSideEffects({\n    styles: {\n      active: {\n        opacity: \"0.4\",\n      },\n    },\n  }),\n};\n\ninterface SortableOverlayProps\n  extends React.ComponentPropsWithRef<typeof DragOverlay> {\n  activeId?: UniqueIdentifier | null;\n}\n\nfunction SortableOverlay({\n  activeId,\n  dropAnimation = dropAnimationOpts,\n  children,\n  ...props\n}: SortableOverlayProps) {\n  return (\n    <DragOverlay dropAnimation={dropAnimation} {...props}>\n      {activeId ? (\n        <SortableItem value={activeId} className=\"cursor-grabbing\" asChild>\n          {children}\n        </SortableItem>\n      ) : null}\n    </DragOverlay>\n  );\n}\n\ninterface SortableItemContextProps {\n  attributes: React.HTMLAttributes<HTMLElement>;\n  listeners: DraggableSyntheticListeners | undefined;\n  isDragging?: boolean;\n}\n\nconst SortableItemContext = React.createContext<SortableItemContextProps>({\n  attributes: {},\n  listeners: undefined,\n  isDragging: false,\n});\n\nfunction useSortableItem() {\n  const context = React.useContext(SortableItemContext);\n\n  if (!context) {\n    throw new Error(\"useSortableItem must be used within a SortableItem\");\n  }\n\n  return context;\n}\n\ninterface SortableItemProps extends SlotProps {\n  /**\n   * The unique identifier of the item.\n   * @example \"item-1\"\n   * @type UniqueIdentifier\n   */\n  value: UniqueIdentifier;\n\n  /**\n   * Specifies whether the item should act as a trigger for the drag-and-drop action.\n   * @default false\n   * @type boolean | undefined\n   */\n  asTrigger?: boolean;\n\n  /**\n   * Merges the item's props into its immediate child.\n   * @default false\n   * @type boolean | undefined\n   */\n  asChild?: boolean;\n}\n\nfunction SortableItem({\n  value,\n  asTrigger,\n  asChild,\n  className,\n  ref,\n  ...props\n}: SortableItemProps & { ref?: React.Ref<HTMLDivElement> }) {\n  const {\n    attributes,\n    listeners,\n    setNodeRef,\n    transform,\n    transition,\n    isDragging,\n  } = useSortable({ id: value });\n\n  const context = React.useMemo<SortableItemContextProps>(\n    () => ({\n      attributes,\n      listeners,\n      isDragging,\n    }),\n    [attributes, listeners, isDragging],\n  );\n  const style: React.CSSProperties = {\n    opacity: isDragging ? 0.5 : 1,\n    transform: CSS.Translate.toString(transform),\n    transition,\n  };\n\n  const Comp = asChild ? Slot : \"div\";\n\n  return (\n    <SortableItemContext.Provider value={context}>\n      <Comp\n        data-state={isDragging ? \"dragging\" : undefined}\n        className={cn(\n          \"data-[state=dragging]:cursor-grabbing\",\n          { \"cursor-grab\": !isDragging && asTrigger },\n          className,\n        )}\n        ref={composeRefs(ref, setNodeRef as React.Ref<HTMLDivElement>)}\n        style={style}\n        {...(asTrigger ? attributes : {})}\n        {...(asTrigger ? listeners : {})}\n        {...props}\n      />\n    </SortableItemContext.Provider>\n  );\n}\n\ninterface SortableDragHandleProps extends React.ComponentProps<typeof Button> {\n  withHandle?: boolean;\n}\n\nfunction SortableDragHandle({\n  className,\n  ref,\n  ...props\n}: SortableDragHandleProps) {\n  const { attributes, listeners, isDragging } = useSortableItem();\n\n  return (\n    <Button\n      ref={composeRefs(ref)}\n      data-state={isDragging ? \"dragging\" : undefined}\n      className={cn(\n        \"cursor-grab data-[state=dragging]:cursor-grabbing\",\n        className,\n      )}\n      {...attributes}\n      {...listeners}\n      {...props}\n    />\n  );\n}\n\nexport { Sortable, SortableDragHandle, SortableItem, SortableOverlay };\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/custom/slider.tsx",
      "content": "// Props to https://github.com/shadcn-ui/ui/issues/885#issuecomment-2059600641\n\n\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\nimport * as React from \"react\";\n\nfunction Slider({\n  className,\n  ...props\n}: React.ComponentProps<typeof SliderPrimitive.Root>) {\n  const initialValue = Array.isArray(props.value)\n    ? props.value\n    : [props.min, props.max];\n\n  return (\n    <SliderPrimitive.Root\n      data-slot=\"slider\"\n      className={cn(\n        \"relative flex w-full touch-none items-center select-none\",\n        className,\n      )}\n      {...props}\n    >\n      <SliderPrimitive.Track className=\"bg-secondary relative h-2 w-full grow overflow-hidden rounded-full\">\n        <SliderPrimitive.Range className=\"bg-primary absolute h-full\" />\n      </SliderPrimitive.Track>\n      {initialValue.map((_, index) => (\n        <React.Fragment key={index}>\n          <SliderPrimitive.Thumb className=\"border-primary bg-background focus-visible:border-ring focus-visible:ring-ring/50 block h-4 w-4 rounded-full border-2 transition-all outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50\" />\n        </React.Fragment>\n      ))}\n    </SliderPrimitive.Root>\n  );\n}\n\nexport { Slider };\n",
      "type": "registry:component"
    },
    {
      "path": "src/components/custom/date-picker-with-range.tsx",
      "content": "\"use client\";\n\nimport type { DatePreset } from \"@/components/data-table/types\";\nimport { Button } from \"@/components/ui/button\";\nimport { Calendar } from \"@/components/ui/calendar\";\nimport { Input } from \"@/components/ui/input\";\nimport { Kbd } from \"@/components/ui/kbd\";\nimport { Label } from \"@/components/ui/label\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectLabel,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { presets as defaultPresets } from \"@/constants/date-preset\";\nimport { useDebounce } from \"@/hooks/use-debounce\";\nimport { cn } from \"@/lib/utils\";\nimport { format } from \"date-fns\";\nimport { Calendar as CalendarIcon } from \"lucide-react\";\nimport * as React from \"react\";\nimport type { DateRange } from \"react-day-picker\";\n\ninterface DatePickerWithRangeProps\n  extends React.HTMLAttributes<HTMLDivElement> {\n  date: DateRange | undefined;\n  setDate: (date: DateRange | undefined) => void;\n  presets?: DatePreset[];\n}\n\nexport function DatePickerWithRange({\n  className,\n  date,\n  setDate,\n  presets = defaultPresets,\n}: DatePickerWithRangeProps) {\n  const [open, setOpen] = React.useState(false);\n  React.useEffect(() => {\n    const down = (e: KeyboardEvent) => {\n      if (!open) return;\n\n      presets.map((preset) => {\n        if (preset.shortcut === e.key) {\n          setDate({ from: preset.from, to: preset.to });\n        }\n      });\n    };\n    document.addEventListener(\"keydown\", down);\n    return () => document.removeEventListener(\"keydown\", down);\n  }, [setDate, presets, open]);\n\n  return (\n    <div className={cn(\"grid gap-2\", className)}>\n      <Popover open={open} onOpenChange={setOpen}>\n        <PopoverTrigger asChild>\n          <Button\n            id=\"date\"\n            variant=\"outline\"\n            className={cn(\n              \"hover:bg-muted/50 max-w-full justify-start truncate text-left font-normal shadow-none\",\n              !date && \"text-muted-foreground\",\n            )}\n          >\n            <CalendarIcon className=\"h-4 w-4\" />\n            {date?.from ? (\n              date.to ? (\n                <span className=\"truncate\">\n                  {format(date.from, \"LLL dd, y\")} -{\" \"}\n                  {format(date.to, \"LLL dd, y\")}\n                </span>\n              ) : (\n                format(date.from, \"LLL dd, y\")\n              )\n            ) : (\n              <span>Pick a date</span>\n            )}\n          </Button>\n        </PopoverTrigger>\n        <PopoverContent className=\"w-auto p-0\" align=\"start\">\n          <div className=\"flex flex-col justify-between sm:flex-row\">\n            <div className=\"hidden sm:block\">\n              <DatePresets\n                onSelect={setDate}\n                selected={date}\n                presets={presets}\n              />\n            </div>\n            <div className=\"block p-3 sm:hidden\">\n              <DatePresetsSelect\n                onSelect={setDate}\n                selected={date}\n                presets={presets}\n              />\n            </div>\n            <Separator orientation=\"vertical\" className=\"h-auto w-px\" />\n            <Calendar\n              initialFocus\n              mode=\"range\"\n              defaultMonth={date?.from}\n              selected={date}\n              onSelect={setDate}\n              numberOfMonths={1}\n            />\n          </div>\n          <Separator />\n          <CustomDateRange onSelect={setDate} selected={date} />\n        </PopoverContent>\n      </Popover>\n    </div>\n  );\n}\n\nfunction DatePresets({\n  selected,\n  onSelect,\n  presets,\n}: {\n  selected: DateRange | undefined;\n  onSelect: (date: DateRange | undefined) => void;\n  presets: DatePreset[];\n}) {\n  return (\n    <div className=\"flex flex-col gap-2 p-3\">\n      <p className=\"text-muted-foreground mx-3 text-xs uppercase\">Date Range</p>\n      <div className=\"grid gap-1\">\n        {presets.map(({ label, shortcut, from, to }) => {\n          const isActive = selected?.from === from && selected?.to === to;\n          return (\n            <Button\n              key={label}\n              variant={isActive ? \"outline\" : \"ghost\"}\n              onClick={() => onSelect({ from, to })}\n              className={cn(\n                \"flex items-center justify-between gap-6\",\n                !isActive && \"border border-transparent!\",\n              )}\n            >\n              <span className=\"mr-auto\">{label}</span>\n              <Kbd className=\"uppercase\">{shortcut}</Kbd>\n            </Button>\n          );\n        })}\n      </div>\n    </div>\n  );\n}\n\nfunction DatePresetsSelect({\n  selected,\n  onSelect,\n  presets,\n}: {\n  selected: DateRange | undefined;\n  onSelect: (date: DateRange | undefined) => void;\n  presets: DatePreset[];\n}) {\n  function findPreset(from?: Date, to?: Date) {\n    return presets.find((p) => p.from === from && p.to === to)?.shortcut;\n  }\n  const [value, setValue] = React.useState<string | undefined>(\n    findPreset(selected?.from, selected?.to),\n  );\n\n  React.useEffect(() => {\n    const preset = findPreset(selected?.from, selected?.to);\n    if (preset === value) return;\n    setValue(preset);\n  }, [selected, presets]);\n\n  return (\n    <Select\n      value={value}\n      onValueChange={(v) => {\n        const preset = presets.find((p) => p.shortcut === v);\n        if (preset) {\n          onSelect({ from: preset.from, to: preset.to });\n        }\n      }}\n    >\n      <SelectTrigger>\n        <SelectValue placeholder=\"Date Presets\" />\n      </SelectTrigger>\n      <SelectContent>\n        <SelectGroup>\n          <SelectLabel>Date Presets</SelectLabel>\n          {presets.map(({ label, shortcut }) => {\n            return (\n              <SelectItem\n                key={label}\n                value={shortcut}\n                className=\"flex items-center justify-between [&>span:last-child]:flex [&>span:last-child]:w-full [&>span:last-child]:justify-between\"\n              >\n                <span>{label}</span>\n                <Kbd className=\"ml-2 uppercase\">{shortcut}</Kbd>\n              </SelectItem>\n            );\n          })}\n        </SelectGroup>\n      </SelectContent>\n    </Select>\n  );\n}\n\n// REMINDER: We can add min max date range validation https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local#setting_maximum_and_minimum_dates_and_times\nfunction CustomDateRange({\n  selected,\n  onSelect,\n}: {\n  selected: DateRange | undefined;\n  onSelect: (date: DateRange | undefined) => void;\n}) {\n  const [dateFrom, setDateFrom] = React.useState<Date | undefined>(\n    selected?.from,\n  );\n  const [dateTo, setDateTo] = React.useState<Date | undefined>(selected?.to);\n  const debounceDateFrom = useDebounce(dateFrom, 1000);\n  const debounceDateTo = useDebounce(dateTo, 1000);\n\n  const formatDateForInput = (date: Date | undefined): string => {\n    if (!date) return \"\";\n    const utcDate = new Date(date.getTime() - date.getTimezoneOffset() * 60000);\n    return utcDate.toISOString().slice(0, 16);\n  };\n\n  React.useEffect(() => {\n    onSelect({ from: debounceDateFrom, to: debounceDateTo });\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [debounceDateFrom, debounceDateTo]);\n\n  return (\n    <div className=\"flex flex-col gap-2 p-3\">\n      <p className=\"text-muted-foreground text-xs uppercase\">Custom Range</p>\n      <div className=\"grid gap-2 sm:grid-cols-2\">\n        <div className=\"grid w-full gap-1.5\">\n          <Label htmlFor=\"from\">Start</Label>\n          <Input\n            key={formatDateForInput(selected?.from)}\n            type=\"datetime-local\"\n            id=\"from\"\n            name=\"from\"\n            defaultValue={formatDateForInput(selected?.from)}\n            onChange={(e) => {\n              const newDate = new Date(e.target.value);\n              if (!Number.isNaN(newDate.getTime())) {\n                setDateFrom(newDate);\n              }\n            }}\n            disabled={!selected?.from}\n          />\n        </div>\n        <div className=\"grid w-full gap-1.5\">\n          <Label htmlFor=\"to\">End</Label>\n          <Input\n            key={formatDateForInput(selected?.to)}\n            type=\"datetime-local\"\n            id=\"to\"\n            name=\"to\"\n            defaultValue={formatDateForInput(selected?.to)}\n            onChange={(e) => {\n              const newDate = new Date(e.target.value);\n              if (!Number.isNaN(newDate.getTime())) {\n                setDateTo(newDate);\n              }\n            }}\n            disabled={!selected?.to}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/providers/controls.tsx",
      "content": "import { useLocalStorage } from \"@/hooks/use-local-storage\";\nimport { CONTROLS_KEY } from \"@/lib/constants/local-storage\";\nimport { createContext, useContext } from \"react\";\n\ninterface ControlsContextType {\n  open: boolean;\n  setOpen: React.Dispatch<React.SetStateAction<boolean>>;\n}\n\nexport const ControlsContext = createContext<ControlsContextType | null>(null);\n\nexport function ControlsProvider({ children }: { children: React.ReactNode }) {\n  const [open, setOpen] = useLocalStorage(CONTROLS_KEY, true);\n\n  return (\n    <ControlsContext.Provider value={{ open, setOpen }}>\n      <div\n        // REMINDER: access the data-expanded state with tailwind via `group-data-[expanded=true]/controls:block`\n        // In tailwindcss v4, we could even use `group-data-expanded/controls:block`\n        className=\"group/controls\"\n        data-expanded={open}\n      >\n        {children}\n      </div>\n    </ControlsContext.Provider>\n  );\n}\n\nexport function useControls() {\n  const context = useContext(ControlsContext);\n\n  if (!context) {\n    throw new Error(\"useControls must be used within a ControlsProvider\");\n  }\n\n  return context as ControlsContextType;\n}\n",
      "type": "registry:component"
    },
    {
      "path": "src/lib/store/context.ts",
      "content": "/**\n * React Context for BYOS Store\n */\n\n\"use client\";\n\nimport { createContext, useContext } from \"react\";\nimport type { StoreAdapter } from \"./adapter/types\";\nimport type { SchemaDefinition } from \"./schema/types\";\n\n/**\n * Context value for the store provider\n */\nexport interface StoreContextValue<T extends Record<string, unknown>> {\n  adapter: StoreAdapter<T>;\n  schema: SchemaDefinition;\n  tableId: string;\n}\n\n/**\n * React context for the store adapter\n * @internal\n */\nexport const StoreContext = createContext<StoreContextValue<\n  Record<string, unknown>\n> | null>(null);\n\n/**\n * Hook to access the store context\n * Returns null if used outside of DataTableStoreProvider\n */\nexport function useStoreContext<\n  T extends Record<string, unknown> = Record<string, unknown>,\n>(): StoreContextValue<T> {\n  const context = useContext(StoreContext) as StoreContextValue<T> | null;\n  if (!context) {\n    throw new Error(\n      \"useStoreContext must be used within a DataTableStoreProvider\",\n    );\n  }\n  return context;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/adapter/index.ts",
      "content": "/**\n * Adapter Interface Exports\n */\n\nexport type {\n  StoreAdapter,\n  CreateAdapterOptions,\n  AdapterFactory,\n  InternalStoreAdapter,\n  AdapterType,\n} from \"./types\";\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/adapter/types.ts",
      "content": "/**\n * Adapter Interface for BYOS (Bring Your Own Store)\n *\n * This interface defines the contract that all store adapters must implement.\n * It is designed to be compatible with React 18's useSyncExternalStore.\n */\n\nimport type { SchemaDefinition, StoreSnapshot } from \"../schema/types\";\n\n/**\n * Store adapter interface that all adapters must implement\n */\nexport interface StoreAdapter<T extends Record<string, unknown>> {\n  /**\n   * Subscribe to state changes.\n   * Compatible with useSyncExternalStore's subscribe parameter.\n   *\n   * @param listener - Callback to invoke when state changes\n   * @returns Unsubscribe function\n   */\n  subscribe(listener: () => void): () => void;\n\n  /**\n   * Get the current state snapshot.\n   * Compatible with useSyncExternalStore's getSnapshot parameter.\n   *\n   * @returns Current state snapshot with version\n   */\n  getSnapshot(): StoreSnapshot<T>;\n\n  /**\n   * Get the server-side state snapshot (for SSR).\n   * Only implemented by URL-based adapters.\n   *\n   * @returns Server state snapshot\n   */\n  getServerSnapshot?(): StoreSnapshot<T>;\n\n  /**\n   * Update state with partial values.\n   * Must use immutable updates (new references for changed values).\n   *\n   * @param partial - Partial state to merge\n   */\n  setState(partial: Partial<T>): void;\n\n  /**\n   * Update a single field value.\n   *\n   * @param key - Field key\n   * @param value - New value\n   */\n  setField<K extends keyof T>(key: K, value: T[K]): void;\n\n  /**\n   * Reset state to defaults.\n   *\n   * @param fields - Optional array of fields to reset. If omitted, resets all.\n   */\n  reset(fields?: (keyof T)[]): void;\n\n  /**\n   * Pause state updates (for live mode).\n   * While paused, setState calls are queued.\n   */\n  pause(): void;\n\n  /**\n   * Resume state updates.\n   * Applies any queued state changes.\n   */\n  resume(): void;\n\n  /**\n   * Check if updates are paused.\n   */\n  isPaused(): boolean;\n\n  /**\n   * Cleanup resources when adapter is destroyed.\n   */\n  destroy(): void;\n\n  /**\n   * Get the unique table ID for this adapter.\n   */\n  getTableId(): string;\n\n  /**\n   * Get the schema definition used by this adapter.\n   */\n  getSchema(): SchemaDefinition;\n\n  /**\n   * Get the default values from the schema.\n   */\n  getDefaults(): T;\n}\n\n/**\n * Options for creating an adapter\n */\nexport interface CreateAdapterOptions<T extends Record<string, unknown>> {\n  /**\n   * Unique ID for this table (required for multi-table support)\n   */\n  id: string;\n\n  /**\n   * Initial state to override defaults\n   */\n  initialState?: Partial<T>;\n}\n\n/**\n * Factory function type for creating adapters\n */\nexport type AdapterFactory<T extends Record<string, unknown>> = (\n  schema: SchemaDefinition,\n  options: CreateAdapterOptions<T>,\n) => StoreAdapter<T>;\n\n/**\n * Available adapter types\n */\nexport type AdapterType = \"nuqs\" | \"zustand\";\n\n/**\n * Internal adapter with additional methods for providers\n * @internal\n */\nexport interface InternalStoreAdapter<T extends Record<string, unknown>>\n  extends StoreAdapter<T> {\n  /**\n   * Internal method to sync external state (used by nuqs adapter)\n   * @internal\n   */\n  _syncState?(state: T): void;\n\n  /**\n   * Internal method to get the state setter (used by nuqs adapter)\n   * @internal\n   */\n  _getStateSetter?(): ((partial: Partial<T>) => void) | null;\n\n  /**\n   * Internal method to set the state setter (used by nuqs adapter)\n   * @internal\n   */\n  _setStateSetter?(setter: (partial: Partial<T>) => void): void;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/schema/index.ts",
      "content": "import { getSchemaDefaults } from \"./serialization\";\nimport type { InferSchemaType, Schema, SchemaDefinition } from \"./types\";\n\n/**\n * Schema System for BYOS (Bring Your Own Store)\n *\n * Provides a fluent API for defining filter schemas with type inference.\n *\n * @example\n * ```typescript\n * import { createSchema, field } from '@/lib/store/schema';\n *\n * const schema = createSchema({\n *   regions: field.array(field.string()).default([]).delimiter(','),\n *   latency: field.array(field.number()).delimiter('-'),\n *   host: field.string().default(''),\n *   live: field.boolean().default(false),\n * });\n *\n * type FilterState = typeof schema._type;\n * ```\n */\n\nexport { field } from \"./field\";\nexport type {\n  FieldBuilder,\n  FieldConfig,\n  SchemaDefinition,\n  InferSchemaType,\n  Schema,\n  StoreSnapshot,\n  AdapterOptions,\n} from \"./types\";\nexport {\n  getSchemaDefaults,\n  serializeState,\n  parseState,\n  validateState,\n  mergeWithDefaults,\n  isStateEqual,\n  stateToSearchString,\n} from \"./serialization\";\n\n/**\n * Create a schema from field definitions\n *\n * @param definition - Object mapping field names to field builders\n * @returns Schema object with definition, defaults, and type inference\n *\n * @example\n * ```typescript\n * const schema = createSchema({\n *   level: field.array(field.stringLiteral(['error', 'warn', 'info'])).default([]),\n *   latency: field.array(field.number()).delimiter('-'),\n *   host: field.string(),\n * });\n * ```\n */\nexport function createSchema<T extends SchemaDefinition>(\n  definition: T,\n): Schema<T> {\n  const defaults = getSchemaDefaults(definition);\n\n  return {\n    definition,\n    defaults,\n    // This is a type-only property for inference\n    _type: defaults,\n  };\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/schema/types.ts",
      "content": "/**\n * Schema Types for BYOS (Bring Your Own Store)\n *\n * These types define the structure for filter schemas with a fluent API.\n */\n\n// Primitive field types\nexport type PrimitiveType =\n  | \"string\"\n  | \"number\"\n  | \"boolean\"\n  | \"timestamp\"\n  | \"stringLiteral\";\n\n// Field configuration stored internally\nexport interface FieldConfig<T> {\n  type: PrimitiveType | \"array\" | \"sort\";\n  defaultValue: T;\n  delimiter: string;\n  serialize: (value: T) => string;\n  parse: (value: string) => T | null;\n  // For stringLiteral type\n  literals?: readonly string[];\n  // For array type\n  itemConfig?: FieldConfig<unknown>;\n}\n\n// Field builder interface (fluent API)\nexport interface FieldBuilder<T> {\n  /**\n   * Set the default value for this field\n   */\n  default(value: T): FieldBuilder<T>;\n\n  /**\n   * Set the delimiter for serialization (used for arrays)\n   */\n  delimiter(separator: string): FieldBuilder<T>;\n\n  /**\n   * Custom serialization function\n   */\n  serialize(fn: (value: T) => string): FieldBuilder<T>;\n\n  /**\n   * Custom parse function\n   */\n  parse(fn: (value: string) => T | null): FieldBuilder<T>;\n\n  /**\n   * Internal config - do not use directly\n   * @internal\n   */\n  readonly _config: FieldConfig<T>;\n}\n\n// Schema definition as a record of field builders\n\nexport type SchemaDefinition = Record<string, FieldBuilder<any>>;\n\n// Infer the TypeScript type from a schema definition\nexport type InferSchemaType<T extends SchemaDefinition> = {\n  [K in keyof T]: T[K] extends FieldBuilder<infer U> ? U : never;\n};\n\n// Schema object returned by createSchema\nexport interface Schema<T extends SchemaDefinition> {\n  /**\n   * The raw schema definition\n   */\n  readonly definition: T;\n\n  /**\n   * Get default values for all fields\n   */\n  readonly defaults: InferSchemaType<T>;\n\n  /**\n   * Inferred TypeScript type (for type inference only)\n   */\n  readonly _type: InferSchemaType<T>;\n}\n\n// Store snapshot with version for change detection\nexport interface StoreSnapshot<T> {\n  state: T;\n  version: number;\n}\n\n// Adapter options passed during creation\nexport interface AdapterOptions {\n  /**\n   * Unique ID for this table (required for multi-table support)\n   */\n  id: string;\n\n  /**\n   * Initial state to override defaults\n   */\n  initialState?: Record<string, unknown>;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/schema/field.ts",
      "content": "/**\n * Field Builders for Schema Definition\n *\n * Provides a fluent API for defining filter field types with serialization.\n */\n\nimport {\n  ARRAY_DELIMITER,\n  SLIDER_DELIMITER,\n  SORT_DELIMITER,\n} from \"@/lib/delimiters\";\nimport type { FieldBuilder, FieldConfig } from \"./types\";\n\n// Helper to create a field builder from config\nfunction createFieldBuilder<T>(config: FieldConfig<T>): FieldBuilder<T> {\n  const builder: FieldBuilder<T> = {\n    default(value: T) {\n      return createFieldBuilder({ ...config, defaultValue: value });\n    },\n\n    delimiter(separator: string) {\n      // Update serialize/parse functions when delimiter changes for arrays\n      if (config.type === \"array\" && config.itemConfig) {\n        const itemConfig = config.itemConfig as FieldConfig<unknown>;\n        return createFieldBuilder({\n          ...config,\n          delimiter: separator,\n          serialize: (value: T) => {\n            if (!Array.isArray(value)) return \"\";\n            return value\n              .map((item) => itemConfig.serialize(item))\n              .join(separator);\n          },\n          parse: (str: string) => {\n            if (!str) return config.defaultValue;\n            const items = str.split(separator);\n            const parsed = items\n              .map((item) => itemConfig.parse(item))\n              .filter(\n                (item): item is NonNullable<typeof item> => item !== null,\n              );\n            return parsed as T;\n          },\n        });\n      }\n      return createFieldBuilder({ ...config, delimiter: separator });\n    },\n\n    serialize(fn: (value: T) => string) {\n      return createFieldBuilder({ ...config, serialize: fn });\n    },\n\n    parse(fn: (value: string) => T | null) {\n      return createFieldBuilder({ ...config, parse: fn });\n    },\n\n    get _config() {\n      return config;\n    },\n  };\n\n  return builder;\n}\n\n// String field\nfunction string(): FieldBuilder<string | null> {\n  return createFieldBuilder<string | null>({\n    type: \"string\",\n    defaultValue: null,\n    delimiter: \"\",\n    serialize: (value) => (value === null ? \"\" : String(value)),\n    parse: (str) => (str === \"\" ? null : str),\n  });\n}\n\n// Number field (integer)\nfunction number(): FieldBuilder<number | null> {\n  return createFieldBuilder<number | null>({\n    type: \"number\",\n    defaultValue: null,\n    delimiter: \"\",\n    serialize: (value) => (value === null ? \"\" : String(value)),\n    parse: (str) => {\n      if (str === \"\") return null;\n      const num = parseInt(str, 10);\n      return isNaN(num) ? null : num;\n    },\n  });\n}\n\n// Boolean field\nfunction boolean(): FieldBuilder<boolean | null> {\n  return createFieldBuilder<boolean | null>({\n    type: \"boolean\",\n    defaultValue: null,\n    delimiter: \"\",\n    serialize: (value) => (value === null ? \"\" : String(value)),\n    parse: (str) => {\n      if (str === \"\") return null;\n      if (str === \"true\") return true;\n      if (str === \"false\") return false;\n      return null;\n    },\n  });\n}\n\n// Timestamp field (Date)\nfunction timestamp(): FieldBuilder<Date | null> {\n  return createFieldBuilder<Date | null>({\n    type: \"timestamp\",\n    defaultValue: null,\n    delimiter: \"\",\n    serialize: (value) => (value === null ? \"\" : String(value.getTime())),\n    parse: (str) => {\n      if (str === \"\") return null;\n      const time = parseInt(str, 10);\n      if (isNaN(time)) return null;\n      const date = new Date(time);\n      return isNaN(date.getTime()) ? null : date;\n    },\n  });\n}\n\n// String literal field (enum-like)\nfunction stringLiteral<T extends readonly string[]>(\n  literals: T,\n): FieldBuilder<T[number] | null> {\n  return createFieldBuilder<T[number] | null>({\n    type: \"stringLiteral\",\n    defaultValue: null,\n    delimiter: \"\",\n    literals,\n    serialize: (value) => (value === null ? \"\" : String(value)),\n    parse: (str) => {\n      if (str === \"\") return null;\n      return literals.includes(str as T[number]) ? (str as T[number]) : null;\n    },\n  });\n}\n\n// Array field\nfunction array<T>(itemBuilder: FieldBuilder<T>): FieldBuilder<T[]> {\n  const itemConfig = itemBuilder._config;\n  const defaultDelimiter =\n    itemConfig.type === \"number\" ? SLIDER_DELIMITER : ARRAY_DELIMITER;\n\n  return createFieldBuilder<T[]>({\n    type: \"array\",\n    defaultValue: [],\n    delimiter: defaultDelimiter,\n    itemConfig: itemConfig as FieldConfig<unknown>,\n    serialize: (value) => {\n      if (!Array.isArray(value) || value.length === 0) return \"\";\n      return value\n        .map((item) => itemConfig.serialize(item))\n        .join(defaultDelimiter);\n    },\n    parse: (str) => {\n      if (!str) return [];\n      const items = str.split(defaultDelimiter);\n      const parsed = items\n        .map((item) => itemConfig.parse(item))\n        .filter((item): item is T => item !== null);\n      return parsed;\n    },\n  });\n}\n\n// Sort field { id: string, desc: boolean }\nfunction sort(): FieldBuilder<{ id: string; desc: boolean } | null> {\n  return createFieldBuilder<{ id: string; desc: boolean } | null>({\n    type: \"sort\",\n    defaultValue: null,\n    delimiter: SORT_DELIMITER,\n    serialize: (value) => {\n      if (value === null) return \"\";\n      return `${value.id}${SORT_DELIMITER}${value.desc ? \"desc\" : \"asc\"}`;\n    },\n    parse: (str) => {\n      if (!str) return null;\n      const [id, desc] = str.split(SORT_DELIMITER);\n      if (!id) return null;\n      return { id, desc: desc === \"desc\" };\n    },\n  });\n}\n\n// Export field builders\nexport const field = {\n  string,\n  number,\n  boolean,\n  timestamp,\n  stringLiteral,\n  array,\n  sort,\n};\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/schema/serialization.ts",
      "content": "/**\n * Serialization Utilities for Schema\n *\n * Functions for serializing state to strings and parsing strings back to state.\n */\n\nimport type { FieldBuilder, InferSchemaType, SchemaDefinition } from \"./types\";\n\n/**\n * Get default values from a schema definition\n */\nexport function getSchemaDefaults<T extends SchemaDefinition>(\n  schema: T,\n): InferSchemaType<T> {\n  const defaults: Record<string, unknown> = {};\n\n  for (const [key, fieldBuilder] of Object.entries(schema)) {\n    defaults[key] = fieldBuilder._config.defaultValue;\n  }\n\n  return defaults as InferSchemaType<T>;\n}\n\n/**\n * Serialize state to a string map (for URL params or storage)\n */\nexport function serializeState<T extends SchemaDefinition>(\n  schema: T,\n  state: Partial<InferSchemaType<T>>,\n): Record<string, string> {\n  const result: Record<string, string> = {};\n\n  for (const [key, value] of Object.entries(state)) {\n    const fieldBuilder = schema[key] as FieldBuilder<unknown> | undefined;\n    if (!fieldBuilder) continue;\n\n    const serialized = fieldBuilder._config.serialize(value);\n    if (serialized !== \"\") {\n      result[key] = serialized;\n    }\n  }\n\n  return result;\n}\n\n/**\n * Parse a string map back to state\n */\nexport function parseState<T extends SchemaDefinition>(\n  schema: T,\n  stringMap: Record<string, string | string[] | undefined>,\n): Partial<InferSchemaType<T>> {\n  const result: Record<string, unknown> = {};\n\n  for (const [key, fieldBuilder] of Object.entries(schema)) {\n    const rawValue = stringMap[key];\n    if (rawValue === undefined) continue;\n\n    // Handle array values from URL (Next.js can pass arrays)\n    const strValue = Array.isArray(rawValue) ? rawValue.join(\",\") : rawValue;\n\n    const parsed = (fieldBuilder as FieldBuilder<unknown>)._config.parse(\n      strValue,\n    );\n    if (parsed !== null) {\n      result[key] = parsed;\n    }\n  }\n\n  return result as Partial<InferSchemaType<T>>;\n}\n\n/**\n * Validate and coerce state, returning defaults for invalid values\n */\nexport function validateState<T extends SchemaDefinition>(\n  schema: T,\n  state: unknown,\n): InferSchemaType<T> {\n  const defaults = getSchemaDefaults(schema);\n\n  if (!state || typeof state !== \"object\") {\n    return defaults;\n  }\n\n  const result: Record<string, unknown> = { ...defaults };\n\n  for (const [key, fieldBuilder] of Object.entries(schema)) {\n    const value = (state as Record<string, unknown>)[key];\n    if (value === undefined) continue;\n\n    // Serialize and re-parse to validate\n    const config = (fieldBuilder as FieldBuilder<unknown>)._config;\n    const serialized = config.serialize(value);\n    const parsed = config.parse(serialized);\n\n    if (parsed !== null) {\n      result[key] = parsed;\n    }\n    // If invalid, default is already in result\n  }\n\n  return result as InferSchemaType<T>;\n}\n\n/**\n * Merge partial state with defaults\n */\nexport function mergeWithDefaults<T extends SchemaDefinition>(\n  schema: T,\n  partial: Partial<InferSchemaType<T>>,\n): InferSchemaType<T> {\n  const defaults = getSchemaDefaults(schema);\n  return { ...defaults, ...partial };\n}\n\n/**\n * Check if two states are equal (shallow comparison for primitives, deep for arrays)\n */\nexport function isStateEqual<T extends Record<string, unknown>>(\n  a: T,\n  b: T,\n): boolean {\n  const keysA = Object.keys(a);\n  const keysB = Object.keys(b);\n\n  if (keysA.length !== keysB.length) return false;\n\n  for (const key of keysA) {\n    const valA = a[key];\n    const valB = b[key];\n\n    if (valA === valB) continue;\n\n    // Handle arrays\n    if (Array.isArray(valA) && Array.isArray(valB)) {\n      if (valA.length !== valB.length) return false;\n      for (let i = 0; i < valA.length; i++) {\n        const itemA = valA[i];\n        const itemB = valB[i];\n        if (itemA instanceof Date && itemB instanceof Date) {\n          if (itemA.getTime() !== itemB.getTime()) return false;\n        } else if (itemA !== itemB) {\n          return false;\n        }\n      }\n      continue;\n    }\n\n    // Handle dates\n    if (valA instanceof Date && valB instanceof Date) {\n      if (valA.getTime() !== valB.getTime()) return false;\n      continue;\n    }\n\n    // Handle objects (sort field)\n    if (\n      valA !== null &&\n      valB !== null &&\n      typeof valA === \"object\" &&\n      typeof valB === \"object\"\n    ) {\n      const objA = valA as Record<string, unknown>;\n      const objB = valB as Record<string, unknown>;\n      if (!isStateEqual(objA, objB)) return false;\n      continue;\n    }\n\n    return false;\n  }\n\n  return true;\n}\n\n/**\n * Create a URL search string from state\n */\nexport function stateToSearchString<T extends SchemaDefinition>(\n  schema: T,\n  state: Partial<InferSchemaType<T>>,\n): string {\n  const serialized = serializeState(schema, state);\n  const params = new URLSearchParams();\n\n  for (const [key, value] of Object.entries(serialized)) {\n    if (value) {\n      params.set(key, value);\n    }\n  }\n\n  const str = params.toString();\n  return str ? `?${str}` : \"\";\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/parser/index.ts",
      "content": "/**\n * Text Parser Exports\n */\n\nexport { createTextParser } from \"./text-parser\";\nexport type { TextParser, TextParserOptions } from \"./types\";\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/parser/types.ts",
      "content": "/**\n * Text Parser Types\n */\n\nimport type { InferSchemaType, SchemaDefinition } from \"../schema/types\";\n\n/**\n * Options for creating a text parser\n */\nexport interface TextParserOptions {\n  /**\n   * Field aliases for shorthand (e.g., { l: 'level', r: 'regions' })\n   */\n  aliases?: Record<string, string>;\n\n  /**\n   * Delimiter between field:value pairs (default: ' ')\n   */\n  fieldDelimiter?: string;\n\n  /**\n   * Delimiter between field and value (default: ':')\n   */\n  keyValueDelimiter?: string;\n}\n\n/**\n * Text parser interface\n */\nexport interface TextParser<T extends SchemaDefinition> {\n  /**\n   * Parse a text input string into filter state\n   *\n   * @example\n   * ```typescript\n   * parser.parse('regions:ams,gru latency:0-1000');\n   * // => { regions: ['ams', 'gru'], latency: [0, 1000] }\n   * ```\n   */\n  parse(input: string): Partial<InferSchemaType<T>>;\n\n  /**\n   * Serialize filter state to text format\n   *\n   * @example\n   * ```typescript\n   * parser.serialize({ regions: ['ams', 'gru'], latency: [0, 1000] });\n   * // => 'regions:ams,gru latency:0-1000'\n   * ```\n   */\n  serialize(state: Partial<InferSchemaType<T>>): string;\n\n  /**\n   * Get the current word at caret position\n   */\n  getWordAtCaret(\n    input: string,\n    caretPosition: number,\n  ): {\n    word: string;\n    start: number;\n    end: number;\n    field: string | null;\n    value: string | null;\n  };\n\n  /**\n   * Replace the current word at caret position\n   */\n  replaceWordAtCaret(\n    input: string,\n    caretPosition: number,\n    replacement: string,\n  ): {\n    newInput: string;\n    newCaretPosition: number;\n  };\n\n  /**\n   * Get suggestions for autocomplete\n   */\n  getSuggestions(\n    input: string,\n    caretPosition: number,\n    fieldOptions: Record<string, string[]>,\n  ): {\n    type: \"field\" | \"value\";\n    field?: string;\n    suggestions: string[];\n  };\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/parser/text-parser.ts",
      "content": "/**\n * Text Parser Implementation\n *\n * Standalone module for parsing filter command text input.\n */\n\nimport type {\n  FieldBuilder,\n  InferSchemaType,\n  SchemaDefinition,\n} from \"../schema/types\";\nimport type { TextParser, TextParserOptions } from \"./types\";\n\n/**\n * Create a text parser for filter command input\n *\n * @example\n * ```typescript\n * import { createTextParser } from '@/lib/store/parser';\n *\n * const parser = createTextParser(schema.definition, {\n *   aliases: { l: 'level', r: 'regions' },\n * });\n *\n * // Parse text to state\n * const state = parser.parse('level:error,warn regions:ams');\n *\n * // Serialize state to text\n * const text = parser.serialize({ level: ['error', 'warn'], regions: ['ams'] });\n * ```\n */\nexport function createTextParser<T extends SchemaDefinition>(\n  schema: T,\n  options: TextParserOptions = {},\n): TextParser<T> {\n  const {\n    aliases = {},\n    fieldDelimiter = \" \",\n    keyValueDelimiter = \":\",\n  } = options;\n\n  // Build reverse alias map\n  const reverseAliases: Record<string, string> = {};\n  for (const [alias, field] of Object.entries(aliases)) {\n    reverseAliases[field] = alias;\n  }\n\n  // Resolve alias to field name\n  const resolveAlias = (key: string): string => {\n    return aliases[key] || key;\n  };\n\n  return {\n    parse(input: string): Partial<InferSchemaType<T>> {\n      const result: Record<string, unknown> = {};\n\n      if (!input.trim()) {\n        return result as Partial<InferSchemaType<T>>;\n      }\n\n      // Split by field delimiter, but handle quoted values\n      const parts = input\n        .trim()\n        .split(new RegExp(`\\\\s*${escapeRegex(fieldDelimiter)}\\\\s*`));\n\n      for (const part of parts) {\n        if (!part) continue;\n\n        const colonIndex = part.indexOf(keyValueDelimiter);\n        if (colonIndex === -1) continue;\n\n        const rawKey = part.slice(0, colonIndex).trim();\n        const rawValue = part.slice(colonIndex + 1).trim();\n\n        if (!rawKey || !rawValue) continue;\n\n        const fieldKey = resolveAlias(rawKey);\n        const fieldBuilder = schema[fieldKey] as\n          | FieldBuilder<unknown>\n          | undefined;\n\n        if (!fieldBuilder) continue;\n\n        try {\n          const parsed = fieldBuilder._config.parse(rawValue);\n          if (parsed !== null) {\n            result[fieldKey] = parsed;\n          }\n        } catch {\n          // Skip invalid values\n        }\n      }\n\n      return result as Partial<InferSchemaType<T>>;\n    },\n\n    serialize(state: Partial<InferSchemaType<T>>): string {\n      const parts: string[] = [];\n\n      for (const [key, value] of Object.entries(state)) {\n        if (value === null || value === undefined) continue;\n        if (Array.isArray(value) && value.length === 0) continue;\n\n        const fieldBuilder = schema[key] as FieldBuilder<unknown> | undefined;\n        if (!fieldBuilder) continue;\n\n        try {\n          const serialized = fieldBuilder._config.serialize(value);\n          if (serialized) {\n            parts.push(`${key}${keyValueDelimiter}${serialized}`);\n          }\n        } catch {\n          // Skip invalid values\n        }\n      }\n\n      return parts.join(fieldDelimiter);\n    },\n\n    getWordAtCaret(input: string, caretPosition: number) {\n      // Find word boundaries\n      let start = caretPosition;\n      let end = caretPosition;\n\n      // Move start backwards to find word start\n      while (start > 0 && input[start - 1] !== fieldDelimiter) {\n        start--;\n      }\n\n      // Move end forwards to find word end\n      while (end < input.length && input[end] !== fieldDelimiter) {\n        end++;\n      }\n\n      const word = input.slice(start, end);\n      const colonIndex = word.indexOf(keyValueDelimiter);\n\n      let field: string | null = null;\n      let value: string | null = null;\n\n      if (colonIndex !== -1) {\n        field = resolveAlias(word.slice(0, colonIndex));\n        value = word.slice(colonIndex + 1);\n      } else {\n        // Could be a partial field name\n        field = null;\n        value = null;\n      }\n\n      return { word, start, end, field, value };\n    },\n\n    replaceWordAtCaret(\n      input: string,\n      caretPosition: number,\n      replacement: string,\n    ) {\n      const { start, end } = this.getWordAtCaret(input, caretPosition);\n\n      const before = input.slice(0, start);\n      const after = input.slice(end);\n\n      const newInput = before + replacement + after;\n      const newCaretPosition = start + replacement.length;\n\n      return { newInput, newCaretPosition };\n    },\n\n    getSuggestions(\n      input: string,\n      caretPosition: number,\n      fieldOptions: Record<string, string[]>,\n    ) {\n      const { word, field, value } = this.getWordAtCaret(input, caretPosition);\n\n      // If we have a field and are typing a value\n      if (field && value !== null) {\n        const options = fieldOptions[field] || [];\n        const filtered = options.filter((opt) =>\n          opt.toLowerCase().includes(value.toLowerCase()),\n        );\n\n        return {\n          type: \"value\" as const,\n          field,\n          suggestions: filtered,\n        };\n      }\n\n      // Otherwise, suggest field names\n      const schemaKeys = Object.keys(schema);\n      const aliasKeys = Object.keys(aliases);\n      const allFields = [...schemaKeys, ...aliasKeys];\n\n      const filtered = word\n        ? allFields.filter((f) =>\n            f.toLowerCase().startsWith(word.toLowerCase()),\n          )\n        : allFields;\n\n      return {\n        type: \"field\" as const,\n        suggestions: filtered,\n      };\n    },\n  };\n}\n\n/**\n * Escape special regex characters\n */\nfunction escapeRegex(str: string): string {\n  return str.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/provider/DataTableStoreProvider.tsx",
      "content": "/**\n * DataTableStoreProvider - Main provider for BYOS\n *\n * Wraps components with store adapter context.\n */\n\n\"use client\";\n\nimport React, { useEffect, useMemo } from \"react\";\nimport type { StoreAdapter } from \"../adapter/types\";\nimport { StoreContext, type StoreContextValue } from \"../context\";\n\nexport interface DataTableStoreProviderProps<\n  T extends Record<string, unknown>,\n> {\n  /**\n   * The store adapter to use\n   */\n  adapter: StoreAdapter<T>;\n\n  /**\n   * Child components\n   */\n  children: React.ReactNode;\n}\n\n/**\n * Provider component for data table store\n *\n * @example\n * ```typescript\n * const adapter = useNuqsAdapter(schema.definition, { id: 'my-table' });\n *\n * return (\n *   <DataTableStoreProvider adapter={adapter}>\n *     <DataTable />\n *   </DataTableStoreProvider>\n * );\n * ```\n */\nexport function DataTableStoreProvider<T extends Record<string, unknown>>({\n  adapter,\n  children,\n}: DataTableStoreProviderProps<T>) {\n  const value = useMemo<StoreContextValue<T>>(\n    () => ({\n      adapter,\n      schema: adapter.getSchema(),\n      tableId: adapter.getTableId(),\n    }),\n    [adapter],\n  );\n\n  // Cleanup on unmount\n  useEffect(() => {\n    return () => {\n      adapter.destroy();\n    };\n  }, [adapter]);\n\n  return (\n    <StoreContext.Provider\n      value={value as StoreContextValue<Record<string, unknown>>}\n    >\n      {children}\n    </StoreContext.Provider>\n  );\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/hooks/index.ts",
      "content": "/**\n * BYOS Hooks Exports\n */\n\nexport { useFilterState } from \"./useFilterState\";\nexport { useFilterActions, type FilterActions } from \"./useFilterActions\";\nexport { useFilterField, type FilterFieldResult } from \"./useFilterField\";\nexport { useReactTableSync } from \"./useReactTableSync\";\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/hooks/useFilterState.ts",
      "content": "/**\n * useFilterState Hook\n *\n * Read filter state from the store adapter.\n */\n\n\"use client\";\n\nimport { useCallback, useSyncExternalStore } from \"react\";\nimport { useStoreContext } from \"../context\";\n\n/**\n * Hook to read filter state from the adapter\n *\n * @example\n * ```typescript\n * // Read entire state\n * const state = useFilterState();\n *\n * // Read with selector (for performance)\n * const regions = useFilterState(s => s.regions);\n * ```\n */\nexport function useFilterState<T extends Record<string, unknown>, R = T>(\n  selector?: (state: T) => R,\n): R {\n  const context = useStoreContext();\n\n  if (!context) {\n    throw new Error(\n      \"useFilterState must be used within a DataTableStoreProvider\",\n    );\n  }\n\n  const { adapter } = context;\n\n  const subscribe = useCallback(\n    (onStoreChange: () => void) => adapter.subscribe(onStoreChange),\n    [adapter],\n  );\n\n  const getSnapshot = useCallback(() => {\n    const snapshot = adapter.getSnapshot();\n    const state = snapshot.state as T;\n    return selector ? selector(state) : (state as unknown as R);\n  }, [adapter, selector]);\n\n  const getServerSnapshot = useCallback(() => {\n    const snapshot = adapter.getServerSnapshot?.() ?? adapter.getSnapshot();\n    const state = snapshot.state as T;\n    return selector ? selector(state) : (state as unknown as R);\n  }, [adapter, selector]);\n\n  return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/hooks/useFilterActions.ts",
      "content": "/**\n * useFilterActions Hook\n *\n * Get actions to modify filter state.\n */\n\n\"use client\";\n\nimport { useCallback, useMemo } from \"react\";\nimport { useStoreContext } from \"../context\";\n\n/**\n * Actions returned by useFilterActions\n */\nexport interface FilterActions<T extends Record<string, unknown>> {\n  /**\n   * Set a single filter field value\n   */\n  setFilter: <K extends keyof T>(key: K, value: T[K]) => void;\n\n  /**\n   * Set multiple filter fields at once\n   */\n  setFilters: (partial: Partial<T>) => void;\n\n  /**\n   * Reset a single filter field to its default value\n   */\n  resetFilter: (key: keyof T) => void;\n\n  /**\n   * Reset all filters to default values\n   */\n  resetAllFilters: () => void;\n\n  /**\n   * Pause state updates (for live mode)\n   */\n  pause: () => void;\n\n  /**\n   * Resume state updates\n   */\n  resume: () => void;\n\n  /**\n   * Check if updates are paused\n   */\n  isPaused: () => boolean;\n}\n\n/**\n * Hook to get filter actions\n *\n * @example\n * ```typescript\n * const { setFilter, setFilters, resetAllFilters, pause, resume } = useFilterActions();\n *\n * // Set single field\n * setFilter('regions', ['ams', 'gru']);\n *\n * // Set multiple fields\n * setFilters({ regions: ['ams'], host: 'api.example.com' });\n *\n * // Reset all\n * resetAllFilters();\n *\n * // Pause/resume for live mode\n * pause();\n * resume();\n * ```\n */\nexport function useFilterActions<\n  T extends Record<string, unknown>,\n>(): FilterActions<T> {\n  const context = useStoreContext();\n\n  if (!context) {\n    throw new Error(\n      \"useFilterActions must be used within a DataTableStoreProvider\",\n    );\n  }\n\n  const { adapter } = context;\n\n  const setFilter = useCallback(\n    <K extends keyof T>(key: K, value: T[K]) => {\n      adapter.setField(key as string, value);\n    },\n    [adapter],\n  );\n\n  const setFilters = useCallback(\n    (partial: Partial<T>) => {\n      adapter.setState(partial as Partial<Record<string, unknown>>);\n    },\n    [adapter],\n  );\n\n  const resetFilter = useCallback(\n    (key: keyof T) => {\n      adapter.reset([key as string]);\n    },\n    [adapter],\n  );\n\n  const resetAllFilters = useCallback(() => {\n    adapter.reset();\n  }, [adapter]);\n\n  const pause = useCallback(() => {\n    adapter.pause();\n  }, [adapter]);\n\n  const resume = useCallback(() => {\n    adapter.resume();\n  }, [adapter]);\n\n  const isPaused = useCallback(() => {\n    return adapter.isPaused();\n  }, [adapter]);\n\n  return useMemo(\n    () => ({\n      setFilter,\n      setFilters,\n      resetFilter,\n      resetAllFilters,\n      pause,\n      resume,\n      isPaused,\n    }),\n    [\n      setFilter,\n      setFilters,\n      resetFilter,\n      resetAllFilters,\n      pause,\n      resume,\n      isPaused,\n    ],\n  );\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/hooks/useFilterField.ts",
      "content": "/**\n * useFilterField Hook\n *\n * Hook for working with a single filter field.\n */\n\n\"use client\";\n\nimport { useCallback, useMemo, useSyncExternalStore } from \"react\";\nimport { useStoreContext } from \"../context\";\n\n/**\n * Return type for useFilterField\n */\nexport interface FilterFieldResult<T> {\n  /**\n   * Current value of the field\n   */\n  value: T;\n\n  /**\n   * Set the field value\n   */\n  setValue: (value: T) => void;\n\n  /**\n   * Reset the field to its default value\n   */\n  reset: () => void;\n}\n\n/**\n * Hook to work with a single filter field\n *\n * @example\n * ```typescript\n * const { value, setValue, reset } = useFilterField<FilterState, 'regions'>('regions');\n *\n * // Read value\n * console.log(value); // ['ams', 'gru']\n *\n * // Update value\n * setValue(['ams', 'gru', 'fra']);\n *\n * // Reset to default\n * reset();\n * ```\n */\nexport function useFilterField<\n  T extends Record<string, unknown>,\n  K extends keyof T,\n>(key: K): FilterFieldResult<T[K]> {\n  const context = useStoreContext();\n\n  if (!context) {\n    throw new Error(\n      \"useFilterField must be used within a DataTableStoreProvider\",\n    );\n  }\n\n  const { adapter } = context;\n\n  const subscribe = useCallback(\n    (onStoreChange: () => void) => adapter.subscribe(onStoreChange),\n    [adapter],\n  );\n\n  const getSnapshot = useCallback(() => {\n    const snapshot = adapter.getSnapshot();\n    return (snapshot.state as T)[key];\n  }, [adapter, key]);\n\n  const getServerSnapshot = useCallback(() => {\n    const snapshot = adapter.getServerSnapshot?.() ?? adapter.getSnapshot();\n    return (snapshot.state as T)[key];\n  }, [adapter, key]);\n\n  const value = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n\n  const setValue = useCallback(\n    (newValue: T[K]) => {\n      adapter.setField(key as string, newValue);\n    },\n    [adapter, key],\n  );\n\n  const reset = useCallback(() => {\n    adapter.reset([key as string]);\n  }, [adapter, key]);\n\n  return useMemo(() => ({ value, setValue, reset }), [value, setValue, reset]);\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/hooks/useReactTableSync.ts",
      "content": "/**\n * useReactTableSync Hook\n *\n * Syncs BYOS filter state with React Table's columnFilters.\n * This hook provides bidirectional synchronization between the adapter state\n * and React Table's internal state.\n */\n\n\"use client\";\n\nimport type { DataTableFilterField } from \"@/components/data-table/types\";\nimport type { ColumnFiltersState, Table } from \"@tanstack/react-table\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport { useFilterActions } from \"./useFilterActions\";\nimport { useFilterState } from \"./useFilterState\";\n\ninterface UseReactTableSyncOptions<TData> {\n  /**\n   * React Table instance\n   */\n  table: Table<TData>;\n\n  /**\n   * Filter field definitions (to know which fields to sync)\n   */\n  filterFields: DataTableFilterField<TData>[];\n\n  /**\n   * Callback when column filters change (from table)\n   */\n  onColumnFiltersChange?: (filters: ColumnFiltersState) => void;\n}\n\n/**\n * Hook to synchronize BYOS adapter state with React Table\n *\n * @example\n * ```typescript\n * const table = useReactTable({ ... });\n *\n * useReactTableSync({\n *   table,\n *   filterFields,\n * });\n * ```\n */\nexport function useReactTableSync<TData>({\n  table,\n  filterFields,\n  onColumnFiltersChange,\n}: UseReactTableSyncOptions<TData>) {\n  const filterState = useFilterState<Record<string, unknown>>();\n  const { setFilters } = useFilterActions<Record<string, unknown>>();\n\n  // Track if we're currently syncing to avoid loops\n  const isSyncingRef = useRef(false);\n\n  // Sync BYOS state → React Table\n  useEffect(() => {\n    if (isSyncingRef.current) return;\n\n    isSyncingRef.current = true;\n\n    // Convert filter state to column filters format\n    for (const field of filterFields) {\n      const fieldKey = field.value as string;\n      const value = filterState[fieldKey];\n      const column = table.getColumn(fieldKey);\n\n      if (column) {\n        // Only update if value is different\n        const currentValue = column.getFilterValue();\n        if (!isEqual(currentValue, value)) {\n          column.setFilterValue(value ?? undefined);\n        }\n      }\n    }\n\n    isSyncingRef.current = false;\n  }, [filterState, filterFields, table]);\n\n  // Sync React Table → BYOS state (via callback)\n  const syncFromTable = useCallback(() => {\n    if (isSyncingRef.current) return;\n\n    isSyncingRef.current = true;\n\n    const columnFilters = table.getState().columnFilters;\n    const updates: Record<string, unknown> = {};\n\n    for (const field of filterFields) {\n      const fieldKey = field.value as string;\n      const filter = columnFilters.find((f) => f.id === fieldKey);\n      updates[fieldKey] = filter?.value ?? null;\n    }\n\n    setFilters(updates);\n    onColumnFiltersChange?.(columnFilters);\n\n    isSyncingRef.current = false;\n  }, [table, filterFields, setFilters, onColumnFiltersChange]);\n\n  return {\n    syncFromTable,\n  };\n}\n\n/**\n * Simple equality check for filter values\n */\nfunction isEqual(a: unknown, b: unknown): boolean {\n  if (a === b) return true;\n  if (a === null || b === null) return a === b;\n  if (a === undefined || b === undefined) return a === b;\n\n  if (Array.isArray(a) && Array.isArray(b)) {\n    if (a.length !== b.length) return false;\n    return a.every((val, i) => isEqual(val, b[i]));\n  }\n\n  if (a instanceof Date && b instanceof Date) {\n    return a.getTime() === b.getTime();\n  }\n\n  if (typeof a === \"object\" && typeof b === \"object\") {\n    const keysA = Object.keys(a as object);\n    const keysB = Object.keys(b as object);\n    if (keysA.length !== keysB.length) return false;\n    return keysA.every((key) =>\n      isEqual(\n        (a as Record<string, unknown>)[key],\n        (b as Record<string, unknown>)[key],\n      ),\n    );\n  }\n\n  return false;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/store/adapters/memory/index.ts",
      "content": "/**\n * In-Memory Adapter for BYOS\n *\n * A lightweight adapter that stores filter state in refs with a subscriber\n * pattern, compatible with React's useSyncExternalStore. No URL sync or\n * external store needed — ideal for the builder where state is ephemeral.\n */\n\n\"use client\";\n\nimport { useCallback, useMemo, useRef } from \"react\";\nimport type { InternalStoreAdapter } from \"../../adapter/types\";\nimport { getSchemaDefaults } from \"../../schema/serialization\";\nimport type { SchemaDefinition, StoreSnapshot } from \"../../schema/types\";\n\nexport function useMemoryAdapter<T extends Record<string, unknown>>(\n  schema: SchemaDefinition,\n  options?: { id?: string },\n): InternalStoreAdapter<T> {\n  const defaults = useMemo(() => getSchemaDefaults(schema) as T, [schema]);\n\n  const stateRef = useRef<T>({ ...defaults });\n  const versionRef = useRef(0);\n  const listenersRef = useRef(new Set<() => void>());\n  const pausedRef = useRef(false);\n  const pendingRef = useRef<Partial<T> | null>(null);\n\n  const notify = useCallback(() => {\n    for (const listener of listenersRef.current) {\n      listener();\n    }\n  }, []);\n\n  const adapter = useMemo<InternalStoreAdapter<T>>(() => {\n    return {\n      subscribe(listener: () => void) {\n        listenersRef.current.add(listener);\n        return () => {\n          listenersRef.current.delete(listener);\n        };\n      },\n\n      getSnapshot(): StoreSnapshot<T> {\n        return {\n          state: stateRef.current,\n          version: versionRef.current,\n        };\n      },\n\n      getServerSnapshot(): StoreSnapshot<T> {\n        return {\n          state: { ...defaults } as T,\n          version: 0,\n        };\n      },\n\n      setState(partial: Partial<T>) {\n        if (pausedRef.current) {\n          pendingRef.current = {\n            ...(pendingRef.current ?? {}),\n            ...partial,\n          } as Partial<T>;\n          return;\n        }\n\n        stateRef.current = { ...stateRef.current, ...partial };\n        versionRef.current++;\n        notify();\n      },\n\n      setField<K extends keyof T>(key: K, value: T[K]) {\n        this.setState({ [key]: value } as unknown as Partial<T>);\n      },\n\n      reset(fields?: (keyof T)[]) {\n        if (fields) {\n          const resetPartial: Partial<T> = {};\n          for (const field of fields) {\n            resetPartial[field] = defaults[field];\n          }\n          this.setState(resetPartial);\n        } else {\n          stateRef.current = { ...defaults };\n          versionRef.current++;\n          notify();\n        }\n      },\n\n      pause() {\n        pausedRef.current = true;\n      },\n\n      resume() {\n        pausedRef.current = false;\n        if (pendingRef.current) {\n          const pending = pendingRef.current;\n          pendingRef.current = null;\n          this.setState(pending);\n        }\n      },\n\n      isPaused() {\n        return pausedRef.current;\n      },\n\n      destroy() {\n        listenersRef.current.clear();\n      },\n\n      getTableId() {\n        return options?.id ?? \"memory\";\n      },\n\n      getSchema() {\n        return schema;\n      },\n\n      getDefaults() {\n        return defaults;\n      },\n    };\n  }, [schema, defaults, notify]);\n\n  return adapter;\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/utils.ts",
      "content": "import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs));\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/format.ts",
      "content": "import { format } from \"date-fns\";\n\nexport function formatLatency(ms: number): string {\n  if (ms >= 1000) {\n    return (\n      new Intl.NumberFormat(\"en-US\", {\n        minimumFractionDigits: 1,\n        maximumFractionDigits: 1,\n      }).format(ms / 1000) + \"s\"\n    );\n  }\n\n  return (\n    new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 3 }).format(ms) +\n    \"ms\"\n  );\n}\n\nexport function formatMilliseconds(value: number) {\n  return new Intl.NumberFormat(\"en-US\", { maximumFractionDigits: 3 }).format(\n    value,\n  );\n}\n\nexport function formatDate(value: Date | string) {\n  return format(new Date(`${value}`), \"LLL dd, y HH:mm\");\n}\n\nexport function formatCompactNumber(value: number) {\n  if (value >= 100 && value < 1000) {\n    return value.toString(); // Keep the number as is if it's in the hundreds\n  } else if (value >= 1000 && value < 1000000) {\n    return (value / 1000).toFixed(1) + \"k\"; // Convert to 'k' for thousands\n  } else if (value >= 1000000) {\n    return (value / 1000000).toFixed(1) + \"M\"; // Convert to 'M' for millions\n  } else {\n    return value.toString(); // Optionally handle numbers less than 100 if needed\n  }\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/delimiters.ts",
      "content": "export const ARRAY_DELIMITER = \",\";\nexport const SLIDER_DELIMITER = \"-\";\nexport const SPACE_DELIMITER = \"_\";\nexport const RANGE_DELIMITER = \"-\";\nexport const SORT_DELIMITER = \".\";\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/is-array.ts",
      "content": "export function isArrayOfNumbers(arr: unknown): arr is number[] {\n  if (!Array.isArray(arr)) return false;\n  return arr.every((item) => typeof item === \"number\");\n}\n\nexport function isArrayOfDates(arr: unknown): arr is Date[] {\n  if (!Array.isArray(arr)) return false;\n  return arr.every((item) => item instanceof Date);\n}\n\nexport function isArrayOfStrings(arr: unknown): arr is string[] {\n  if (!Array.isArray(arr)) return false;\n  return arr.every((item) => typeof item === \"string\");\n}\n\nexport function isArrayOfBooleans(arr: unknown): arr is boolean[] {\n  if (!Array.isArray(arr)) return false;\n  return arr.every((item) => typeof item === \"boolean\");\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/compose-refs.ts",
      "content": "// @see https://github.com/radix-ui/primitives/blob/main/packages/react/compose-refs/src/composeRefs.tsx\n\nimport * as React from \"react\";\n\ntype PossibleRef<T> = React.Ref<T> | undefined;\n\n/**\n * Set a given ref to a given value\n * This utility takes care of different types of refs: callback refs and RefObject(s)\n */\nfunction setRef<T>(ref: PossibleRef<T>, value: T) {\n  if (typeof ref === \"function\") {\n    ref(value);\n  } else if (ref !== null && ref !== undefined) {\n    (ref as React.MutableRefObject<T>).current = value;\n  }\n}\n\n/**\n * A utility to compose multiple refs together\n * Accepts callback refs and RefObject(s)\n */\nfunction composeRefs<T>(...refs: PossibleRef<T>[]) {\n  return (node: T) => refs.forEach((ref) => setRef(ref, node));\n}\n\n/**\n * A custom hook that composes multiple refs\n * Accepts callback refs and RefObject(s)\n */\nfunction useComposedRefs<T>(...refs: PossibleRef<T>[]) {\n  // eslint-disable-next-line react-hooks/exhaustive-deps\n  return React.useCallback(composeRefs(...refs), refs);\n}\n\nexport { composeRefs, useComposedRefs };\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/table/filterfns.ts",
      "content": "import { FilterFn } from \"@tanstack/react-table\";\nimport { isSameDay } from \"date-fns\";\nimport { isArrayOfDates } from \"../is-array\";\n\nexport const inDateRange: FilterFn<unknown> = (row, columnId, value) => {\n  const date = new Date(row.getValue(columnId));\n  const [start, end] = value as Date[];\n\n  if (isNaN(date.getTime())) return false;\n\n  // if no end date, check if it's the same day\n  if (!end) return isSameDay(date, start);\n\n  // Inclusive bounds to match server-side filtering\n  return date.getTime() >= start.getTime() && date.getTime() <= end.getTime();\n};\n\ninDateRange.autoRemove = (val: unknown) =>\n  !Array.isArray(val) || !val.length || !isArrayOfDates(val);\n\nexport const arrSome: FilterFn<unknown> = (row, columnId, filterValue) => {\n  if (!Array.isArray(filterValue)) return false;\n  return filterValue.some((val) => row.getValue<unknown[]>(columnId) === val);\n};\n\narrSome.autoRemove = (val: unknown) => !Array.isArray(val) || !val?.length;\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/constants/local-storage.ts",
      "content": "// Column visibility state per table\nexport const getColumnVisibilityKey = (tableId: string) =>\n  `data-table-visibility-${tableId}`;\n\n// Column order state per table\nexport const getColumnOrderKey = (tableId: string) =>\n  `data-table-column-order-${tableId}`;\n\n// Filter command search history per table\nexport const getCommandHistoryKey = (tableId: string) =>\n  `data-table-command-${tableId}`;\n\n// Controls panel open/close state (global)\nexport const CONTROLS_KEY = \"data-table-controls\";\n",
      "type": "registry:lib"
    },
    {
      "path": "src/lib/data-table/types.ts",
      "content": "import { z } from \"zod\";\n\nexport const facetMetadataSchema = z.object({\n  rows: z.array(z.object({ value: z.any(), total: z.number() })),\n  total: z.number(),\n  min: z.number().optional(),\n  max: z.number().optional(),\n});\n\nexport type FacetMetadataSchema = z.infer<typeof facetMetadataSchema>;\n\nexport type BaseChartSchema = { timestamp: number; [key: string]: number };\n",
      "type": "registry:lib"
    },
    {
      "path": "src/react-table.d.ts",
      "content": "import \"@tanstack/react-table\";\n\ndeclare module \"@tanstack/react-table\" {\n  // https://github.com/TanStack/table/issues/44#issuecomment-1377024296\n  interface TableMeta<TData extends unknown> {\n    getRowClassName?: (row: Row<TData>) => string;\n  }\n\n  interface ColumnMeta {\n    headerClassName?: string;\n    cellClassName?: string;\n    label?: string;\n    kind?: string;\n  }\n\n  interface FilterFns {\n    inDateRange?: FilterFn<any>;\n    arrSome?: FilterFn<any>;\n  }\n\n  // https://github.com/TanStack/table/discussions/4554\n  interface ColumnFiltersOptions<TData extends RowData> {\n    filterFns?: Record<string, FilterFn<TData>>;\n  }\n}\n",
      "type": "registry:lib"
    },
    {
      "path": "src/constants/date-preset.ts",
      "content": "import { DatePreset } from \"@/components/data-table/types\";\nimport { addDays, addHours, endOfDay, startOfDay } from \"date-fns\";\n\nexport const presets = [\n  {\n    label: \"Today\",\n    from: startOfDay(new Date()),\n    to: endOfDay(new Date()),\n    shortcut: \"d\", // day\n  },\n  {\n    label: \"Yesterday\",\n    from: startOfDay(addDays(new Date(), -1)),\n    to: endOfDay(addDays(new Date(), -1)),\n    shortcut: \"y\",\n  },\n  {\n    label: \"Last hour\",\n    from: addHours(new Date(), -1),\n    to: new Date(),\n    shortcut: \"h\",\n  },\n  {\n    label: \"Last 7 days\",\n    from: startOfDay(addDays(new Date(), -7)),\n    to: endOfDay(new Date()),\n    shortcut: \"w\",\n  },\n  {\n    label: \"Last 14 days\",\n    from: startOfDay(addDays(new Date(), -14)),\n    to: endOfDay(new Date()),\n    shortcut: \"b\", // bi-weekly\n  },\n  {\n    label: \"Last 30 days\",\n    from: startOfDay(addDays(new Date(), -30)),\n    to: endOfDay(new Date()),\n    shortcut: \"m\",\n  },\n] satisfies DatePreset[];\n",
      "type": "registry:lib"
    },
    {
      "path": "src/hooks/use-debounce.ts",
      "content": "import * as React from \"react\";\n\n// consider using https://github.com/xnimorz/use-debounce\nexport function useDebounce<T>(value: T, delay?: number): T {\n  const [debouncedValue, setDebouncedValue] = React.useState<T>(value);\n\n  React.useEffect(() => {\n    const timer = setTimeout(() => setDebouncedValue(value), delay ?? 500);\n\n    return () => {\n      clearTimeout(timer);\n    };\n  }, [value, delay]);\n\n  return debouncedValue;\n}\n",
      "type": "registry:hook"
    },
    {
      "path": "src/hooks/use-hot-key.ts",
      "content": "import { useEffect, useRef } from \"react\";\n\nexport function useHotKey(\n  callback: () => void,\n  key: string,\n  options?: { shift?: boolean },\n): void {\n  // Use ref to always have the latest callback without re-registering the listener\n  const callbackRef = useRef(callback);\n  // eslint-disable-next-line react-hooks/refs\n  callbackRef.current = callback;\n\n  useEffect(() => {\n    function handler(e: KeyboardEvent) {\n      const shiftMatch = options?.shift ? e.shiftKey : !e.shiftKey;\n      if (\n        e.key.toLowerCase() === key.toLowerCase() &&\n        (e.metaKey || e.ctrlKey) &&\n        shiftMatch\n      ) {\n        // e.preventDefault();\n        callbackRef.current();\n      }\n    }\n\n    window.addEventListener(\"keydown\", handler);\n    return () => {\n      window.removeEventListener(\"keydown\", handler);\n    };\n  }, [key, options?.shift]);\n}\n",
      "type": "registry:hook"
    },
    {
      "path": "src/hooks/use-media-query.ts",
      "content": "import * as React from \"react\";\n\nexport function useMediaQuery(query: string) {\n  const [value, setValue] = React.useState(false);\n\n  React.useEffect(() => {\n    function onChange(event: MediaQueryListEvent) {\n      setValue(event.matches);\n    }\n\n    const result = matchMedia(query);\n    result.addEventListener(\"change\", onChange);\n    setValue(result.matches);\n\n    return () => result.removeEventListener(\"change\", onChange);\n  }, [query]);\n\n  return value;\n}\n",
      "type": "registry:hook"
    },
    {
      "path": "src/hooks/use-local-storage.ts",
      "content": "\"use client\";\n\nimport { useCallback, useState } from \"react\";\n\nfunction getItemFromLocalStorage<T>(key: string, fallback: T): T {\n  if (typeof window === \"undefined\") return fallback;\n  try {\n    const item = window.localStorage.getItem(key);\n    return item ? JSON.parse(item) : fallback;\n  } catch {\n    return fallback;\n  }\n}\n\nexport function useLocalStorage<T>(\n  key: string,\n  initialValue: T,\n): [T, React.Dispatch<React.SetStateAction<T>>] {\n  // Initialize directly from localStorage to avoid hydration mismatch\n  const [storedValue, setStoredValue] = useState<T>(() =>\n    getItemFromLocalStorage(key, initialValue),\n  );\n\n  const setValue: React.Dispatch<React.SetStateAction<T>> = useCallback(\n    (value) => {\n      setStoredValue((prev) => {\n        const newValue = value instanceof Function ? value(prev) : value;\n        // Save to localStorage asynchronously to avoid blocking UI\n        queueMicrotask(() => {\n          try {\n            window.localStorage.setItem(key, JSON.stringify(newValue));\n          } catch {\n            // Ignore localStorage errors (quota exceeded, etc.)\n          }\n        });\n        return newValue;\n      });\n    },\n    [key],\n  );\n\n  return [storedValue, setValue];\n}\n",
      "type": "registry:hook"
    }
  ],
  "cssVars": {
    "theme": {
      "--color-success": "var(--success)",
      "--color-warning": "var(--warning)",
      "--color-error": "var(--error)",
      "--color-info": "var(--info)"
    },
    "light": {
      "success": "hsl(210 40% 96.1%)",
      "warning": "hsl(24.6 95% 53.1%)",
      "error": "hsl(0 84.2% 60.2%)",
      "info": "hsl(198.63 88.66% 48.43%)"
    },
    "dark": {
      "success": "hsl(217.2 32.6% 17.5%)",
      "warning": "hsl(24.6 95% 53.1%)",
      "error": "hsl(0 84.2% 60.2%)",
      "info": "hsl(198.63 88.66% 48.43%)"
    }
  },
  "docs": "https://data-table.openstatus.dev/docs",
  "type": "registry:block"
}