diff --git a/.automaker/feature_list.json b/.automaker/feature_list.json index 0637a088..d38c0cd7 100644 --- a/.automaker/feature_list.json +++ b/.automaker/feature_list.json @@ -1 +1,28 @@ -[] \ No newline at end of file +[ + { + "id": "feature-1765387670653-bl83444lj", + "category": "Kanban", + "description": "In the output logs of the proc agent output in the file diffs Can you add a scroll bar so it actually scroll to see all these new styles right now it seems like I can't scroll", + "steps": [], + "status": "in_progress", + "startedAt": "2025-12-10T17:32:30.636Z", + "imagePaths": [], + "skipTests": true, + "summary": "Added always-visible scrollbar to file diffs in agent output modal. Created new 'scrollbar-visible' CSS utility class in globals.css with theme support for all themes (light, dark, retro, etc.). Applied scrollbar-visible class to: git-diff-panel.tsx (FileDiffSection expanded content), agent-output-modal.tsx (changes view container and logs/raw view container). Scrollbars now remain visible even on macOS where they normally auto-hide.", + "model": "opus", + "thinkingLevel": "none" + }, + { + "id": "feature-1765387746902-na752mp1y", + "category": "Kanban", + "description": "When the add feature modal pops up, make sure that the description is always the main focus. When it first loads up. Do not focus the prompt tab, which is currently doing this.", + "steps": [], + "status": "waiting_approval", + "startedAt": "2025-12-10T17:29:13.854Z", + "imagePaths": [], + "skipTests": true, + "summary": "Added autoFocus prop to DescriptionImageDropZone component. Modified: description-image-dropzone.tsx (added autoFocus prop support), board-view.tsx (enabled autoFocus on add feature modal). Now the description textarea receives focus when the modal opens instead of the prompt tab.", + "model": "opus", + "thinkingLevel": "none" + } +] \ No newline at end of file diff --git a/app/electron/auto-mode-service.js b/app/electron/auto-mode-service.js index 1ebe370e..054c3b03 100644 --- a/app/electron/auto-mode-service.js +++ b/app/electron/auto-mode-service.js @@ -235,14 +235,16 @@ class AutoModeService { // Update feature status based on result // For skipTests features, go to waiting_approval on success instead of verified - // On failure, skipTests features should also go to waiting_approval for user review + // On failure, ALL features go to waiting_approval so user can review and decide next steps + // This prevents infinite retry loops when the same issue keeps failing let newStatus; if (result.passes) { newStatus = feature.skipTests ? "waiting_approval" : "verified"; } else { - // For skipTests features, keep in waiting_approval so user can review - // For normal TDD features, move to backlog for retry - newStatus = feature.skipTests ? "waiting_approval" : "backlog"; + // On failure, go to waiting_approval for user review + // Don't automatically move back to backlog to avoid infinite retry loops + // (especially when hitting rate limits or persistent errors) + newStatus = "waiting_approval"; } await featureLoader.updateFeatureStatus( feature.id, @@ -507,11 +509,13 @@ class AutoModeService { // Update feature status based on final result // For skipTests features, go to waiting_approval on success instead of verified + // On failure, go to waiting_approval so user can review and decide next steps let newStatus; if (finalResult.passes) { newStatus = feature.skipTests ? "waiting_approval" : "verified"; } else { - newStatus = "in_progress"; + // On failure after all retry attempts, go to waiting_approval for user review + newStatus = "waiting_approval"; } await featureLoader.updateFeatureStatus( featureId, @@ -691,14 +695,16 @@ class AutoModeService { // Update feature status based on result // For skipTests features, go to waiting_approval on success instead of verified - // On failure, skipTests features should also go to waiting_approval for user review + // On failure, ALL features go to waiting_approval so user can review and decide next steps + // This prevents infinite retry loops when the same issue keeps failing let newStatus; if (result.passes) { newStatus = feature.skipTests ? "waiting_approval" : "verified"; } else { - // For skipTests features, keep in waiting_approval so user can review - // For normal TDD features, move to backlog for retry - newStatus = feature.skipTests ? "waiting_approval" : "backlog"; + // On failure, go to waiting_approval for user review + // Don't automatically move back to backlog to avoid infinite retry loops + // (especially when hitting rate limits or persistent errors) + newStatus = "waiting_approval"; } await featureLoader.updateFeatureStatus( feature.id, @@ -936,11 +942,12 @@ class AutoModeService { ); // For skipTests features, go to waiting_approval on success instead of verified + // On failure, go to waiting_approval so user can review and decide next steps const newStatus = result.passes ? feature.skipTests ? "waiting_approval" : "verified" - : "in_progress"; + : "waiting_approval"; await featureLoader.updateFeatureStatus( feature.id, diff --git a/app/src/app/globals.css b/app/src/app/globals.css index 09207187..4920fa6a 100644 --- a/app/src/app/globals.css +++ b/app/src/app/globals.css @@ -1039,6 +1039,62 @@ background: var(--background); } +/* 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; +} + +/* Light mode scrollbar-visible adjustments */ +.light .scrollbar-visible::-webkit-scrollbar-track { + background: oklch(0.95 0 0); +} + +.light .scrollbar-visible::-webkit-scrollbar-thumb { + background: oklch(0.7 0 0); +} + +.light .scrollbar-visible::-webkit-scrollbar-thumb:hover { + background: oklch(0.6 0 0); +} + +/* Retro mode scrollbar-visible adjustments */ +.retro .scrollbar-visible::-webkit-scrollbar-thumb { + background: var(--primary); + border-radius: 0; +} + +.retro .scrollbar-visible::-webkit-scrollbar-track { + background: var(--background); + border-radius: 0; +} + /* Glass morphism utilities */ @layer utilities { .glass { diff --git a/app/src/components/ui/description-image-dropzone.tsx b/app/src/components/ui/description-image-dropzone.tsx index f483fb1a..7e11c9e5 100644 --- a/app/src/components/ui/description-image-dropzone.tsx +++ b/app/src/components/ui/description-image-dropzone.tsx @@ -30,6 +30,7 @@ interface DescriptionImageDropZoneProps { // Optional: pass preview map from parent to persist across tab switches previewMap?: ImagePreviewMap; onPreviewMapChange?: (map: ImagePreviewMap) => void; + autoFocus?: boolean; } const ACCEPTED_IMAGE_TYPES = [ @@ -53,6 +54,7 @@ export function DescriptionImageDropZone({ maxFileSize = DEFAULT_MAX_FILE_SIZE, previewMap, onPreviewMapChange, + autoFocus = false, }: DescriptionImageDropZoneProps) { const [isDragOver, setIsDragOver] = useState(false); const [isProcessing, setIsProcessing] = useState(false); @@ -303,6 +305,7 @@ export function DescriptionImageDropZone({ value={value} onChange={(e) => onChange(e.target.value)} disabled={disabled} + autoFocus={autoFocus} className={cn( "min-h-[120px]", isProcessing && "opacity-50 pointer-events-none" diff --git a/app/src/components/ui/dialog.tsx b/app/src/components/ui/dialog.tsx index 57fbbc61..2438783e 100644 --- a/app/src/components/ui/dialog.tsx +++ b/app/src/components/ui/dialog.tsx @@ -1,33 +1,33 @@ -"use client" +"use client"; -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { XIcon } from "lucide-react" +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { XIcon } from "lucide-react"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; function Dialog({ ...props }: React.ComponentProps) { - return + return ; } function DialogTrigger({ ...props }: React.ComponentProps) { - return + return ; } function DialogPortal({ ...props }: React.ComponentProps) { - return + return ; } function DialogClose({ ...props }: React.ComponentProps) { - return + return ; } function DialogOverlay({ @@ -43,7 +43,7 @@ function DialogOverlay({ )} {...props} /> - ) + ); } function DialogContent({ @@ -53,9 +53,13 @@ function DialogContent({ compact = false, ...props }: React.ComponentProps & { - showCloseButton?: boolean - compact?: boolean + showCloseButton?: boolean; + compact?: boolean; }) { + // Check if className contains a custom max-width + const hasCustomMaxWidth = + typeof className === "string" && className.includes("max-w-"); + return ( @@ -63,7 +67,11 @@ function DialogContent({ data-slot="dialog-content" className={cn( "bg-background 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 fixed top-[50%] left-[50%] z-50 flex flex-col w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] rounded-lg border shadow-lg duration-200 max-h-[calc(100vh-4rem)]", - compact ? "max-w-2xl p-4" : "sm:max-w-2xl p-6", + compact + ? "max-w-4xl p-4" + : !hasCustomMaxWidth + ? "sm:max-w-2xl p-6" + : "p-6", className )} {...props} @@ -83,7 +91,7 @@ function DialogContent({ )} - ) + ); } function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { @@ -93,7 +101,7 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { className={cn("flex flex-col gap-2 text-center sm:text-left", className)} {...props} /> - ) + ); } function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { @@ -106,7 +114,7 @@ function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { )} {...props} /> - ) + ); } function DialogTitle({ @@ -119,7 +127,7 @@ function DialogTitle({ className={cn("text-lg leading-none font-semibold", className)} {...props} /> - ) + ); } function DialogDescription({ @@ -132,7 +140,7 @@ function DialogDescription({ className={cn("text-muted-foreground text-sm", className)} {...props} /> - ) + ); } export { @@ -146,4 +154,4 @@ export { DialogPortal, DialogTitle, DialogTrigger, -} +}; diff --git a/app/src/components/ui/git-diff-panel.tsx b/app/src/components/ui/git-diff-panel.tsx index ae734a9c..2ddd5015 100644 --- a/app/src/components/ui/git-diff-panel.tsx +++ b/app/src/components/ui/git-diff-panel.tsx @@ -333,7 +333,7 @@ function FileDiffSection({ {isExpanded && ( -
+
{fileDiff.hunks.map((hunk, hunkIndex) => (
{hunk.lines.map((line, lineIndex) => ( @@ -458,7 +458,7 @@ export function GitDiffPanel({ return (
setIsExpanded(!isExpanded)} - className="w-full px-4 py-3 flex items-center justify-between bg-card hover:bg-accent/50 transition-colors text-left" + className="w-full px-4 py-3 flex items-center justify-between bg-card hover:bg-accent/50 transition-colors text-left flex-shrink-0" data-testid="git-diff-panel-toggle" >
@@ -497,7 +497,7 @@ export function GitDiffPanel({ {/* Content */} {isExpanded && ( -
+
{isLoading ? (
diff --git a/app/src/components/views/agent-output-modal.tsx b/app/src/components/views/agent-output-modal.tsx index f463737b..a5f05585 100644 --- a/app/src/components/views/agent-output-modal.tsx +++ b/app/src/components/views/agent-output-modal.tsx @@ -154,29 +154,31 @@ export function AgentOutputModal({ case "auto_mode_ultrathink_preparation": // Format thinking level preparation information let prepContent = `\n🧠 Ultrathink Preparation\n`; - + if (event.warnings && event.warnings.length > 0) { prepContent += `\n⚠️ Warnings:\n`; event.warnings.forEach((warning: string) => { prepContent += ` • ${warning}\n`; }); } - + if (event.recommendations && event.recommendations.length > 0) { prepContent += `\n💡 Recommendations:\n`; event.recommendations.forEach((rec: string) => { prepContent += ` • ${rec}\n`; }); } - + if (event.estimatedCost !== undefined) { - prepContent += `\n💰 Estimated Cost: ~$${event.estimatedCost.toFixed(2)} per execution\n`; + prepContent += `\n💰 Estimated Cost: ~$${event.estimatedCost.toFixed( + 2 + )} per execution\n`; } - + if (event.estimatedTime) { prepContent += `\n⏱️ Estimated Time: ${event.estimatedTime}\n`; } - + newContent = prepContent; break; case "auto_mode_feature_complete": @@ -299,7 +301,7 @@ export function AgentOutputModal({ {viewMode === "changes" ? ( -
+
{projectPath ? ( {isLoading && !output ? (
diff --git a/app/src/components/views/board-view.tsx b/app/src/components/views/board-view.tsx index 150ec84f..480d3ae1 100644 --- a/app/src/components/views/board-view.tsx +++ b/app/src/components/views/board-view.tsx @@ -1738,6 +1738,7 @@ export function BoardView() { placeholder="Describe the feature..." previewMap={newFeaturePreviewMap} onPreviewMapChange={setNewFeaturePreviewMap} + autoFocus />