mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 20:23:36 +00:00
fix: adress pr comments
This commit is contained in:
@@ -16,6 +16,9 @@ import {
|
||||
import { NoProjectState, AgentHeader, ChatArea } from './agent-view/components';
|
||||
import { AgentInputArea } from './agent-view/input-area';
|
||||
|
||||
/** Tailwind lg breakpoint in pixels */
|
||||
const LG_BREAKPOINT = 1024;
|
||||
|
||||
export function AgentView() {
|
||||
const { currentProject } = useAppStore();
|
||||
const [input, setInput] = useState('');
|
||||
@@ -24,12 +27,21 @@ export function AgentView() {
|
||||
// Then updates on mount based on actual screen size to prevent hydration mismatch
|
||||
const [showSessionManager, setShowSessionManager] = useState(true);
|
||||
|
||||
// Update session manager visibility based on screen size after mount
|
||||
// Update session manager visibility based on screen size after mount and on resize
|
||||
useEffect(() => {
|
||||
// Check if we're on a mobile screen (< lg breakpoint = 1024px)
|
||||
const isDesktop = window.innerWidth >= 1024;
|
||||
setShowSessionManager(isDesktop);
|
||||
const updateVisibility = () => {
|
||||
const isDesktop = window.innerWidth >= LG_BREAKPOINT;
|
||||
setShowSessionManager(isDesktop);
|
||||
};
|
||||
|
||||
// Set initial value
|
||||
updateVisibility();
|
||||
|
||||
// Listen for resize events
|
||||
window.addEventListener('resize', updateVisibility);
|
||||
return () => window.removeEventListener('resize', updateVisibility);
|
||||
}, []);
|
||||
|
||||
const [modelSelection, setModelSelection] = useState<PhaseModelEntry>({ model: 'sonnet' });
|
||||
|
||||
// Input ref for auto-focus
|
||||
|
||||
@@ -1320,7 +1320,7 @@ export function BoardView() {
|
||||
handleViewOutput(feature);
|
||||
}
|
||||
}}
|
||||
className="transition-opacity duration-250"
|
||||
className="transition-opacity duration-200"
|
||||
/>
|
||||
) : (
|
||||
<KanbanBoard
|
||||
@@ -1362,7 +1362,7 @@ export function BoardView() {
|
||||
viewMode={viewMode}
|
||||
isDragging={activeFeature !== null}
|
||||
onAiSuggest={() => setShowPlanDialog(true)}
|
||||
className="transition-opacity duration-250"
|
||||
className="transition-opacity duration-200"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// TODO: Remove @ts-nocheck after fixing BaseFeature's index signature issue
|
||||
// The `[key: string]: unknown` in BaseFeature causes property access type errors
|
||||
// @ts-nocheck
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { memo, useCallback, useState, useEffect } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||
import { AlertCircle, Lock, Hand, Sparkles, FileText } from 'lucide-react';
|
||||
@@ -49,14 +49,34 @@ const IndicatorBadges = memo(function IndicatorBadges({
|
||||
feature.skipTests && !feature.error && feature.status === 'backlog';
|
||||
const hasPlan = feature.planSpec?.content;
|
||||
|
||||
// Check if just finished (within 2 minutes)
|
||||
const isJustFinished = useMemo(() => {
|
||||
// Check if just finished (within 2 minutes) - uses timer to auto-expire
|
||||
const [isJustFinished, setIsJustFinished] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!feature.justFinishedAt || feature.status !== 'waiting_approval' || feature.error) {
|
||||
return false;
|
||||
setIsJustFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const finishedTime = new Date(feature.justFinishedAt).getTime();
|
||||
const twoMinutes = 2 * 60 * 1000;
|
||||
return Date.now() - finishedTime < twoMinutes;
|
||||
const elapsed = Date.now() - finishedTime;
|
||||
|
||||
if (elapsed >= twoMinutes) {
|
||||
setIsJustFinished(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set as just finished
|
||||
setIsJustFinished(true);
|
||||
|
||||
// Set a timeout to clear the "just finished" state when 2 minutes have passed
|
||||
const remainingTime = twoMinutes - elapsed;
|
||||
const timeoutId = setTimeout(() => {
|
||||
setIsJustFinished(false);
|
||||
}, remainingTime);
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [feature.justFinishedAt, feature.status, feature.error]);
|
||||
|
||||
const badges: Array<{
|
||||
|
||||
@@ -12,6 +12,9 @@ import { getStatusLabel, getStatusOrder } from './status-badge';
|
||||
import { getColumnsWithPipeline } from '../../constants';
|
||||
import type { SortConfig, SortColumn } from '../../hooks/use-list-view-state';
|
||||
|
||||
/** Empty set constant to avoid creating new instances on each render */
|
||||
const EMPTY_SET = new Set<string>();
|
||||
|
||||
/**
|
||||
* Status group configuration for the list view
|
||||
*/
|
||||
@@ -184,7 +187,7 @@ export const ListView = memo(function ListView({
|
||||
pipelineConfig = null,
|
||||
onAddFeature,
|
||||
isSelectionMode = false,
|
||||
selectedFeatureIds = new Set(),
|
||||
selectedFeatureIds = EMPTY_SET,
|
||||
onToggleFeatureSelection,
|
||||
onRowClick,
|
||||
className,
|
||||
|
||||
@@ -80,7 +80,8 @@ export function CommitWorktreeDialog({
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && e.metaKey && !isLoading && message.trim()) {
|
||||
// Prevent commit while loading or while AI is generating a message
|
||||
if (e.key === 'Enter' && e.metaKey && !isLoading && !isGenerating && message.trim()) {
|
||||
handleCommit();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,7 +114,7 @@ export function KanbanBoard({
|
||||
<div
|
||||
className={cn(
|
||||
'flex-1 overflow-x-auto px-5 pt-4 pb-4 relative',
|
||||
'transition-opacity duration-250',
|
||||
'transition-opacity duration-200',
|
||||
className
|
||||
)}
|
||||
style={backgroundImageStyle}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useEffect, useCallback, useState } from 'react';
|
||||
import { RefreshCw, AlertTriangle } from 'lucide-react';
|
||||
import { useEffect, useCallback, useState, type ComponentType, type ReactNode } from 'react';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
import { useAppStore, type ClaudeUsage, type CodexUsage } from '@/store/app-store';
|
||||
import { useSetupStore } from '@/store/setup-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { AnthropicIcon, OpenAIIcon } from '@/components/ui/provider-icon';
|
||||
|
||||
interface MobileUsageBarProps {
|
||||
@@ -70,11 +69,11 @@ function UsageItem({
|
||||
onRefresh,
|
||||
children,
|
||||
}: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
label: string;
|
||||
isLoading: boolean;
|
||||
onRefresh: () => void;
|
||||
children: React.ReactNode;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="px-2 py-2">
|
||||
|
||||
@@ -26,6 +26,58 @@ const USAGE_COLOR_CRITICAL = 'bg-red-500';
|
||||
const USAGE_COLOR_WARNING = 'bg-amber-500';
|
||||
const USAGE_COLOR_OK = 'bg-indigo-500';
|
||||
|
||||
/**
|
||||
* Get the appropriate color class for a usage percentage
|
||||
*/
|
||||
function getUsageColor(percentage: number): string {
|
||||
if (percentage >= WARNING_THRESHOLD) {
|
||||
return USAGE_COLOR_CRITICAL;
|
||||
}
|
||||
if (percentage >= CAUTION_THRESHOLD) {
|
||||
return USAGE_COLOR_WARNING;
|
||||
}
|
||||
return USAGE_COLOR_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual usage card displaying a usage metric with progress bar
|
||||
*/
|
||||
function UsageCard({
|
||||
title,
|
||||
subtitle,
|
||||
percentage,
|
||||
resetText,
|
||||
}: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
percentage: number;
|
||||
resetText?: string;
|
||||
}) {
|
||||
const safePercentage = Math.min(Math.max(percentage, 0), MAX_PERCENTAGE);
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-foreground">{title}</p>
|
||||
<p className="text-xs text-muted-foreground">{subtitle}</p>
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-foreground">{Math.round(safePercentage)}%</span>
|
||||
</div>
|
||||
<div className="mt-3 h-2 w-full rounded-full bg-secondary/60">
|
||||
<div
|
||||
className={cn(
|
||||
'h-full rounded-full transition-all duration-300',
|
||||
getUsageColor(safePercentage)
|
||||
)}
|
||||
style={{ width: `${safePercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
{resetText && <p className="mt-2 text-xs text-muted-foreground">{resetText}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ClaudeUsageSection() {
|
||||
const claudeAuthStatus = useSetupStore((state) => state.claudeAuthStatus);
|
||||
const { claudeUsage, claudeUsageLastUpdated, setClaudeUsage } = useAppStore();
|
||||
@@ -100,54 +152,6 @@ export function ClaudeUsageSection() {
|
||||
return () => clearInterval(intervalId);
|
||||
}, [fetchUsage, canFetchUsage]);
|
||||
|
||||
const getUsageColor = (percentage: number) => {
|
||||
if (percentage >= WARNING_THRESHOLD) {
|
||||
return USAGE_COLOR_CRITICAL;
|
||||
}
|
||||
if (percentage >= CAUTION_THRESHOLD) {
|
||||
return USAGE_COLOR_WARNING;
|
||||
}
|
||||
return USAGE_COLOR_OK;
|
||||
};
|
||||
|
||||
const UsageCard = ({
|
||||
title,
|
||||
subtitle,
|
||||
percentage,
|
||||
resetText,
|
||||
}: {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
percentage: number;
|
||||
resetText?: string;
|
||||
}) => {
|
||||
const safePercentage = Math.min(Math.max(percentage, 0), MAX_PERCENTAGE);
|
||||
|
||||
return (
|
||||
<div className="rounded-xl border border-border/60 bg-card/50 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-foreground">{title}</p>
|
||||
<p className="text-xs text-muted-foreground">{subtitle}</p>
|
||||
</div>
|
||||
<span className="text-sm font-semibold text-foreground">
|
||||
{Math.round(safePercentage)}%
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-3 h-2 w-full rounded-full bg-secondary/60">
|
||||
<div
|
||||
className={cn(
|
||||
'h-full rounded-full transition-all duration-300',
|
||||
getUsageColor(safePercentage)
|
||||
)}
|
||||
style={{ width: `${safePercentage}%` }}
|
||||
/>
|
||||
</div>
|
||||
{resetText && <p className="mt-2 text-xs text-muted-foreground">{resetText}</p>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user