diff --git a/apps/server/src/services/settings-service.ts b/apps/server/src/services/settings-service.ts
index 9270b3fb..f1dfd45c 100644
--- a/apps/server/src/services/settings-service.ts
+++ b/apps/server/src/services/settings-service.ts
@@ -600,8 +600,6 @@ export class SettingsService {
theme: (appState.theme as GlobalSettings['theme']) || 'dark',
sidebarOpen: appState.sidebarOpen !== undefined ? (appState.sidebarOpen as boolean) : true,
chatHistoryOpen: (appState.chatHistoryOpen as boolean) || false,
- kanbanCardDetailLevel:
- (appState.kanbanCardDetailLevel as GlobalSettings['kanbanCardDetailLevel']) || 'standard',
maxConcurrency: (appState.maxConcurrency as number) || 3,
defaultSkipTests:
appState.defaultSkipTests !== undefined ? (appState.defaultSkipTests as boolean) : true,
diff --git a/apps/server/src/types/settings.ts b/apps/server/src/types/settings.ts
index e785f8ea..98bce97f 100644
--- a/apps/server/src/types/settings.ts
+++ b/apps/server/src/types/settings.ts
@@ -7,7 +7,6 @@
export type {
ThemeMode,
- KanbanCardDetailLevel,
ModelAlias,
PlanningMode,
ThinkingLevel,
diff --git a/apps/ui/scripts/setup-e2e-fixtures.mjs b/apps/ui/scripts/setup-e2e-fixtures.mjs
index 26488791..356e419b 100644
--- a/apps/ui/scripts/setup-e2e-fixtures.mjs
+++ b/apps/ui/scripts/setup-e2e-fixtures.mjs
@@ -41,7 +41,6 @@ const E2E_SETTINGS = {
theme: 'dark',
sidebarOpen: true,
chatHistoryOpen: false,
- kanbanCardDetailLevel: 'standard',
maxConcurrency: 3,
defaultSkipTests: true,
enableDependencyBlocking: true,
diff --git a/apps/ui/src/components/views/board-view.tsx b/apps/ui/src/components/views/board-view.tsx
index ebd83fac..810d1a91 100644
--- a/apps/ui/src/components/views/board-view.tsx
+++ b/apps/ui/src/components/views/board-view.tsx
@@ -7,7 +7,24 @@ import {
useSensors,
rectIntersection,
pointerWithin,
+ type PointerEvent as DndPointerEvent,
} 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 { getElectronAPI } from '@/lib/electron';
import { getHttpApiClient } from '@/lib/http-api-client';
@@ -73,8 +90,6 @@ export function BoardView() {
maxConcurrency,
setMaxConcurrency,
defaultSkipTests,
- kanbanCardDetailLevel,
- setKanbanCardDetailLevel,
boardViewMode,
setBoardViewMode,
specCreatingForProject,
@@ -248,7 +263,7 @@ export function BoardView() {
}, []);
const sensors = useSensors(
- useSensor(PointerSensor, {
+ useSensor(DialogAwarePointerSensor, {
activationConstraint: {
distance: 8,
},
@@ -1209,8 +1224,6 @@ export function BoardView() {
onShowBoardBackground={() => setShowBoardBackgroundModal(true)}
onShowCompletedModal={() => setShowCompletedModal(true)}
completedCount={completedFeatures.length}
- kanbanCardDetailLevel={kanbanCardDetailLevel}
- onDetailLevelChange={setKanbanCardDetailLevel}
boardViewMode={boardViewMode}
onBoardViewModeChange={setBoardViewMode}
/>
diff --git a/apps/ui/src/components/views/board-view/board-controls.tsx b/apps/ui/src/components/views/board-view/board-controls.tsx
index 14733637..2d398c18 100644
--- a/apps/ui/src/components/views/board-view/board-controls.tsx
+++ b/apps/ui/src/components/views/board-view/board-controls.tsx
@@ -1,6 +1,6 @@
import { Button } from '@/components/ui/button';
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 { BoardViewMode } from '@/store/app-store';
@@ -9,8 +9,6 @@ interface BoardControlsProps {
onShowBoardBackground: () => void;
onShowCompletedModal: () => void;
completedCount: number;
- kanbanCardDetailLevel: 'minimal' | 'standard' | 'detailed';
- onDetailLevelChange: (level: 'minimal' | 'standard' | 'detailed') => void;
boardViewMode: BoardViewMode;
onBoardViewModeChange: (mode: BoardViewMode) => void;
}
@@ -20,8 +18,6 @@ export function BoardControls({
onShowBoardBackground,
onShowCompletedModal,
completedCount,
- kanbanCardDetailLevel,
- onDetailLevelChange,
boardViewMode,
onBoardViewModeChange,
}: BoardControlsProps) {
@@ -115,70 +111,6 @@ export function BoardControls({
Completed Features ({completedCount})
-
- {/* Kanban Card Detail Level Toggle */}
-
-
-
- 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"
- >
-
-
-
-
- Minimal - Title & category only
-
-
-
-
- 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"
- >
-
-
-
-
- Standard - Steps & progress
-
-
-
-
- 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"
- >
-
-
-
-
- Detailed - Model, tools & tasks
-
-
-
);
diff --git a/apps/ui/src/components/views/board-view/board-header.tsx b/apps/ui/src/components/views/board-view/board-header.tsx
index 36b19acc..4611c843 100644
--- a/apps/ui/src/components/views/board-view/board-header.tsx
+++ b/apps/ui/src/components/views/board-view/board-header.tsx
@@ -3,6 +3,7 @@ import { Button } from '@/components/ui/button';
import { Slider } from '@/components/ui/slider';
import { Switch } from '@/components/ui/switch';
import { Label } from '@/components/ui/label';
+import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Bot, Wand2, Settings2, GitBranch } from 'lucide-react';
import { UsagePopover } from '@/components/usage-popover';
import { useAppStore } from '@/store/app-store';
@@ -107,27 +108,47 @@ export function BoardHeader({
)}
- {/* Concurrency Slider - only show after mount to prevent hydration issues */}
+ {/* Concurrency Control - only show after mount to prevent hydration issues */}
{isMounted && (
-
-
- Agents
- onConcurrencyChange(value[0])}
- min={1}
- max={10}
- step={1}
- className="w-20"
- data-testid="concurrency-slider"
- />
-
- {runningAgentsCount} / {maxConcurrency}
-
-
+
+
+
+
+ Agents
+
+ {runningAgentsCount}/{maxConcurrency}
+
+
+
+
+
+
+
Max Concurrent Agents
+
+ Controls how many AI agents can run simultaneously. Higher values process more
+ features in parallel but use more API resources.
+
+
+
+ onConcurrencyChange(value[0])}
+ min={1}
+ max={10}
+ step={1}
+ className="flex-1"
+ data-testid="concurrency-slider"
+ />
+
+ {maxConcurrency}
+
+
+
+
+
)}
{/* Auto Mode Toggle - only show after mount to prevent hydration issues */}
diff --git a/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx b/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
index 5439b675..b73a8d04 100644
--- a/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
+++ b/apps/ui/src/components/views/board-view/components/kanban-card/agent-info-panel.tsx
@@ -1,6 +1,6 @@
// @ts-nocheck
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 { getProviderFromModel } from '@/lib/utils';
import {
@@ -68,12 +68,9 @@ export function AgentInfoPanel({
summary,
isCurrentAutoTask,
}: AgentInfoPanelProps) {
- const { kanbanCardDetailLevel } = useAppStore();
const [agentInfo, setAgentInfo] = useState(null);
const [isSummaryDialogOpen, setIsSummaryDialogOpen] = useState(false);
- const showAgentInfo = kanbanCardDetailLevel === 'detailed';
-
useEffect(() => {
const loadContext = async () => {
if (contextContent) {
@@ -123,7 +120,7 @@ export function AgentInfoPanel({
}
}, [feature.id, feature.status, contextContent, isCurrentAutoTask]);
// Model/Preset Info for Backlog Cards
- if (showAgentInfo && feature.status === 'backlog') {
+ if (feature.status === 'backlog') {
const provider = getProviderFromModel(feature.model);
const isCodex = provider === 'codex';
const isClaude = provider === 'claude';
@@ -160,7 +157,7 @@ export function AgentInfoPanel({
}
// Agent Info Panel for non-backlog cards
- if (showAgentInfo && feature.status !== 'backlog' && agentInfo) {
+ if (feature.status !== 'backlog' && agentInfo) {
return (
<>
@@ -255,7 +252,11 @@ export function AgentInfoPanel({
-
+
e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
+ >
{feature.summary || summary || agentInfo.summary}
@@ -292,58 +293,15 @@ export function AgentInfoPanel({
);
}
- // Show just the todo list for non-backlog features when showAgentInfo is false
- // This ensures users always see what the agent is working on
- if (!showAgentInfo && feature.status !== 'backlog' && agentInfo && agentInfo.todos.length > 0) {
- return (
-
-
-
-
- {agentInfo.todos.filter((t) => t.status === 'completed').length}/
- {agentInfo.todos.length} tasks
-
-
-
- {agentInfo.todos.map((todo, idx) => (
-
- {todo.status === 'completed' ? (
-
- ) : todo.status === 'in_progress' ? (
-
- ) : (
-
- )}
-
- {todo.content}
-
-
- ))}
-
-
- );
- }
-
- // Always render SummaryDialog if showAgentInfo is true (even if no agentInfo yet)
+ // Always render SummaryDialog (even if no agentInfo yet)
// This ensures the dialog can be opened from the expand button
return (
- <>
- {showAgentInfo && (
-
- )}
- >
+
);
}
diff --git a/apps/ui/src/components/views/board-view/components/kanban-card/summary-dialog.tsx b/apps/ui/src/components/views/board-view/components/kanban-card/summary-dialog.tsx
index b469da8f..db9f579d 100644
--- a/apps/ui/src/components/views/board-view/components/kanban-card/summary-dialog.tsx
+++ b/apps/ui/src/components/views/board-view/components/kanban-card/summary-dialog.tsx
@@ -31,8 +31,10 @@ export function SummaryDialog({
return (
e.stopPropagation()}
+ onMouseDown={(e) => e.stopPropagation()}
>
diff --git a/apps/ui/src/components/views/board-view/kanban-board.tsx b/apps/ui/src/components/views/board-view/kanban-board.tsx
index 9a3a5c26..1fd39843 100644
--- a/apps/ui/src/components/views/board-view/kanban-board.tsx
+++ b/apps/ui/src/components/views/board-view/kanban-board.tsx
@@ -3,7 +3,7 @@ import { DndContext, DragOverlay } from '@dnd-kit/core';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { Button } from '@/components/ui/button';
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 { useResponsiveKanban } from '@/hooks/use-responsive-kanban';
import { getColumnsWithPipeline, type ColumnId } from './constants';
@@ -90,6 +90,10 @@ export function KanbanBoard({
// Generate columns including pipeline steps
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
// containerStyle handles centering and ensures columns fit without horizontal scroll in Electron
const { columnWidth, containerStyle } = useResponsiveKanban(columns.length);
@@ -196,6 +200,9 @@ export function KanbanBoard({
>
Add Feature
+
+ {formatShortcut(addFeatureShortcut, true)}
+
) : undefined
}
diff --git a/apps/ui/src/hooks/use-settings-migration.ts b/apps/ui/src/hooks/use-settings-migration.ts
index 3449abf5..f5d421e0 100644
--- a/apps/ui/src/hooks/use-settings-migration.ts
+++ b/apps/ui/src/hooks/use-settings-migration.ts
@@ -139,7 +139,6 @@ export function parseLocalStorageSettings(): Partial | null {
theme: state.theme as GlobalSettings['theme'],
sidebarOpen: state.sidebarOpen as boolean,
chatHistoryOpen: state.chatHistoryOpen as boolean,
- kanbanCardDetailLevel: state.kanbanCardDetailLevel as GlobalSettings['kanbanCardDetailLevel'],
maxConcurrency: state.maxConcurrency as number,
defaultSkipTests: state.defaultSkipTests 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,
sidebarOpen: settings.sidebarOpen ?? true,
chatHistoryOpen: settings.chatHistoryOpen ?? false,
- kanbanCardDetailLevel: settings.kanbanCardDetailLevel ?? 'standard',
maxConcurrency: settings.maxConcurrency ?? 3,
defaultSkipTests: settings.defaultSkipTests ?? true,
enableDependencyBlocking: settings.enableDependencyBlocking ?? true,
@@ -582,7 +580,6 @@ function buildSettingsUpdateFromStore(): Record {
theme: state.theme,
sidebarOpen: state.sidebarOpen,
chatHistoryOpen: state.chatHistoryOpen,
- kanbanCardDetailLevel: state.kanbanCardDetailLevel,
maxConcurrency: state.maxConcurrency,
defaultSkipTests: state.defaultSkipTests,
enableDependencyBlocking: state.enableDependencyBlocking,
diff --git a/apps/ui/src/hooks/use-settings-sync.ts b/apps/ui/src/hooks/use-settings-sync.ts
index 8f17a283..4788bfb1 100644
--- a/apps/ui/src/hooks/use-settings-sync.ts
+++ b/apps/ui/src/hooks/use-settings-sync.ts
@@ -31,7 +31,6 @@ const SETTINGS_FIELDS_TO_SYNC = [
'theme',
'sidebarOpen',
'chatHistoryOpen',
- 'kanbanCardDetailLevel',
'maxConcurrency',
'defaultSkipTests',
'enableDependencyBlocking',
@@ -379,7 +378,6 @@ export async function refreshSettingsFromServer(): Promise {
theme: serverSettings.theme as unknown as ThemeMode,
sidebarOpen: serverSettings.sidebarOpen,
chatHistoryOpen: serverSettings.chatHistoryOpen,
- kanbanCardDetailLevel: serverSettings.kanbanCardDetailLevel,
maxConcurrency: serverSettings.maxConcurrency,
defaultSkipTests: serverSettings.defaultSkipTests,
enableDependencyBlocking: serverSettings.enableDependencyBlocking,
diff --git a/apps/ui/src/lib/http-api-client.ts b/apps/ui/src/lib/http-api-client.ts
index c46005bd..85392000 100644
--- a/apps/ui/src/lib/http-api-client.ts
+++ b/apps/ui/src/lib/http-api-client.ts
@@ -1858,7 +1858,6 @@ export class HttpApiClient implements ElectronAPI {
theme: string;
sidebarOpen: boolean;
chatHistoryOpen: boolean;
- kanbanCardDetailLevel: string;
maxConcurrency: number;
defaultSkipTests: boolean;
enableDependencyBlocking: boolean;
diff --git a/apps/ui/src/store/app-store.ts b/apps/ui/src/store/app-store.ts
index 91dea9bd..fa1daf4c 100644
--- a/apps/ui/src/store/app-store.ts
+++ b/apps/ui/src/store/app-store.ts
@@ -114,8 +114,6 @@ function saveThemeToStorage(theme: ThemeMode): void {
setItem(THEME_STORAGE_KEY, theme);
}
-export type KanbanCardDetailLevel = 'minimal' | 'standard' | 'detailed';
-
export type BoardViewMode = 'kanban' | 'graph';
export interface ApiKeys {
@@ -508,7 +506,6 @@ export interface AppState {
maxConcurrency: number; // Maximum number of concurrent agent tasks
// Kanban Card Display Settings
- kanbanCardDetailLevel: KanbanCardDetailLevel; // Level of detail shown on kanban cards
boardViewMode: BoardViewMode; // Whether to show kanban or dependency graph view
// Feature Default Settings
@@ -874,7 +871,6 @@ export interface AppActions {
setMaxConcurrency: (max: number) => void;
// Kanban Card Settings actions
- setKanbanCardDetailLevel: (level: KanbanCardDetailLevel) => void;
setBoardViewMode: (mode: BoardViewMode) => void;
// Feature Default Settings actions
@@ -1127,7 +1123,6 @@ const initialState: AppState = {
autoModeByProject: {},
autoModeActivityLog: [],
maxConcurrency: 3, // Default to 3 concurrent agents
- kanbanCardDetailLevel: 'standard', // Default to standard detail level
boardViewMode: 'kanban', // Default to kanban view
defaultSkipTests: true, // Default to manual verification (tests disabled)
enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
@@ -1731,7 +1726,6 @@ export const useAppStore = create()((set, get) => ({
setMaxConcurrency: (max) => set({ maxConcurrency: max }),
// Kanban Card Settings actions
- setKanbanCardDetailLevel: (level) => set({ kanbanCardDetailLevel: level }),
setBoardViewMode: (mode) => set({ boardViewMode: mode }),
// Feature Default Settings actions
diff --git a/docs/settings-api-migration.md b/docs/settings-api-migration.md
index 85bc6979..5a20fc21 100644
--- a/docs/settings-api-migration.md
+++ b/docs/settings-api-migration.md
@@ -96,7 +96,6 @@ const SETTINGS_FIELDS_TO_SYNC = [
'theme',
'sidebarOpen',
'chatHistoryOpen',
- 'kanbanCardDetailLevel',
'maxConcurrency',
'defaultSkipTests',
'enableDependencyBlocking',
diff --git a/libs/types/src/index.ts b/libs/types/src/index.ts
index d2e49cbf..7b402274 100644
--- a/libs/types/src/index.ts
+++ b/libs/types/src/index.ts
@@ -109,7 +109,6 @@ export { DEFAULT_PROMPT_CUSTOMIZATION } from './prompts.js';
// Settings types and constants
export type {
ThemeMode,
- KanbanCardDetailLevel,
PlanningMode,
ThinkingLevel,
ModelProvider,
diff --git a/libs/types/src/settings.ts b/libs/types/src/settings.ts
index 3ce87a76..07b4290d 100644
--- a/libs/types/src/settings.ts
+++ b/libs/types/src/settings.ts
@@ -65,9 +65,6 @@ export type ThemeMode =
| 'nordlight'
| 'blossom';
-/** KanbanCardDetailLevel - Controls how much information is displayed on kanban cards */
-export type KanbanCardDetailLevel = 'minimal' | 'standard' | 'detailed';
-
/** PlanningMode - Planning levels for feature generation workflows */
export type PlanningMode = 'skip' | 'lite' | 'spec' | 'full';
@@ -362,8 +359,6 @@ export interface GlobalSettings {
sidebarOpen: boolean;
/** Whether chat history panel is open */
chatHistoryOpen: boolean;
- /** How much detail to show on kanban cards */
- kanbanCardDetailLevel: KanbanCardDetailLevel;
// Feature Generation Defaults
/** Max features to generate concurrently */
@@ -682,7 +677,6 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
theme: 'dark',
sidebarOpen: true,
chatHistoryOpen: false,
- kanbanCardDetailLevel: 'standard',
maxConcurrency: 3,
defaultSkipTests: true,
enableDependencyBlocking: true,