mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 14:22:02 +00:00
Compare commits
1 Commits
v0.6.0
...
clean-them
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4907a610e |
File diff suppressed because it is too large
Load Diff
@@ -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" />
|
||||
|
||||
@@ -150,6 +150,7 @@ function HomeContent() {
|
||||
"cream",
|
||||
"sunset",
|
||||
"gray",
|
||||
"clean",
|
||||
];
|
||||
|
||||
// Remove all theme classes
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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) */}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -33,7 +33,8 @@ export type Theme =
|
||||
| "red"
|
||||
| "cream"
|
||||
| "sunset"
|
||||
| "gray";
|
||||
| "gray"
|
||||
| "clean";
|
||||
|
||||
export type KanbanDetailLevel = "minimal" | "standard" | "detailed";
|
||||
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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
499
index (25).html
Normal 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>
|
||||
Reference in New Issue
Block a user