-
-
- {/* Project Actions - Moved above project selector */}
- {sidebarOpen && (
-
- )}
+ {/* Floating Navigation Dock */}
+
+ {/* Project Selector Dialog (Hidden logic, controlled by state) */}
+
-
+ {/* Dialogs & Modals - Preservation of Logic */}
- {/* New Project Setup Dialog */}
- {/* Delete Project Confirmation Dialog */}
- {/* New Project Modal */}
-
+ >
);
}
diff --git a/apps/ui/src/components/ui/badge.tsx b/apps/ui/src/components/ui/badge.tsx
index f1a425bc..8e99a2a7 100644
--- a/apps/ui/src/components/ui/badge.tsx
+++ b/apps/ui/src/components/ui/badge.tsx
@@ -24,6 +24,13 @@ const badgeVariants = cva(
// Muted variants for subtle indication
muted: 'border-border/50 bg-muted/50 text-muted-foreground',
brand: 'border-transparent bg-brand-500/15 text-brand-500 border border-brand-500/30',
+ // Prism variants
+ prism:
+ 'border-cyan-500/30 bg-cyan-500/10 text-cyan-400 hover:bg-cyan-500/20 font-mono tracking-wide rounded-md',
+ 'prism-orange':
+ 'border-amber-500/30 bg-amber-500/10 text-amber-400 hover:bg-amber-500/20 font-mono tracking-wide rounded-md',
+ 'prism-green':
+ 'border-emerald-500/30 bg-emerald-500/10 text-emerald-400 hover:bg-emerald-500/20 font-mono tracking-wide rounded-md',
},
size: {
default: 'px-2.5 py-0.5 text-xs',
diff --git a/apps/ui/src/components/ui/button.tsx b/apps/ui/src/components/ui/button.tsx
index fa970a52..bda9cd56 100644
--- a/apps/ui/src/components/ui/button.tsx
+++ b/apps/ui/src/components/ui/button.tsx
@@ -6,25 +6,32 @@ import { Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
const buttonVariants = cva(
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-[0.98]",
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-lg text-sm font-medium transition-all duration-300 cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-[0.98]",
{
variants: {
variant: {
default:
- 'bg-primary text-primary-foreground shadow-sm hover:bg-primary/90 hover:shadow-md hover:shadow-primary/25',
+ 'bg-primary text-primary-foreground shadow-lg shadow-primary/20 hover:bg-primary/90 hover:shadow-primary/40 hover:-translate-y-0.5',
destructive:
'bg-destructive text-white shadow-sm hover:bg-destructive/90 hover:shadow-md hover:shadow-destructive/25 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
outline:
- 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
- secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
- ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
+ 'border border-border/50 bg-background/50 backdrop-blur-sm shadow-sm hover:bg-accent hover:text-accent-foreground dark:bg-white/5 dark:hover:bg-white/10 hover:border-accent',
+ secondary:
+ 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80 hover:shadow-md',
+ ghost: 'hover:bg-accent/50 hover:text-accent-foreground hover:backdrop-blur-sm',
link: 'text-primary underline-offset-4 hover:underline active:scale-100',
+ glass:
+ 'border border-white/10 bg-white/5 text-foreground shadow-sm drop-shadow-sm backdrop-blur-md hover:bg-white/10 hover:border-white/20 hover:shadow-md transition-all duration-300',
'animated-outline': 'relative overflow-hidden rounded-xl hover:bg-transparent shadow-none',
+ 'prism-primary':
+ 'bg-cyan-400 text-slate-950 font-extrabold shadow-lg shadow-cyan-400/20 hover:brightness-110 hover:shadow-cyan-400/40 transition-all duration-200 tracking-wide',
+ 'prism-glass':
+ 'glass hover:bg-white/10 text-xs font-bold rounded-xl transition-all duration-200',
},
size: {
default: 'h-9 px-4 py-2 has-[>svg]:px-3',
- sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
- lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5 text-xs',
+ lg: 'h-11 rounded-md px-8 has-[>svg]:px-5 text-base',
icon: 'size-9',
'icon-sm': 'size-8',
'icon-lg': 'size-10',
diff --git a/apps/ui/src/components/ui/card.tsx b/apps/ui/src/components/ui/card.tsx
index 86963f11..594e63dd 100644
--- a/apps/ui/src/components/ui/card.tsx
+++ b/apps/ui/src/components/ui/card.tsx
@@ -11,9 +11,9 @@ function Card({ className, gradient = false, ...props }: CardProps) {
(
className={cn(
'fixed top-[50%] left-[50%] z-50 translate-x-[-50%] translate-y-[-50%]',
'flex flex-col w-full max-w-[calc(100%-2rem)] max-h-[calc(100vh-4rem)]',
- 'bg-card border border-border rounded-xl shadow-2xl',
+ 'bg-card/90 border border-white/10 rounded-2xl shadow-2xl backdrop-blur-xl',
// Premium shadow
- 'shadow-[0_25px_50px_-12px_rgba(0,0,0,0.25)]',
+ 'shadow-[0_40px_80px_-12px_rgba(0,0,0,0.5)]',
// Animations - smoother with scale
'data-[state=open]:animate-in data-[state=closed]:animate-out',
'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
'data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95',
'data-[state=closed]:slide-out-to-top-[2%] data-[state=open]:slide-in-from-top-[2%]',
- 'duration-200',
+ 'duration-300 ease-out',
compact ? 'max-w-4xl p-4' : !hasCustomMaxWidth ? 'sm:max-w-2xl p-6' : 'p-6',
className
)}
diff --git a/apps/ui/src/components/ui/dropdown-menu.tsx b/apps/ui/src/components/ui/dropdown-menu.tsx
index ad1cd836..3309d409 100644
--- a/apps/ui/src/components/ui/dropdown-menu.tsx
+++ b/apps/ui/src/components/ui/dropdown-menu.tsx
@@ -157,7 +157,8 @@ const DropdownMenuContent = React.forwardRef<
ref={ref}
sideOffset={sideOffset}
className={cn(
- 'z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
+ 'z-50 min-w-[8rem] overflow-hidden rounded-lg border border-white/10 bg-popover/80 p-1 text-popover-foreground shadow-xl backdrop-blur-xl',
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
className
)}
{...props}
diff --git a/apps/ui/src/components/ui/input.tsx b/apps/ui/src/components/ui/input.tsx
index a757fa17..c6a106f4 100644
--- a/apps/ui/src/components/ui/input.tsx
+++ b/apps/ui/src/components/ui/input.tsx
@@ -15,17 +15,21 @@ function Input({ className, type, startAddon, endAddon, ...props }: InputProps)
type={type}
data-slot="input"
className={cn(
- 'file:text-foreground placeholder:text-muted-foreground/60 selection:bg-primary selection:text-primary-foreground bg-input border-border h-9 w-full min-w-0 rounded-md border px-3 py-1 text-base shadow-xs outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
- // Inner shadow for depth
- 'shadow-[inset_0_1px_2px_rgba(0,0,0,0.05)]',
- // Animated focus ring
- 'transition-[color,box-shadow,border-color] duration-200 ease-out',
- 'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
+ 'file:text-foreground placeholder:text-muted-foreground/50 selection:bg-cyan-500/30 selection:text-cyan-100',
+ 'bg-white/5 border-white/10 h-9 w-full min-w-0 rounded-xl border px-3 py-1 text-sm shadow-sm outline-none transition-all duration-200',
+ 'file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium',
+ 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50',
+ 'backdrop-blur-sm',
+ // Hover state
+ 'hover:bg-white/10 hover:border-white/20',
+ // Focus state with ring
+ 'focus:bg-white/10 focus:border-cyan-500/50',
+ 'focus-visible:border-cyan-500/50 focus-visible:ring-cyan-500/20 focus-visible:ring-[4px]',
'aria-invalid:ring-destructive/20 aria-invalid:border-destructive',
// Adjust padding for addons
startAddon && 'pl-0',
endAddon && 'pr-0',
- hasAddons && 'border-0 shadow-none focus-visible:ring-0',
+ hasAddons && 'border-0 shadow-none focus-visible:ring-0 bg-transparent',
className
)}
{...props}
@@ -39,10 +43,10 @@ function Input({ className, type, startAddon, endAddon, ...props }: InputProps)
return (
(({ className, ...p
className={cn('relative flex w-full touch-none select-none items-center', className)}
{...props}
>
-
-
+
+
-
+
));
Slider.displayName = SliderPrimitive.Root.displayName;
diff --git a/apps/ui/src/components/ui/switch.tsx b/apps/ui/src/components/ui/switch.tsx
index 47b2bc51..eaceebe8 100644
--- a/apps/ui/src/components/ui/switch.tsx
+++ b/apps/ui/src/components/ui/switch.tsx
@@ -11,7 +11,7 @@ const Switch = React.forwardRef<
>(({ className, ...props }, ref) => (
diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx
index 0038b6d3..9c0ec6ad 100644
--- a/apps/ui/src/components/views/board-view.tsx
+++ b/apps/ui/src/components/views/board-view.tsx
@@ -16,6 +16,7 @@ import { RefreshCw } from 'lucide-react';
import { useAutoMode } from '@/hooks/use-auto-mode';
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
import { useWindowState } from '@/hooks/use-window-state';
+import { PageShell } from '@/components/layout/page-shell';
// Board-view specific imports
import { BoardHeader } from './board-view/board-header';
import { BoardSearchBar } from './board-view/board-search-bar';
diff --git a/apps/ui/src/components/views/board-view/board-header.tsx b/apps/ui/src/components/views/board-view/board-header.tsx
index 0dae58d3..cb7b88fa 100644
--- a/apps/ui/src/components/views/board-view/board-header.tsx
+++ b/apps/ui/src/components/views/board-view/board-header.tsx
@@ -1,4 +1,5 @@
import { HotkeyButton } from '@/components/ui/hotkey-button';
+import { cn } from '@/lib/utils';
import { Slider } from '@/components/ui/slider';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
@@ -39,23 +40,20 @@ export function BoardHeader({
const showUsageTracking = !apiKeys.anthropic && !isWindows;
return (
-
+
-
Kanban Board
-
{projectName}
+
Kanban Board
+
+ {projectName}
+
-
- {/* Usage Popover - only show for CLI users (not API key users) */}
- {isMounted && showUsageTracking &&
}
- {/* Concurrency Slider - only show after mount to prevent hydration issues */}
+
+ {/* Concurrency/Agent Control - Styled as Toggle for visual matching, but keeps slider logic if needed or simplified */}
{isMounted && (
-
-
-
Agents
+
+
+ {/* We keep the slider for functionality, but could style it to look like the toggle or just use the slider cleanly */}
onConcurrencyChange(value[0])}
@@ -63,43 +61,43 @@ export function BoardHeader({
max={10}
step={1}
className="w-20"
- data-testid="concurrency-slider"
/>
-
+
{runningAgentsCount} / {maxConcurrency}
)}
- {/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
+ {/* Auto Mode Button */}
{isMounted && (
-
-
-
onAutoModeToggle(!isAutoModeRunning)}
+ className={cn(
+ 'flex items-center gap-2 px-5 py-2 rounded-xl text-xs font-bold transition',
+ isAutoModeRunning
+ ? 'bg-cyan-500/10 text-cyan-400 border border-cyan-500/20'
+ : 'glass hover:bg-white/10'
+ )}
+ >
+
-
+ Auto Mode
+
)}
-
-
- Add Feature
-
+
+ ADD FEATURE
+
-
+
);
}
diff --git a/apps/ui/src/components/views/board-view/components/kanban-column.tsx b/apps/ui/src/components/views/board-view/components/kanban-column.tsx
index 4e08cfba..2010f349 100644
--- a/apps/ui/src/components/views/board-view/components/kanban-column.tsx
+++ b/apps/ui/src/components/views/board-view/components/kanban-column.tsx
@@ -7,6 +7,7 @@ interface KanbanColumnProps {
id: string;
title: string;
colorClass: string;
+ columnClass?: string;
count: number;
children: ReactNode;
headerAction?: ReactNode;
@@ -21,6 +22,7 @@ export const KanbanColumn = memo(function KanbanColumn({
id,
title,
colorClass,
+ columnClass,
count,
children,
headerAction,
@@ -43,7 +45,8 @@ export const KanbanColumn = memo(function KanbanColumn({
'transition-[box-shadow,ring] duration-200',
!width && 'w-72', // Only apply w-72 if no custom width
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',
+ columnClass
)}
style={widthStyle}
data-testid={`kanban-column-${id}`}
diff --git a/apps/ui/src/components/views/board-view/constants.ts b/apps/ui/src/components/views/board-view/constants.ts
index ae239d94..9dc3fb56 100644
--- a/apps/ui/src/components/views/board-view/constants.ts
+++ b/apps/ui/src/components/views/board-view/constants.ts
@@ -2,21 +2,25 @@ import { Feature } from '@/store/app-store';
export type ColumnId = Feature['status'];
-export const COLUMNS: { id: ColumnId; title: string; colorClass: string }[] = [
- { id: 'backlog', title: 'Backlog', colorClass: 'bg-[var(--status-backlog)]' },
- {
- id: 'in_progress',
- title: 'In Progress',
- colorClass: 'bg-[var(--status-in-progress)]',
- },
- {
- id: 'waiting_approval',
- title: 'Waiting Approval',
- colorClass: 'bg-[var(--status-waiting)]',
- },
- {
- id: 'verified',
- title: 'Verified',
- colorClass: 'bg-[var(--status-success)]',
- },
-];
+export const COLUMNS: { id: ColumnId; title: string; colorClass: string; columnClass?: string }[] =
+ [
+ { id: 'backlog', title: 'Backlog', colorClass: 'bg-white/20', columnClass: '' },
+ {
+ id: 'in_progress',
+ title: 'In Progress',
+ colorClass: 'bg-cyan-400',
+ columnClass: 'col-in-progress',
+ },
+ {
+ id: 'waiting_approval',
+ title: 'Waiting Approval',
+ colorClass: 'bg-amber-500',
+ columnClass: 'col-waiting',
+ },
+ {
+ id: 'verified',
+ title: 'Verified',
+ colorClass: 'bg-emerald-500',
+ columnClass: 'col-verified',
+ },
+ ];
diff --git a/apps/ui/src/components/views/board-view/kanban-board.tsx b/apps/ui/src/components/views/board-view/kanban-board.tsx
index 65cd00ea..e93e9052 100644
--- a/apps/ui/src/components/views/board-view/kanban-board.tsx
+++ b/apps/ui/src/components/views/board-view/kanban-board.tsx
@@ -102,6 +102,7 @@ export function KanbanBoard({
id={column.id}
title={column.title}
colorClass={column.colorClass}
+ columnClass={column.columnClass}
count={columnFeatures.length}
width={columnWidth}
opacity={backgroundSettings.columnOpacity}
diff --git a/apps/ui/src/components/views/settings-view.tsx b/apps/ui/src/components/views/settings-view.tsx
index 0580c18e..b8ebd051 100644
--- a/apps/ui/src/components/views/settings-view.tsx
+++ b/apps/ui/src/components/views/settings-view.tsx
@@ -1,5 +1,6 @@
import { useState } from 'react';
import { useAppStore } from '@/store/app-store';
+import { PageShell } from '@/components/layout/page-shell';
import { useCliStatus, useSettingsView } from './settings-view/hooks';
import { NAV_ITEMS } from './settings-view/config/navigation';
@@ -156,36 +157,38 @@ export function SettingsView() {
};
return (
-
- {/* Header Section */}
-
+
+
+ {/* Header Section */}
+
- {/* Content Area with Sidebar */}
-
- {/* Side Navigation - No longer scrolls, just switches views */}
-
+ {/* Content Area with Sidebar */}
+
+ {/* Side Navigation - No longer scrolls, just switches views */}
+
- {/* Content Panel - Shows only the active section */}
-
-
{renderActiveSection()}
+ {/* Content Panel - Shows only the active section */}
+
+
{renderActiveSection()}
+
+
+ {/* Keyboard Map Dialog */}
+
+
+ {/* Delete Project Confirmation Dialog */}
+
-
- {/* Keyboard Map Dialog */}
-
-
- {/* Delete Project Confirmation Dialog */}
-
-
+
);
}
diff --git a/apps/ui/src/components/views/settings-view/components/settings-header.tsx b/apps/ui/src/components/views/settings-view/components/settings-header.tsx
index e99159ba..fe5c876f 100644
--- a/apps/ui/src/components/views/settings-view/components/settings-header.tsx
+++ b/apps/ui/src/components/views/settings-view/components/settings-header.tsx
@@ -11,13 +11,7 @@ export function SettingsHeader({
description = 'Configure your API keys and preferences',
}: SettingsHeaderProps) {
return (
-
+
+
+
diff --git a/apps/ui/src/components/views/terminal-view.tsx b/apps/ui/src/components/views/terminal-view.tsx
index db79ce0f..67ed2a43 100644
--- a/apps/ui/src/components/views/terminal-view.tsx
+++ b/apps/ui/src/components/views/terminal-view.tsx
@@ -46,6 +46,7 @@ import {
defaultDropAnimationSideEffects,
} from '@dnd-kit/core';
import { cn } from '@/lib/utils';
+import { PageShell } from '@/components/layout/page-shell';
interface TerminalStatus {
enabled: boolean;
@@ -141,11 +142,11 @@ function TerminalTabButton({
{...dragAttributes}
{...dragListeners}
className={cn(
- 'flex items-center gap-1 px-3 py-1.5 text-sm rounded-t-md border-b-2 cursor-grab active:cursor-grabbing transition-colors select-none',
+ 'flex items-center gap-1 px-3 py-1.5 text-sm rounded-t-md border-b-2 cursor-grab active:cursor-grabbing transition-all select-none',
isActive
- ? 'bg-background border-brand-500 text-foreground'
- : 'bg-muted border-transparent text-muted-foreground hover:text-foreground hover:bg-accent',
- isOver && isDropTarget && isDraggingTab && 'ring-2 ring-blue-500 bg-blue-500/10',
+ ? 'bg-white/10 border-cyan-500 text-cyan-100 shadow-[0_-1px_10px_rgba(6,182,212,0.1)]'
+ : 'bg-white/5 border-transparent text-muted-foreground hover:text-cyan-50 hover:bg-white/10',
+ isOver && isDropTarget && isDraggingTab && 'ring-2 ring-cyan-500/50 bg-cyan-500/10',
isDragging && 'opacity-50'
)}
onClick={onClick}
@@ -192,8 +193,8 @@ function NewTabDropZone({ isDropTarget }: { isDropTarget: boolean }) {
className={cn(
'flex items-center justify-center px-3 py-1.5 rounded-t-md border-2 border-dashed transition-all',
isOver && isDropTarget
- ? 'border-green-500 bg-green-500/10 text-green-500'
- : 'border-transparent text-muted-foreground hover:border-border'
+ ? 'border-cyan-500/50 bg-cyan-500/10 text-cyan-400'
+ : 'border-transparent text-muted-foreground hover:border-white/10'
)}
>
@@ -1414,252 +1415,256 @@ export function TerminalView() {
// Terminal view with tabs
return (
-
-
- {/* Tab bar */}
-
- {/* Tabs */}
-
- {terminalState.tabs.map((tab) => (
-
setActiveTerminalTab(tab.id)}
- onClose={() => killTerminalTab(tab.id)}
- onRename={(newName) => renameTerminalTab(tab.id, newName)}
- isDropTarget={activeDragId !== null || activeDragTabId !== null}
- isDraggingTab={activeDragTabId !== null}
- />
- ))}
-
- {(activeDragId || activeDragTabId) && }
-
- {/* New tab button */}
-
-
-
- {/* Toolbar buttons */}
-
-
-
-
- {/* Global Terminal Settings */}
-
-
-
-
-
-
-
-
Terminal Settings
-
- Configure global terminal appearance
-
-
-
- {/* Default Font Size */}
-
-
-
-
- {terminalState.defaultFontSize}px
-
-
-
setTerminalDefaultFontSize(value)}
- onValueCommit={() => {
- toast.info('Font size changed', {
- description: 'New terminals will use this size',
- });
- }}
- />
-
-
- {/* Font Family */}
-
-
-
-
-
- {/* Line Height */}
-
-
-
-
- {terminalState.lineHeight.toFixed(1)}
-
-
-
setTerminalLineHeight(value)}
- onValueCommit={() => {
- toast.info('Line height changed', {
- description: 'Restart terminal for changes to take effect',
- });
- }}
- />
-
-
- {/* Default Run Script */}
-
-
-
setTerminalDefaultRunScript(e.target.value)}
- placeholder="e.g., claude, npm run dev"
- className="h-8 text-sm"
- />
-
- Command to run when opening new terminals
-
-
-
-
-
-
-
-
- {/* Active tab content */}
-
- {terminalState.maximizedSessionId ? (
- // When a terminal is maximized, render only that terminal
-
{
- const sessionId = terminalState.maximizedSessionId!;
- toggleTerminalMaximized(sessionId);
- killTerminal(sessionId);
- createTerminal();
- }}
- >
- setActiveTerminalSession(terminalState.maximizedSessionId!)}
- onClose={() => killTerminal(terminalState.maximizedSessionId!)}
- onSplitHorizontal={() =>
- createTerminal('horizontal', terminalState.maximizedSessionId!)
- }
- onSplitVertical={() =>
- createTerminal('vertical', terminalState.maximizedSessionId!)
- }
- onNewTab={createTerminalInNewTab}
- onSessionInvalid={() => {
- const sessionId = terminalState.maximizedSessionId!;
- console.log(
- `[Terminal] Maximized session ${sessionId} is invalid, removing from layout`
- );
- killTerminal(sessionId);
- }}
- isDragging={false}
- isDropTarget={false}
- fontSize={findTerminalFontSize(terminalState.maximizedSessionId)}
- onFontSizeChange={(size) =>
- setTerminalPanelFontSize(terminalState.maximizedSessionId!, size)
- }
- isMaximized={true}
- onToggleMaximize={() => toggleTerminalMaximized(terminalState.maximizedSessionId!)}
- />
-
- ) : activeTab?.layout ? (
- renderPanelContent(activeTab.layout)
- ) : (
-
-
This tab is empty
-
-
- )}
-
-
-
- {/* Drag overlay */}
-
+
- {activeDragId ? (
-
-
-
- {dragOverTabId === 'new' ? 'New tab' : dragOverTabId ? 'Move to tab' : 'Terminal'}
-
+
+ {/* Tab bar */}
+
+ {/* Tabs */}
+
+ {terminalState.tabs.map((tab) => (
+
setActiveTerminalTab(tab.id)}
+ onClose={() => killTerminalTab(tab.id)}
+ onRename={(newName) => renameTerminalTab(tab.id, newName)}
+ isDropTarget={activeDragId !== null || activeDragTabId !== null}
+ isDraggingTab={activeDragTabId !== null}
+ />
+ ))}
+
+ {(activeDragId || activeDragTabId) && }
+
+ {/* New tab button */}
+
+
+
+ {/* Toolbar buttons */}
+
+
+
+
+ {/* Global Terminal Settings */}
+
+
+
+
+
+
+
+
Terminal Settings
+
+ Configure global terminal appearance
+
+
+
+ {/* Default Font Size */}
+
+
+
+
+ {terminalState.defaultFontSize}px
+
+
+
setTerminalDefaultFontSize(value)}
+ onValueCommit={() => {
+ toast.info('Font size changed', {
+ description: 'New terminals will use this size',
+ });
+ }}
+ />
+
+
+ {/* Font Family */}
+
+
+
+
+
+ {/* Line Height */}
+
+
+
+
+ {terminalState.lineHeight.toFixed(1)}
+
+
+
setTerminalLineHeight(value)}
+ onValueCommit={() => {
+ toast.info('Line height changed', {
+ description: 'Restart terminal for changes to take effect',
+ });
+ }}
+ />
+
+
+ {/* Default Run Script */}
+
+
+
setTerminalDefaultRunScript(e.target.value)}
+ placeholder="e.g., claude, npm run dev"
+ className="h-8 text-sm"
+ />
+
+ Command to run when opening new terminals
+
+
+
+
+
+
- ) : null}
-
-
+
+ {/* Active tab content */}
+
+ {terminalState.maximizedSessionId ? (
+ // When a terminal is maximized, render only that terminal
+
{
+ const sessionId = terminalState.maximizedSessionId!;
+ toggleTerminalMaximized(sessionId);
+ killTerminal(sessionId);
+ createTerminal();
+ }}
+ >
+ setActiveTerminalSession(terminalState.maximizedSessionId!)}
+ onClose={() => killTerminal(terminalState.maximizedSessionId!)}
+ onSplitHorizontal={() =>
+ createTerminal('horizontal', terminalState.maximizedSessionId!)
+ }
+ onSplitVertical={() =>
+ createTerminal('vertical', terminalState.maximizedSessionId!)
+ }
+ onNewTab={createTerminalInNewTab}
+ onSessionInvalid={() => {
+ const sessionId = terminalState.maximizedSessionId!;
+ console.log(
+ `[Terminal] Maximized session ${sessionId} is invalid, removing from layout`
+ );
+ killTerminal(sessionId);
+ }}
+ isDragging={false}
+ isDropTarget={false}
+ fontSize={findTerminalFontSize(terminalState.maximizedSessionId)}
+ onFontSizeChange={(size) =>
+ setTerminalPanelFontSize(terminalState.maximizedSessionId!, size)
+ }
+ isMaximized={true}
+ onToggleMaximize={() =>
+ toggleTerminalMaximized(terminalState.maximizedSessionId!)
+ }
+ />
+
+ ) : activeTab?.layout ? (
+ renderPanelContent(activeTab.layout)
+ ) : (
+
+
This tab is empty
+
+
+ )}
+
+
+
+ {/* Drag overlay */}
+
+ {activeDragId ? (
+
+
+
+ {dragOverTabId === 'new' ? 'New tab' : dragOverTabId ? 'Move to tab' : 'Terminal'}
+
+
+ ) : null}
+
+
+
);
}
diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts
index 80ad7019..b1e86bae 100644
--- a/apps/ui/src/store/app-store.ts
+++ b/apps/ui/src/store/app-store.ts
@@ -4,14 +4,12 @@ import type { Project, TrashedProject } from '@/lib/electron';
import type {
Feature as BaseFeature,
FeatureImagePath,
+ FeatureTextFilePath, // Import missing type
AgentModel,
PlanningMode,
AIProfile,
} from '@automaker/types';
-// Re-export ThemeMode for convenience
-export type { ThemeMode };
-
export type ViewMode =
| 'welcome'
| 'setup'
@@ -43,7 +41,25 @@ export type ThemeMode =
| 'red'
| 'cream'
| 'sunset'
- | 'gray';
+ | 'gray'
+ | 'forest'
+ | 'ocean'
+ | 'light'
+ | 'cream'
+ | 'solarizedlight'
+ | 'github'
+ | 'paper'
+ | 'rose'
+ | 'mint'
+ | 'lavender'
+ | 'sand'
+ | 'sky'
+ | 'peach'
+ | 'snow'
+ | 'sepia'
+ | 'gruvboxlight'
+ | 'nordlight'
+ | 'blossom';
export type KanbanCardDetailLevel = 'minimal' | 'standard' | 'detailed';
@@ -903,19 +919,19 @@ const initialState: AppState = {
chatHistoryOpen: false,
autoModeByProject: {},
autoModeActivityLog: [],
- maxConcurrency: 3, // Default to 3 concurrent agents
- kanbanCardDetailLevel: 'standard', // Default to standard detail level
- boardViewMode: 'kanban', // Default to kanban view
- defaultSkipTests: true, // Default to manual verification (tests disabled)
- enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
- useWorktrees: false, // Default to disabled (worktree feature is experimental)
+ maxConcurrency: 3,
+ kanbanCardDetailLevel: 'standard',
+ boardViewMode: 'kanban',
+ defaultSkipTests: false,
+ enableDependencyBlocking: true,
+ useWorktrees: false,
currentWorktreeByProject: {},
worktreesByProject: {},
- showProfilesOnly: false, // Default to showing all options (not profiles only)
- keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS, // Default keyboard shortcuts
- muteDoneSound: false, // Default to sound enabled (not muted)
- enhancementModel: 'sonnet', // Default to sonnet for feature enhancement
aiProfiles: DEFAULT_AI_PROFILES,
+ showProfilesOnly: false,
+ keyboardShortcuts: DEFAULT_KEYBOARD_SHORTCUTS,
+ muteDoneSound: false,
+ enhancementModel: 'sonnet',
projectAnalysis: null,
isAnalyzing: false,
boardBackgroundByProject: {},
@@ -928,19 +944,23 @@ const initialState: AppState = {
activeSessionId: null,
maximizedSessionId: null,
defaultFontSize: 14,
- defaultRunScript: '',
+ defaultRunScript: '', // Empty string = standard shell
screenReaderMode: false,
- fontFamily: "Menlo, Monaco, 'Courier New', monospace",
- scrollbackLines: 5000,
- lineHeight: 1.0,
- maxSessions: 100,
+ fontFamily: 'monospace',
+ scrollbackLines: 1000,
+ lineHeight: 1.2,
+ maxSessions: 20,
},
terminalLayoutByProject: {},
specCreatingForProject: null,
- defaultPlanningMode: 'skip' as PlanningMode,
- defaultRequirePlanApproval: false,
+ defaultPlanningMode: 'lite',
+ defaultRequirePlanApproval: true,
defaultAIProfileId: null,
pendingPlanApproval: null,
+ // Claude Usage Defaults
+ claudeRefreshInterval: 60,
+ claudeUsage: null,
+ claudeUsageLastUpdated: null,
};
export const useAppStore = create
()(
diff --git a/apps/ui/src/styles/global.css b/apps/ui/src/styles/global.css
index 351007db..b927916b 100644
--- a/apps/ui/src/styles/global.css
+++ b/apps/ui/src/styles/global.css
@@ -1,3 +1,4 @@
+@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap');
@import 'tailwindcss';
@import 'tw-animate-css';
@@ -43,8 +44,8 @@
--color-foreground: var(--foreground);
--color-foreground-secondary: var(--foreground-secondary);
--color-foreground-muted: var(--foreground-muted);
- --font-sans: var(--font-geist-sans);
- --font-mono: var(--font-geist-mono);
+ --font-sans: var(--font-geist-sans), ui-sans-serif, system-ui, sans-serif;
+ --font-mono: var(--font-geist-mono), ui-monospace, SFMono-Regular, monospace;
/* Sidebar colors */
--color-sidebar-ring: var(--sidebar-ring);
@@ -119,29 +120,94 @@
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
+ --radius-2xl: calc(var(--radius) + 8px);
+
+ /* Animations */
+ --animate-in-fade: in-fade 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+ --animate-in-scale: in-scale 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+ --animate-out-fade: out-fade 0.2s cubic-bezier(0.16, 1, 0.3, 1);
+ --animate-out-scale: out-scale 0.2s cubic-bezier(0.16, 1, 0.3, 1);
+
+ @keyframes in-fade {
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+
+ @keyframes in-scale {
+ from {
+ opacity: 0;
+ transform: scale(0.96);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+ }
+
+ @keyframes out-fade {
+ from {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ to {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ }
+
+ @keyframes out-scale {
+ from {
+ opacity: 1;
+ transform: scale(1);
+ }
+ to {
+ opacity: 0;
+ transform: scale(0.96);
+ }
+ }
}
:root {
- /* Default to light mode */
- --radius: 0.625rem;
- --background: oklch(1 0 0);
- --foreground: oklch(0.145 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.205 0 0);
- --primary-foreground: oklch(0.985 0 0);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.97 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --input: oklch(0.922 0 0);
- --ring: oklch(0.708 0 0);
+ /* Default to light mode (overridden by Prism values below for now as we pivot) */
+ --radius: 0.75rem;
+
+ /* PRISM THEME VALUES (Base) */
+ --bg-deep: #0b101a;
+ --sidebar-bg: rgba(13, 17, 26, 0.7);
+ --glass-bg: rgba(255, 255, 255, 0.03);
+ --glass-border: rgba(255, 255, 255, 0.07);
+
+ /* Accents */
+ --accent-cyan: #22d3ee;
+ --accent-orange: #f59e0b;
+ --accent-green: #10b981;
+ --accent-red: #ef4444;
+
+ /* Mapping to existing variables where possible, or overriding for the visual appearance */
+ --background: oklch(0.08 0.03 260); /* Approximation of #0b101a */
+ --foreground: oklch(0.95 0 0);
+ --card: oklch(1 0 0 / 0.03); /* Glass card default */
+ --card-foreground: oklch(0.95 0 0);
+ --popover: oklch(0.08 0.03 260 / 0.9);
+ --popover-foreground: oklch(0.95 0 0);
+ --primary: oklch(0.78 0.15 200); /* Cyan-ish */
+ --primary-foreground: oklch(0.1 0.03 260);
+ --secondary: oklch(1 0 0 / 0.1);
+ --secondary-foreground: oklch(0.98 0 0);
+ --muted: oklch(1 0 0 / 0.05);
+ --muted-foreground: oklch(0.7 0 0);
+ --accent: oklch(0.78 0.15 200);
+ --accent-foreground: oklch(0.1 0.03 260);
+ --destructive: oklch(0.6 0.2 20);
+ --border: oklch(1 0 0 / 0.1);
+ --input: oklch(1 0 0 / 0.05);
+ --ring: oklch(0.78 0.15 200);
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
@@ -159,7 +225,7 @@
--background-80: oklch(1 0 0 / 0.8);
--foreground-secondary: oklch(0.4 0 0);
--foreground-muted: oklch(0.556 0 0);
- --border-glass: oklch(0.145 0 0 / 0.1);
+ --border-glass: oklch(0.145 0 0 / 0.08);
--brand-400: oklch(0.6 0.22 265);
--brand-500: oklch(0.55 0.25 265);
--brand-600: oklch(0.5 0.28 270);
@@ -191,17 +257,17 @@
--status-in-progress: oklch(0.7 0.15 70);
--status-waiting: oklch(0.65 0.18 50);
- /* Shadow tokens */
+ /* Shadow tokens - more diffused for modern feel */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
- --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.06);
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
- --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08), 0 4px 10px rgba(0, 0, 0, 0.02);
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.08), 0 12px 24px -4px rgba(0, 0, 0, 0.04);
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.08), 0 20px 40px -8px rgba(0, 0, 0, 0.06);
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.08), 0 30px 60px -12px rgba(0, 0, 0, 0.1);
/* Transition tokens */
- --transition-fast: 150ms ease;
- --transition-normal: 200ms ease;
- --transition-slow: 300ms ease-out;
+ --transition-fast: 150ms cubic-bezier(0.16, 1, 0.3, 1);
+ --transition-normal: 250ms cubic-bezier(0.16, 1, 0.3, 1);
+ --transition-slow: 400ms cubic-bezier(0.16, 1, 0.3, 1);
}
/* Apply dark mode immediately based on system preference (before JS runs) */
@@ -214,13 +280,13 @@
/* Text colors following hierarchy */
--foreground: oklch(1 0 0); /* text-white */
- --foreground-secondary: oklch(0.588 0 0); /* text-zinc-400 */
- --foreground-muted: oklch(0.525 0 0); /* text-zinc-500 */
+ --foreground-secondary: oklch(0.65 0 0); /* lighter for better readability */
+ --foreground-muted: oklch(0.525 0 0);
/* Card and popover backgrounds */
- --card: oklch(0.14 0 0);
+ --card: oklch(0.1 0 0 / 0.6); /* Slightly transparent */
--card-foreground: oklch(1 0 0);
- --popover: oklch(0.1 0 0);
+ --popover: oklch(0.08 0 0 / 0.9);
--popover-foreground: oklch(1 0 0);
/* Brand colors - purple/violet theme */
@@ -231,18 +297,18 @@
--brand-600: oklch(0.5 0.28 270);
/* Glass morphism borders and accents */
- --secondary: oklch(1 0 0 / 0.05);
+ --secondary: oklch(1 0 0 / 0.08);
--secondary-foreground: oklch(1 0 0);
--muted: oklch(0.176 0 0);
- --muted-foreground: oklch(0.588 0 0);
- --accent: oklch(1 0 0 / 0.1);
+ --muted-foreground: oklch(0.6 0 0);
+ --accent: oklch(1 0 0 / 0.12);
--accent-foreground: oklch(1 0 0);
/* Borders with transparency for glass effect */
- --border: oklch(0.176 0 0);
- --border-glass: oklch(1 0 0 / 0.1);
+ --border: oklch(1 0 0 / 0.08);
+ --border-glass: oklch(1 0 0 / 0.12);
--destructive: oklch(0.6 0.25 25);
- --input: oklch(0.04 0 0 / 0.8);
+ --input: oklch(1 0 0 / 0.08);
--ring: oklch(0.55 0.25 265);
/* Chart colors with brand theme */
@@ -253,13 +319,13 @@
--chart-5: oklch(0.6 0.25 20);
/* Sidebar with glass morphism */
- --sidebar: oklch(0.04 0 0 / 0.5);
+ --sidebar: oklch(0.04 0 0 / 0.6);
--sidebar-foreground: oklch(1 0 0);
--sidebar-primary: oklch(0.55 0.25 265);
--sidebar-primary-foreground: oklch(1 0 0);
- --sidebar-accent: oklch(1 0 0 / 0.05);
+ --sidebar-accent: oklch(1 0 0 / 0.08);
--sidebar-accent-foreground: oklch(1 0 0);
- --sidebar-border: oklch(1 0 0 / 0.1);
+ --sidebar-border: oklch(1 0 0 / 0.08);
--sidebar-ring: oklch(0.55 0.25 265);
/* Action button colors */
@@ -292,67 +358,21 @@
/* Shadow tokens - darker for dark mode */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3);
- --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
- --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.2);
- --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.5), 0 10px 10px -5px rgba(0, 0, 0, 0.3);
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.4);
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.6), 0 4px 6px -2px rgba(0, 0, 0, 0.4);
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.7), 0 10px 10px -5px rgba(0, 0, 0, 0.5);
}
}
.light {
- /* Explicit light mode - same as root but ensures it overrides any dark defaults */
+ /* Explicit light mode overrides */
--background: oklch(1 0 0); /* White */
- --background-50: oklch(1 0 0 / 0.5);
- --background-80: oklch(1 0 0 / 0.8);
- --foreground: oklch(0.145 0 0); /* Dark text */
- --foreground-secondary: oklch(0.4 0 0);
- --foreground-muted: oklch(0.556 0 0);
- --card: oklch(1 0 0);
- --card-foreground: oklch(0.145 0 0);
- --popover: oklch(1 0 0);
- --popover-foreground: oklch(0.145 0 0);
- --primary: oklch(0.55 0.25 265);
- --primary-foreground: oklch(1 0 0);
- --brand-400: oklch(0.6 0.22 265);
- --brand-500: oklch(0.55 0.25 265);
- --brand-600: oklch(0.5 0.28 270);
- --secondary: oklch(0.97 0 0);
- --secondary-foreground: oklch(0.205 0 0);
- --muted: oklch(0.97 0 0);
- --muted-foreground: oklch(0.556 0 0);
- --accent: oklch(0.95 0 0);
- --accent-foreground: oklch(0.205 0 0);
- --destructive: oklch(0.577 0.245 27.325);
- --border: oklch(0.922 0 0);
- --border-glass: oklch(0.145 0 0 / 0.1);
- --input: oklch(1 0 0);
- --ring: oklch(0.55 0.25 265);
- --chart-1: oklch(0.646 0.222 41.116);
- --chart-2: oklch(0.6 0.118 184.704);
- --chart-3: oklch(0.398 0.07 227.392);
- --chart-4: oklch(0.828 0.189 84.429);
- --chart-5: oklch(0.769 0.188 70.08);
- --sidebar: oklch(0.98 0 0);
- --sidebar-foreground: oklch(0.145 0 0);
- --sidebar-primary: oklch(0.55 0.25 265);
- --sidebar-primary-foreground: oklch(1 0 0);
- --sidebar-accent: oklch(0.95 0 0);
- --sidebar-accent-foreground: oklch(0.205 0 0);
- --sidebar-border: oklch(0.9 0 0);
- --sidebar-ring: oklch(0.55 0.25 265);
-
- /* Action button colors */
- --action-view: oklch(0.55 0.25 265); /* Purple */
- --action-view-hover: oklch(0.5 0.28 270);
- --action-followup: oklch(0.55 0.2 230); /* Blue */
- --action-followup-hover: oklch(0.5 0.22 230);
- --action-commit: oklch(0.55 0.2 140); /* Green */
- --action-commit-hover: oklch(0.5 0.22 140);
- --action-verify: oklch(0.55 0.2 140); /* Green */
- --action-verify-hover: oklch(0.5 0.22 140);
-
- /* Running indicator - Purple */
- --running-indicator: oklch(0.55 0.25 265);
- --running-indicator-text: oklch(0.6 0.22 265);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ /* Re-declare all light vars to ensure priority if needed,
+ but :root typically handles this.
+ Just ensuring important overrides. */
+ --card: oklch(1 0 0 / 0.8);
}
@layer base {
@@ -360,10 +380,13 @@
@apply border-border outline-ring/50;
}
html {
- @apply bg-background;
+ @apply bg-background antialiased;
+ font-feature-settings:
+ 'rlig' 1,
+ 'calt' 1;
}
body {
- @apply bg-background text-foreground;
+ @apply bg-background text-foreground tracking-tight;
background-color: var(--background);
}
@@ -386,6 +409,7 @@
select:disabled,
textarea:disabled {
cursor: not-allowed;
+ opacity: 0.5;
}
}
@@ -407,8 +431,8 @@
.gray
)
::-webkit-scrollbar {
- width: 8px;
- height: 8px;
+ width: 6px;
+ height: 6px;
}
:is(
@@ -428,110 +452,35 @@
.gray
)
::-webkit-scrollbar-track {
- background: var(--muted);
+ background: transparent;
}
:is(.dark, .retro) ::-webkit-scrollbar-thumb {
- background: oklch(0.3 0 0);
- border-radius: 4px;
+ background: oklch(1 0 0 / 0.2);
+ border-radius: 99px;
+ transition: background 0.2s;
}
:is(.dark, .retro) ::-webkit-scrollbar-thumb:hover {
- background: oklch(0.4 0 0);
-}
-
-/* Retro Scrollbar override */
-.retro ::-webkit-scrollbar-thumb {
- background: var(--primary);
- border-radius: 0;
-}
-.retro ::-webkit-scrollbar-track {
- background: var(--background);
-}
-
-/* Red theme scrollbar */
-.red ::-webkit-scrollbar-thumb {
- background: oklch(0.35 0.15 25);
- border-radius: 4px;
-}
-
-.red ::-webkit-scrollbar-thumb:hover {
- background: oklch(0.45 0.18 25);
-}
-
-.red ::-webkit-scrollbar-track {
- background: oklch(0.15 0.05 25);
-}
-
-/* Always visible scrollbar for file diffs and code blocks */
-.scrollbar-visible {
- overflow-y: auto !important;
- scrollbar-width: thin;
- scrollbar-color: var(--muted-foreground) var(--muted);
-}
-
-.scrollbar-visible::-webkit-scrollbar {
- width: 8px;
- height: 8px;
-}
-
-.scrollbar-visible::-webkit-scrollbar-track {
- background: var(--muted);
- border-radius: 4px;
-}
-
-.scrollbar-visible::-webkit-scrollbar-thumb {
- background: var(--muted-foreground);
- border-radius: 4px;
- min-height: 40px;
-}
-
-.scrollbar-visible::-webkit-scrollbar-thumb:hover {
- background: var(--foreground-secondary);
-}
-
-/* Force scrollbar to always be visible (not auto-hide) */
-.scrollbar-visible::-webkit-scrollbar-thumb {
- visibility: visible;
-}
-
-/* Styled scrollbar for code blocks and log entries (horizontal/vertical) */
-.scrollbar-styled {
- scrollbar-width: thin;
- scrollbar-color: var(--muted-foreground) transparent;
-}
-
-.scrollbar-styled::-webkit-scrollbar {
- width: 6px;
- height: 6px;
-}
-
-.scrollbar-styled::-webkit-scrollbar-track {
- background: transparent;
- border-radius: 3px;
-}
-
-.scrollbar-styled::-webkit-scrollbar-thumb {
- background: oklch(0.35 0 0);
- border-radius: 3px;
-}
-
-.scrollbar-styled::-webkit-scrollbar-thumb:hover {
- background: oklch(0.45 0 0);
+ background: oklch(1 0 0 / 0.3);
}
/* Glass morphism utilities */
@layer utilities {
.glass {
- @apply backdrop-blur-md border-white/10;
+ @apply backdrop-blur-xl border border-white/10 bg-white/5;
}
.glass-subtle {
- @apply backdrop-blur-sm border-white/5;
+ @apply backdrop-blur-md border border-white/5 bg-white/3;
}
.glass-strong {
- @apply backdrop-blur-xl border-white/20;
+ @apply backdrop-blur-2xl border border-white/10 bg-black/40;
+ }
+
+ .glass-panel {
+ @apply backdrop-blur-3xl border border-border-glass bg-background/60 shadow-xl;
}
/* Text color hierarchy utilities */
@@ -540,55 +489,73 @@
}
.text-secondary {
- color: oklch(0.588 0 0); /* zinc-400 equivalent */
+ color: var(--foreground-secondary);
}
.text-muted {
- color: oklch(0.525 0 0); /* zinc-500 equivalent */
+ color: var(--foreground-muted);
}
/* Brand gradient utilities */
.gradient-brand {
- background: linear-gradient(135deg, oklch(0.55 0.25 265), oklch(0.5 0.28 270));
+ background: linear-gradient(135deg, var(--brand-500), var(--brand-600));
}
.gradient-brand-subtle {
- background: linear-gradient(135deg, oklch(0.55 0.25 265 / 0.1), oklch(0.5 0.28 270 / 0.1));
+ background: linear-gradient(
+ 135deg,
+ color-mix(in oklch, var(--brand-500), transparent 90%),
+ color-mix(in oklch, var(--brand-600), transparent 90%)
+ );
}
- /* Glass morphism background utilities */
- .bg-glass {
- background: var(--card);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- }
-
- .bg-glass-80 {
- background: var(--popover);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
+ .gradient-glass-border {
+ background: linear-gradient(
+ to bottom right,
+ rgba(255, 255, 255, 0.2),
+ rgba(255, 255, 255, 0.05)
+ );
}
/* Hover state utilities */
.hover-glass {
- transition: background-color 0.2s ease;
+ transition:
+ background-color 0.2s ease,
+ transform 0.2s ease;
}
.hover-glass:hover {
- background: oklch(1 0 0 / 0.05);
+ background: color-mix(in oklch, var(--foreground), transparent 95%);
}
- .hover-glass-strong {
- transition: background-color 0.2s ease;
+ .hover-lift {
+ transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
}
- .hover-glass-strong:hover {
- background: oklch(1 0 0 / 0.1);
+ .hover-lift:hover {
+ transform: translateY(-2px);
+ }
+
+ .hover-glow {
+ transition: box-shadow 0.3s ease;
+ }
+
+ .hover-glow:hover {
+ box-shadow: 0 0 20px -5px var(--primary);
}
/* Content area background */
.content-bg {
- background: var(--background);
+ background: transparent;
+ }
+
+ /* Utils for hiding scrollbar but allowing scroll */
+ .no-scrollbar::-webkit-scrollbar {
+ display: none;
+ }
+ .no-scrollbar {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
}
/* Action button utilities */
@@ -644,7 +611,6 @@
}
/* Animated border for in-progress cards */
- /* Using a subtle pulse animation instead of continuous gradient rotation for GPU efficiency */
@keyframes border-pulse {
0%,
100% {
@@ -657,8 +623,8 @@
.animated-border-wrapper {
position: relative;
- border-radius: 0.75rem;
- padding: 2px;
+ border-radius: var(--radius);
+ padding: 1px;
background: linear-gradient(
135deg,
var(--running-indicator),
@@ -677,7 +643,7 @@
}
.animated-border-wrapper > * {
- border-radius: calc(0.75rem - 2px);
+ border-radius: calc(var(--radius) - 1px);
}
}
@@ -685,6 +651,7 @@
.retro * {
border-radius: 0 !important;
+ --radius: 0;
}
/* Animated Outline Button Styles */
@@ -693,12 +660,6 @@
background: conic-gradient(from 90deg at 50% 50%, #a855f7 0%, #3b82f6 50%, #a855f7 100%);
}
-/* Light mode - deeper purple to blue gradient for better visibility */
-
-/* Dark mode - purple to blue gradient */
-
-/* Retro mode - unique scanline + neon effect */
-
@keyframes retro-glow {
from {
filter: brightness(1) drop-shadow(0 0 2px #00ff41);
@@ -708,24 +669,6 @@
}
}
-/* Dracula animated-outline - purple/pink */
-
-/* Nord animated-outline - frost blue */
-
-/* Monokai animated-outline - pink/yellow */
-
-/* Tokyo Night animated-outline - blue/magenta */
-
-/* Solarized animated-outline - blue/cyan */
-
-/* Gruvbox animated-outline - yellow/orange */
-
-/* Catppuccin animated-outline - mauve/pink */
-
-/* One Dark animated-outline - blue/magenta */
-
-/* Synthwave animated-outline - hot pink/cyan with glow */
-
@keyframes synthwave-glow {
from {
filter: brightness(1) drop-shadow(0 0 3px #f97e72);
@@ -735,26 +678,6 @@
}
}
-/* Slider Theme Styles */
-
-/* Dracula slider */
-
-/* Nord slider */
-
-/* Monokai slider */
-
-/* Tokyo Night slider */
-
-/* Solarized slider */
-
-/* Gruvbox slider */
-
-/* Catppuccin slider */
-
-/* One Dark slider */
-
-/* Synthwave slider */
-
/* Line clamp utilities for text overflow prevention */
.line-clamp-2 {
display: -webkit-box;
@@ -802,30 +725,6 @@
Theme-aware colors for XML editor
======================================== */
-/* Light theme - professional and readable */
-
-/* Dark theme - high contrast */
-
-/* Retro theme - neon green on black */
-
-/* Dracula theme */
-
-/* Nord theme */
-
-/* Monokai theme */
-
-/* Tokyo Night theme */
-
-/* Solarized theme */
-
-/* Gruvbox theme */
-
-/* Catppuccin theme */
-
-/* One Dark theme */
-
-/* Synthwave theme */
-
/* XML Editor container styles */
.xml-editor {
position: relative;
@@ -1058,3 +957,88 @@
animation: none;
}
}
+
+/* Prism 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);
+}
+
+/* Prism 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: var(--font-mono);
+ font-weight: 600;
+}
+
+/* Prism 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);
+}
+
+/* Prism Column Styles */
+.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);
+}
+
+/* Prism Font Overrides - Ensure these take precedence */
+:root {
+ --font-sans:
+ 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+ 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
+ --font-mono:
+ 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
+ 'Courier New', monospace;
+
+ /* Ensure backgrounds are deep prism color */
+ --background: oklch(0.08 0.03 260);
+ --foreground: oklch(0.95 0 0);
+}
+
+body {
+ background-color: var(--bg-deep);
+ font-family: var(--font-sans);
+}
+
+.mono {
+ font-family: var(--font-mono) !important;
+}
diff --git a/index (25).html b/index (25).html
new file mode 100644
index 00000000..222b4cee
--- /dev/null
+++ b/index (25).html
@@ -0,0 +1,723 @@
+
+
+
+
+
+ automaker. | Kanban Board
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Kanban Board
+
+ test case 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 56c88d73..f35b6be1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -106,6 +106,7 @@
"cmdk": "^1.1.1",
"dagre": "^0.8.5",
"dotenv": "^17.2.3",
+ "framer-motion": "^12.23.26",
"geist": "^1.5.1",
"lucide-react": "^0.562.0",
"react": "19.2.3",
@@ -113,6 +114,7 @@
"react-markdown": "^10.1.0",
"react-resizable-panels": "^3.0.6",
"rehype-raw": "^7.0.0",
+ "rehype-sanitize": "^6.0.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"zustand": "^5.0.9"
@@ -1212,7 +1214,7 @@
},
"node_modules/@electron/node-gyp": {
"version": "10.2.0-electron.1",
- "resolved": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
+ "resolved": "git+ssh://git@github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2",
"integrity": "sha512-4MSBTT8y07YUDqf69/vSh80Hh791epYqGtWHO3zSKhYFwQg+gx9wi1PqbqP6YqC4WMsNxZ5l9oDmnWdK5pfCKQ==",
"dev": true,
"license": "MIT",
@@ -9712,6 +9714,33 @@
"node": ">= 0.6"
}
},
+ "node_modules/framer-motion": {
+ "version": "12.23.26",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
+ "integrity": "sha512-cPcIhgR42xBn1Uj+PzOyheMtZ73H927+uWPDVhUMqxy8UHt6Okavb6xIz9J/phFUHUj0OncR6UvMfJTXoc/LKA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.23",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/fresh": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
@@ -12744,6 +12773,21 @@
"node": ">= 0.8"
}
},
+ "node_modules/motion-dom": {
+ "version": "12.23.23",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
+ "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",