Repository: vercel/next.js
Pull request: bundle-analyzer: use <Select> and multiselect for top bar

The top bar is getting quite wide with all of its button toggle groups. This:

- Converts the Compressed/Uncompressed and Client/Server toggles to shadcn `<Select>`s
- Adds a custom `<MultiSelect>` implementation since this is missing in shadcn. This composes shadcn’s popovers and a series of checkboxes, along with accessibility attributes and keyboard shortcuts.
- Uses this `<MultiSelect>` for the type filter
- Removes the divider between these since there aren’t any groups left to divide

<img width="1547" height="719" alt="image" src="https://github.com/user-attachments/assets/4041f6b7-2a47-4e17-b020-448ac0103740" />


Changed files:
filename: apps/bundle-analyzer/app/page.tsx
patch: @@ -13,11 +13,28 @@ import { TreemapVisualizer } from '@/components/treemap-visualizer'
 
 import { Badge } from '@/components/ui/badge'
 import { TreemapSkeleton } from '@/components/ui/skeleton'
-import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
+import {
+  Select,
+  SelectContent,
+  SelectItem,
+  SelectTrigger,
+  SelectValue,
+} from '@/components/ui/select'
+import { MultiSelect } from '@/components/ui/multi-select'
 import { AnalyzeData, ModulesData } from '@/lib/analyze-data'
 import { computeActiveEntries, computeModuleDepthMap } from '@/lib/module-graph'
 import { fetchStrict } from '@/lib/utils'
 import { formatBytes } from '@/lib/utils'
+import {
+  File,
+  FileArchive,
+  Monitor,
+  Server,
+  FileCode,
+  FileJson,
+  Palette,
+  Package,
+} from 'lucide-react'
 
 enum Environment {
   Client = 'client',
@@ -153,78 +170,21 @@ export default function Home() {
       onMouseMove={handleMouseMove}
       onMouseUp={handleMouseUp}
     >
-      <div className="flex-none px-4 py-2 border-b border-border flex items-center gap-4">
-        <div className="basis-1/3 flex">
-          <RouteTypeahead
-            selectedRoute={selectedRoute}
-            onRouteSelected={(route) => {
-              setSelectedRoute(route)
-              setSelectedSourceIndex(null)
-              setFocusedSourceIndex(null)
-            }}
-          />
-        </div>
-
-        <div className="basis-2/3 flex justify-end items-center space-x-4">
-          {analyzeData && (
-            <>
-              <ToggleGroup
-                type="single"
-                value={sizeMode}
-                onValueChange={(value) => {
-                  if (value) setSizeMode(value as SizeMode)
-                }}
-                size="sm"
-              >
-                <ToggleGroupItem value={SizeMode.Uncompressed}>
-                  Uncompressed
-                </ToggleGroupItem>
-                <ToggleGroupItem value={SizeMode.Compressed}>
-                  Compressed
-                </ToggleGroupItem>
-              </ToggleGroup>
-
-              <ControlDivider />
-
-              <ToggleGroup
-                type="single"
-                value={environmentFilter}
-                onValueChange={(value) => {
-                  if (value) setEnvironmentFilter(value as Environment)
-                }}
-                size="sm"
-              >
-                <ToggleGroupItem value={Environment.Client}>
-                  Client
-                </ToggleGroupItem>
-                <ToggleGroupItem value={Environment.Server}>
-                  Server
-                </ToggleGroupItem>
-              </ToggleGroup>
-
-              <ControlDivider />
-
-              <ToggleGroup
-                type="multiple"
-                value={typeFilter}
-                onValueChange={(value) => {
-                  if (value.length > 0) setTypeFilter(value)
-                }}
-                size="sm"
-              >
-                <ToggleGroupItem value="js">JS</ToggleGroupItem>
-                <ToggleGroupItem value="css">CSS</ToggleGroupItem>
-                <ToggleGroupItem value="json">JSON</ToggleGroupItem>
-                <ToggleGroupItem value="asset">Asset</ToggleGroupItem>
-              </ToggleGroup>
-
-              <ControlDivider />
-
-              <FileSearch value={searchQuery} onChange={setSearchQuery} />
-            </>
-          )}
-        </div>
-      </div>
+      <TopBar
+        analyzeData={analyzeData}
+        selectedRoute={selectedRoute}
+        setSelectedRoute={setSelectedRoute}
+        sizeMode={sizeMode}
+        setSizeMode={setSizeMode}
+        environmentFilter={environmentFilter}
+        setEnvironmentFilter={setEnvironmentFilter}
+        setSelectedSourceIndex={setSelectedSourceIndex}
+        setFocusedSourceIndex={setFocusedSourceIndex}
+        typeFilter={typeFilter}
+        setTypeFilter={setTypeFilter}
+        searchQuery={searchQuery}
+        setSearchQuery={setSearchQuery}
+      />
 
       <div className="flex-1 flex min-h-0">
         {error && !analyzeData ? (
@@ -323,6 +283,137 @@ export default function Home() {
   )
 }
 
+const typeFilterOptions = [
+  {
+    value: 'js',
+    label: 'JavaScript',
+    icon: <FileCode className="h-3.5 w-3.5" />,
+  },
+  { value: 'css', label: 'CSS', icon: <Palette className="h-3.5 w-3.5" /> },
+  {
+    value: 'json',
+    label: 'JSON',
+    icon: <FileJson className="h-3.5 w-3.5" />,
+  },
+  {
+    value: 'asset',
+    label: 'Asset',
+    icon: <Package className="h-3.5 w-3.5" />,
+  },
+]
+
+function TopBar({
+  analyzeData,
+  selectedRoute,
+  setSelectedRoute,
+  sizeMode,
+  setSizeMode,
+  environmentFilter,
+  setEnvironmentFilter,
+  setSelectedSourceIndex,
+  setFocusedSourceIndex,
+  typeFilter,
+  setTypeFilter,
+  searchQuery,
+  setSearchQuery,
+}: {
+  analyzeData: AnalyzeData | undefined
+  selectedRoute: string | null
+  setSelectedRoute: (route: string | null) => void
+  sizeMode: SizeMode
+  setSizeMode: (mode: SizeMode) => void
+  environmentFilter: Environment
+  setEnvironmentFilter: (env: Environment) => void
+  setSelectedSourceIndex: (index: number | null) => void
+  setFocusedSourceIndex: (index: number | null) => void
+  typeFilter: string[]
+  setTypeFilter: (types: string[]) => void
+  searchQuery: string
+  setSearchQuery: (query: string) => void
+}) {
+  return (
+    <div className="flex-none px-4 py-2 border-b border-border flex items-center gap-3">
+      <div className="flex-1 flex">
+        <RouteTypeahead
+          selectedRoute={selectedRoute}
+          onRouteSelected={(route) => {
+            setSelectedRoute(route)
+            setSelectedSourceIndex(null)
+            setFocusedSourceIndex(null)
+          }}
+        />
+      </div>
+
+      <div className="flex items-center gap-2">
+        {analyzeData && (
+          <>
+            <Select
+              value={sizeMode}
+              onValueChange={(value: SizeMode) => setSizeMode(value)}
+            >
+              <SelectTrigger className="w-fit min-w-[120px] h-8 text-xs">
+                <SelectValue />
+              </SelectTrigger>
+              <SelectContent>
+                <SelectItem value={SizeMode.Uncompressed} className="text-xs">
+                  <div className="flex items-center gap-1.5">
+                    <File className="h-3.5 w-3.5" />
+                    <span className="text-xs">Uncompressed</span>
+                  </div>
+                </SelectItem>
+                <SelectItem value={SizeMode.Compressed} className="text-xs">
+                  <div className="flex items-center gap-1.5">
+                    <FileArchive className="h-3.5 w-3.5" />
+                    <span className="text-xs">Compressed</span>
+                  </div>
+                </SelectItem>
+              </SelectContent>
+            </Select>
+
+            <Select
+              value={environmentFilter}
+              onValueChange={(value: Environment) =>
+                setEnvironmentFilter(value)
+              }
+            >
+              <SelectTrigger className="w-fit min-w-[100px]">
+                <SelectValue />
+              </SelectTrigger>
+              <SelectContent>
+                <SelectItem value={Environment.Client}>
+                  <div className="flex items-center gap-1.5">
+                    <Monitor className="h-3.5 w-3.5" />
+                    <span className="text-xs">Client</span>
+                  </div>
+                </SelectItem>
+                <SelectItem value={Environment.Server}>
+                  <div className="flex items-center gap-1.5">
+                    <Server className="h-3.5 w-3.5" />
+                    <span className="text-xs">Server</span>
+                  </div>
+                </SelectItem>
+              </SelectContent>
+            </Select>
+
+            <MultiSelect
+              options={typeFilterOptions}
+              value={typeFilter}
+              onValueChange={setTypeFilter}
+              selectionName={{ singular: 'file type', plural: 'file types' }}
+              triggerIcon={<FileCode className="h-3.5 w-3.5" />}
+              aria-label="Filter by file type"
+            />
+
+            <ControlDivider />
+
+            <FileSearch value={searchQuery} onChange={setSearchQuery} />
+          </>
+        )}
+      </div>
+    </div>
+  )
+}
+
 function ControlDivider() {
   return <span className="h-6 w-px bg-muted-foreground/30" />
 }

filename: apps/bundle-analyzer/components/ui/multi-select.tsx
patch: @@ -0,0 +1,187 @@
+'use client'
+
+import * as React from 'react'
+import { Button } from '@/components/ui/button'
+import {
+  Popover,
+  PopoverContent,
+  PopoverTrigger,
+} from '@/components/ui/popover'
+import { ChevronDown, CheckIcon } from 'lucide-react'
+import { cn } from '@/lib/utils'
+
+export interface MultiSelectPropOption {
+  value: string
+  label: string
+  icon?: React.ReactNode
+}
+
+interface MultiSelectProps {
+  options: MultiSelectPropOption[]
+  value: string[]
+  onValueChange: (value: string[]) => void
+  placeholder?: string
+  emptyMessage?: string
+  selectionName?: {
+    singular: string
+    plural: string
+  }
+  className?: string
+  triggerClassName?: string
+  triggerIcon?: React.ReactNode
+  'aria-label'?: string
+  size?: 'sm' | 'default'
+}
+
+export function MultiSelect({
+  options,
+  value,
+  onValueChange,
+  placeholder = 'Select items...',
+  selectionName = {
+    singular: 'item',
+    plural: 'items',
+  },
+  className,
+  triggerClassName,
+  triggerIcon,
+  'aria-label': ariaLabel,
+}: MultiSelectProps) {
+  const [open, setOpen] = React.useState(false)
+
+  function handleToggle(optionValue: string, checked: boolean) {
+    const newValue = checked
+      ? [...value, optionValue]
+      : value.filter((v) => v !== optionValue)
+    if (newValue.length > 0) {
+      onValueChange(newValue)
+    }
+  }
+
+  let displayText: string
+  if (value.length === options.length) {
+    displayText = `All ${selectionName.plural}`
+  } else if (value.length === 0) {
+    displayText = placeholder
+  } else {
+    displayText = `${value.length} ${value.length === 1 ? selectionName.singular : selectionName.plural}`
+  }
+
+  return (
+    <Popover open={open} onOpenChange={setOpen}>
+      <PopoverTrigger asChild>
+        <Button
+          variant="outline"
+          className={cn(
+            'justify-between border focus-visible:ring-[3px] focus-visible:ring-ring/50 font-normal px-3 py-2 h-9 text-xs',
+            triggerClassName
+          )}
+          role="combobox"
+          aria-expanded={open}
+          aria-haspopup="dialog"
+          aria-label={ariaLabel}
+          onKeyDown={(e) => {
+            if ((e.key === 'ArrowDown' || e.key === 'ArrowUp') && !open) {
+              e.preventDefault()
+              setOpen(true)
+            }
+          }}
+        >
+          <div className="flex items-center gap-1">
+            {triggerIcon && (
+              <span
+                className="shrink-0 text-muted-foreground"
+                aria-hidden="true"
+              >
+                {triggerIcon}
+              </span>
+            )}
+            <span>{displayText}</span>
+          </div>
+          <ChevronDown className="h-3.5 w-3.5 opacity-50" aria-hidden="true" />
+        </Button>
+      </PopoverTrigger>
+      <PopoverContent
+        className={cn('w-48 p-2', className)}
+        align="end"
+        onKeyDown={(e) => {
+          if (e.key === 'Escape') {
+            e.preventDefault()
+            setOpen(false)
+            return
+          }
+
+          const checkboxes = Array.from(
+            e.currentTarget.querySelectorAll('input[type="checkbox"]')
+          ) as HTMLInputElement[]
+          const currentIndex = checkboxes.indexOf(
+            document.activeElement as HTMLInputElement
+          )
+
+          if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
+            e.preventDefault()
+            if (currentIndex === -1) {
+              checkboxes[0]?.focus()
+            } else {
+              const nextIndex =
+                e.key === 'ArrowDown'
+                  ? (currentIndex + 1) % checkboxes.length
+                  : (currentIndex - 1 + checkboxes.length) % checkboxes.length
+              checkboxes[nextIndex]?.focus()
+            }
+          }
+        }}
+      >
+        <fieldset className="space-y-2">
+          <legend className="sr-only">{ariaLabel || placeholder}</legend>
+          {options.map((option) => (
+            <MultiSelectOption
+              key={option.value}
+              label={option.label}
+              icon={option.icon}
+              checked={value.includes(option.value)}
+              onChange={(checked) => handleToggle(option.value, checked)}
+            />
+          ))}
+        </fieldset>
+      </PopoverContent>
+    </Popover>
+  )
+}
+
+function MultiSelectOption({
+  label,
+  icon,
+  checked,
+  onChange,
+}: {
+  label: string
+  icon?: React.ReactNode
+  checked: boolean
+  onChange: (checked: boolean) => void
+}) {
+  return (
+    <label
+      className={cn(
+        "text-xs hover:bg-accent hover:text-accent-foreground has-[input:focus-visible]:bg-accent has-[input:focus-visible]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 select-none outline-hidden [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
+      )}
+    >
+      <input
+        type="checkbox"
+        checked={checked}
+        onChange={(e) => onChange(e.target.checked)}
+        className="sr-only peer focus:outline-hidden focus-visible:outline-2"
+        aria-label={label}
+      />
+      {icon && (
+        <span className="shrink-0" aria-hidden="true">
+          {icon}
+        </span>
+      )}
+      <span className="flex items-center gap-2">{label}</span>
+      <span className="absolute right-2 flex size-3.5 items-center justify-center">
+        {checked && <CheckIcon className="size-4" />}
+      </span>
+    </label>
+  )
+}

filename: apps/bundle-analyzer/components/ui/select.tsx
patch: @@ -0,0 +1,190 @@
+'use client'
+
+import * as React from 'react'
+import * as SelectPrimitive from '@radix-ui/react-select'
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+function Select({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Root>) {
+  return <SelectPrimitive.Root data-slot="select" {...props} />
+}
+
+function SelectGroup({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Group>) {
+  return <SelectPrimitive.Group data-slot="select-group" {...props} />
+}
+
+function SelectValue({
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Value>) {
+  return <SelectPrimitive.Value data-slot="select-value" {...props} />
+}
+
+function SelectTrigger({
+  className,
+  size = 'default',
+  children,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
+  size?: 'sm' | 'default'
+}) {
+  return (
+    <SelectPrimitive.Trigger
+      data-slot="select-trigger"
+      data-size={size}
+      className={cn(
+        "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
+        className
+      )}
+      {...props}
+    >
+      {children}
+      <SelectPrimitive.Icon asChild>
+        <ChevronDownIcon className="size-4 opacity-50" />
+      </SelectPrimitive.Icon>
+    </SelectPrimitive.Trigger>
+  )
+}
+
+function SelectContent({
+  className,
+  children,
+  position = 'item-aligned',
+  align = 'center',
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Content>) {
+  return (
+    <SelectPrimitive.Portal>
+      <SelectPrimitive.Content
+        data-slot="select-content"
+        className={cn(
+          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md',
+          position === 'popper' &&
+            'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
+          className
+        )}
+        position={position}
+        align={align}
+        {...props}
+      >
+        <SelectScrollUpButton />
+        <SelectPrimitive.Viewport
+          className={cn(
+            'p-1',
+            position === 'popper' &&
+              'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1'
+          )}
+        >
+          {children}
+        </SelectPrimitive.Viewport>
+        <SelectScrollDownButton />
+      </SelectPrimitive.Content>
+    </SelectPrimitive.Portal>
+  )
+}
+
+function SelectLabel({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Label>) {
+  return (
+    <SelectPrimitive.Label
+      data-slot="select-label"
+      className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}
+      {...props}
+    />
+  )
+}
+
+function SelectItem({
+  className,
+  children,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Item>) {
+  return (
+    <SelectPrimitive.Item
+      data-slot="select-item"
+      className={cn(
+        "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
+        className
+      )}
+      {...props}
+    >
+      <span
+        data-slot="select-item-indicator"
+        className="absolute right-2 flex size-3.5 items-center justify-center"
+      >
+        <SelectPrimitive.ItemIndicator>
+          <CheckIcon className="size-4" />
+        </SelectPrimitive.ItemIndicator>
+      </span>
+      <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
+    </SelectPrimitive.Item>
+  )
+}
+
+function SelectSeparator({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
+  return (
+    <SelectPrimitive.Separator
+      data-slot="select-separator"
+      className={cn('bg-border pointer-events-none -mx-1 my-1 h-px', className)}
+      {...props}
+    />
+  )
+}
+
+function SelectScrollUpButton({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
+  return (
+    <SelectPrimitive.ScrollUpButton
+      data-slot="select-scroll-up-button"
+      className={cn(
+        'flex cursor-default items-center justify-center py-1',
+        className
+      )}
+      {...props}
+    >
+      <ChevronUpIcon className="size-4" />
+    </SelectPrimitive.ScrollUpButton>
+  )
+}
+
+function SelectScrollDownButton({
+  className,
+  ...props
+}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
+  return (
+    <SelectPrimitive.ScrollDownButton
+      data-slot="select-scroll-down-button"
+      className={cn(
+        'flex cursor-default items-center justify-center py-1',
+        className
+      )}
+      {...props}
+    >
+      <ChevronDownIcon className="size-4" />
+    </SelectPrimitive.ScrollDownButton>
+  )
+}
+
+export {
+  Select,
+  SelectContent,
+  SelectGroup,
+  SelectItem,
+  SelectLabel,
+  SelectScrollDownButton,
+  SelectScrollUpButton,
+  SelectSeparator,
+  SelectTrigger,
+  SelectValue,
+}

filename: apps/bundle-analyzer/package.json
patch: @@ -11,9 +11,10 @@
   "dependencies": {
     "@radix-ui/react-dialog": "1.1.4",
     "@radix-ui/react-popover": "1.1.4",
-    "@radix-ui/react-tooltip": "1.1.4",
+    "@radix-ui/react-select": "2.2.6",
     "@radix-ui/react-slot": "1.1.1",
     "@radix-ui/react-toggle-group": "1.1.1",
+    "@radix-ui/react-tooltip": "1.1.4",
     "autoprefixer": "^10.4.20",
     "class-variance-authority": "^0.7.1",
     "clsx": "^2.1.1",

filename: pnpm-lock.yaml
patch: @@ -624,6 +624,9 @@ importers:
       '@radix-ui/react-popover':
         specifier: 1.1.4
         version: 1.1.4(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-select':
+        specifier: 2.2.6
+        version: 2.2.6(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
       '@radix-ui/react-slot':
         specifier: 1.1.1
         version: 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
@@ -748,7 +751,7 @@ importers:
         version: 9.37.0(jiti@2.5.1)
       eslint-config-next:
         specifier: canary
-        version: 16.1.0-canary.16(eslint-plugin-import-x@4.3.1(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2)
+        version: link:../../packages/eslint-config-next
       tailwindcss:
         specifier: 4.1.13
         version: 4.1.13
@@ -4247,9 +4250,6 @@ packages:
   '@next/env@16.0.8':
     resolution: {integrity: sha512-xP4WrQZuj9MdmLJy3eWFHepo+R3vznsMSS8Dy3wdA7FKpjCiesQ6DxZvdGziQisj0tEtCgBKJzjcAc4yZOgLEQ==}
 
-  '@next/eslint-plugin-next@16.1.0-canary.16':
-    resolution: {integrity: sha512-kI4gMhJlikhegVcOZS7xM/4rV1sYhgxVcFEXp2p52h4ZHuwVOoN9+Mvz50Knp5At+trsbTcbYgqnS2X4gfi11Q==}
-
   '@next/rspack-binding-android-arm-eabi@1.0.2':
     resolution: {integrity: sha512-ayoNrm5Z9xYIHdYfhUSvzPzNXRunMXx5B6Bonch9pJBfyC/3tUlcqVlI5+1HS+H4euWtPMeGqe8bARjQ71J/ug==}
     cpu: [arm]
@@ -5381,6 +5381,19 @@ packages:
       '@types/react-dom':
         optional: true
 
+  '@radix-ui/react-select@2.2.6':
+    resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==}
+    peerDependencies:
+      '@types/react': 19.2.2
+      '@types/react-dom': 19.2.1
+      react: 19.3.0-canary-b45bb335-20251211
+      react-dom: 19.3.0-canary-b45bb335-20251211
+    peerDependenciesMeta:
+      '@types/react':
+        optional: true
+      '@types/react-dom':
+        optional: true
+
   '@radix-ui/react-slot@1.1.0':
     resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
     peerDependencies:
@@ -9746,15 +9759,6 @@ packages:
     engines: {node: '>=6.0'}
     hasBin: true
 
-  eslint-config-next@16.1.0-canary.16:
-    resolution: {integrity: sha512-qA+eBQL2bfO7VPYQtngaYe6qtOhcdI83yjtOps/JI067XSV3sWxuy2H7tOPhkQqMZ38T5qnO/MpZ8BE5KhTT+w==}
-    peerDependencies:
-      eslint: '>=9.0.0'
-      typescript: '>=3.3.1'
-    peerDependenciesMeta:
-      typescript:
-        optional: true
-
   eslint-formatter-codeframe@7.32.1:
     resolution: {integrity: sha512-DK/3Q3+zVKq/7PdSYiCxPrsDF8H/TRMK5n8Hziwr4IMkMy+XiKSwbpj25AdajS63I/B61Snetq4uVvX9fOLyAg==}
     engines: {node: ^10.12.0 || >=12.0.0}
@@ -13725,6 +13729,7 @@ packages:
   next@15.5.8:
     resolution: {integrity: sha512-Tma2R50eiM7Fx6fbDeHiThq7sPgl06mBr76j6Ga0lMFGrmaLitFsy31kykgb8Z++DR2uIEKi2RZ0iyjIwFd15Q==}
     engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+    deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.
     hasBin: true
     peerDependencies:
       '@opentelemetry/api': ^1.1.0
@@ -13746,6 +13751,7 @@ packages:
   next@16.0.8:
     resolution: {integrity: sha512-LmcZzG04JuzNXi48s5P+TnJBsTGPJunViNKV/iE4uM6kstjTQsQhvsAv+xF6MJxU2Pr26tl15eVbp0jQnsv6/g==}
     engines: {node: '>=20.9.0'}
+    deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.
     hasBin: true
     peerDependencies:
       '@opentelemetry/api': ^1.1.0
@@ -21309,10 +21315,6 @@ snapshots:
 
   '@next/env@16.0.8': {}
 
-  '@next/eslint-plugin-next@16.1.0-canary.16':
-    dependencies:
-      fast-glob: 3.3.1
-
   '@next/rspack-binding-android-arm-eabi@1.0.2':
     optional: true
 
@@ -22514,6 +22516,35 @@ snapshots:
       '@types/react': 19.2.2
       '@types/react-dom': 19.2.1(@types/react@19.2.2)
 
+  '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)':
+    dependencies:
+      '@radix-ui/number': 1.1.1
+      '@radix-ui/primitive': 1.1.3
+      '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+      '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.1(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211))(react@19.3.0-canary-b45bb335-20251211)
+      aria-hidden: 1.2.6
+      react: 19.3.0-canary-b45bb335-20251211
+      react-dom: 19.3.0-canary-b45bb335-20251211(react@19.3.0-canary-b45bb335-20251211)
+      react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
+    optionalDependencies:
+      '@types/react': 19.2.2
+      '@types/react-dom': 19.2.1(@types/react@19.2.2)
+
   '@radix-ui/react-slot@1.1.0(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)':
     dependencies:
       '@radix-ui/react-compose-refs': 1.1.0(@types/react@19.2.2)(react@19.3.0-canary-b45bb335-20251211)
@@ -27587,26 +27618,6 @@ snapshots:
     optionalDependencies:
       source-map: 0.6.1
 
-  eslint-config-next@16.1.0-canary.16(eslint-plugin-import-x@4.3.1(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2))(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2):
-    dependencies:
-      '@next/eslint-plugin-next': 16.1.0-canary.16
-      eslint: 9.37.0(jiti@2.5.1)
-      eslint-import-resolver-node: 0.3.9
-      eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import-x@4.3.1(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2))(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.5.1))
-      eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.5.1))
-      eslint-plugin-jsx-a11y: 6.10.2(eslint@9.37.0(jiti@2.5.1))
-      eslint-plugin-react: 7.37.0(eslint@9.37.0(jiti@2.5.1))
-      eslint-plugin-react-hooks: 7.0.0(eslint@9.37.0(jiti@2.5.1))
-      globals: 16.4.0
-      typescript-eslint: 8.46.0(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2)
-    optionalDependencies:
-      typescript: 5.9.2
-    transitivePeerDependencies:
-      - '@typescript-eslint/parser'
-      - eslint-import-resolver-webpack
-      - eslint-plugin-import-x
-      - supports-color
-
   eslint-formatter-codeframe@7.32.1:
     dependencies:
       '@babel/code-frame': 7.12.11
@@ -27757,33 +27768,6 @@ snapshots:
       - eslint-import-resolver-webpack
       - supports-color
 
-  eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.5.1)):
-    dependencies:
-      '@rtsao/scc': 1.1.0
-      array-includes: 3.1.9
-      array.prototype.findlastindex: 1.2.6
-      array.prototype.flat: 1.3.3
-      array.prototype.flatmap: 1.3.3
-      debug: 3.2.7
-      doctrine: 2.1.0
-      eslint: 9.37.0(jiti@2.5.1)
-      eslint-import-resolver-node: 0.3.9
-      eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import-x@4.3.1(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2))(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.5.1)))(eslint@9.37.0(jiti@2.5.1))
-      hasown: 2.0.2
-      is-core-module: 2.16.1
-      is-glob: 4.0.3
-      minimatch: 3.1.2
-      object.fromentries: 2.0.8
-      object.groupby: 1.0.3
-      object.values: 1.2.1
-      semver: 6.3.1
-      string.prototype.trimend: 1.0.9
-      tsconfig-paths: 3.15.0
-    transitivePeerDependencies:
-      - eslint-import-resolver-typescript
-      - eslint-import-resolver-webpack
-      - supports-color
-
   eslint-plugin-jest@27.6.3(eslint@9.37.0(jiti@2.5.1))(jest@29.7.0(@types/node@20.17.6(patch_hash=rvl3vkomen3tospgr67bzubfyu))(babel-plugin-macros@3.1.0))(typescript@5.9.2):
     dependencies:
       '@typescript-eslint/utils': 5.62.0(eslint@9.37.0(jiti@2.5.1))(typescript@5.9.2)