mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
refactor: remove kanbanCardDetailLevel from settings and UI components
- Eliminated kanbanCardDetailLevel from the SettingsService, app state, and various UI components including BoardView and BoardControls. - Updated related hooks and API client to reflect the removal of kanbanCardDetailLevel. - Cleaned up imports and props associated with kanbanCardDetailLevel across the codebase for improved clarity and maintainability.
This commit is contained in:
@@ -600,8 +600,6 @@ export class SettingsService {
|
|||||||
theme: (appState.theme as GlobalSettings['theme']) || 'dark',
|
theme: (appState.theme as GlobalSettings['theme']) || 'dark',
|
||||||
sidebarOpen: appState.sidebarOpen !== undefined ? (appState.sidebarOpen as boolean) : true,
|
sidebarOpen: appState.sidebarOpen !== undefined ? (appState.sidebarOpen as boolean) : true,
|
||||||
chatHistoryOpen: (appState.chatHistoryOpen as boolean) || false,
|
chatHistoryOpen: (appState.chatHistoryOpen as boolean) || false,
|
||||||
kanbanCardDetailLevel:
|
|
||||||
(appState.kanbanCardDetailLevel as GlobalSettings['kanbanCardDetailLevel']) || 'standard',
|
|
||||||
maxConcurrency: (appState.maxConcurrency as number) || 3,
|
maxConcurrency: (appState.maxConcurrency as number) || 3,
|
||||||
defaultSkipTests:
|
defaultSkipTests:
|
||||||
appState.defaultSkipTests !== undefined ? (appState.defaultSkipTests as boolean) : true,
|
appState.defaultSkipTests !== undefined ? (appState.defaultSkipTests as boolean) : true,
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
export type {
|
export type {
|
||||||
ThemeMode,
|
ThemeMode,
|
||||||
KanbanCardDetailLevel,
|
|
||||||
ModelAlias,
|
ModelAlias,
|
||||||
PlanningMode,
|
PlanningMode,
|
||||||
ThinkingLevel,
|
ThinkingLevel,
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ const E2E_SETTINGS = {
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
sidebarOpen: true,
|
sidebarOpen: true,
|
||||||
chatHistoryOpen: false,
|
chatHistoryOpen: false,
|
||||||
kanbanCardDetailLevel: 'standard',
|
|
||||||
maxConcurrency: 3,
|
maxConcurrency: 3,
|
||||||
defaultSkipTests: true,
|
defaultSkipTests: true,
|
||||||
enableDependencyBlocking: true,
|
enableDependencyBlocking: true,
|
||||||
|
|||||||
@@ -7,7 +7,24 @@ import {
|
|||||||
useSensors,
|
useSensors,
|
||||||
rectIntersection,
|
rectIntersection,
|
||||||
pointerWithin,
|
pointerWithin,
|
||||||
|
type PointerEvent as DndPointerEvent,
|
||||||
} from '@dnd-kit/core';
|
} from '@dnd-kit/core';
|
||||||
|
|
||||||
|
// Custom pointer sensor that ignores drag events from within dialogs
|
||||||
|
class DialogAwarePointerSensor extends PointerSensor {
|
||||||
|
static activators = [
|
||||||
|
{
|
||||||
|
eventName: 'onPointerDown' as const,
|
||||||
|
handler: ({ nativeEvent: event }: { nativeEvent: DndPointerEvent }) => {
|
||||||
|
// Don't start drag if the event originated from inside a dialog
|
||||||
|
if ((event.target as Element)?.closest?.('[role="dialog"]')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
import { useAppStore, Feature } from '@/store/app-store';
|
import { useAppStore, Feature } from '@/store/app-store';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
@@ -73,8 +90,6 @@ export function BoardView() {
|
|||||||
maxConcurrency,
|
maxConcurrency,
|
||||||
setMaxConcurrency,
|
setMaxConcurrency,
|
||||||
defaultSkipTests,
|
defaultSkipTests,
|
||||||
kanbanCardDetailLevel,
|
|
||||||
setKanbanCardDetailLevel,
|
|
||||||
boardViewMode,
|
boardViewMode,
|
||||||
setBoardViewMode,
|
setBoardViewMode,
|
||||||
specCreatingForProject,
|
specCreatingForProject,
|
||||||
@@ -248,7 +263,7 @@ export function BoardView() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(PointerSensor, {
|
useSensor(DialogAwarePointerSensor, {
|
||||||
activationConstraint: {
|
activationConstraint: {
|
||||||
distance: 8,
|
distance: 8,
|
||||||
},
|
},
|
||||||
@@ -1209,8 +1224,6 @@ export function BoardView() {
|
|||||||
onShowBoardBackground={() => setShowBoardBackgroundModal(true)}
|
onShowBoardBackground={() => setShowBoardBackgroundModal(true)}
|
||||||
onShowCompletedModal={() => setShowCompletedModal(true)}
|
onShowCompletedModal={() => setShowCompletedModal(true)}
|
||||||
completedCount={completedFeatures.length}
|
completedCount={completedFeatures.length}
|
||||||
kanbanCardDetailLevel={kanbanCardDetailLevel}
|
|
||||||
onDetailLevelChange={setKanbanCardDetailLevel}
|
|
||||||
boardViewMode={boardViewMode}
|
boardViewMode={boardViewMode}
|
||||||
onBoardViewModeChange={setBoardViewMode}
|
onBoardViewModeChange={setBoardViewMode}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { ImageIcon, Archive, Minimize2, Square, Maximize2, Columns3, Network } from 'lucide-react';
|
import { ImageIcon, Archive, Columns3, Network } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { BoardViewMode } from '@/store/app-store';
|
import { BoardViewMode } from '@/store/app-store';
|
||||||
|
|
||||||
@@ -9,8 +9,6 @@ interface BoardControlsProps {
|
|||||||
onShowBoardBackground: () => void;
|
onShowBoardBackground: () => void;
|
||||||
onShowCompletedModal: () => void;
|
onShowCompletedModal: () => void;
|
||||||
completedCount: number;
|
completedCount: number;
|
||||||
kanbanCardDetailLevel: 'minimal' | 'standard' | 'detailed';
|
|
||||||
onDetailLevelChange: (level: 'minimal' | 'standard' | 'detailed') => void;
|
|
||||||
boardViewMode: BoardViewMode;
|
boardViewMode: BoardViewMode;
|
||||||
onBoardViewModeChange: (mode: BoardViewMode) => void;
|
onBoardViewModeChange: (mode: BoardViewMode) => void;
|
||||||
}
|
}
|
||||||
@@ -20,8 +18,6 @@ export function BoardControls({
|
|||||||
onShowBoardBackground,
|
onShowBoardBackground,
|
||||||
onShowCompletedModal,
|
onShowCompletedModal,
|
||||||
completedCount,
|
completedCount,
|
||||||
kanbanCardDetailLevel,
|
|
||||||
onDetailLevelChange,
|
|
||||||
boardViewMode,
|
boardViewMode,
|
||||||
onBoardViewModeChange,
|
onBoardViewModeChange,
|
||||||
}: BoardControlsProps) {
|
}: BoardControlsProps) {
|
||||||
@@ -115,70 +111,6 @@ export function BoardControls({
|
|||||||
<p>Completed Features ({completedCount})</p>
|
<p>Completed Features ({completedCount})</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
{/* Kanban Card Detail Level Toggle */}
|
|
||||||
<div
|
|
||||||
className="flex items-center rounded-lg bg-secondary border border-border"
|
|
||||||
data-testid="kanban-detail-toggle"
|
|
||||||
>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={() => onDetailLevelChange('minimal')}
|
|
||||||
className={cn(
|
|
||||||
'p-2 rounded-l-lg transition-colors',
|
|
||||||
kanbanCardDetailLevel === 'minimal'
|
|
||||||
? 'bg-brand-500/20 text-brand-500'
|
|
||||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
||||||
)}
|
|
||||||
data-testid="kanban-toggle-minimal"
|
|
||||||
>
|
|
||||||
<Minimize2 className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Minimal - Title & category only</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={() => onDetailLevelChange('standard')}
|
|
||||||
className={cn(
|
|
||||||
'p-2 transition-colors',
|
|
||||||
kanbanCardDetailLevel === 'standard'
|
|
||||||
? 'bg-brand-500/20 text-brand-500'
|
|
||||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
||||||
)}
|
|
||||||
data-testid="kanban-toggle-standard"
|
|
||||||
>
|
|
||||||
<Square className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Standard - Steps & progress</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
<Tooltip>
|
|
||||||
<TooltipTrigger asChild>
|
|
||||||
<button
|
|
||||||
onClick={() => onDetailLevelChange('detailed')}
|
|
||||||
className={cn(
|
|
||||||
'p-2 rounded-r-lg transition-colors',
|
|
||||||
kanbanCardDetailLevel === 'detailed'
|
|
||||||
? 'bg-brand-500/20 text-brand-500'
|
|
||||||
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
||||||
)}
|
|
||||||
data-testid="kanban-toggle-detailed"
|
|
||||||
>
|
|
||||||
<Maximize2 className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Detailed - Model, tools & tasks</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button';
|
|||||||
import { Slider } from '@/components/ui/slider';
|
import { Slider } from '@/components/ui/slider';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Bot, Wand2, Settings2, GitBranch } from 'lucide-react';
|
import { Bot, Wand2, Settings2, GitBranch } from 'lucide-react';
|
||||||
import { UsagePopover } from '@/components/usage-popover';
|
import { UsagePopover } from '@/components/usage-popover';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -107,27 +108,47 @@ export function BoardHeader({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Concurrency Slider - only show after mount to prevent hydration issues */}
|
{/* Concurrency Control - only show after mount to prevent hydration issues */}
|
||||||
{isMounted && (
|
{isMounted && (
|
||||||
<div className={controlContainerClass} data-testid="concurrency-slider-container">
|
<Popover>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<button
|
||||||
|
className={`${controlContainerClass} cursor-pointer hover:bg-accent/50 transition-colors`}
|
||||||
|
data-testid="concurrency-slider-container"
|
||||||
|
>
|
||||||
<Bot className="w-4 h-4 text-muted-foreground" />
|
<Bot className="w-4 h-4 text-muted-foreground" />
|
||||||
<span className="text-sm font-medium">Agents</span>
|
<span className="text-sm font-medium">Agents</span>
|
||||||
|
<span className="text-sm text-muted-foreground" data-testid="concurrency-value">
|
||||||
|
{runningAgentsCount}/{maxConcurrency}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-64" align="end">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-sm mb-1">Max Concurrent Agents</h4>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
Controls how many AI agents can run simultaneously. Higher values process more
|
||||||
|
features in parallel but use more API resources.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
<Slider
|
<Slider
|
||||||
value={[maxConcurrency]}
|
value={[maxConcurrency]}
|
||||||
onValueChange={(value) => onConcurrencyChange(value[0])}
|
onValueChange={(value) => onConcurrencyChange(value[0])}
|
||||||
min={1}
|
min={1}
|
||||||
max={10}
|
max={10}
|
||||||
step={1}
|
step={1}
|
||||||
className="w-20"
|
className="flex-1"
|
||||||
data-testid="concurrency-slider"
|
data-testid="concurrency-slider"
|
||||||
/>
|
/>
|
||||||
<span
|
<span className="text-sm font-medium min-w-[2ch] text-right">
|
||||||
className="text-sm text-muted-foreground min-w-[5ch] text-center"
|
{maxConcurrency}
|
||||||
data-testid="concurrency-value"
|
|
||||||
>
|
|
||||||
{runningAgentsCount} / {maxConcurrency}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
|
{/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Feature, ThinkingLevel, useAppStore } from '@/store/app-store';
|
import { Feature, ThinkingLevel } from '@/store/app-store';
|
||||||
import type { ReasoningEffort } from '@automaker/types';
|
import type { ReasoningEffort } from '@automaker/types';
|
||||||
import { getProviderFromModel } from '@/lib/utils';
|
import { getProviderFromModel } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -68,12 +68,9 @@ export function AgentInfoPanel({
|
|||||||
summary,
|
summary,
|
||||||
isCurrentAutoTask,
|
isCurrentAutoTask,
|
||||||
}: AgentInfoPanelProps) {
|
}: AgentInfoPanelProps) {
|
||||||
const { kanbanCardDetailLevel } = useAppStore();
|
|
||||||
const [agentInfo, setAgentInfo] = useState<AgentTaskInfo | null>(null);
|
const [agentInfo, setAgentInfo] = useState<AgentTaskInfo | null>(null);
|
||||||
const [isSummaryDialogOpen, setIsSummaryDialogOpen] = useState(false);
|
const [isSummaryDialogOpen, setIsSummaryDialogOpen] = useState(false);
|
||||||
|
|
||||||
const showAgentInfo = kanbanCardDetailLevel === 'detailed';
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadContext = async () => {
|
const loadContext = async () => {
|
||||||
if (contextContent) {
|
if (contextContent) {
|
||||||
@@ -123,7 +120,7 @@ export function AgentInfoPanel({
|
|||||||
}
|
}
|
||||||
}, [feature.id, feature.status, contextContent, isCurrentAutoTask]);
|
}, [feature.id, feature.status, contextContent, isCurrentAutoTask]);
|
||||||
// Model/Preset Info for Backlog Cards
|
// Model/Preset Info for Backlog Cards
|
||||||
if (showAgentInfo && feature.status === 'backlog') {
|
if (feature.status === 'backlog') {
|
||||||
const provider = getProviderFromModel(feature.model);
|
const provider = getProviderFromModel(feature.model);
|
||||||
const isCodex = provider === 'codex';
|
const isCodex = provider === 'codex';
|
||||||
const isClaude = provider === 'claude';
|
const isClaude = provider === 'claude';
|
||||||
@@ -160,7 +157,7 @@ export function AgentInfoPanel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Agent Info Panel for non-backlog cards
|
// Agent Info Panel for non-backlog cards
|
||||||
if (showAgentInfo && feature.status !== 'backlog' && agentInfo) {
|
if (feature.status !== 'backlog' && agentInfo) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="mb-3 space-y-2 overflow-hidden">
|
<div className="mb-3 space-y-2 overflow-hidden">
|
||||||
@@ -255,7 +252,11 @@ export function AgentInfoPanel({
|
|||||||
<Expand className="w-3 h-3" />
|
<Expand className="w-3 h-3" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-[10px] text-muted-foreground/70 line-clamp-3 break-words hyphens-auto leading-relaxed overflow-hidden">
|
<p
|
||||||
|
className="text-[10px] text-muted-foreground/70 line-clamp-3 break-words hyphens-auto leading-relaxed overflow-hidden select-text cursor-text"
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
{feature.summary || summary || agentInfo.summary}
|
{feature.summary || summary || agentInfo.summary}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -292,50 +293,9 @@ export function AgentInfoPanel({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show just the todo list for non-backlog features when showAgentInfo is false
|
// Always render SummaryDialog (even if no agentInfo yet)
|
||||||
// This ensures users always see what the agent is working on
|
|
||||||
if (!showAgentInfo && feature.status !== 'backlog' && agentInfo && agentInfo.todos.length > 0) {
|
|
||||||
return (
|
|
||||||
<div className="mb-3 space-y-1 overflow-hidden">
|
|
||||||
<div className="flex items-center gap-1 text-[10px] text-muted-foreground/70">
|
|
||||||
<ListTodo className="w-3 h-3" />
|
|
||||||
<span>
|
|
||||||
{agentInfo.todos.filter((t) => t.status === 'completed').length}/
|
|
||||||
{agentInfo.todos.length} tasks
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-0.5 max-h-24 overflow-y-auto">
|
|
||||||
{agentInfo.todos.map((todo, idx) => (
|
|
||||||
<div key={idx} className="flex items-center gap-1.5 text-[10px]">
|
|
||||||
{todo.status === 'completed' ? (
|
|
||||||
<CheckCircle2 className="w-2.5 h-2.5 text-[var(--status-success)] shrink-0" />
|
|
||||||
) : todo.status === 'in_progress' ? (
|
|
||||||
<Loader2 className="w-2.5 h-2.5 text-[var(--status-warning)] animate-spin shrink-0" />
|
|
||||||
) : (
|
|
||||||
<Circle className="w-2.5 h-2.5 text-muted-foreground/50 shrink-0" />
|
|
||||||
)}
|
|
||||||
<span
|
|
||||||
className={cn(
|
|
||||||
'break-words hyphens-auto line-clamp-2 leading-relaxed',
|
|
||||||
todo.status === 'completed' && 'text-muted-foreground/60 line-through',
|
|
||||||
todo.status === 'in_progress' && 'text-[var(--status-warning)]',
|
|
||||||
todo.status === 'pending' && 'text-muted-foreground/80'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{todo.content}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Always render SummaryDialog if showAgentInfo is true (even if no agentInfo yet)
|
|
||||||
// This ensures the dialog can be opened from the expand button
|
// This ensures the dialog can be opened from the expand button
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{showAgentInfo && (
|
|
||||||
<SummaryDialog
|
<SummaryDialog
|
||||||
feature={feature}
|
feature={feature}
|
||||||
agentInfo={agentInfo}
|
agentInfo={agentInfo}
|
||||||
@@ -343,7 +303,5 @@ export function AgentInfoPanel({
|
|||||||
isOpen={isSummaryDialogOpen}
|
isOpen={isSummaryDialogOpen}
|
||||||
onOpenChange={setIsSummaryDialogOpen}
|
onOpenChange={setIsSummaryDialogOpen}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,8 +31,10 @@ export function SummaryDialog({
|
|||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col"
|
className="max-w-4xl max-h-[80vh] overflow-hidden flex flex-col select-text"
|
||||||
data-testid={`summary-dialog-${feature.id}`}
|
data-testid={`summary-dialog-${feature.id}`}
|
||||||
|
onPointerDown={(e) => e.stopPropagation()}
|
||||||
|
onMouseDown={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { DndContext, DragOverlay } from '@dnd-kit/core';
|
|||||||
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { KanbanColumn, KanbanCard } from './components';
|
import { KanbanColumn, KanbanCard } from './components';
|
||||||
import { Feature } from '@/store/app-store';
|
import { Feature, useAppStore, formatShortcut } from '@/store/app-store';
|
||||||
import { Archive, Settings2, CheckSquare, GripVertical, Plus } from 'lucide-react';
|
import { Archive, Settings2, CheckSquare, GripVertical, Plus } from 'lucide-react';
|
||||||
import { useResponsiveKanban } from '@/hooks/use-responsive-kanban';
|
import { useResponsiveKanban } from '@/hooks/use-responsive-kanban';
|
||||||
import { getColumnsWithPipeline, type ColumnId } from './constants';
|
import { getColumnsWithPipeline, type ColumnId } from './constants';
|
||||||
@@ -90,6 +90,10 @@ export function KanbanBoard({
|
|||||||
// Generate columns including pipeline steps
|
// Generate columns including pipeline steps
|
||||||
const columns = useMemo(() => getColumnsWithPipeline(pipelineConfig), [pipelineConfig]);
|
const columns = useMemo(() => getColumnsWithPipeline(pipelineConfig), [pipelineConfig]);
|
||||||
|
|
||||||
|
// Get the keyboard shortcut for adding features
|
||||||
|
const { keyboardShortcuts } = useAppStore();
|
||||||
|
const addFeatureShortcut = keyboardShortcuts.addFeature || 'N';
|
||||||
|
|
||||||
// Use responsive column widths based on window size
|
// Use responsive column widths based on window size
|
||||||
// containerStyle handles centering and ensures columns fit without horizontal scroll in Electron
|
// containerStyle handles centering and ensures columns fit without horizontal scroll in Electron
|
||||||
const { columnWidth, containerStyle } = useResponsiveKanban(columns.length);
|
const { columnWidth, containerStyle } = useResponsiveKanban(columns.length);
|
||||||
@@ -196,6 +200,9 @@ export function KanbanBoard({
|
|||||||
>
|
>
|
||||||
<Plus className="w-4 h-4 mr-2" />
|
<Plus className="w-4 h-4 mr-2" />
|
||||||
Add Feature
|
Add Feature
|
||||||
|
<span className="ml-auto pl-2 text-[10px] font-mono opacity-70 bg-black/20 px-1.5 py-0.5 rounded">
|
||||||
|
{formatShortcut(addFeatureShortcut, true)}
|
||||||
|
</span>
|
||||||
</Button>
|
</Button>
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -139,7 +139,6 @@ export function parseLocalStorageSettings(): Partial<GlobalSettings> | null {
|
|||||||
theme: state.theme as GlobalSettings['theme'],
|
theme: state.theme as GlobalSettings['theme'],
|
||||||
sidebarOpen: state.sidebarOpen as boolean,
|
sidebarOpen: state.sidebarOpen as boolean,
|
||||||
chatHistoryOpen: state.chatHistoryOpen as boolean,
|
chatHistoryOpen: state.chatHistoryOpen as boolean,
|
||||||
kanbanCardDetailLevel: state.kanbanCardDetailLevel as GlobalSettings['kanbanCardDetailLevel'],
|
|
||||||
maxConcurrency: state.maxConcurrency as number,
|
maxConcurrency: state.maxConcurrency as number,
|
||||||
defaultSkipTests: state.defaultSkipTests as boolean,
|
defaultSkipTests: state.defaultSkipTests as boolean,
|
||||||
enableDependencyBlocking: state.enableDependencyBlocking as boolean,
|
enableDependencyBlocking: state.enableDependencyBlocking as boolean,
|
||||||
@@ -526,7 +525,6 @@ export function hydrateStoreFromSettings(settings: GlobalSettings): void {
|
|||||||
theme: settings.theme as unknown as import('@/store/app-store').ThemeMode,
|
theme: settings.theme as unknown as import('@/store/app-store').ThemeMode,
|
||||||
sidebarOpen: settings.sidebarOpen ?? true,
|
sidebarOpen: settings.sidebarOpen ?? true,
|
||||||
chatHistoryOpen: settings.chatHistoryOpen ?? false,
|
chatHistoryOpen: settings.chatHistoryOpen ?? false,
|
||||||
kanbanCardDetailLevel: settings.kanbanCardDetailLevel ?? 'standard',
|
|
||||||
maxConcurrency: settings.maxConcurrency ?? 3,
|
maxConcurrency: settings.maxConcurrency ?? 3,
|
||||||
defaultSkipTests: settings.defaultSkipTests ?? true,
|
defaultSkipTests: settings.defaultSkipTests ?? true,
|
||||||
enableDependencyBlocking: settings.enableDependencyBlocking ?? true,
|
enableDependencyBlocking: settings.enableDependencyBlocking ?? true,
|
||||||
@@ -582,7 +580,6 @@ function buildSettingsUpdateFromStore(): Record<string, unknown> {
|
|||||||
theme: state.theme,
|
theme: state.theme,
|
||||||
sidebarOpen: state.sidebarOpen,
|
sidebarOpen: state.sidebarOpen,
|
||||||
chatHistoryOpen: state.chatHistoryOpen,
|
chatHistoryOpen: state.chatHistoryOpen,
|
||||||
kanbanCardDetailLevel: state.kanbanCardDetailLevel,
|
|
||||||
maxConcurrency: state.maxConcurrency,
|
maxConcurrency: state.maxConcurrency,
|
||||||
defaultSkipTests: state.defaultSkipTests,
|
defaultSkipTests: state.defaultSkipTests,
|
||||||
enableDependencyBlocking: state.enableDependencyBlocking,
|
enableDependencyBlocking: state.enableDependencyBlocking,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'theme',
|
'theme',
|
||||||
'sidebarOpen',
|
'sidebarOpen',
|
||||||
'chatHistoryOpen',
|
'chatHistoryOpen',
|
||||||
'kanbanCardDetailLevel',
|
|
||||||
'maxConcurrency',
|
'maxConcurrency',
|
||||||
'defaultSkipTests',
|
'defaultSkipTests',
|
||||||
'enableDependencyBlocking',
|
'enableDependencyBlocking',
|
||||||
@@ -379,7 +378,6 @@ export async function refreshSettingsFromServer(): Promise<boolean> {
|
|||||||
theme: serverSettings.theme as unknown as ThemeMode,
|
theme: serverSettings.theme as unknown as ThemeMode,
|
||||||
sidebarOpen: serverSettings.sidebarOpen,
|
sidebarOpen: serverSettings.sidebarOpen,
|
||||||
chatHistoryOpen: serverSettings.chatHistoryOpen,
|
chatHistoryOpen: serverSettings.chatHistoryOpen,
|
||||||
kanbanCardDetailLevel: serverSettings.kanbanCardDetailLevel,
|
|
||||||
maxConcurrency: serverSettings.maxConcurrency,
|
maxConcurrency: serverSettings.maxConcurrency,
|
||||||
defaultSkipTests: serverSettings.defaultSkipTests,
|
defaultSkipTests: serverSettings.defaultSkipTests,
|
||||||
enableDependencyBlocking: serverSettings.enableDependencyBlocking,
|
enableDependencyBlocking: serverSettings.enableDependencyBlocking,
|
||||||
|
|||||||
@@ -1858,7 +1858,6 @@ export class HttpApiClient implements ElectronAPI {
|
|||||||
theme: string;
|
theme: string;
|
||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
chatHistoryOpen: boolean;
|
chatHistoryOpen: boolean;
|
||||||
kanbanCardDetailLevel: string;
|
|
||||||
maxConcurrency: number;
|
maxConcurrency: number;
|
||||||
defaultSkipTests: boolean;
|
defaultSkipTests: boolean;
|
||||||
enableDependencyBlocking: boolean;
|
enableDependencyBlocking: boolean;
|
||||||
|
|||||||
@@ -114,8 +114,6 @@ function saveThemeToStorage(theme: ThemeMode): void {
|
|||||||
setItem(THEME_STORAGE_KEY, theme);
|
setItem(THEME_STORAGE_KEY, theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
export type KanbanCardDetailLevel = 'minimal' | 'standard' | 'detailed';
|
|
||||||
|
|
||||||
export type BoardViewMode = 'kanban' | 'graph';
|
export type BoardViewMode = 'kanban' | 'graph';
|
||||||
|
|
||||||
export interface ApiKeys {
|
export interface ApiKeys {
|
||||||
@@ -508,7 +506,6 @@ export interface AppState {
|
|||||||
maxConcurrency: number; // Maximum number of concurrent agent tasks
|
maxConcurrency: number; // Maximum number of concurrent agent tasks
|
||||||
|
|
||||||
// Kanban Card Display Settings
|
// Kanban Card Display Settings
|
||||||
kanbanCardDetailLevel: KanbanCardDetailLevel; // Level of detail shown on kanban cards
|
|
||||||
boardViewMode: BoardViewMode; // Whether to show kanban or dependency graph view
|
boardViewMode: BoardViewMode; // Whether to show kanban or dependency graph view
|
||||||
|
|
||||||
// Feature Default Settings
|
// Feature Default Settings
|
||||||
@@ -874,7 +871,6 @@ export interface AppActions {
|
|||||||
setMaxConcurrency: (max: number) => void;
|
setMaxConcurrency: (max: number) => void;
|
||||||
|
|
||||||
// Kanban Card Settings actions
|
// Kanban Card Settings actions
|
||||||
setKanbanCardDetailLevel: (level: KanbanCardDetailLevel) => void;
|
|
||||||
setBoardViewMode: (mode: BoardViewMode) => void;
|
setBoardViewMode: (mode: BoardViewMode) => void;
|
||||||
|
|
||||||
// Feature Default Settings actions
|
// Feature Default Settings actions
|
||||||
@@ -1127,7 +1123,6 @@ const initialState: AppState = {
|
|||||||
autoModeByProject: {},
|
autoModeByProject: {},
|
||||||
autoModeActivityLog: [],
|
autoModeActivityLog: [],
|
||||||
maxConcurrency: 3, // Default to 3 concurrent agents
|
maxConcurrency: 3, // Default to 3 concurrent agents
|
||||||
kanbanCardDetailLevel: 'standard', // Default to standard detail level
|
|
||||||
boardViewMode: 'kanban', // Default to kanban view
|
boardViewMode: 'kanban', // Default to kanban view
|
||||||
defaultSkipTests: true, // Default to manual verification (tests disabled)
|
defaultSkipTests: true, // Default to manual verification (tests disabled)
|
||||||
enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
|
enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
|
||||||
@@ -1731,7 +1726,6 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
setMaxConcurrency: (max) => set({ maxConcurrency: max }),
|
setMaxConcurrency: (max) => set({ maxConcurrency: max }),
|
||||||
|
|
||||||
// Kanban Card Settings actions
|
// Kanban Card Settings actions
|
||||||
setKanbanCardDetailLevel: (level) => set({ kanbanCardDetailLevel: level }),
|
|
||||||
setBoardViewMode: (mode) => set({ boardViewMode: mode }),
|
setBoardViewMode: (mode) => set({ boardViewMode: mode }),
|
||||||
|
|
||||||
// Feature Default Settings actions
|
// Feature Default Settings actions
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ const SETTINGS_FIELDS_TO_SYNC = [
|
|||||||
'theme',
|
'theme',
|
||||||
'sidebarOpen',
|
'sidebarOpen',
|
||||||
'chatHistoryOpen',
|
'chatHistoryOpen',
|
||||||
'kanbanCardDetailLevel',
|
|
||||||
'maxConcurrency',
|
'maxConcurrency',
|
||||||
'defaultSkipTests',
|
'defaultSkipTests',
|
||||||
'enableDependencyBlocking',
|
'enableDependencyBlocking',
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ export { DEFAULT_PROMPT_CUSTOMIZATION } from './prompts.js';
|
|||||||
// Settings types and constants
|
// Settings types and constants
|
||||||
export type {
|
export type {
|
||||||
ThemeMode,
|
ThemeMode,
|
||||||
KanbanCardDetailLevel,
|
|
||||||
PlanningMode,
|
PlanningMode,
|
||||||
ThinkingLevel,
|
ThinkingLevel,
|
||||||
ModelProvider,
|
ModelProvider,
|
||||||
|
|||||||
@@ -65,9 +65,6 @@ export type ThemeMode =
|
|||||||
| 'nordlight'
|
| 'nordlight'
|
||||||
| 'blossom';
|
| 'blossom';
|
||||||
|
|
||||||
/** KanbanCardDetailLevel - Controls how much information is displayed on kanban cards */
|
|
||||||
export type KanbanCardDetailLevel = 'minimal' | 'standard' | 'detailed';
|
|
||||||
|
|
||||||
/** PlanningMode - Planning levels for feature generation workflows */
|
/** PlanningMode - Planning levels for feature generation workflows */
|
||||||
export type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
|
export type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
|
||||||
|
|
||||||
@@ -362,8 +359,6 @@ export interface GlobalSettings {
|
|||||||
sidebarOpen: boolean;
|
sidebarOpen: boolean;
|
||||||
/** Whether chat history panel is open */
|
/** Whether chat history panel is open */
|
||||||
chatHistoryOpen: boolean;
|
chatHistoryOpen: boolean;
|
||||||
/** How much detail to show on kanban cards */
|
|
||||||
kanbanCardDetailLevel: KanbanCardDetailLevel;
|
|
||||||
|
|
||||||
// Feature Generation Defaults
|
// Feature Generation Defaults
|
||||||
/** Max features to generate concurrently */
|
/** Max features to generate concurrently */
|
||||||
@@ -682,7 +677,6 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
|
|||||||
theme: 'dark',
|
theme: 'dark',
|
||||||
sidebarOpen: true,
|
sidebarOpen: true,
|
||||||
chatHistoryOpen: false,
|
chatHistoryOpen: false,
|
||||||
kanbanCardDetailLevel: 'standard',
|
|
||||||
maxConcurrency: 3,
|
maxConcurrency: 3,
|
||||||
defaultSkipTests: true,
|
defaultSkipTests: true,
|
||||||
enableDependencyBlocking: true,
|
enableDependencyBlocking: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user