Merge remote-tracking branch 'origin/main' into feat/extend-models-support

# Conflicts:
#	.automaker/feature_list.json
#	app/src/components/views/board-view.tsx
#	app/src/components/views/kanban-column.tsx
#	app/src/components/views/settings-view.tsx
This commit is contained in:
Kacper
2025-12-10 03:03:07 +01:00
18 changed files with 1197 additions and 120 deletions

View File

@@ -64,6 +64,20 @@
--color-brand-500: var(--brand-500);
--color-brand-600: var(--brand-600);
/* Action button colors */
--color-action-view: var(--action-view);
--color-action-view-hover: var(--action-view-hover);
--color-action-followup: var(--action-followup);
--color-action-followup-hover: var(--action-followup-hover);
--color-action-commit: var(--action-commit);
--color-action-commit-hover: var(--action-commit-hover);
--color-action-verify: var(--action-verify);
--color-action-verify-hover: var(--action-verify-hover);
/* Running task indicator colors */
--color-running-indicator: var(--running-indicator);
--color-running-indicator-text: var(--running-indicator-text);
/* Border radius */
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
@@ -113,6 +127,20 @@
--brand-400: oklch(0.6 0.22 265);
--brand-500: oklch(0.55 0.25 265);
--brand-600: oklch(0.5 0.28 270);
/* Action button colors - Light mode */
--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);
}
.light {
@@ -156,6 +184,20 @@
--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);
}
.dark {
@@ -213,6 +255,20 @@
--sidebar-accent-foreground: oklch(1 0 0);
--sidebar-border: oklch(1 0 0 / 0.1); /* white/10 for glass borders */
--sidebar-ring: oklch(0.55 0.25 265);
/* Action button colors */
--action-view: oklch(0.6 0.25 265); /* Purple */
--action-view-hover: oklch(0.55 0.27 270);
--action-followup: oklch(0.6 0.2 230); /* Blue */
--action-followup-hover: oklch(0.55 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.6 0.25 265);
--running-indicator-text: oklch(0.65 0.22 265);
}
.retro {
@@ -278,6 +334,20 @@
/* Fonts */
--font-sans: var(--font-geist-mono); /* Force Mono everywhere */
/* Action button colors - All green neon for retro theme */
--action-view: oklch(0.85 0.25 145); /* Neon Green */
--action-view-hover: oklch(0.9 0.25 145);
--action-followup: oklch(0.85 0.25 145); /* Neon Green */
--action-followup-hover: oklch(0.9 0.25 145);
--action-commit: oklch(0.85 0.25 145); /* Neon Green */
--action-commit-hover: oklch(0.9 0.25 145);
--action-verify: oklch(0.85 0.25 145); /* Neon Green */
--action-verify-hover: oklch(0.9 0.25 145);
/* Running indicator - Neon Green for retro */
--running-indicator: oklch(0.85 0.25 145);
--running-indicator-text: oklch(0.85 0.25 145);
}
/* ========================================
@@ -336,6 +406,20 @@
--sidebar-accent-foreground: oklch(0.95 0.01 280);
--sidebar-border: oklch(0.35 0.05 280);
--sidebar-ring: oklch(0.7 0.2 320);
/* Action button colors - Dracula purple/pink theme */
--action-view: oklch(0.7 0.2 320); /* Purple */
--action-view-hover: oklch(0.65 0.22 320);
--action-followup: oklch(0.65 0.25 350); /* Pink */
--action-followup-hover: oklch(0.6 0.27 350);
--action-commit: oklch(0.75 0.2 130); /* Green */
--action-commit-hover: oklch(0.7 0.22 130);
--action-verify: oklch(0.75 0.2 130); /* Green */
--action-verify-hover: oklch(0.7 0.22 130);
/* Running indicator - Purple */
--running-indicator: oklch(0.7 0.2 320);
--running-indicator-text: oklch(0.75 0.18 320);
}
/* ========================================
@@ -394,6 +478,20 @@
--sidebar-accent-foreground: oklch(0.9 0.01 230);
--sidebar-border: oklch(0.35 0.03 240);
--sidebar-ring: oklch(0.7 0.12 220);
/* Action button colors - Nord frost blue theme */
--action-view: oklch(0.7 0.12 220); /* Frost blue */
--action-view-hover: oklch(0.65 0.14 220);
--action-followup: oklch(0.65 0.14 220); /* Darker frost */
--action-followup-hover: oklch(0.6 0.16 220);
--action-commit: oklch(0.7 0.15 140); /* Green */
--action-commit-hover: oklch(0.65 0.17 140);
--action-verify: oklch(0.7 0.15 140); /* Green */
--action-verify-hover: oklch(0.65 0.17 140);
/* Running indicator - Frost blue */
--running-indicator: oklch(0.7 0.12 220);
--running-indicator-text: oklch(0.75 0.1 220);
}
/* ========================================
@@ -452,6 +550,20 @@
--sidebar-accent-foreground: oklch(0.95 0.02 100);
--sidebar-border: oklch(0.35 0.03 90);
--sidebar-ring: oklch(0.8 0.2 350);
/* Action button colors - Monokai pink/yellow theme */
--action-view: oklch(0.8 0.2 350); /* Pink */
--action-view-hover: oklch(0.75 0.22 350);
--action-followup: oklch(0.75 0.2 200); /* Cyan */
--action-followup-hover: oklch(0.7 0.22 200);
--action-commit: oklch(0.8 0.2 140); /* Green */
--action-commit-hover: oklch(0.75 0.22 140);
--action-verify: oklch(0.8 0.2 140); /* Green */
--action-verify-hover: oklch(0.75 0.22 140);
/* Running indicator - Pink */
--running-indicator: oklch(0.8 0.2 350);
--running-indicator-text: oklch(0.85 0.18 350);
}
/* ========================================
@@ -510,6 +622,20 @@
--sidebar-accent-foreground: oklch(0.85 0.02 250);
--sidebar-border: oklch(0.32 0.04 260);
--sidebar-ring: oklch(0.7 0.18 280);
/* Action button colors - Tokyo Night blue/magenta theme */
--action-view: oklch(0.7 0.18 280); /* Blue */
--action-view-hover: oklch(0.65 0.2 280);
--action-followup: oklch(0.75 0.18 200); /* Cyan */
--action-followup-hover: oklch(0.7 0.2 200);
--action-commit: oklch(0.75 0.18 140); /* Green */
--action-commit-hover: oklch(0.7 0.2 140);
--action-verify: oklch(0.75 0.18 140); /* Green */
--action-verify-hover: oklch(0.7 0.2 140);
/* Running indicator - Blue */
--running-indicator: oklch(0.7 0.18 280);
--running-indicator-text: oklch(0.75 0.16 280);
}
/* ========================================
@@ -568,6 +694,20 @@
--sidebar-accent-foreground: oklch(0.75 0.02 90);
--sidebar-border: oklch(0.35 0.03 230);
--sidebar-ring: oklch(0.65 0.15 220);
/* Action button colors - Solarized blue/cyan theme */
--action-view: oklch(0.65 0.15 220); /* Blue */
--action-view-hover: oklch(0.6 0.17 220);
--action-followup: oklch(0.6 0.18 180); /* Cyan */
--action-followup-hover: oklch(0.55 0.2 180);
--action-commit: oklch(0.65 0.2 140); /* Green */
--action-commit-hover: oklch(0.6 0.22 140);
--action-verify: oklch(0.65 0.2 140); /* Green */
--action-verify-hover: oklch(0.6 0.22 140);
/* Running indicator - Blue */
--running-indicator: oklch(0.65 0.15 220);
--running-indicator-text: oklch(0.7 0.13 220);
}
/* ========================================
@@ -626,6 +766,20 @@
--sidebar-accent-foreground: oklch(0.85 0.05 85);
--sidebar-border: oklch(0.35 0.03 60);
--sidebar-ring: oklch(0.7 0.18 55);
/* Action button colors - Gruvbox yellow/orange theme */
--action-view: oklch(0.7 0.18 55); /* Yellow */
--action-view-hover: oklch(0.65 0.2 55);
--action-followup: oklch(0.7 0.15 200); /* Aqua */
--action-followup-hover: oklch(0.65 0.17 200);
--action-commit: oklch(0.65 0.2 140); /* Green */
--action-commit-hover: oklch(0.6 0.22 140);
--action-verify: oklch(0.65 0.2 140); /* Green */
--action-verify-hover: oklch(0.6 0.22 140);
/* Running indicator - Yellow */
--running-indicator: oklch(0.7 0.18 55);
--running-indicator-text: oklch(0.75 0.16 55);
}
/* ========================================
@@ -684,6 +838,20 @@
--sidebar-accent-foreground: oklch(0.9 0.01 280);
--sidebar-border: oklch(0.35 0.03 260);
--sidebar-ring: oklch(0.75 0.15 280);
/* Action button colors - Catppuccin mauve/pink theme */
--action-view: oklch(0.75 0.15 280); /* Mauve */
--action-view-hover: oklch(0.7 0.17 280);
--action-followup: oklch(0.75 0.15 220); /* Blue */
--action-followup-hover: oklch(0.7 0.17 220);
--action-commit: oklch(0.8 0.15 160); /* Green */
--action-commit-hover: oklch(0.75 0.17 160);
--action-verify: oklch(0.8 0.15 160); /* Green */
--action-verify-hover: oklch(0.75 0.17 160);
/* Running indicator - Mauve */
--running-indicator: oklch(0.75 0.15 280);
--running-indicator-text: oklch(0.8 0.13 280);
}
/* ========================================
@@ -742,6 +910,20 @@
--sidebar-accent-foreground: oklch(0.85 0.02 240);
--sidebar-border: oklch(0.35 0.02 250);
--sidebar-ring: oklch(0.7 0.18 230);
/* Action button colors - One Dark blue/magenta theme */
--action-view: oklch(0.7 0.18 230); /* Blue */
--action-view-hover: oklch(0.65 0.2 230);
--action-followup: oklch(0.75 0.15 320); /* Magenta */
--action-followup-hover: oklch(0.7 0.17 320);
--action-commit: oklch(0.75 0.18 150); /* Green */
--action-commit-hover: oklch(0.7 0.2 150);
--action-verify: oklch(0.75 0.18 150); /* Green */
--action-verify-hover: oklch(0.7 0.2 150);
/* Running indicator - Blue */
--running-indicator: oklch(0.7 0.18 230);
--running-indicator-text: oklch(0.75 0.16 230);
}
/* ========================================
@@ -800,6 +982,20 @@
--sidebar-accent-foreground: oklch(0.95 0.02 320);
--sidebar-border: oklch(0.4 0.1 290);
--sidebar-ring: oklch(0.7 0.28 350);
/* Action button colors - Synthwave hot pink/cyan theme */
--action-view: oklch(0.7 0.28 350); /* Hot pink */
--action-view-hover: oklch(0.65 0.3 350);
--action-followup: oklch(0.8 0.25 200); /* Cyan */
--action-followup-hover: oklch(0.75 0.27 200);
--action-commit: oklch(0.85 0.2 60); /* Yellow */
--action-commit-hover: oklch(0.8 0.22 60);
--action-verify: oklch(0.85 0.2 60); /* Yellow */
--action-verify-hover: oklch(0.8 0.22 60);
/* Running indicator - Hot pink */
--running-indicator: oklch(0.7 0.28 350);
--running-indicator-text: oklch(0.75 0.26 350);
}
@layer base {
@@ -921,14 +1117,65 @@
.content-bg {
background: var(--background);
}
.light .content-bg {
background: linear-gradient(135deg, oklch(0.99 0 0), oklch(0.98 0 0), oklch(0.99 0 0));
}
.dark .content-bg {
background: linear-gradient(135deg, oklch(0.04 0 0), oklch(0.08 0 0), oklch(0.04 0 0));
}
/* Action button utilities */
.bg-action-view {
background-color: var(--action-view);
}
.hover\:bg-action-view-hover:hover {
background-color: var(--action-view-hover);
}
.bg-action-followup {
background-color: var(--action-followup);
}
.hover\:bg-action-followup-hover:hover {
background-color: var(--action-followup-hover);
}
.bg-action-commit {
background-color: var(--action-commit);
}
.hover\:bg-action-commit-hover:hover {
background-color: var(--action-commit-hover);
}
.bg-action-verify {
background-color: var(--action-verify);
}
.hover\:bg-action-verify-hover:hover {
background-color: var(--action-verify-hover);
}
/* Running task indicator utilities */
.border-running-indicator {
border-color: var(--running-indicator);
}
.bg-running-indicator\/20 {
background-color: color-mix(in oklch, var(--running-indicator), transparent 80%);
}
.shadow-running-indicator\/50 {
box-shadow: 0 10px 15px -3px color-mix(in oklch, var(--running-indicator), transparent 50%),
0 4px 6px -4px color-mix(in oklch, var(--running-indicator), transparent 50%);
}
.text-running-indicator {
color: var(--running-indicator-text);
}
}
/* Retro Overrides for Utilities */

View File

@@ -178,7 +178,7 @@ export function Sidebar() {
[projects, setCurrentProject]
);
// Handle number key presses when project picker is open
// Handle keyboard events when project picker is open
useEffect(() => {
if (!isProjectPickerOpen) return;
@@ -189,6 +189,10 @@ export function Sidebar() {
selectProjectByNumber(num);
} else if (event.key === "Escape") {
setIsProjectPickerOpen(false);
} else if (event.key.toLowerCase() === "p") {
// Toggle off when P is pressed while dropdown is open
event.preventDefault();
setIsProjectPickerOpen(false);
}
};
@@ -218,8 +222,8 @@ export function Sidebar() {
if (projects.length > 0) {
shortcuts.push({
key: ACTION_SHORTCUTS.projectPicker,
action: () => setIsProjectPickerOpen(true),
description: "Open project picker",
action: () => setIsProjectPickerOpen((prev) => !prev),
description: "Toggle project picker",
});
}

View File

@@ -174,6 +174,7 @@ export function BoardView() {
runningAutoTasks,
maxConcurrency,
setMaxConcurrency,
defaultSkipTests,
} = useAppStore();
const [activeFeature, setActiveFeature] = useState<Feature | null>(null);
const [editingFeature, setEditingFeature] = useState<Feature | null>(null);
@@ -411,6 +412,16 @@ export function BoardView() {
[currentProject, persistedCategories]
);
// Sync skipTests default when dialog opens
useEffect(() => {
if (showAddDialog) {
setNewFeature((prev) => ({
...prev,
skipTests: defaultSkipTests,
}));
}
}, [showAddDialog, defaultSkipTests]);
// Auto-show activity log when auto mode starts
useEffect(() => {
if (autoMode.isRunning && !showActivityLog) {
@@ -690,7 +701,7 @@ export function BoardView() {
steps: [""],
images: [],
imagePaths: [],
skipTests: false,
skipTests: defaultSkipTests,
model: "opus",
thinkingLevel: "none",
});
@@ -1378,7 +1389,6 @@ export function BoardView() {
title={column.title}
color={column.color}
count={columnFeatures.length}
isDoubleWidth={column.id === "in_progress"}
headerAction={
column.id === "verified" && columnFeatures.length > 0 ? (
<Button
@@ -1497,18 +1507,6 @@ export function BoardView() {
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4 overflow-y-auto flex-1 min-h-0">
<div className="space-y-2">
<Label htmlFor="category">Category</Label>
<CategoryAutocomplete
value={newFeature.category}
onChange={(value) =>
setNewFeature({ ...newFeature, category: value })
}
suggestions={categorySuggestions}
placeholder="e.g., Core, UI, API"
data-testid="feature-category-input"
/>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description</Label>
<DescriptionImageDropZone
@@ -1524,34 +1522,16 @@ export function BoardView() {
/>
</div>
<div className="space-y-2">
<Label>Steps</Label>
{newFeature.steps.map((step, index) => (
<Input
key={index}
placeholder={`Step ${index + 1}`}
value={step}
onChange={(e) => {
const steps = [...newFeature.steps];
steps[index] = e.target.value;
setNewFeature({ ...newFeature, steps });
}}
data-testid={`feature-step-${index}-input`}
/>
))}
<Button
variant="outline"
size="sm"
onClick={() =>
setNewFeature({
...newFeature,
steps: [...newFeature.steps, ""],
})
<Label htmlFor="category">Category (optional)</Label>
<CategoryAutocomplete
value={newFeature.category}
onChange={(value) =>
setNewFeature({ ...newFeature, category: value })
}
data-testid="add-step-button"
>
<Plus className="w-4 h-4 mr-2" />
Add Step
</Button>
suggestions={categorySuggestions}
placeholder="e.g., Core, UI, API"
data-testid="feature-category-input"
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
@@ -1675,6 +1655,40 @@ export function BoardView() {
</p>
</div>
)}
{/* Verification Steps - Only shown when skipTests is enabled */}
{newFeature.skipTests && (
<div className="space-y-2">
<Label>Verification Steps</Label>
{newFeature.steps.map((step, index) => (
<Input
key={index}
placeholder={`Verification step ${index + 1}`}
value={step}
onChange={(e) => {
const steps = [...newFeature.steps];
steps[index] = e.target.value;
setNewFeature({ ...newFeature, steps });
}}
data-testid={`feature-step-${index}-input`}
/>
))}
<Button
variant="outline"
size="sm"
onClick={() =>
setNewFeature({
...newFeature,
steps: [...newFeature.steps, ""],
})
}
data-testid="add-step-button"
>
<Plus className="w-4 h-4 mr-2" />
Add Verification Step
</Button>
</div>
)}
</div>
<DialogFooter>
<Button variant="ghost" onClick={() => setShowAddDialog(false)}>
@@ -1709,21 +1723,6 @@ export function BoardView() {
</DialogHeader>
{editingFeature && (
<div className="space-y-4 py-4 overflow-y-auto flex-1 min-h-0">
<div className="space-y-2">
<Label htmlFor="edit-category">Category</Label>
<CategoryAutocomplete
value={editingFeature.category}
onChange={(value) =>
setEditingFeature({
...editingFeature,
category: value,
})
}
suggestions={categorySuggestions}
placeholder="e.g., Core, UI, API"
data-testid="edit-feature-category"
/>
</div>
<div className="space-y-2">
<Label htmlFor="edit-description">Description</Label>
<Textarea
@@ -1740,32 +1739,19 @@ export function BoardView() {
/>
</div>
<div className="space-y-2">
<Label>Steps</Label>
{editingFeature.steps.map((step, index) => (
<Input
key={index}
value={step}
onChange={(e) => {
const steps = [...editingFeature.steps];
steps[index] = e.target.value;
setEditingFeature({ ...editingFeature, steps });
}}
data-testid={`edit-feature-step-${index}`}
/>
))}
<Button
variant="outline"
size="sm"
onClick={() =>
<Label htmlFor="edit-category">Category (optional)</Label>
<CategoryAutocomplete
value={editingFeature.category}
onChange={(value) =>
setEditingFeature({
...editingFeature,
steps: [...editingFeature.steps, ""],
category: value,
})
}
>
<Plus className="w-4 h-4 mr-2" />
Add Step
</Button>
suggestions={categorySuggestions}
placeholder="e.g., Core, UI, API"
data-testid="edit-feature-category"
/>
</div>
<div className="flex items-center space-x-2">
<Checkbox
@@ -1897,6 +1883,39 @@ export function BoardView() {
</p>
</div>
)}
{/* Verification Steps - Only shown when skipTests is enabled */}
{editingFeature.skipTests && (
<div className="space-y-2">
<Label>Verification Steps</Label>
{editingFeature.steps.map((step, index) => (
<Input
key={index}
value={step}
placeholder={`Verification step ${index + 1}`}
onChange={(e) => {
const steps = [...editingFeature.steps];
steps[index] = e.target.value;
setEditingFeature({ ...editingFeature, steps });
}}
data-testid={`edit-feature-step-${index}`}
/>
))}
<Button
variant="outline"
size="sm"
onClick={() =>
setEditingFeature({
...editingFeature,
steps: [...editingFeature.steps, ""],
})
}
>
<Plus className="w-4 h-4 mr-2" />
Add Verification Step
</Button>
</div>
)}
</div>
)}
<DialogFooter>

View File

@@ -194,7 +194,7 @@ export function KanbanCard({
"cursor-grab active:cursor-grabbing transition-all backdrop-blur-sm border-border relative kanban-card-content",
isDragging && "opacity-50 scale-105 shadow-lg",
isCurrentAutoTask &&
"border-purple-500 border-2 shadow-purple-500/50 shadow-lg animate-pulse"
"border-running-indicator border-2 shadow-running-indicator/50 shadow-lg animate-pulse"
)}
data-testid={`kanban-card-${feature.id}`}
{...attributes}
@@ -225,15 +225,15 @@ export function KanbanCard({
)}
<CardHeader className="p-3 pb-2">
{isCurrentAutoTask && (
<div className="absolute top-2 right-2 flex items-center gap-2 bg-purple-500/20 border border-purple-500 rounded px-2 py-0.5">
<Loader2 className="w-4 h-4 text-purple-400 animate-spin" />
<span className="text-xs text-purple-400 font-medium">
<div className="absolute top-2 right-2 flex items-center gap-2 bg-running-indicator/20 border border-running-indicator rounded px-2 py-0.5">
<Loader2 className="w-4 h-4 text-running-indicator animate-spin" />
<span className="text-xs text-running-indicator font-medium">
Running...
</span>
{feature.startedAt && (
<CountUpTimer
startedAt={feature.startedAt}
className="text-purple-400"
className="text-running-indicator"
/>
)}
</div>
@@ -491,7 +491,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-purple-600 hover:bg-purple-700"
className="flex-1 h-7 text-xs bg-action-view hover:bg-action-view-hover"
onClick={(e) => {
e.stopPropagation();
onViewOutput();
@@ -526,7 +526,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-green-600 hover:bg-green-700"
className="flex-1 h-7 text-xs bg-action-verify hover:bg-action-verify-hover"
onClick={(e) => {
e.stopPropagation();
onManualVerify();
@@ -540,7 +540,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-blue-600 hover:bg-blue-700"
className="flex-1 h-7 text-xs bg-action-verify hover:bg-action-verify-hover"
onClick={(e) => {
e.stopPropagation();
onResume();
@@ -554,7 +554,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-green-600 hover:bg-green-700"
className="flex-1 h-7 text-xs bg-action-verify hover:bg-action-verify-hover"
onClick={(e) => {
e.stopPropagation();
onVerify();
@@ -640,7 +640,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-blue-600 hover:bg-blue-700"
className="flex-1 h-7 text-xs bg-action-followup hover:bg-action-followup-hover"
onClick={(e) => {
e.stopPropagation();
onFollowUp();
@@ -656,7 +656,7 @@ export function KanbanCard({
<Button
variant="default"
size="sm"
className="flex-1 h-7 text-xs bg-green-600 hover:bg-green-700"
className="flex-1 h-7 text-xs bg-action-commit hover:bg-action-commit-hover"
onClick={(e) => {
e.stopPropagation();
onCommit();

View File

@@ -10,7 +10,6 @@ interface KanbanColumnProps {
color: string;
count: number;
children: ReactNode;
isDoubleWidth?: boolean;
headerAction?: ReactNode;
}
@@ -20,7 +19,6 @@ export function KanbanColumn({
color,
count,
children,
isDoubleWidth = false,
headerAction,
}: KanbanColumnProps) {
const { setNodeRef, isOver } = useDroppable({ id });
@@ -29,8 +27,7 @@ export function KanbanColumn({
<div
ref={setNodeRef}
className={cn(
"flex flex-col h-full rounded-lg bg-card backdrop-blur-sm border border-border transition-colors",
isDoubleWidth ? "w-[37rem]" : "w-72",
"flex flex-col h-full rounded-lg bg-card backdrop-blur-sm border border-border transition-colors w-72",
isOver && "bg-accent"
)}
data-testid={`kanban-column-${id}`}
@@ -46,14 +43,7 @@ export function KanbanColumn({
</div>
{/* Column Content */}
<div
className={cn(
"flex-1 overflow-y-auto p-2",
isDoubleWidth
? "columns-2 gap-3 [&>*]:break-inside-avoid [&>*]:mb-3 [&>*]:overflow-hidden kanban-columns-layout"
: "space-y-2"
)}
>
<div className="flex-1 overflow-y-auto p-2 space-y-2">
{children}
</div>
</div>

View File

@@ -31,8 +31,10 @@ import {
Minimize2,
Square,
Maximize2,
FlaskConical,
} from "lucide-react";
import { getElectronAPI } from "@/lib/electron";
import { Checkbox } from "@/components/ui/checkbox";
export function SettingsView() {
const {
@@ -43,6 +45,8 @@ export function SettingsView() {
setTheme,
kanbanCardDetailLevel,
setKanbanCardDetailLevel,
defaultSkipTests,
setDefaultSkipTests,
} = useAppStore();
const [anthropicKey, setAnthropicKey] = useState(apiKeys.anthropic);
const [googleKey, setGoogleKey] = useState(apiKeys.google);
@@ -956,7 +960,7 @@ export function SettingsView() {
</div>
<div className="p-6 space-y-4">
<div className="space-y-3">
<Label className="text-foreground-secondary">Detail Level</Label>
<Label className="text-foreground">Detail Level</Label>
<div className="grid grid-cols-3 gap-3">
<button
onClick={() => setKanbanCardDetailLevel("minimal")}
@@ -1016,6 +1020,49 @@ export function SettingsView() {
</div>
</div>
{/* Feature Defaults Section */}
<div className="rounded-xl border border-border bg-card backdrop-blur-md overflow-hidden">
<div className="p-6 border-b border-border">
<div className="flex items-center gap-2 mb-2">
<FlaskConical className="w-5 h-5 text-brand-500" />
<h2 className="text-lg font-semibold text-foreground">
Feature Defaults
</h2>
</div>
<p className="text-sm text-muted-foreground">
Configure default settings for new features.
</p>
</div>
<div className="p-6 space-y-4">
<div className="space-y-3">
<div className="flex items-start space-x-3">
<Checkbox
id="default-skip-tests"
checked={defaultSkipTests}
onCheckedChange={(checked) =>
setDefaultSkipTests(checked === true)
}
className="mt-0.5"
data-testid="default-skip-tests-checkbox"
/>
<div className="space-y-1">
<Label
htmlFor="default-skip-tests"
className="text-foreground cursor-pointer font-medium"
>
Skip automated testing by default
</Label>
<p className="text-xs text-muted-foreground">
When enabled, new features will default to manual
verification instead of TDD (test-driven development).
You can still override this for individual features.
</p>
</div>
</div>
</div>
</div>
</div>
{/* Save Button */}
<div className="flex items-center gap-4">
<Button

View File

@@ -149,6 +149,9 @@ export interface AppState {
// Kanban Card Display Settings
kanbanCardDetailLevel: KanbanCardDetailLevel; // Level of detail shown on kanban cards
// Feature Default Settings
defaultSkipTests: boolean; // Default value for skip tests when creating new features
}
export interface AutoModeActivity {
@@ -226,6 +229,9 @@ export interface AppActions {
// Kanban Card Settings actions
setKanbanCardDetailLevel: (level: KanbanCardDetailLevel) => void;
// Feature Default Settings actions
setDefaultSkipTests: (skip: boolean) => void;
// Reset
reset: () => void;
}
@@ -252,6 +258,7 @@ const initialState: AppState = {
autoModeActivityLog: [],
maxConcurrency: 3, // Default to 3 concurrent agents
kanbanCardDetailLevel: "standard", // Default to standard detail level
defaultSkipTests: false, // Default to TDD mode (tests enabled)
};
export const useAppStore = create<AppState & AppActions>()(
@@ -494,6 +501,9 @@ export const useAppStore = create<AppState & AppActions>()(
setKanbanCardDetailLevel: (level) =>
set({ kanbanCardDetailLevel: level }),
// Feature Default Settings actions
setDefaultSkipTests: (skip) => set({ defaultSkipTests: skip }),
// Reset
reset: () => set(initialState),
}),
@@ -510,6 +520,7 @@ export const useAppStore = create<AppState & AppActions>()(
chatHistoryOpen: state.chatHistoryOpen,
maxConcurrency: state.maxConcurrency,
kanbanCardDetailLevel: state.kanbanCardDetailLevel,
defaultSkipTests: state.defaultSkipTests,
}),
}
)