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

@@ -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