PresetNavigationBar.tsx27.2 KBView on GitHub
/**
 * PresetNavigationBar Component
 *
 * Renders inside the "Fields" popover of CanvasFilterSearchSection.
 * Layout:
 *   [ Columns | Presets ] tabs
 *
 * - "Columns" tab: live column visibility checkboxes for the active canvas.
 * - "Presets" tab: preset buttons in a vertical list + "Add preset" at the bottom.
 * - Keyboard navigation (1-5, left/right): always active via PresetHotkeysLayer,
 *   which must be mounted in the canvas view (not just when this popover is open).
 */

import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { Input } from '@/components/ui/input';
import { Popover, PopoverAnchor, PopoverContent } from '@/components/ui/popover';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { cn } from '@/lib/utils';
import type { CanvasPreset, ConversationViewConfig } from '@/modules/canvas/types/canvas-types';
import Container3D from '@/modules/cedar-os/src/cedar-os-components/containers/Container3D';
import { useCedarStore } from '@/modules/store';
import { useGlobalCanvasPresets } from '../../hooks/use-global-canvas-presets';
import { Pencil, Plus, Search, Trash2 } from 'lucide-react';
import { motion } from 'motion/react';
import { memo, useCallback, useMemo, useRef, useState } from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useCanvasConfiguration } from '../../hooks/use-canvas-configuration';
import { BUILTIN_CANVAS_PRESETS } from './ColumnConfigurationItem';

// ─── Shared helpers ───────────────────────────────────────────────────────────

type ColumnMeta = {
  id: string;
  name?: string;
  type: string;
  visible?: boolean;
  parentColumnId?: string;
  signalDimension?: 'signal';
};

function getColumnLabel(column: ColumnMeta): string {
  if (column.type === 'company-combined') return 'Company';
  if (column.type === 'current-action') return 'Suggested Action';
  if (column.type === 'future-action') return 'Future Action';
  return column.name ?? column.id;
}

// ─── Shared preset logic hook ─────────────────────────────────────────────────

function useCanvasPresets(canvasId?: string) {
  const { columns: visibleColumns, allColumns } = useCanvasConfiguration(canvasId);
  const {
    globalPresets,
    hasGlobalPresets,
    upsertGlobalPreset,
    removeGlobalPreset,
    syncActivePresetColumns,
  } = useGlobalCanvasPresets();

  const canvasViewConfig = useCedarStore((state) => {
    if (!canvasId) return null;
    return state.canvasesById[canvasId]?.viewConfig as ConversationViewConfig | null | undefined;
  });
  const updateCanvasViewConfig = useCedarStore((state) => state.updateCanvasViewConfig);
  const saveCanvasViewConfig = useCedarStore((state) => state.saveCanvasViewConfig);

  const activePresets: CanvasPreset[] = hasGlobalPresets ? globalPresets : BUILTIN_CANVAS_PRESETS;

  const applyPreset = useCallback(
    (preset: CanvasPreset) => {
      if (!canvasId || !canvasViewConfig) return;
      const visibleSet = new Set(preset.columnIds);
      const orderMap = new Map(preset.columnIds.map((id, idx) => [id, idx + 1]));
      const currentConfig = canvasViewConfig.columnConfiguration ?? {};
      const newColumnConfig = { ...currentConfig };
      allColumns
        .filter(
          (col) =>
            col.type !== 'timeline' && col.type !== 'working-memory' && col.id !== 'wm_custom',
        )
        .forEach((col) => {
          newColumnConfig[col.id] = {
            ...newColumnConfig[col.id],
            visible: col.id === 'primaryCompany' ? true : visibleSet.has(col.id),
            order: col.id === 'primaryCompany' ? 0 : (orderMap.get(col.id) ?? 999),
          };
        });
      updateCanvasViewConfig(canvasId, {
        ...canvasViewConfig,
        presetId: preset.id,
        columnConfiguration: newColumnConfig,
        expanded: preset.expanded ?? false,
      });
      saveCanvasViewConfig(canvasId);
    },
    [canvasId, canvasViewConfig, allColumns, updateCanvasViewConfig, saveCanvasViewConfig],
  );

  const applyPresetByIndex = useCallback(
    (index: number) => {
      if (index >= 0 && index < activePresets.length) applyPreset(activePresets[index]);
    },
    [applyPreset, activePresets],
  );

  const isPresetActive = useCallback(
    (preset: CanvasPreset) => {
      const visibleColIds = visibleColumns
        .filter(
          (col) =>
            col.type !== 'timeline' &&
            col.type !== 'working-memory' &&
            col.id !== 'wm_custom' &&
            col.id !== 'primaryCompany',
        )
        .map((col) => col.id);
      if (visibleColIds.length !== preset.columnIds.length) return false;
      return preset.columnIds.every((id) => visibleColIds.includes(id));
    },
    [visibleColumns],
  );

  const getActivePresetIndex = useCallback(
    () => activePresets.findIndex((preset) => isPresetActive(preset)),
    [isPresetActive, activePresets],
  );

  const navigateToPreviousPreset = useCallback(() => {
    const currentIndex = getActivePresetIndex();
    applyPreset(activePresets[currentIndex <= 0 ? activePresets.length - 1 : currentIndex - 1]);
  }, [getActivePresetIndex, applyPreset, activePresets]);

  const navigateToNextPreset = useCallback(() => {
    const currentIndex = getActivePresetIndex();
    applyPreset(activePresets[currentIndex >= activePresets.length - 1 ? 0 : currentIndex + 1]);
  }, [getActivePresetIndex, applyPreset, activePresets]);

  return {
    activePresets,
    visibleColumns,
    allColumns,
    canvasViewConfig,
    applyPreset,
    applyPresetByIndex,
    isPresetActive,
    navigateToPreviousPreset,
    navigateToNextPreset,
    upsertGlobalPreset,
    removeGlobalPreset,
    syncActivePresetColumns,
    updateCanvasViewConfig,
    saveCanvasViewConfig,
  };
}

// ─── PresetHotkeysLayer ───────────────────────────────────────────────────────
// Always-mounted component that keeps preset hotkeys active regardless of
// whether the Fields popover is open. Place this in ConversationCanvas /
// GroupedConversationCanvas alongside ConversationListHotkeys.

export function PresetHotkeysLayer({ canvasId }: { canvasId?: string }) {
  const { applyPresetByIndex, navigateToPreviousPreset, navigateToNextPreset } =
    useCanvasPresets(canvasId);

  useHotkeys('left', navigateToPreviousPreset, { preventDefault: true }, [
    navigateToPreviousPreset,
  ]);
  useHotkeys('right', navigateToNextPreset, { preventDefault: true }, [navigateToNextPreset]);
  useHotkeys('1', () => applyPresetByIndex(0), { preventDefault: true }, [applyPresetByIndex]);
  useHotkeys('2', () => applyPresetByIndex(1), { preventDefault: true }, [applyPresetByIndex]);
  useHotkeys('3', () => applyPresetByIndex(2), { preventDefault: true }, [applyPresetByIndex]);
  useHotkeys('4', () => applyPresetByIndex(3), { preventDefault: true }, [applyPresetByIndex]);
  useHotkeys('5', () => applyPresetByIndex(4), { preventDefault: true }, [applyPresetByIndex]);

  return null;
}

// ─── Columns tab content ──────────────────────────────────────────────────────

function ColumnsTabContent({
  allColumns,
  onToggleColumn,
}: {
  allColumns: ColumnMeta[];
  onToggleColumn: (id: string) => void;
}) {
  const [columnSearch, setColumnSearch] = useState('');
  const searchInputRef = useRef<HTMLInputElement>(null);

  const editableColumns = allColumns.filter(
    (col) =>
      col.type !== 'timeline' &&
      col.type !== 'working-memory' &&
      col.type !== 'score' &&
      col.id !== 'wm_custom' &&
      col.id !== 'primaryCompany',
  );
  const scoreColumns = allColumns.filter((col) => col.type === 'score');

  const filteredColumns = useMemo(() => {
    if (!columnSearch.trim()) return editableColumns;
    const q = columnSearch.toLowerCase();
    return editableColumns.filter((col) => getColumnLabel(col).toLowerCase().includes(q));
  }, [editableColumns, columnSearch]);

  return (
    <>
      <div className="mb-1 flex items-center gap-2 rounded-md bg-muted/50 px-2">
        <Search className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
        <Input
          ref={searchInputRef}
          value={columnSearch}
          onChange={(e) => setColumnSearch(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === 'Enter' && filteredColumns.length > 0) {
              e.preventDefault();
              onToggleColumn(filteredColumns[0].id);
              setColumnSearch('');
            }
          }}
          className="h-7 border-0 bg-transparent p-0 text-sm shadow-none focus-visible:ring-0"
          placeholder="Search columns..."
        />
      </div>

      <div className="max-h-[50vh] overflow-y-auto px-1 py-0.5">
        {filteredColumns.map((column, idx) => {
          const isChecked = column.visible !== false;
          const label = getColumnLabel(column);
          const isFirstMatch = idx === 0 && columnSearch.trim().length > 0;
          const childScores = scoreColumns.filter((sc) => sc.parentColumnId === column.id);
          return (
            <div key=[redacted]
              <div
                className={cn(
                  'flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 hover:bg-muted/50',
                  isFirstMatch && 'bg-muted/50',
                )}
                onClick={() => onToggleColumn(column.id)}
              >
                <Checkbox checked={isChecked} tabIndex={-1} className="pointer-events-none" />
                <span className="flex-1 cursor-pointer text-sm">{label}</span>
              </div>
              {childScores.map((scoreCol) => {
                const scoreVisible = allColumns.find((c) => c.id === scoreCol.id)?.visible ?? false;
                return (
                  <div
                    key=[redacted]
                    className="flex cursor-pointer items-center gap-2.5 rounded-md py-1.5 pl-9 pr-2 hover:bg-muted/50"
                    onClick={() => onToggleColumn(scoreCol.id)}
                  >
                    <Checkbox
                      checked={scoreVisible}
                      tabIndex={-1}
                      className="pointer-events-none"
                    />
                    <span className="flex-1 cursor-pointer text-sm text-muted-foreground">
                      Signal
                    </span>
                  </div>
                );
              })}
            </div>
          );
        })}
      </div>
    </>
  );
}

// ─── Preset Editor Popover ────────────────────────────────────────────────────

interface PresetEditorPopoverProps {
  preset: CanvasPreset;
  allColumns: ColumnMeta[];
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onSave: (updated: CanvasPreset) => void;
  onDelete: () => void;
  onOpen: () => void;
  onExpandedChange: (expanded: boolean) => void;
  onToggleColumn: (colId: string) => void;
  getOrderedColumnIds: () => string[];
  children: React.ReactNode;
}

export const PresetEditorPopover = memo(function PresetEditorPopover({
  preset,
  allColumns,
  open,
  onOpenChange,
  onSave,
  onDelete,
  onOpen,
  onExpandedChange,
  onToggleColumn,
  getOrderedColumnIds,
  children,
}: PresetEditorPopoverProps) {
  const [name, setName] = useState(preset.name);
  const [expanded, setExpanded] = useState(preset.expanded ?? false);
  const [columnSearch, setColumnSearch] = useState('');
  const inputRef = useRef<HTMLInputElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);

  const handleOpenChange = useCallback(
    (nextOpen: boolean) => {
      if (nextOpen) {
        setName(preset.name);
        setExpanded(preset.expanded ?? false);
        setColumnSearch('');
        onOpen();
        setTimeout(() => inputRef.current?.focus(), 50);
      } else {
        const currentColumnIds = getOrderedColumnIds();
        const trimmedName = name.trim() || preset.name;
        onSave({ ...preset, name: trimmedName, columnIds: currentColumnIds, expanded });
      }
      onOpenChange(nextOpen);
    },
    [preset, name, expanded, onSave, onOpen, onOpenChange, getOrderedColumnIds],
  );

  const editableColumns = allColumns.filter(
    (col) =>
      col.type !== 'timeline' &&
      col.type !== 'working-memory' &&
      col.type !== 'score' &&
      col.id !== 'wm_custom' &&
      col.id !== 'primaryCompany',
  );
  const scoreColumns = allColumns.filter((col) => col.type === 'score');

  const filteredEditableColumns = useMemo(() => {
    if (!columnSearch.trim()) return editableColumns;
    const q = columnSearch.toLowerCase();
    return editableColumns.filter((col) => getColumnLabel(col).toLowerCase().includes(q));
  }, [editableColumns, columnSearch]);

  return (
    <Popover open={open} onOpenChange={handleOpenChange}>
      <PopoverAnchor asChild>{children}</PopoverAnchor>
      <PopoverContent className="w-80 p-0" align="start" onClick={(e) => e.stopPropagation()}>
        {/* Header: editable name */}
        <div className="border-b px-3 py-2.5">
          <Input
            ref={inputRef}
            value={name}
            onChange={(e) => setName(e.target.value)}
            onKeyDown={(e) => {
              if (e.key === 'Enter') (e.target as HTMLInputElement).blur();
            }}
            className="h-7 border-0 bg-transparent p-0 text-sm font-semibold shadow-none focus-visible:ring-0"
            placeholder="Preset name..."
          />
        </div>

        {/* Expanded toggle */}
        <div className="border-b px-1 py-1.5">
          <div className="flex items-center gap-2.5 rounded-md px-2 py-1.5 hover:bg-muted/50">
            <Checkbox
              id="pe-expanded"
              checked={expanded}
              onCheckedChange={(v) => {
                const next = !!v;
                setExpanded(next);
                onExpandedChange(next);
              }}
            />
            <label htmlFor="pe-expanded" className="flex-1 cursor-pointer select-none text-sm">
              Expanded view
            </label>
          </div>
        </div>

        {/* Column search */}
        <div className="border-b px-2 py-1.5">
          <div className="flex items-center gap-2 rounded-md bg-muted/50 px-2">
            <Search className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
            <Input
              ref={searchInputRef}
              value={columnSearch}
              onChange={(e) => setColumnSearch(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter' && filteredEditableColumns.length > 0) {
                  e.preventDefault();
                  onToggleColumn(filteredEditableColumns[0].id);
                  setColumnSearch('');
                }
              }}
              className="h-7 border-0 bg-transparent p-0 text-sm shadow-none focus-visible:ring-0"
              placeholder="Search columns..."
            />
          </div>
        </div>

        {/* Column selection */}
        <div className="max-h-[50vh] overflow-y-auto px-1 py-1.5">
          {filteredEditableColumns.map((column, idx) => {
            const isChecked = column.visible !== false;
            const label = getColumnLabel(column);
            const isFirstMatch = idx === 0 && columnSearch.trim().length > 0;
            const childScores = scoreColumns.filter((sc) => sc.parentColumnId === column.id);
            return (
              <div key=[redacted]
                <div
                  className={cn(
                    'flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-1.5 hover:bg-muted/50',
                    isFirstMatch && 'bg-muted/50',
                  )}
                  onClick={() => onToggleColumn(column.id)}
                >
                  <Checkbox checked={isChecked} tabIndex={-1} className="pointer-events-none" />
                  <span className="flex-1 cursor-pointer text-sm">{label}</span>
                </div>
                {childScores.map((scoreCol) => {
                  const scoreVisible = allColumns.find((c) => c.id === scoreCol.id)?.visible ?? false;
                  return (
                    <div
                      key=[redacted]
                      className="flex cursor-pointer items-center gap-2.5 rounded-md py-1.5 pl-9 pr-2 hover:bg-muted/50"
                      onClick={() => onToggleColumn(scoreCol.id)}
                    >
                      <Checkbox
                        checked={scoreVisible}
                        tabIndex={-1}
                        className="pointer-events-none"
                      />
                      <span className="flex-1 cursor-pointer text-sm text-muted-foreground">
                        Signal
                      </span>
                    </div>
                  );
                })}
              </div>
            );
          })}
        </div>

        {/* Footer: delete */}
        <div className="border-t px-3 py-2">
          <Button
            variant="ghost"
            size="sm"
            className="h-7 w-full justify-start gap-1.5 px-2 text-xs text-destructive hover:text-destructive"
            onClick={(e) => {
              e.stopPropagation();
              onOpenChange(false);
              onDelete();
            }}
          >
            <Trash2 className="h-3.5 w-3.5" />
            Delete preset
          </Button>
        </div>
      </PopoverContent>
    </Popover>
  );
});

// ─── PresetNavigationBar ──────────────────────────────────────────────────────

export const PresetNavigationBar = memo(function PresetNavigationBar({
  canvasId,
}: {
  canvasId?: string;
  /** @deprecated – refresh is now in the canvas toolbar */
  onRefresh?: () => void;
  /** @deprecated */
  isLoading?: boolean;
  onOpenColumnConfig?: () => void;
}) {
  const [editingPresetId, setEditingPresetId] = useState<string | null>(null);

  const {
    activePresets,
    allColumns,
    visibleColumns,
    canvasViewConfig,
    applyPreset,
    isPresetActive,
    upsertGlobalPreset,
    removeGlobalPreset,
    syncActivePresetColumns,
    updateCanvasViewConfig,
    saveCanvasViewConfig,
  } = useCanvasPresets(canvasId);

  // ── Preset CRUD ──────────────────────────────────────────────────────────

  const handleSavePreset = useCallback(
    (updated: CanvasPreset) => void upsertGlobalPreset(updated),
    [upsertGlobalPreset],
  );

  const handlePresetExpandedChange = useCallback(
    (preset: CanvasPreset, expanded: boolean) =>
      void upsertGlobalPreset({ ...preset, expanded }, { silent: true }),
    [upsertGlobalPreset],
  );

  const handleDeletePreset = useCallback(
    (presetId: string) => void removeGlobalPreset(presetId),
    [removeGlobalPreset],
  );

  const getOrderedPresetColumnIds = useCallback(
    () =>
      visibleColumns
        .filter(
          (col) =>
            col.type !== 'timeline' &&
            col.type !== 'working-memory' &&
            col.id !== 'wm_custom' &&
            col.id !== 'primaryCompany',
        )
        .map((col) => col.id),
    [visibleColumns],
  );

  const toggleColumnVisibilityInCanvas = useCallback(
    (columnId: string) => {
      if (!canvasId || !canvasViewConfig) return;
      const currentConfig = canvasViewConfig.columnConfiguration ?? {};
      const currentVisible = allColumns.find((col) => col.id === columnId)?.visible ?? true;
      updateCanvasViewConfig(canvasId, {
        ...canvasViewConfig,
        columnConfiguration: {
          ...currentConfig,
          [columnId]: { ...currentConfig[columnId], visible: !currentVisible },
        },
      });
      saveCanvasViewConfig(canvasId);
      const targetPresetId = editingPresetId ?? canvasViewConfig.presetId;
      const newIds = allColumns
        .filter((col) => {
          if (
            col.type === 'timeline' ||
            col.type === 'working-memory' ||
            col.id === 'wm_custom' ||
            col.id === 'primaryCompany'
          )
            return false;
          return col.id === columnId ? !currentVisible : (col.visible ?? true);
        })
        .map((col) => col.id);
      void syncActivePresetColumns(targetPresetId, newIds);
    },
    [
      canvasId,
      canvasViewConfig,
      allColumns,
      updateCanvasViewConfig,
      saveCanvasViewConfig,
      syncActivePresetColumns,
      editingPresetId,
    ],
  );

  const handleAddPreset = useCallback(() => {
    const defaultColumnIds = visibleColumns
      .filter(
        (col) =>
          col.type !== 'timeline' &&
          col.type !== 'working-memory' &&
          col.id !== 'wm_custom' &&
          col.id !== 'primaryCompany' &&
          col.visible !== false,
      )
      .map((col) => col.id);
    const newPreset: CanvasPreset = {
      id: crypto.randomUUID(),
      name: 'New preset',
      columnIds: defaultColumnIds,
    };
    void upsertGlobalPreset(newPreset);
    setEditingPresetId(newPreset.id);
  }, [visibleColumns, upsertGlobalPreset]);

  // ── Render ───────────────────────────────────────────────────────────────

  return (
    <div className="w-72 p-2">
      <Tabs defaultValue="columns" type="underline">
        {/* Tab switcher */}
        <TabsList className="mb-2 w-full">
          <TabsTrigger value="columns" className="flex-1 text-xs">Columns</TabsTrigger>
          <TabsTrigger value="presets" className="flex-1 text-xs">Presets</TabsTrigger>
        </TabsList>

        {/* Columns tab */}
        <TabsContent value="columns" className="mt-0">
          <ColumnsTabContent
            allColumns={allColumns}
            onToggleColumn={toggleColumnVisibilityInCanvas}
          />
        </TabsContent>

        {/* Presets tab — vertical list + Add preset at bottom */}
        <TabsContent value="presets" className="mt-0">
          <div className="flex flex-col gap-1">
            {activePresets.map((preset, index) => {
              const isActive = isPresetActive(preset);
              const hotkeyNumber = index + 1;
              const isEditing = editingPresetId === preset.id;

              const buttonContent = (
                <motion.button
                  onClick={() => applyPreset(preset)}
                  whileFocus={{ backgroundColor: 'var(--accent)' }}
                  className={`relative z-10 flex w-full items-center gap-2 rounded-md bg-transparent px-3 py-1.5 text-sm transition-colors cursor-pointer ${
                    isActive ? 'text-primary-foreground' : 'hover:bg-muted/80'
                  }`}
                >
                  {hotkeyNumber <= 9 && (
                    <kbd
                      className={cn(
                        'h-5 w-5 min-w-[1.25rem] rounded border px-1 py-0.5 text-center font-mono text-[10px] font-medium',
                        isActive
                          ? 'border-primary-foreground/30 bg-primary-foreground/20'
                          : 'border-border bg-background',
                      )}
                    >
                      {hotkeyNumber}
                    </kbd>
                  )}
                  <span className="text-xs font-medium">{preset.name}</span>
                </motion.button>
              );

              return (
                <div key=[redacted] className="group/preset relative flex items-center">
                  <Container3D
                    className="relative flex w-full rounded-md"
                    shadowSize={isActive ? 'm' : 's'}
                    style={{ zIndex: isActive ? 10 : 1 }}
                    motionProps={{ whileTap: { scale: 0.97 } }}
                  >
                    {isActive && (
                      <motion.div
                        layoutId="preset-selected-indicator"
                        className="absolute inset-0 rounded-md bg-primary"
                        transition={{ type: 'spring', stiffness: 500, damping: 35 }}
                      />
                    )}
                    <PresetEditorPopover
                      preset={preset}
                      allColumns={allColumns}
                      open={isEditing}
                      onOpenChange={(nextOpen) =>
                        setEditingPresetId(nextOpen ? preset.id : null)
                      }
                      onSave={handleSavePreset}
                      onDelete={() => handleDeletePreset(preset.id)}
                      onOpen={() => applyPreset(preset)}
                      onExpandedChange={(expanded) =>
                        handlePresetExpandedChange(preset, expanded)
                      }
                      onToggleColumn={toggleColumnVisibilityInCanvas}
                      getOrderedColumnIds={getOrderedPresetColumnIds}
                    >
                      {buttonContent}
                    </PresetEditorPopover>
                  </Container3D>

                  {/* Pencil (hover) → X (while editing) */}
                  <button
                    type="button"
                    onClick={(e) => {
                      e.stopPropagation();
                      if (isEditing) {
                        setEditingPresetId(null);
                      } else {
                        applyPreset(preset);
                        setEditingPresetId(preset.id);
                      }
                    }}
                    className={cn(
                      'absolute -right-1.5 -top-1.5 z-20 flex h-4 w-4 items-center justify-center rounded-full border transition-opacity',
                      isEditing
                        ? 'border-border bg-background text-muted-foreground opacity-100 hover:text-foreground'
                        : 'border-border bg-background text-muted-foreground opacity-0 group-hover/preset:opacity-100 hover:!opacity-100 hover:text-foreground',
                    )}
                  >
                    {isEditing ? (
                      <span className="text-[9px] font-bold leading-none">{'\u2715'}</span>
                    ) : (
                      <Pencil className="h-2 w-2" />
                    )}
                  </button>
                </div>
              );
            })}

            {/* Add preset — bottom of the list */}
            <Container3D
              className="mt-1 flex rounded-md"
              shadowSize="s"
              motionProps={{ whileTap: { scale: 0.97 } }}
            >
              <motion.button
                onClick={handleAddPreset}
                whileFocus={{ backgroundColor: 'var(--accent)' }}
                className="relative z-10 flex w-full items-center gap-1.5 rounded-md bg-transparent px-3 py-1.5 text-sm transition-colors cursor-pointer hover:bg-muted/80"
              >
                <Plus className="h-3.5 w-3.5 text-muted-foreground" />
                <span className="whitespace-nowrap text-xs font-medium">Add preset</span>
              </motion.button>
            </Container3D>
          </div>
        </TabsContent>
      </Tabs>
    </div>
  );
});