Compare commits

...

1 Commits

Author SHA1 Message Date
SuperComboGamer
d4907a610e IDK 2025-12-18 19:06:14 -05:00
16 changed files with 3688 additions and 268 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,24 @@
import type { Metadata } from "next";
import { GeistSans } from "geist/font/sans";
import { GeistMono } from "geist/font/mono";
import { Inter, JetBrains_Mono } from "next/font/google";
import { Toaster } from "sonner";
import "./globals.css";
// Inter font for clean theme
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
display: "swap",
});
// JetBrains Mono for clean theme
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
variable: "--font-jetbrains-mono",
display: "swap",
});
export const metadata: Metadata = {
title: "Automaker - Autonomous AI Development Studio",
description: "Build software autonomously with intelligent orchestration",
@@ -16,7 +32,7 @@ export default function RootLayout({
return (
<html lang="en" suppressHydrationWarning>
<body
className={`${GeistSans.variable} ${GeistMono.variable} antialiased`}
className={`${GeistSans.variable} ${GeistMono.variable} ${inter.variable} ${jetbrainsMono.variable} antialiased`}
>
{children}
<Toaster richColors position="bottom-right" />

View File

@@ -150,6 +150,7 @@ function HomeContent() {
"cream",
"sunset",
"gray",
"clean",
];
// Remove all theme classes

View File

@@ -1217,6 +1217,8 @@ export function Sidebar() {
<aside
className={cn(
"flex-shrink-0 flex flex-col z-30 relative",
// Clean theme sidebar-glass class
"sidebar-glass",
// Glass morphism background with gradient
"bg-gradient-to-b from-sidebar/95 via-sidebar/85 to-sidebar/90 backdrop-blur-2xl",
// Premium border with subtle glow
@@ -1854,6 +1856,8 @@ export function Sidebar() {
isActive
? [
// Active: Premium gradient with glow
// Clean theme nav-active class
"nav-active",
"bg-gradient-to-r from-brand-500/20 via-brand-500/15 to-brand-600/10",
"text-foreground font-medium",
"border border-brand-500/30",
@@ -1894,6 +1898,8 @@ export function Sidebar() {
{item.shortcut && sidebarOpen && (
<span
className={cn(
// Clean theme shortcut-badge class
"shortcut-badge",
"hidden lg:flex items-center justify-center min-w-5 h-5 px-1.5 text-[10px] font-mono rounded-md transition-all duration-200",
isActive
? "bg-brand-500/20 text-brand-400"
@@ -1919,7 +1925,7 @@ export function Sidebar() {
>
{item.label}
{item.shortcut && (
<span className="ml-2 px-1.5 py-0.5 bg-muted rounded text-[10px] font-mono text-muted-foreground">
<span className="shortcut-badge ml-2 px-1.5 py-0.5 bg-muted rounded text-[10px] font-mono text-muted-foreground">
{formatShortcut(item.shortcut, true)}
</span>
)}
@@ -2053,6 +2059,8 @@ export function Sidebar() {
{!sidebarOpen && runningAgentsCount > 0 && (
<span
className={cn(
// Clean theme running-agents-badge class
"running-agents-badge",
"absolute -top-1.5 -right-1.5 flex items-center justify-center",
"min-w-4 h-4 px-1 text-[9px] font-bold rounded-full",
"bg-brand-500 text-white shadow-sm",
@@ -2076,6 +2084,8 @@ export function Sidebar() {
{sidebarOpen && runningAgentsCount > 0 && (
<span
className={cn(
// Clean theme running-agents-badge class
"running-agents-badge",
"hidden lg:flex items-center justify-center",
"min-w-6 h-6 px-1.5 text-xs font-semibold rounded-full",
"bg-brand-500 text-white shadow-sm",

View File

@@ -10,7 +10,7 @@ import {
} from "@dnd-kit/core";
import { useAppStore, Feature } from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import { pathsEqual } from "@/lib/utils";
import { pathsEqual, cn } from "@/lib/utils";
import { BoardBackgroundModal } from "@/components/dialogs/board-background-modal";
import { RefreshCw } from "lucide-react";
import { useAutoMode } from "@/hooks/use-auto-mode";
@@ -76,6 +76,7 @@ export function BoardView() {
setCurrentWorktree,
getWorktrees,
setWorktrees,
getEffectiveTheme,
} = useAppStore();
const shortcuts = useKeyboardShortcutsConfig();
const {
@@ -521,6 +522,9 @@ export function BoardView() {
[currentProject, setPendingPlanApproval]
);
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
if (!currentProject) {
return (
<div
@@ -597,7 +601,7 @@ export function BoardView() {
{/* Main Content Area */}
<div className="flex-1 flex flex-col overflow-hidden">
{/* Search Bar Row */}
<div className="px-4 pt-4 pb-2 flex items-center justify-between">
<div className={cn("flex items-center justify-between shrink-0", isCleanTheme ? "px-8 py-4" : "px-4 pt-4 pb-2")}>
<BoardSearchBar
searchQuery={searchQuery}
onSearchChange={setSearchQuery}

View File

@@ -2,8 +2,9 @@
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { ImageIcon, Archive, Minimize2, Square, Maximize2 } from "lucide-react";
import { ImageIcon, Archive, Minimize2, Square, Maximize2, History, Trash2, Layout } from "lucide-react";
import { cn } from "@/lib/utils";
import { useAppStore } from "@/store/app-store";
interface BoardControlsProps {
isMounted: boolean;
@@ -22,8 +23,41 @@ export function BoardControls({
kanbanCardDetailLevel,
onDetailLevelChange,
}: BoardControlsProps) {
const { getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
if (!isMounted) return null;
if (isCleanTheme) {
return (
<div className="flex items-center gap-2 ml-6">
<button
className="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"
onClick={onShowCompletedModal}
>
<History className="w-[18px] h-[18px]" />
</button>
<button className="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition">
<Trash2 className="w-[18px] h-[18px]" />
</button>
<div className="w-px h-6 bg-white/10 mx-1"></div>
<button
className="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"
onClick={onShowBoardBackground}
>
<Maximize2 className="w-[18px] h-[18px]" />
</button>
<button
className="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"
onClick={() => onDetailLevelChange(kanbanCardDetailLevel === 'minimal' ? 'standard' : kanbanCardDetailLevel === 'standard' ? 'detailed' : 'minimal')}
>
<Layout className="w-[18px] h-[18px]" />
</button>
</div>
);
}
return (
<TooltipProvider>
<div className="flex items-center gap-2 ml-4">

View File

@@ -5,6 +5,7 @@ import { HotkeyButton } from "@/components/ui/hotkey-button";
import { Slider } from "@/components/ui/slider";
import { Play, StopCircle, Plus, Users } from "lucide-react";
import { KeyboardShortcut } from "@/hooks/use-keyboard-shortcuts";
import { useAppStore } from "@/store/app-store";
interface BoardHeaderProps {
projectName: string;
@@ -29,6 +30,59 @@ export function BoardHeader({
addFeatureShortcut,
isMounted,
}: BoardHeaderProps) {
const { getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
if (isCleanTheme) {
return (
<header className="h-16 flex items-center justify-between px-8 border-b border-white/5 bg-[#0b101a]/40 backdrop-blur-md z-20 shrink-0">
<div>
<h2 className="text-lg font-bold text-white tracking-tight">Kanban Board</h2>
<p className="text-[10px] text-slate-500 uppercase tracking-[0.2em] font-bold mono">
{projectName}
</p>
</div>
<div className="flex items-center gap-5">
{/* Concurrency Display (Visual only to match mockup for now, or interactive if needed) */}
<div className="flex items-center bg-white/5 border border-white/10 rounded-full px-4 py-1.5 gap-3">
<Users className="w-4 h-4 text-slate-500" />
<div className="toggle-track">
<div className="toggle-thumb"></div>
</div>
<span className="mono text-xs font-bold text-slate-400">{maxConcurrency}</span>
</div>
{/* Auto Mode Button */}
{isAutoModeRunning ? (
<button
className="flex items-center gap-2 glass px-5 py-2 rounded-xl text-xs font-bold hover:bg-white/10 transition text-rose-400 border-rose-500/30"
onClick={onStopAutoMode}
>
<StopCircle className="w-3.5 h-3.5" /> Stop
</button>
) : (
<button
className="flex items-center gap-2 glass px-5 py-2 rounded-xl text-xs font-bold hover:bg-white/10 transition"
onClick={onStartAutoMode}
>
<Play className="w-3.5 h-3.5 text-cyan-400 fill-cyan-400" /> Auto Mode
</button>
)}
{/* Add Feature Button */}
<button
className="btn-cyan px-6 py-2 rounded-xl text-xs font-black flex items-center gap-2 shadow-lg shadow-cyan-500/20"
onClick={onAddFeature}
>
<Plus className="w-4 h-4 stroke-[3.5px]" /> ADD FEATURE
</button>
</div>
</header>
);
}
return (
<div className="flex items-center justify-between p-4 border-b border-border bg-glass backdrop-blur-md">
<div>

View File

@@ -3,6 +3,7 @@
import { useRef, useEffect } from "react";
import { Input } from "@/components/ui/input";
import { Search, X, Loader2 } from "lucide-react";
import { useAppStore } from "@/store/app-store";
interface BoardSearchBarProps {
searchQuery: string;
@@ -20,6 +21,9 @@ export function BoardSearchBar({
currentProjectPath,
}: BoardSearchBarProps) {
const searchInputRef = useRef<HTMLInputElement>(null);
const { getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
// Focus search input when "/" is pressed
useEffect(() => {
@@ -39,6 +43,25 @@ export function BoardSearchBar({
return () => window.removeEventListener("keydown", handleKeyDown);
}, []);
if (isCleanTheme) {
return (
<div className="relative flex-1 max-w-2xl group">
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 group-focus-within:text-cyan-400 transition-colors" />
<input
ref={searchInputRef}
type="text"
placeholder="Search features by keyword..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
className="w-full bg-white/5 border border-white/10 rounded-2xl py-2.5 pl-12 pr-12 text-sm focus:outline-none focus:border-cyan-500/50 transition-all mono"
/>
<div className="absolute right-4 top-1/2 -translate-y-1/2">
<span className="shortcut-badge">/</span>
</div>
</div>
);
}
return (
<div className="relative max-w-md flex-1 flex items-center gap-2">
<div className="relative flex-1">

View File

@@ -58,6 +58,12 @@ import {
Wand2,
Archive,
Lock,
Target,
Square,
Terminal,
RefreshCw,
Layers,
Edit3,
} from "lucide-react";
import { CountUpTimer } from "@/components/ui/count-up-timer";
import { getElectronAPI } from "@/lib/electron";
@@ -149,7 +155,9 @@ export const KanbanCard = memo(function KanbanCard({
const [agentInfo, setAgentInfo] = useState<AgentTaskInfo | null>(null);
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);
const [currentTime, setCurrentTime] = useState(() => Date.now());
const { kanbanCardDetailLevel, enableDependencyBlocking, features, useWorktrees } = useAppStore();
const { kanbanCardDetailLevel, enableDependencyBlocking, features, useWorktrees, getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
// Calculate blocking dependencies (if feature is in backlog and has incomplete dependencies)
const blockingDependencies = useMemo(() => {
@@ -160,9 +168,9 @@ export const KanbanCard = memo(function KanbanCard({
}, [enableDependencyBlocking, feature, features]);
const showSteps =
kanbanCardDetailLevel === "standard" ||
kanbanCardDetailLevel === "detailed";
const showAgentInfo = kanbanCardDetailLevel === "detailed";
(kanbanCardDetailLevel === "standard" ||
kanbanCardDetailLevel === "detailed") && !isCleanTheme; // Hide steps in clean theme
const showAgentInfo = kanbanCardDetailLevel === "detailed" || isCleanTheme; // Always show model info in clean theme
const isJustFinished = useMemo(() => {
if (
@@ -291,17 +299,261 @@ export const KanbanCard = memo(function KanbanCard({
).borderColor = `color-mix(in oklch, var(--border) ${cardBorderOpacity}%, transparent)`;
}
// CLEAN THEME IMPLEMENTATION
if (isCleanTheme) {
return (
<>
<div
ref={setNodeRef}
style={style}
className={cn(
"glass kanban-card flex flex-col gap-4 group relative",
// Verified state
feature.status === "verified" && "opacity-60 hover:opacity-100 transition-all",
// Running card state
isCurrentAutoTask && "border-cyan-500/40 bg-cyan-500/[0.08]",
// Dragging state
isDragging && "scale-105 shadow-xl shadow-black/20 opacity-50 z-50",
!isDraggable && "cursor-default"
)}
onDoubleClick={onEdit}
{...attributes}
{...(isDraggable ? listeners : {})}
>
{/* Action Icons - Waiting/Verified (In Flow, First Child) */}
{(feature.status === "waiting_approval" || feature.status === "verified") && (
<div className={cn(
"flex justify-end gap-3.5 transition-opacity",
feature.status === "waiting_approval" ? "opacity-30 group-hover:opacity-100" : "opacity-20"
)}>
<Edit3
className="w-4 h-4 cursor-pointer hover:text-white transition"
onClick={(e) => { e.stopPropagation(); onEdit(); }}
/>
<Trash2
className="w-4 h-4 cursor-pointer hover:text-rose-400 transition"
onClick={(e) => { e.stopPropagation(); handleDeleteClick(e); }}
/>
</div>
)}
{/* Top Bar - Running State */}
{isCurrentAutoTask && (
<div className="flex justify-end items-center gap-2">
<div className="bg-orange-500/15 text-orange-400 text-[9px] px-2.5 py-1 rounded-lg border border-orange-500/20 flex items-center gap-1.5 font-black mono">
<RefreshCw className="w-3 h-3" /> {formatModelName(feature.model ?? DEFAULT_MODEL)}
</div>
<div className="bg-slate-900/50 text-slate-500 text-[9px] px-2 py-1 rounded-lg border border-white/5 font-mono">
{feature.startedAt ? (
<CountUpTimer
startedAt={feature.startedAt}
className="text-inherit"
/>
) : "00:00"}
</div>
</div>
)}
{/* Top Bar - In Progress (Inactive) State */}
{!isCurrentAutoTask && feature.status === "in_progress" && (
<div className="flex justify-end gap-2">
<div className="bg-orange-500/10 text-orange-400 text-[9px] px-2.5 py-1 rounded-lg border border-orange-500/10 flex items-center gap-1.5 font-bold mono">
<RefreshCw className="w-3 h-3" /> {formatModelName(feature.model ?? DEFAULT_MODEL)}
</div>
{/* Duration if available - mocked for now as not in Feature type */}
<div className="bg-slate-900/50 text-slate-500 text-[9px] px-2 py-1 rounded-lg border border-white/5 font-mono">
00:07
</div>
</div>
)}
{/* Delete Icon - Top Right for Backlog (Absolute) */}
{feature.status === "backlog" && (
<div className="absolute top-5 right-6 opacity-0 group-hover:opacity-100 transition-opacity">
<Trash2
className="w-4 h-4 text-slate-600 hover:text-red-400 cursor-pointer"
onClick={handleDeleteClick}
/>
</div>
)}
{/* Description */}
<p className={cn(
"text-[13px] leading-relaxed font-medium line-clamp-3",
isCurrentAutoTask ? "text-white font-semibold" : "text-slate-300",
feature.status === "waiting_approval" && "italic",
feature.status === "verified" && "line-through decoration-slate-600"
)}>
{feature.description || feature.summary || "No description"}
</p>
{/* More link */}
{(feature.description || "").length > 100 && (
<div className="flex items-center gap-1 text-[10px] text-slate-500 -mt-1 cursor-pointer hover:text-slate-300">
<ChevronDown className="w-3 h-3" /> More
</div>
)}
{/* Backlog Info */}
{feature.status === "backlog" && (
<div className="text-[10px] font-bold text-cyan-400/80 mono flex items-center gap-1.5 uppercase tracking-tight">
<Layers className="w-3.5 h-3.5" /> {feature.category || "General"}
</div>
)}
{/* Buttons */}
<div className="flex gap-2 mt-auto">
{/* Backlog Buttons */}
{feature.status === "backlog" && (
<>
<button
onClick={(e) => { e.stopPropagation(); onEdit(); }}
className="flex-1 glass py-2.5 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:bg-white/10 transition"
>
<Edit3 className="w-3.5 h-3.5" /> Edit
</button>
{onImplement && (
<button
onClick={(e) => { e.stopPropagation(); onImplement(); }}
className="flex-1 bg-cyan-500/10 hover:bg-cyan-500/20 text-cyan-400 border border-cyan-500/20 py-2.5 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 transition"
>
<Target className="w-3.5 h-3.5" /> Make
</button>
)}
</>
)}
{/* In Progress Buttons */}
{feature.status === "in_progress" && (
<>
{onViewOutput && (
<button
onClick={(e) => { e.stopPropagation(); onViewOutput(); }}
className={cn(
"flex-[4] py-3 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2",
isCurrentAutoTask ? "btn-cyan font-black tracking-widest" : "bg-cyan-500/15 text-cyan-400 border border-cyan-500/20"
)}
>
<Terminal className={cn("w-4 h-4", isCurrentAutoTask && "stroke-[2.5px]")} /> LOGS
{agentInfo?.toolCallCount ? (
<span className={cn("px-1.5 rounded ml-1", isCurrentAutoTask ? "bg-black/10" : "bg-cyan-500/10")}>{agentInfo.toolCallCount}</span>
) : null}
</button>
)}
{onForceStop && (
<button
onClick={(e) => { e.stopPropagation(); onForceStop(); }}
className={cn(
"flex-1 rounded-xl flex items-center justify-center transition",
isCurrentAutoTask ? "bg-rose-500 hover:bg-rose-600 text-white shadow-lg shadow-rose-500/20" : "bg-rose-500/20 text-rose-500/50 border border-rose-500/20"
)}
>
<Square className="w-4 h-4 fill-current" />
</button>
)}
</>
)}
{/* Waiting Buttons */}
{feature.status === "waiting_approval" && (
<>
{onFollowUp && (
<button
onClick={(e) => { e.stopPropagation(); onFollowUp(); }}
className="flex-1 glass border-white/10 py-3 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:bg-white/10 transition"
>
<Wand2 className="w-4 h-4" /> Refine
</button>
)}
{onCommit && (
<button
onClick={(e) => { e.stopPropagation(); onCommit(); }}
className="flex-1 btn-cyan py-3 rounded-xl text-[11px] font-black flex items-center justify-center gap-2 tracking-widest"
>
<GitCommit className="w-4 h-4 stroke-[2.5px]" /> COMMIT
</button>
)}
</>
)}
{/* Verified Buttons */}
{feature.status === "verified" && (
<>
{onViewOutput && (
<button
onClick={(e) => { e.stopPropagation(); onViewOutput(); }}
className="px-7 glass border-white/10 py-3 rounded-xl text-[11px] font-bold text-slate-500 hover:text-slate-300 transition"
>
Logs
</button>
)}
{onComplete && (
<button
onClick={(e) => { e.stopPropagation(); onComplete(); }}
className="flex-1 bg-emerald-500/15 text-emerald-400 border border-emerald-500/20 py-3 rounded-xl text-[11px] font-black flex items-center justify-center gap-2 tracking-widest"
>
<CheckCircle2 className="w-4 h-4 stroke-[2.5px]" /> COMPLETE
</button>
)}
</>
)}
</div>
</div>
{/* Delete Confirmation Dialog */}
<DeleteConfirmDialog
open={isDeleteDialogOpen}
onOpenChange={setIsDeleteDialogOpen}
onConfirm={handleConfirmDelete}
title="Delete Feature"
description="Are you sure you want to delete this feature? This action cannot be undone."
testId="delete-confirmation-dialog"
confirmTestId="confirm-delete-button"
/>
{/* Summary Modal - Reusing existing logic */}
<Dialog open={isSummaryDialogOpen} onOpenChange={setIsSummaryDialogOpen}>
<DialogContent
className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col"
data-testid={`summary-dialog-${feature.id}`}
>
{/* ... Existing dialog content ... */}
<DialogHeader>
<DialogTitle>Summary</DialogTitle>
<DialogDescription>{feature.summary}</DialogDescription>
</DialogHeader>
<div className="flex-1 overflow-y-auto p-4 bg-card rounded-lg border border-border/50">
<Markdown>
{feature.summary ||
summary ||
agentInfo?.summary ||
"No summary available"}
</Markdown>
</div>
<DialogFooter>
<Button onClick={() => setIsSummaryDialogOpen(false)}>Close</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}
const cardElement = (
<Card
ref={setNodeRef}
style={isCurrentAutoTask ? style : borderStyle}
className={cn(
// Clean theme kanban-card class
"kanban-card",
"cursor-grab active:cursor-grabbing relative kanban-card-content select-none",
"transition-all duration-200 ease-out",
// Premium shadow system
"shadow-sm hover:shadow-md hover:shadow-black/10",
// Subtle lift on hover
"hover:-translate-y-0.5",
// Running card state for clean theme
isCurrentAutoTask && "is-running kanban-card-active",
!isCurrentAutoTask &&
cardBorderEnabled &&
cardBorderOpacity === 100 &&
@@ -725,7 +977,10 @@ export const KanbanCard = memo(function KanbanCard({
{/* Model/Preset Info for Backlog Cards */}
{showAgentInfo && feature.status === "backlog" && (
<div className="mb-3 space-y-2 overflow-hidden">
<div
className="mb-3 space-y-2 overflow-hidden"
style={isCleanTheme ? { order: 1 } : undefined}
>
<div className="flex items-center gap-2 text-[11px] flex-wrap">
<div className="flex items-center gap-1 text-[var(--status-info)]">
<Cpu className="w-3 h-3" />
@@ -747,7 +1002,10 @@ export const KanbanCard = memo(function KanbanCard({
{/* Agent Info Panel */}
{showAgentInfo && feature.status !== "backlog" && agentInfo && (
<div className="mb-3 space-y-2 overflow-hidden">
<div
className="mb-3 space-y-2 overflow-hidden"
style={isCleanTheme ? { order: 1 } : undefined}
>
{/* Model & Phase */}
<div className="flex items-center gap-2 text-[11px] flex-wrap">
<div className="flex items-center gap-1 text-[var(--status-info)]">
@@ -880,7 +1138,10 @@ export const KanbanCard = memo(function KanbanCard({
)}
{/* Actions */}
<div className="flex flex-wrap gap-1.5">
<div
className="flex flex-wrap gap-1.5"
style={isCleanTheme ? { order: 2 } : undefined}
>
{isCurrentAutoTask && (
<>
{/* Approve Plan button - PRIORITY: shows even when agent is "running" (paused for approval) */}

View File

@@ -4,6 +4,7 @@ import { memo } from "react";
import { useDroppable } from "@dnd-kit/core";
import { cn } from "@/lib/utils";
import type { ReactNode } from "react";
import { useAppStore } from "@/store/app-store";
interface KanbanColumnProps {
id: string;
@@ -29,16 +30,127 @@ export const KanbanColumn = memo(function KanbanColumn({
hideScrollbar = false,
}: KanbanColumnProps) {
const { setNodeRef, isOver } = useDroppable({ id });
const { getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
// Map column IDs to clean theme classes
const getColumnClasses = () => {
switch (id) {
case "in_progress":
return "col-in-progress";
case "waiting_approval":
return "col-waiting";
case "verified":
return "col-verified";
default:
return "";
}
};
// Map column IDs to status dot glow classes
const getStatusDotClasses = () => {
switch (id) {
case "in_progress":
return "status-dot-in-progress glow-cyan";
case "waiting_approval":
return "status-dot-waiting glow-orange";
case "verified":
return "status-dot-verified glow-green";
default:
return "";
}
};
// Clean theme column styles
if (isCleanTheme) {
const isBacklog = id === "backlog";
// Explicitly match mockup classes for status dots
const getCleanStatusDotClass = () => {
switch (id) {
case "backlog":
return "status-dot bg-slate-600";
case "in_progress":
return "status-dot bg-cyan-400 glow-cyan";
case "waiting_approval":
return "status-dot bg-orange-500 glow-orange";
case "verified":
return "status-dot bg-emerald-500 glow-green";
default:
return "status-dot bg-slate-600";
}
};
// Explicitly match mockup classes for badges
const getBadgeClass = () => {
switch (id) {
case "in_progress":
return "mono text-[10px] bg-cyan-500/10 px-2.5 py-0.5 rounded-full text-cyan-400 border border-cyan-500/20";
case "verified":
return "mono text-[10px] bg-emerald-500/10 px-2.5 py-0.5 rounded-full text-emerald-500 border border-emerald-500/20";
case "backlog":
case "waiting_approval":
default:
return "mono text-[10px] bg-white/5 px-2.5 py-0.5 rounded-full text-slate-500 border border-white/5";
}
};
return (
<div
ref={setNodeRef}
className={cn(
"flex flex-col h-full w-80 gap-5",
!isBacklog && "rounded-[2.5rem] p-3",
getColumnClasses()
)}
data-testid={`kanban-column-${id}`}
data-column-id={id}
>
{/* Header */}
<div className="flex items-center justify-between px-2 shrink-0">
<div className="flex items-center gap-3">
<span className={getCleanStatusDotClass()} />
<h3 className={cn(
"text-[11px] font-black uppercase tracking-widest",
id === "backlog" ? "text-slate-400" :
id === "in_progress" ? "text-slate-200" : "text-slate-300"
)}>
{title}
</h3>
{headerAction}
</div>
<span className={getBadgeClass()}>
{count}
</span>
</div>
{/* Content */}
<div
className={cn(
"flex-1 overflow-y-auto custom-scrollbar space-y-4",
isBacklog ? "pr-2" : "pr-1",
hideScrollbar && "scrollbar-hide"
)}
>
{children}
</div>
</div>
);
}
return (
<div
ref={setNodeRef}
className={cn(
"relative flex flex-col h-full rounded-xl transition-all duration-200 w-72",
"relative flex flex-col h-full rounded-xl transition-all duration-200 w-72 clean:w-80",
showBorder && "border border-border/60",
isOver && "ring-2 ring-primary/30 ring-offset-1 ring-offset-background"
isOver && "ring-2 ring-primary/30 ring-offset-1 ring-offset-background",
getColumnClasses()
)}
data-testid={`kanban-column-${id}`}
data-column-id={id}
>
{/* Background layer with opacity */}
<div
@@ -56,7 +168,7 @@ export const KanbanColumn = memo(function KanbanColumn({
showBorder && "border-b border-border/40"
)}
>
<div className={cn("w-2.5 h-2.5 rounded-full shrink-0", colorClass)} />
<div className={cn("w-2.5 h-2.5 rounded-full shrink-0 status-dot", colorClass, getStatusDotClasses())} />
<h3 className="font-semibold text-sm text-foreground/90 flex-1 tracking-tight">{title}</h3>
{headerAction}
<span className="text-xs font-medium text-muted-foreground/80 bg-muted/50 px-2 py-0.5 rounded-md tabular-nums">

View File

@@ -13,10 +13,11 @@ import { Button } from "@/components/ui/button";
import { HotkeyButton } from "@/components/ui/hotkey-button";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { KanbanColumn, KanbanCard } from "./components";
import { Feature } from "@/store/app-store";
import { FastForward, Lightbulb, Trash2 } from "lucide-react";
import { Feature, useAppStore } from "@/store/app-store";
import { FastForward, Lightbulb, Trash2, GitBranch } from "lucide-react";
import { useKeyboardShortcutsConfig } from "@/hooks/use-keyboard-shortcuts";
import { COLUMNS, ColumnId } from "./constants";
import { cn } from "@/lib/utils";
interface KanbanBoardProps {
sensors: any;
@@ -89,9 +90,16 @@ export function KanbanBoard({
suggestionsCount,
onDeleteAllVerified,
}: KanbanBoardProps) {
const { getEffectiveTheme } = useAppStore();
const effectiveTheme = getEffectiveTheme();
const isCleanTheme = effectiveTheme === "clean";
return (
<div
className="flex-1 overflow-x-auto px-4 pb-4 relative"
className={cn(
"flex-1 overflow-x-auto relative",
isCleanTheme ? "custom-scrollbar px-8 pb-8" : "px-4 pb-4"
)}
style={backgroundImageStyle}
>
<DndContext
@@ -100,9 +108,91 @@ export function KanbanBoard({
onDragStart={onDragStart}
onDragEnd={onDragEnd}
>
<div className="flex gap-5 h-full min-w-max py-1">
<div
className={cn(
"flex h-full min-w-max",
isCleanTheme ? "gap-6 items-start" : "gap-5 py-1"
)}
>
{COLUMNS.map((column) => {
const columnFeatures = getColumnFeatures(column.id);
// Clean Theme Header Actions
let headerAction;
if (isCleanTheme) {
if (column.id === "backlog") {
headerAction = (
<div className="flex items-center gap-1.5 opacity-40">
<Lightbulb className="w-3.5 h-3.5 text-yellow-500" />
<GitBranch className="w-3.5 h-3.5 text-cyan-400" />
<span className="mono text-[9px] text-cyan-400 font-bold">Mabe 6</span>
</div>
);
} else if (column.id === "verified" && columnFeatures.length > 0) {
headerAction = (
<button
className="ml-2 text-[10px] text-rose-500 flex items-center gap-1 hover:underline font-black transition"
onClick={onDeleteAllVerified}
>
<Trash2 className="w-3.5 h-3.5" /> Delete All
</button>
);
}
} else {
// Standard Theme Header Actions
if (column.id === "verified" && columnFeatures.length > 0) {
headerAction = (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={onDeleteAllVerified}
data-testid="delete-all-verified-button"
>
<Trash2 className="w-3 h-3 mr-1" />
Delete All
</Button>
);
} else if (column.id === "backlog") {
headerAction = (
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-yellow-500 hover:text-yellow-400 hover:bg-yellow-500/10 relative"
onClick={onShowSuggestions}
title="Feature Suggestions"
data-testid="feature-suggestions-button"
>
<Lightbulb className="w-3.5 h-3.5" />
{suggestionsCount > 0 && (
<span
className="absolute -top-1 -right-1 w-4 h-4 text-[9px] font-mono rounded-full bg-yellow-500 text-black flex items-center justify-center"
data-testid="suggestions-count"
>
{suggestionsCount}
</span>
)}
</Button>
{columnFeatures.length > 0 && (
<HotkeyButton
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-primary hover:text-primary hover:bg-primary/10"
onClick={onStartNextFeatures}
hotkey={shortcuts.startNext}
hotkeyActive={false}
data-testid="start-next-button"
>
<FastForward className="w-3 h-3 mr-1" />
Make
</HotkeyButton>
)}
</div>
);
}
}
return (
<KanbanColumn
key={column.id}
@@ -113,56 +203,7 @@ export function KanbanBoard({
opacity={backgroundSettings.columnOpacity}
showBorder={backgroundSettings.columnBorderEnabled}
hideScrollbar={backgroundSettings.hideScrollbar}
headerAction={
column.id === "verified" &&
columnFeatures.length > 0 ? (
<Button
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={onDeleteAllVerified}
data-testid="delete-all-verified-button"
>
<Trash2 className="w-3 h-3 mr-1" />
Delete All
</Button>
) : column.id === "backlog" ? (
<div className="flex items-center gap-1">
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0 text-yellow-500 hover:text-yellow-400 hover:bg-yellow-500/10 relative"
onClick={onShowSuggestions}
title="Feature Suggestions"
data-testid="feature-suggestions-button"
>
<Lightbulb className="w-3.5 h-3.5" />
{suggestionsCount > 0 && (
<span
className="absolute -top-1 -right-1 w-4 h-4 text-[9px] font-mono rounded-full bg-yellow-500 text-black flex items-center justify-center"
data-testid="suggestions-count"
>
{suggestionsCount}
</span>
)}
</Button>
{columnFeatures.length > 0 && (
<HotkeyButton
variant="ghost"
size="sm"
className="h-6 px-2 text-xs text-primary hover:text-primary hover:bg-primary/10"
onClick={onStartNextFeatures}
hotkey={shortcuts.startNext}
hotkeyActive={false}
data-testid="start-next-button"
>
<FastForward className="w-3 h-3 mr-1" />
Make
</HotkeyButton>
)}
</div>
) : undefined
}
headerAction={headerAction}
>
<SortableContext
items={columnFeatures.map((f) => f.id)}

View File

@@ -33,7 +33,8 @@ export type Theme =
| "red"
| "cream"
| "sunset"
| "gray";
| "gray"
| "clean";
export type KanbanDetailLevel = "minimal" | "standard" | "detailed";

View File

@@ -431,6 +431,31 @@ const grayTheme: TerminalTheme = {
brightWhite: "#e0e0e8",
};
// Clean theme - Modern glassmorphism with cyan accents
const cleanTheme: TerminalTheme = {
background: "#0b101a",
foreground: "#e2e8f0",
cursor: "#22d3ee",
cursorAccent: "#0b101a",
selectionBackground: "#22d3ee33",
black: "#0f1419",
red: "#ef4444",
green: "#10b981",
yellow: "#f59e0b",
blue: "#22d3ee",
magenta: "#a78bfa",
cyan: "#22d3ee",
white: "#e2e8f0",
brightBlack: "#4b5563",
brightRed: "#f87171",
brightGreen: "#34d399",
brightYellow: "#fbbf24",
brightBlue: "#67e8f9",
brightMagenta: "#c4b5fd",
brightCyan: "#67e8f9",
brightWhite: "#f8fafc",
};
// Theme mapping
const terminalThemes: Record<ThemeMode, TerminalTheme> = {
light: lightTheme,
@@ -450,6 +475,7 @@ const terminalThemes: Record<ThemeMode, TerminalTheme> = {
cream: creamTheme,
sunset: sunsetTheme,
gray: grayTheme,
clean: cleanTheme,
};
/**

View File

@@ -4,6 +4,7 @@ import {
Cat,
CloudSun,
Coffee,
Droplets,
Eclipse,
Flame,
Ghost,
@@ -113,4 +114,10 @@ export const themeOptions: ReadonlyArray<ThemeOption> = [
Icon: Square,
testId: "gray-mode-button",
},
{
value: "clean",
label: "Clean",
Icon: Droplets,
testId: "clean-mode-button",
},
];

View File

@@ -33,7 +33,8 @@ export type ThemeMode =
| "red"
| "cream"
| "sunset"
| "gray";
| "gray"
| "clean";
export type KanbanCardDetailLevel = "minimal" | "standard" | "detailed";

499
index (25).html Normal file
View File

@@ -0,0 +1,499 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>automaker. | Kanban Board</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root {
--bg-deep: #0b101a;
--sidebar-bg: rgba(13, 17, 26, 0.7);
--accent-cyan: #22d3ee;
--accent-orange: #f59e0b;
--accent-green: #10b981;
--accent-red: #ef4444;
--glass-bg: rgba(255, 255, 255, 0.03);
--glass-border: rgba(255, 255, 255, 0.07);
--card-radius: 1.5rem;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--bg-deep);
color: #e2e8f0;
overflow: hidden;
height: 100vh;
}
.mono { font-family: 'JetBrains Mono', monospace; }
/* Rainbow Prism Background Effect */
.prism-bg {
position: fixed;
inset: 0;
z-index: -1;
background:
radial-gradient(circle at 10% 20%, rgba(34, 211, 238, 0.1) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(139, 92, 246, 0.08) 0%, transparent 40%),
radial-gradient(circle at 50% 50%, rgba(245, 158, 11, 0.04) 0%, transparent 60%),
linear-gradient(145deg,
rgba(255, 0, 0, 0.02) 0%,
rgba(0, 255, 255, 0.02) 50%,
rgba(0, 0, 255, 0.02) 100%
);
filter: blur(80px);
}
/* Glassmorphism */
.glass {
background: var(--glass-bg);
backdrop-filter: blur(24px);
border: 1px solid var(--glass-border);
}
.sidebar-glass {
background: var(--sidebar-bg);
backdrop-filter: blur(40px);
border-right: 1px solid rgba(255, 255, 255, 0.04);
}
/* Nav Active State */
.nav-active {
background: linear-gradient(90deg, rgba(34, 211, 238, 0.12) 0%, transparent 100%);
border-left: 3px solid var(--accent-cyan);
color: #fff;
}
/* Column Specific Visuals */
.col-in-progress {
border: 1px solid rgba(34, 211, 238, 0.25);
background: rgba(34, 211, 238, 0.02);
box-shadow: inset 0 0 40px rgba(34, 211, 238, 0.03);
}
.col-waiting { border-top: 2px solid rgba(245, 158, 11, 0.3); }
.col-verified { border-top: 2px solid rgba(16, 185, 129, 0.3); }
/* Custom Scrollbar */
.custom-scrollbar::-webkit-scrollbar { width: 5px; height: 5px; }
.custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
.custom-scrollbar::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.1); border-radius: 10px; }
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.2); }
/* Shortcut Badge */
.shortcut-badge {
font-size: 9px;
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.3);
padding: 1px 5px;
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.08);
font-family: 'JetBrains Mono', monospace;
font-weight: 600;
}
/* Toggle Switch */
.toggle-track {
width: 42px;
height: 20px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 20px;
position: relative;
cursor: pointer;
}
.toggle-thumb {
position: absolute;
right: 3px;
top: 3px;
width: 12px;
height: 12px;
background: var(--accent-cyan);
border-radius: 50%;
box-shadow: 0 0 8px var(--accent-cyan);
}
/* Card Styles */
.kanban-card {
border-radius: var(--card-radius);
padding: 1.5rem;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.kanban-card:hover {
transform: translateY(-3px);
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.15);
}
.btn-cyan {
background: var(--accent-cyan);
color: #0b101a;
font-weight: 800;
transition: all 0.2s;
}
.btn-cyan:hover {
filter: brightness(1.1);
box-shadow: 0 0 15px rgba(34, 211, 238, 0.4);
}
.status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.glow-cyan { box-shadow: 0 0 10px var(--accent-cyan); }
.glow-orange { box-shadow: 0 0 10px var(--accent-orange); }
.glow-green { box-shadow: 0 0 10px var(--accent-green); }
.line-clamp-3 {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>
</head>
<body class="flex h-screen w-screen overflow-hidden">
<div class="prism-bg"></div>
<!-- SIDEBAR -->
<aside class="w-64 h-full sidebar-glass flex flex-col z-30 shrink-0">
<!-- Brand -->
<div class="p-6 flex items-center gap-2.5">
<div class="bg-cyan-500/10 p-1.5 rounded-lg border border-cyan-500/30">
<i data-lucide="code-2" class="w-5 h-5 text-cyan-400 stroke-[2.5px]"></i>
</div>
<h1 class="text-xl font-bold tracking-tighter text-white">automaker<span class="text-cyan-400">.</span></h1>
</div>
<!-- Top Actions -->
<div class="px-4 space-y-3 mb-8">
<div class="flex gap-2">
<button class="flex-1 glass py-2 rounded-xl text-[11px] font-bold hover:bg-white/10 transition flex items-center justify-center gap-2">
<i data-lucide="plus" class="w-3.5 h-3.5"></i> New
</button>
<button class="w-10 glass rounded-xl flex items-center justify-center hover:bg-white/10 transition relative">
<i data-lucide="file-text" class="w-4 h-4 opacity-40"></i>
<span class="mono text-[8px] absolute top-1 right-1.5 opacity-40 font-bold">0</span>
</button>
<button class="w-10 glass rounded-xl flex items-center justify-center hover:bg-white/10 transition relative">
<i data-lucide="layers" class="w-4 h-4 opacity-40"></i>
<span class="absolute -top-0.5 -right-0.5 w-2.5 h-2.5 bg-rose-500 rounded-full border border-[#0b101a]"></span>
</button>
</div>
<!-- Project Dropdown -->
<div class="glass p-3.5 rounded-2xl flex items-center justify-between cursor-pointer hover:bg-white/5 transition group">
<div class="flex items-center gap-3">
<i data-lucide="folder-kanban" class="w-4 h-4 text-cyan-400"></i>
<span class="text-xs font-bold text-slate-200">test case 1</span>
</div>
<div class="flex items-center gap-2">
<span class="shortcut-badge">P</span>
<i data-lucide="chevron-down" class="w-4 h-4 text-slate-500 group-hover:text-white"></i>
</div>
</div>
</div>
<!-- Navigation -->
<nav class="flex-1 px-0 space-y-8 overflow-y-auto custom-scrollbar">
<div>
<p class="px-6 text-[10px] font-black text-slate-600 uppercase tracking-[0.2em] mb-3">Project</p>
<div class="space-y-0.5">
<a href="#" class="nav-active flex items-center justify-between px-6 py-3 text-sm">
<div class="flex items-center gap-3"><i data-lucide="layout-grid" class="w-4 h-4"></i><span class="font-medium">Kanban Board</span></div>
<span class="shortcut-badge">E</span>
</a>
<a href="#" class="flex items-center justify-between px-6 py-3 text-sm text-slate-400 hover:text-white hover:bg-white/5 transition">
<div class="flex items-center gap-3"><i data-lucide="zap" class="w-4 h-4"></i><span class="font-medium">Agent Runner</span></div>
<span class="shortcut-badge">A</span>
</a>
</div>
</div>
<div>
<p class="px-6 text-[10px] font-black text-slate-600 uppercase tracking-[0.2em] mb-3">Tools</p>
<div class="space-y-0.5">
<a href="#" class="flex items-center justify-between px-6 py-3 text-sm text-slate-400 hover:text-white hover:bg-white/5 transition">
<div class="flex items-center gap-3"><i data-lucide="file-edit" class="w-4 h-4"></i><span>Spec Editor</span></div>
<span class="shortcut-badge">D</span>
</a>
<a href="#" class="flex items-center justify-between px-6 py-3 text-sm text-slate-400 hover:text-white hover:bg-white/5 transition">
<div class="flex items-center gap-3"><i data-lucide="database" class="w-4 h-4"></i><span>Context</span></div>
<span class="shortcut-badge">C</span>
</a>
<a href="#" class="flex items-center justify-between px-6 py-3 text-sm text-slate-400 hover:text-white hover:bg-white/5 transition">
<div class="flex items-center gap-3"><i data-lucide="user-circle" class="w-4 h-4"></i><span>AI Profiles</span></div>
<span class="shortcut-badge">H</span>
</a>
<a href="#" class="flex items-center justify-between px-6 py-3 text-sm text-slate-400 hover:text-white hover:bg-white/5 transition">
<div class="flex items-center gap-3"><i data-lucide="terminal" class="w-4 h-4"></i><span>Terminal</span></div>
<span class="shortcut-badge">T</span>
</a>
</div>
</div>
</nav>
<!-- Footer -->
<div class="p-4 border-t border-white/5 space-y-1 mt-auto bg-black/10">
<a href="#" class="flex items-center gap-3 px-3 py-2 text-sm text-slate-400 hover:text-white transition">
<i data-lucide="book-open" class="w-4 h-4"></i> Wiki
</a>
<div class="flex items-center justify-between px-3 py-2 text-sm text-slate-400 hover:text-white cursor-pointer group transition">
<div class="flex items-center gap-3">
<i data-lucide="activity" class="w-4 h-4 text-cyan-400"></i> Running Agents
</div>
<span class="bg-cyan-500 text-slate-950 font-black text-[10px] px-2 py-0.5 rounded-full">3</span>
</div>
<a href="#" class="flex items-center gap-3 px-3 py-2 text-sm text-slate-400 hover:text-white transition">
<i data-lucide="settings" class="w-4 h-4"></i> Settings
<span class="ml-auto shortcut-badge">S</span>
</a>
</div>
</aside>
<!-- MAIN CONTENT -->
<main class="flex-1 flex flex-col min-w-0">
<!-- Header -->
<header class="h-16 flex items-center justify-between px-8 border-b border-white/5 bg-[#0b101a]/40 backdrop-blur-md z-20 shrink-0">
<div>
<h2 class="text-lg font-bold text-white tracking-tight">Kanban Board</h2>
<p class="text-[10px] text-slate-500 uppercase tracking-[0.2em] font-bold mono">test case 1</p>
</div>
<div class="flex items-center gap-5">
<div class="flex items-center bg-white/5 border border-white/10 rounded-full px-4 py-1.5 gap-3">
<i data-lucide="users" class="w-4 h-4 text-slate-500"></i>
<div class="toggle-track">
<div class="toggle-thumb"></div>
</div>
<span class="mono text-xs font-bold text-slate-400">3</span>
</div>
<button class="flex items-center gap-2 glass px-5 py-2 rounded-xl text-xs font-bold hover:bg-white/10 transition">
<i data-lucide="play" class="w-3.5 h-3.5 text-cyan-400 fill-cyan-400"></i> Auto Mode
</button>
<button class="btn-cyan px-6 py-2 rounded-xl text-xs font-black flex items-center gap-2 shadow-lg shadow-cyan-500/20">
<i data-lucide="plus" class="w-4 h-4 stroke-[3.5px]"></i> ADD FEATURE
</button>
</div>
</header>
<!-- Search Bar -->
<div class="px-8 py-4 flex items-center justify-between shrink-0">
<div class="relative flex-1 max-w-2xl group">
<i data-lucide="search" class="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-500 group-focus-within:text-cyan-400 transition-colors"></i>
<input type="text" placeholder="Search features by keyword..." class="w-full bg-white/5 border border-white/10 rounded-2xl py-2.5 pl-12 pr-12 text-sm focus:outline-none focus:border-cyan-500/50 transition-all mono">
<div class="absolute right-4 top-1/2 -translate-y-1/2">
<span class="shortcut-badge">/</span>
</div>
</div>
<div class="flex items-center gap-2 ml-6">
<button class="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"><i data-lucide="history" class="w-4.5 h-4.5"></i></button>
<button class="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"><i data-lucide="trash-2" class="w-4.5 h-4.5"></i></button>
<div class="w-px h-6 bg-white/10 mx-1"></div>
<button class="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"><i data-lucide="maximize-2" class="w-4.5 h-4.5"></i></button>
<button class="p-2.5 glass rounded-xl text-slate-500 hover:text-white transition"><i data-lucide="layout" class="w-4.5 h-4.5"></i></button>
</div>
</div>
<!-- Kanban Grid -->
<div class="flex-1 overflow-x-auto custom-scrollbar px-8 pb-8">
<div class="flex gap-6 h-full min-w-max items-start">
<!-- COLUMN: BACKLOG -->
<div class="w-80 flex flex-col gap-5 h-full">
<div class="flex items-center justify-between px-2 shrink-0">
<div class="flex items-center gap-3">
<span class="status-dot bg-slate-600"></span>
<h3 class="text-[11px] font-black text-slate-400 uppercase tracking-widest">Backlog</h3>
<div class="flex items-center gap-1.5 opacity-40">
<i data-lucide="lightbulb" class="w-3.5 h-3.5 text-yellow-500"></i>
<i data-lucide="git-branch" class="w-3.5 h-3.5 text-cyan-400"></i>
<span class="mono text-[9px] text-cyan-400 font-bold">Mabe 6</span>
</div>
</div>
<span class="mono text-[10px] bg-white/5 px-2.5 py-0.5 rounded-full text-slate-500 border border-white/5">47</span>
</div>
<div class="flex-1 overflow-y-auto custom-scrollbar space-y-4 pr-2">
<div class="glass kanban-card flex flex-col gap-4 group relative">
<div class="absolute top-5 right-6 opacity-0 group-hover:opacity-100 transition-opacity">
<i data-lucide="trash-2" class="w-4 h-4 text-slate-600 hover:text-red-400 cursor-pointer"></i>
</div>
<p class="text-[13px] text-slate-300 leading-relaxed font-medium line-clamp-3">Create a bouncing animation using CSS keyframes that simulates elastic motion...</p>
<div class="flex items-center gap-1 text-[10px] text-slate-500 -mt-1 cursor-pointer hover:text-slate-300">
<i data-lucide="chevron-down" class="w-3 h-3"></i> More
</div>
<div class="text-[10px] font-bold text-cyan-400/80 mono flex items-center gap-1.5 uppercase tracking-tight">
<i data-lucide="layers" class="w-3.5 h-3.5"></i> Opus 4.2
</div>
<div class="flex gap-2">
<button class="flex-1 glass py-2.5 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:bg-white/10 transition">
<i data-lucide="edit-3" class="w-3.5 h-3.5"></i> Edit
</button>
<button class="flex-1 bg-cyan-500/10 hover:bg-cyan-500/20 text-cyan-400 border border-cyan-500/20 py-2.5 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 transition">
<i data-lucide="target" class="w-3.5 h-3.5"></i> Make
</button>
</div>
</div>
<div class="glass kanban-card flex flex-col gap-4">
<p class="text-[13px] text-slate-300 leading-relaxed font-medium line-clamp-3">Implement CSS reset rules and establish base styling for the page including typography, spacing...</p>
<div class="text-[10px] font-bold text-cyan-400/80 mono flex items-center gap-1.5 uppercase">
<i data-lucide="layers" class="w-3.5 h-3.5"></i> Opus 4.3
</div>
<div class="flex gap-2">
<button class="flex-1 glass py-2.5 rounded-xl text-[11px] font-bold">Edit</button>
<button class="flex-1 bg-cyan-500/10 text-cyan-400 border border-cyan-500/20 py-2.5 rounded-xl text-[11px] font-bold">Make</button>
</div>
</div>
</div>
</div>
<!-- COLUMN: IN PROGRESS -->
<div class="w-80 flex flex-col gap-5 col-in-progress rounded-[2.5rem] p-3 h-full">
<div class="flex items-center justify-between px-2 shrink-0">
<div class="flex items-center gap-3">
<span class="status-dot bg-cyan-400 glow-cyan"></span>
<h3 class="text-[11px] font-black text-slate-200 uppercase tracking-widest">In Progress</h3>
</div>
<span class="mono text-[10px] bg-cyan-500/10 px-2.5 py-0.5 rounded-full text-cyan-400 border border-cyan-500/20">3</span>
</div>
<div class="flex-1 overflow-y-auto custom-scrollbar space-y-4 pr-1">
<!-- Active Card -->
<div class="glass kanban-card border-cyan-500/40 bg-cyan-500/[0.08] flex flex-col gap-4">
<div class="flex justify-end items-center gap-2">
<div class="bg-orange-500/15 text-orange-400 text-[9px] px-2.5 py-1 rounded-lg border border-orange-500/20 flex items-center gap-1.5 font-black mono">
<i data-lucide="refresh-cw" class="w-3 h-3"></i> Opus 6.5
</div>
<div class="bg-slate-900/50 text-slate-500 text-[9px] px-2 py-1 rounded-lg border border-white/5 font-mono">00:04</div>
</div>
<p class="text-[13px] text-white leading-relaxed font-semibold">Configure the application for deployment to a web hosting platform. Set up necessary build processes...</p>
<div class="flex gap-2 mt-2">
<button class="flex-[4] btn-cyan py-3 rounded-xl text-[11px] font-black flex items-center justify-center gap-2 tracking-widest">
<i data-lucide="terminal" class="w-4 h-4 stroke-[2.5px]"></i> LOGS <span class="bg-black/10 px-1.5 rounded ml-1">8</span>
</button>
<button class="flex-1 bg-rose-500 hover:bg-rose-600 text-white py-3 rounded-xl flex items-center justify-center transition shadow-lg shadow-rose-500/20">
<i data-lucide="square" class="w-4 h-4 fill-current"></i>
</button>
</div>
</div>
<!-- Card 2 -->
<div class="glass kanban-card flex flex-col gap-4">
<div class="flex justify-end gap-2">
<div class="bg-orange-500/10 text-orange-400 text-[9px] px-2.5 py-1 rounded-lg border border-orange-500/10 flex items-center gap-1.5 font-bold mono">
<i data-lucide="refresh-cw" class="w-3 h-3"></i> Opus 4.5
</div>
<div class="bg-slate-900/50 text-slate-500 text-[9px] px-2 py-1 rounded-lg border border-white/5 font-mono">00:07</div>
</div>
<p class="text-[13px] text-slate-300 leading-relaxed font-medium">Create helper functions for selecting and querying DOM elements. Provide reusable utilities for element...</p>
<div class="flex gap-2 mt-2">
<button class="flex-[4] bg-cyan-500/15 text-cyan-400 border border-cyan-500/20 py-3 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2">
<i data-lucide="terminal" class="w-4 h-4"></i> LOGS <span class="bg-cyan-500/10 px-1.5 rounded ml-1">2</span>
</button>
<button class="flex-1 bg-rose-500/20 text-rose-500/50 border border-rose-500/20 py-3 rounded-xl flex items-center justify-center">
<i data-lucide="square" class="w-4 h-4"></i>
</button>
</div>
</div>
</div>
</div>
<!-- COLUMN: WAITING APPROVAL -->
<div class="w-80 flex flex-col gap-5 col-waiting rounded-[2.5rem] p-3 h-full">
<div class="flex items-center justify-between px-2 shrink-0">
<div class="flex items-center gap-3">
<span class="status-dot bg-orange-500 glow-orange"></span>
<h3 class="text-[11px] font-black text-slate-300 uppercase tracking-widest">Waiting Approval</h3>
</div>
<span class="mono text-[10px] bg-white/5 px-2.5 py-0.5 rounded-full text-slate-500 border border-white/5">2</span>
</div>
<div class="flex-1 overflow-y-auto custom-scrollbar space-y-4 pr-1">
<div class="glass kanban-card flex flex-col gap-4 group">
<div class="flex justify-end gap-3.5 opacity-30 group-hover:opacity-100 transition-opacity">
<i data-lucide="edit-3" class="w-4 h-4 cursor-pointer hover:text-white transition"></i>
<i data-lucide="copy" class="w-4 h-4 cursor-pointer hover:text-white transition"></i>
<i data-lucide="trash-2" class="w-4 h-4 cursor-pointer hover:text-rose-400 transition"></i>
</div>
<p class="text-[13px] text-slate-300 leading-relaxed font-medium italic">Add descriptive labels and titles for each button animation style to identify them visually. Help users...</p>
<div class="flex gap-2.5 mt-2">
<button class="flex-1 glass border-white/10 py-3 rounded-xl text-[11px] font-bold flex items-center justify-center gap-2 hover:bg-white/10 transition">
<i data-lucide="wand-2" class="w-4 h-4"></i> Refine
</button>
<button class="flex-1 btn-cyan py-3 rounded-xl text-[11px] font-black flex items-center justify-center gap-2 tracking-widest">
<i data-lucide="git-commit" class="w-4 h-4 stroke-[2.5px]"></i> COMMIT
</button>
</div>
</div>
</div>
</div>
<!-- COLUMN: VERIFIED -->
<div class="w-80 flex flex-col gap-5 col-verified rounded-[2.5rem] p-3 h-full">
<div class="flex items-center justify-between px-2 shrink-0">
<div class="flex items-center gap-3">
<span class="status-dot bg-emerald-500 glow-green"></span>
<h3 class="text-[11px] font-black text-slate-300 uppercase tracking-widest">Verified</h3>
<button class="ml-2 text-[10px] text-rose-500 flex items-center gap-1 hover:underline font-black transition">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i> Delete All
</button>
</div>
<span class="mono text-[10px] bg-emerald-500/10 px-2.5 py-0.5 rounded-full text-emerald-500 border border-emerald-500/20">4</span>
</div>
<div class="flex-1 overflow-y-auto custom-scrollbar space-y-4 pr-1">
<div class="glass kanban-card opacity-60 hover:opacity-100 flex flex-col gap-4 transition-all">
<div class="flex justify-end gap-3.5 opacity-20">
<i data-lucide="edit-3" class="w-4 h-4"></i>
<i data-lucide="trash-2" class="w-4 h-4"></i>
</div>
<p class="text-[13px] text-slate-400 leading-relaxed line-through decoration-slate-600 font-medium">Define foundational button styles with padding, borders, radius, and default colors. Create...</p>
<div class="flex gap-2.5 mt-2">
<button class="px-7 glass border-white/10 py-3 rounded-xl text-[11px] font-bold text-slate-500 hover:text-slate-300 transition">Logs</button>
<button class="flex-1 bg-emerald-500/15 text-emerald-400 border border-emerald-500/20 py-3 rounded-xl text-[11px] font-black flex items-center justify-center gap-2 tracking-widest">
<i data-lucide="check-circle" class="w-4 h-4 stroke-[2.5px]"></i> COMPLETE
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Floating UI -->
<div class="fixed bottom-8 right-8 flex flex-col gap-4 z-50">
<button class="w-12 h-12 glass rounded-2xl flex items-center justify-center text-slate-400 hover:text-white transition shadow-2xl">
<i data-lucide="history" class="w-5 h-5"></i>
</button>
<button class="w-14 h-14 bg-cyan-500 rounded-2xl flex items-center justify-center text-slate-950 shadow-2xl shadow-cyan-500/40 hover:scale-110 active:scale-95 transition-all">
<i data-lucide="message-square" class="w-7 h-7 stroke-[2.5px]"></i>
</button>
</div>
<script>
// Initialize Lucide Icons
lucide.createIcons();
// Smooth horizontal scroll for the board
const board = document.querySelector('.overflow-x-auto');
board.addEventListener('wheel', (evt) => {
if (evt.deltaY !== 0) {
evt.preventDefault();
board.scrollLeft += evt.deltaY * 1.5;
}
}, { passive: false });
// Keyboard shortcut for search
window.addEventListener('keydown', (e) => {
if (e.key === '/') {
const searchInput = document.querySelector('input[type="text"]');
if (document.activeElement !== searchInput) {
e.preventDefault();
searchInput.focus();
}
}
});
</script>
</body>
</html>