mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
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:
@@ -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",
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user