refactor(ui): migrate to shadcn/ui components and fix scroll issues

Migrate UI component library from custom implementations to shadcn/ui:
- Add shadcn/ui primitives (Button, Card, Dialog, Input, etc.)
- Replace custom styles with Tailwind CSS v4 theme configuration
- Remove custom-theme.css in favor of globals.css with @theme directive

Fix scroll overflow issues in multiple components:
- ProjectSelector: "New Project" button no longer overlays project list
- FolderBrowser: folder list now scrolls properly within modal
- AgentCard: log modal content stays within bounds
- ConversationHistory: conversation list scrolls correctly
- KanbanColumn: feature cards scroll within fixed height
- ScheduleModal: schedule form content scrolls properly

Key technical changes:
- Replace ScrollArea component with native overflow-y-auto divs
- Add min-h-0 to flex containers to allow proper shrinking
- Restructure dropdown layouts with flex-col for fixed footers

New files:
- ui/components.json (shadcn/ui configuration)
- ui/src/components/ui/* (20 UI primitive components)
- ui/src/lib/utils.ts (cn utility for class merging)
- ui/tsconfig.app.json (app-specific TypeScript config)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Auto
2026-01-26 18:25:55 +02:00
parent e45b5b064e
commit c917582a64
69 changed files with 4900 additions and 4287 deletions

View File

@@ -1,5 +1,12 @@
import { useEffect, useCallback } from 'react'
import { X, Keyboard } from 'lucide-react'
import { Keyboard } from 'lucide-react'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
import { Badge } from '@/components/ui/badge'
interface Shortcut {
key: string
@@ -20,10 +27,11 @@ const shortcuts: Shortcut[] = [
]
interface KeyboardShortcutsHelpProps {
isOpen: boolean
onClose: () => void
}
export function KeyboardShortcutsHelp({ onClose }: KeyboardShortcutsHelpProps) {
export function KeyboardShortcutsHelp({ isOpen, onClose }: KeyboardShortcutsHelpProps) {
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (e.key === 'Escape' || e.key === '?') {
@@ -35,59 +43,49 @@ export function KeyboardShortcutsHelp({ onClose }: KeyboardShortcutsHelpProps) {
)
useEffect(() => {
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [handleKeyDown])
if (isOpen) {
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}
}, [isOpen, handleKeyDown])
return (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={onClose}
>
<div
className="neo-card p-6 max-w-md w-full mx-4"
onClick={(e) => e.stopPropagation()}
>
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-2">
<Keyboard size={20} className="text-neo-accent" />
<h2 className="font-display text-lg font-bold">Keyboard Shortcuts</h2>
</div>
<button
onClick={onClose}
className="neo-btn p-1.5"
aria-label="Close"
>
<X size={16} />
</button>
</div>
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Keyboard size={20} className="text-primary" />
Keyboard Shortcuts
</DialogTitle>
</DialogHeader>
{/* Shortcuts list */}
<ul className="space-y-2">
<ul className="space-y-1">
{shortcuts.map((shortcut) => (
<li
key={shortcut.key}
className="flex items-center justify-between py-2 border-b border-neo-border/30 last:border-0"
className="flex items-center justify-between py-2 border-b border-border/50 last:border-0"
>
<div className="flex items-center gap-2">
<kbd className="px-2 py-1 text-sm font-mono bg-neo-bg rounded border border-neo-border shadow-neo-sm min-w-[2rem] text-center">
<div className="flex items-center gap-3">
<kbd className="px-2 py-1 text-xs font-mono bg-muted rounded border border-border min-w-[2rem] text-center">
{shortcut.key}
</kbd>
<span className="text-neo-text">{shortcut.description}</span>
<span className="text-sm">{shortcut.description}</span>
</div>
{shortcut.context && (
<span className="text-xs text-neo-muted">{shortcut.context}</span>
<Badge variant="secondary" className="text-xs">
{shortcut.context}
</Badge>
)}
</li>
))}
</ul>
{/* Footer */}
<p className="text-xs text-neo-muted text-center mt-6">
<p className="text-xs text-muted-foreground text-center pt-2">
Press ? or Esc to close
</p>
</div>
</div>
</DialogContent>
</Dialog>
)
}