style: fix formatting with Prettier

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
SuperComboGamer
2025-12-21 20:31:57 -05:00
parent 584f5a3426
commit 8d578558ff
295 changed files with 9088 additions and 10546 deletions

View File

@@ -1,10 +1,10 @@
export { useBoardFeatures } from "./use-board-features";
export { useBoardDragDrop } from "./use-board-drag-drop";
export { useBoardActions } from "./use-board-actions";
export { useBoardKeyboardShortcuts } from "./use-board-keyboard-shortcuts";
export { useBoardColumnFeatures } from "./use-board-column-features";
export { useBoardEffects } from "./use-board-effects";
export { useBoardBackground } from "./use-board-background";
export { useBoardPersistence } from "./use-board-persistence";
export { useFollowUpState } from "./use-follow-up-state";
export { useSuggestionsState } from "./use-suggestions-state";
export { useBoardFeatures } from './use-board-features';
export { useBoardDragDrop } from './use-board-drag-drop';
export { useBoardActions } from './use-board-actions';
export { useBoardKeyboardShortcuts } from './use-board-keyboard-shortcuts';
export { useBoardColumnFeatures } from './use-board-column-features';
export { useBoardEffects } from './use-board-effects';
export { useBoardBackground } from './use-board-background';
export { useBoardPersistence } from './use-board-persistence';
export { useFollowUpState } from './use-follow-up-state';
export { useSuggestionsState } from './use-suggestions-state';

View File

@@ -1,20 +1,17 @@
import { useMemo } from "react";
import { useAppStore, defaultBackgroundSettings } from "@/store/app-store";
import { useMemo } from 'react';
import { useAppStore, defaultBackgroundSettings } from '@/store/app-store';
interface UseBoardBackgroundProps {
currentProject: { path: string; id: string } | null;
}
export function useBoardBackground({ currentProject }: UseBoardBackgroundProps) {
const boardBackgroundByProject = useAppStore(
(state) => state.boardBackgroundByProject
);
const boardBackgroundByProject = useAppStore((state) => state.boardBackgroundByProject);
// Get background settings for current project
const backgroundSettings = useMemo(() => {
return (
(currentProject && boardBackgroundByProject[currentProject.path]) ||
defaultBackgroundSettings
(currentProject && boardBackgroundByProject[currentProject.path]) || defaultBackgroundSettings
);
}, [currentProject, boardBackgroundByProject]);
@@ -26,17 +23,15 @@ export function useBoardBackground({ currentProject }: UseBoardBackgroundProps)
return {
backgroundImage: `url(${
import.meta.env.VITE_SERVER_URL || "http://localhost:3008"
import.meta.env.VITE_SERVER_URL || 'http://localhost:3008'
}/api/fs/image?path=${encodeURIComponent(
backgroundSettings.imagePath
)}&projectPath=${encodeURIComponent(currentProject.path)}${
backgroundSettings.imageVersion
? `&v=${backgroundSettings.imageVersion}`
: ""
backgroundSettings.imageVersion ? `&v=${backgroundSettings.imageVersion}` : ''
})`,
backgroundSize: "cover",
backgroundPosition: "center",
backgroundRepeat: "no-repeat",
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
} as React.CSSProperties;
}, [backgroundSettings, currentProject]);

View File

@@ -1,18 +1,15 @@
import { useState, useCallback } from "react";
import { DragStartEvent, DragEndEvent } from "@dnd-kit/core";
import { Feature } from "@/store/app-store";
import { useAppStore } from "@/store/app-store";
import { toast } from "sonner";
import { COLUMNS, ColumnId } from "../constants";
import { useState, useCallback } from 'react';
import { DragStartEvent, DragEndEvent } from '@dnd-kit/core';
import { Feature } from '@/store/app-store';
import { useAppStore } from '@/store/app-store';
import { toast } from 'sonner';
import { COLUMNS, ColumnId } from '../constants';
interface UseBoardDragDropProps {
features: Feature[];
currentProject: { path: string; id: string } | null;
runningAutoTasks: string[];
persistFeatureUpdate: (
featureId: string,
updates: Partial<Feature>
) => Promise<void>;
persistFeatureUpdate: (featureId: string, updates: Partial<Feature>) => Promise<void>;
handleStartImplementation: (feature: Feature) => Promise<boolean>;
}
@@ -63,12 +60,10 @@ export function useBoardDragDrop({
// - verified items can always be dragged (to allow moving back to waiting_approval)
// - in_progress items can be dragged (but not if they're currently running)
// - Non-skipTests (TDD) items that are in progress cannot be dragged if they are running
if (draggedFeature.status === "in_progress") {
if (draggedFeature.status === 'in_progress') {
// Only allow dragging in_progress if it's not currently running
if (isRunningTask) {
console.log(
"[Board] Cannot drag feature - currently running"
);
console.log('[Board] Cannot drag feature - currently running');
return;
}
}
@@ -94,9 +89,9 @@ export function useBoardDragDrop({
// Handle different drag scenarios
// Note: Worktrees are created server-side at execution time based on feature.branchName
if (draggedFeature.status === "backlog") {
if (draggedFeature.status === 'backlog') {
// From backlog
if (targetStatus === "in_progress") {
if (targetStatus === 'in_progress') {
// Use helper function to handle concurrency check and start implementation
// Server will derive workDir from feature.branchName
await handleStartImplementation(draggedFeature);
@@ -104,122 +99,110 @@ export function useBoardDragDrop({
moveFeature(featureId, targetStatus);
persistFeatureUpdate(featureId, { status: targetStatus });
}
} else if (draggedFeature.status === "waiting_approval") {
} else if (draggedFeature.status === 'waiting_approval') {
// waiting_approval features can be dragged to verified for manual verification
// NOTE: This check must come BEFORE skipTests check because waiting_approval
// features often have skipTests=true, and we want status-based handling first
if (targetStatus === "verified") {
moveFeature(featureId, "verified");
if (targetStatus === 'verified') {
moveFeature(featureId, 'verified');
// Clear justFinishedAt timestamp when manually verifying via drag
persistFeatureUpdate(featureId, {
status: "verified",
status: 'verified',
justFinishedAt: undefined,
});
toast.success("Feature verified", {
toast.success('Feature verified', {
description: `Manually verified: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
} else if (targetStatus === "backlog") {
} else if (targetStatus === 'backlog') {
// Allow moving waiting_approval cards back to backlog
moveFeature(featureId, "backlog");
moveFeature(featureId, 'backlog');
// Clear justFinishedAt timestamp when moving back to backlog
persistFeatureUpdate(featureId, {
status: "backlog",
status: 'backlog',
justFinishedAt: undefined,
});
toast.info("Feature moved to backlog", {
toast.info('Feature moved to backlog', {
description: `Moved to Backlog: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
}
} else if (draggedFeature.status === "in_progress") {
} else if (draggedFeature.status === 'in_progress') {
// Handle in_progress features being moved
if (targetStatus === "backlog") {
if (targetStatus === 'backlog') {
// Allow moving in_progress cards back to backlog
moveFeature(featureId, "backlog");
persistFeatureUpdate(featureId, { status: "backlog" });
toast.info("Feature moved to backlog", {
moveFeature(featureId, 'backlog');
persistFeatureUpdate(featureId, { status: 'backlog' });
toast.info('Feature moved to backlog', {
description: `Moved to Backlog: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
} else if (
targetStatus === "verified" &&
draggedFeature.skipTests
) {
} else if (targetStatus === 'verified' && draggedFeature.skipTests) {
// Manual verify via drag (only for skipTests features)
moveFeature(featureId, "verified");
persistFeatureUpdate(featureId, { status: "verified" });
toast.success("Feature verified", {
moveFeature(featureId, 'verified');
persistFeatureUpdate(featureId, { status: 'verified' });
toast.success('Feature verified', {
description: `Marked as verified: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
}
} else if (draggedFeature.skipTests) {
// skipTests feature being moved between verified and waiting_approval
if (
targetStatus === "waiting_approval" &&
draggedFeature.status === "verified"
) {
if (targetStatus === 'waiting_approval' && draggedFeature.status === 'verified') {
// Move verified feature back to waiting_approval
moveFeature(featureId, "waiting_approval");
persistFeatureUpdate(featureId, { status: "waiting_approval" });
toast.info("Feature moved back", {
moveFeature(featureId, 'waiting_approval');
persistFeatureUpdate(featureId, { status: 'waiting_approval' });
toast.info('Feature moved back', {
description: `Moved back to Waiting Approval: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
} else if (targetStatus === "backlog") {
} else if (targetStatus === 'backlog') {
// Allow moving skipTests cards back to backlog (from verified)
moveFeature(featureId, "backlog");
persistFeatureUpdate(featureId, { status: "backlog" });
toast.info("Feature moved to backlog", {
moveFeature(featureId, 'backlog');
persistFeatureUpdate(featureId, { status: 'backlog' });
toast.info('Feature moved to backlog', {
description: `Moved to Backlog: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
}
} else if (draggedFeature.status === "verified") {
} else if (draggedFeature.status === 'verified') {
// Handle verified TDD (non-skipTests) features being moved back
if (targetStatus === "waiting_approval") {
if (targetStatus === 'waiting_approval') {
// Move verified feature back to waiting_approval
moveFeature(featureId, "waiting_approval");
persistFeatureUpdate(featureId, { status: "waiting_approval" });
toast.info("Feature moved back", {
moveFeature(featureId, 'waiting_approval');
persistFeatureUpdate(featureId, { status: 'waiting_approval' });
toast.info('Feature moved back', {
description: `Moved back to Waiting Approval: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
} else if (targetStatus === "backlog") {
} else if (targetStatus === 'backlog') {
// Allow moving verified cards back to backlog
moveFeature(featureId, "backlog");
persistFeatureUpdate(featureId, { status: "backlog" });
toast.info("Feature moved to backlog", {
moveFeature(featureId, 'backlog');
persistFeatureUpdate(featureId, { status: 'backlog' });
toast.info('Feature moved to backlog', {
description: `Moved to Backlog: ${draggedFeature.description.slice(
0,
50
)}${draggedFeature.description.length > 50 ? "..." : ""}`,
)}${draggedFeature.description.length > 50 ? '...' : ''}`,
});
}
}
},
[
features,
runningAutoTasks,
moveFeature,
persistFeatureUpdate,
handleStartImplementation,
]
[features, runningAutoTasks, moveFeature, persistFeatureUpdate, handleStartImplementation]
);
return {

View File

@@ -1,6 +1,6 @@
import { useEffect } from "react";
import { getElectronAPI } from "@/lib/electron";
import { useAppStore } from "@/store/app-store";
import { useEffect } from 'react';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
interface UseBoardEffectsProps {
currentProject: { path: string; id: string } | null;
@@ -43,11 +43,11 @@ export function useBoardEffects({
if (!api?.suggestions) return;
const unsubscribe = api.suggestions.onEvent((event) => {
if (event.type === "suggestions_complete" && event.suggestions) {
if (event.type === 'suggestions_complete' && event.suggestions) {
setSuggestionsCount(event.suggestions.length);
setFeatureSuggestions(event.suggestions);
setIsGeneratingSuggestions(false);
} else if (event.type === "suggestions_error") {
} else if (event.type === 'suggestions_error') {
setIsGeneratingSuggestions(false);
}
});
@@ -64,9 +64,9 @@ export function useBoardEffects({
const unsubscribe = api.specRegeneration.onEvent((event) => {
console.log(
"[BoardView] Spec regeneration event:",
'[BoardView] Spec regeneration event:',
event.type,
"for project:",
'for project:',
event.projectPath
);
@@ -74,9 +74,9 @@ export function useBoardEffects({
return;
}
if (event.type === "spec_regeneration_complete") {
if (event.type === 'spec_regeneration_complete') {
setSpecCreatingForProject(null);
} else if (event.type === "spec_regeneration_error") {
} else if (event.type === 'spec_regeneration_error') {
setSpecCreatingForProject(null);
}
});
@@ -101,10 +101,7 @@ export function useBoardEffects({
const { clearRunningTasks, addRunningTask } = useAppStore.getState();
if (status.runningFeatures) {
console.log(
"[Board] Syncing running tasks from backend:",
status.runningFeatures
);
console.log('[Board] Syncing running tasks from backend:', status.runningFeatures);
clearRunningTasks(projectId);
@@ -114,7 +111,7 @@ export function useBoardEffects({
}
}
} catch (error) {
console.error("[Board] Failed to sync running tasks:", error);
console.error('[Board] Failed to sync running tasks:', error);
}
};
@@ -126,9 +123,7 @@ export function useBoardEffects({
const checkAllContexts = async () => {
const featuresWithPotentialContext = features.filter(
(f) =>
f.status === "in_progress" ||
f.status === "waiting_approval" ||
f.status === "verified"
f.status === 'in_progress' || f.status === 'waiting_approval' || f.status === 'verified'
);
const contextChecks = await Promise.all(
featuresWithPotentialContext.map(async (f) => ({

View File

@@ -1,7 +1,7 @@
import { useState, useCallback, useEffect, useRef } from "react";
import { useAppStore, Feature } from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import { toast } from "sonner";
import { useState, useCallback, useEffect, useRef } from 'react';
import { useAppStore, Feature } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import { toast } from 'sonner';
interface UseBoardFeaturesProps {
currentProject: { path: string; id: string } | null;
@@ -24,8 +24,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
const currentPath = currentProject.path;
const previousPath = prevProjectPathRef.current;
const isProjectSwitch =
previousPath !== null && currentPath !== previousPath;
const isProjectSwitch = previousPath !== null && currentPath !== previousPath;
// Get cached features from store (without adding to dependencies)
const cachedFeatures = useAppStore.getState().features;
@@ -33,9 +32,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// If project switched, mark it but don't clear features yet
// We'll clear after successful API load to prevent data loss
if (isProjectSwitch) {
console.log(
`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`
);
console.log(`[BoardView] Project switch detected: ${previousPath} -> ${currentPath}`);
isSwitchingProjectRef.current = true;
isInitialLoadRef.current = true;
}
@@ -51,7 +48,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
try {
const api = getElectronAPI();
if (!api.features) {
console.error("[BoardView] Features API not available");
console.error('[BoardView] Features API not available');
// Keep cached features if API is unavailable
return;
}
@@ -59,17 +56,15 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
const result = await api.features.getAll(currentProject.path);
if (result.success && result.features) {
const featuresWithIds = result.features.map(
(f: any, index: number) => ({
...f,
id: f.id || `feature-${index}-${Date.now()}`,
status: f.status || "backlog",
startedAt: f.startedAt, // Preserve startedAt timestamp
// Ensure model and thinkingLevel are set for backward compatibility
model: f.model || "opus",
thinkingLevel: f.thinkingLevel || "none",
})
);
const featuresWithIds = result.features.map((f: any, index: number) => ({
...f,
id: f.id || `feature-${index}-${Date.now()}`,
status: f.status || 'backlog',
startedAt: f.startedAt, // Preserve startedAt timestamp
// Ensure model and thinkingLevel are set for backward compatibility
model: f.model || 'opus',
thinkingLevel: f.thinkingLevel || 'none',
}));
// Successfully loaded features - now safe to set them
setFeatures(featuresWithIds);
@@ -78,7 +73,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories([]);
}
} else if (!result.success && result.error) {
console.error("[BoardView] API returned error:", result.error);
console.error('[BoardView] API returned error:', result.error);
// If it's a new project or the error indicates no features found,
// that's expected - start with empty array
if (isProjectSwitch) {
@@ -88,7 +83,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// Otherwise keep cached features
}
} catch (error) {
console.error("Failed to load features:", error);
console.error('Failed to load features:', error);
// On error, keep existing cached features for the current project
// Only clear on project switch if we have no features from server
if (isProjectSwitch && cachedFeatures.length === 0) {
@@ -108,9 +103,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
try {
const api = getElectronAPI();
const result = await api.readFile(
`${currentProject.path}/.automaker/categories.json`
);
const result = await api.readFile(`${currentProject.path}/.automaker/categories.json`);
if (result.success && result.content) {
const parsed = JSON.parse(result.content);
@@ -122,7 +115,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories([]);
}
} catch (error) {
console.error("Failed to load categories:", error);
console.error('Failed to load categories:', error);
// If file doesn't exist, ensure categories are cleared
setPersistedCategories([]);
}
@@ -154,7 +147,7 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
setPersistedCategories(categories);
}
} catch (error) {
console.error("Failed to save category:", error);
console.error('Failed to save category:', error);
}
},
[currentProject, persistedCategories]
@@ -168,13 +161,11 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
const unsubscribe = api.specRegeneration.onEvent((event) => {
// Refresh the kanban board when spec regeneration completes for the current project
if (
event.type === "spec_regeneration_complete" &&
event.type === 'spec_regeneration_complete' &&
currentProject &&
event.projectPath === currentProject.path
) {
console.log(
"[BoardView] Spec regeneration complete, refreshing features"
);
console.log('[BoardView] Spec regeneration complete, refreshing features');
loadFeatures();
}
});
@@ -195,32 +186,26 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
const unsubscribe = api.autoMode.onEvent((event) => {
// Use event's projectPath or projectId if available, otherwise use current project
// Board view only reacts to events for the currently selected project
const eventProjectId =
("projectId" in event && event.projectId) || projectId;
const eventProjectId = ('projectId' in event && event.projectId) || projectId;
if (event.type === "auto_mode_feature_complete") {
if (event.type === 'auto_mode_feature_complete') {
// Reload features when a feature is completed
console.log("[Board] Feature completed, reloading features...");
console.log('[Board] Feature completed, reloading features...');
loadFeatures();
// Play ding sound when feature is done (unless muted)
const { muteDoneSound } = useAppStore.getState();
if (!muteDoneSound) {
const audio = new Audio("/sounds/ding.mp3");
audio
.play()
.catch((err) => console.warn("Could not play ding sound:", err));
const audio = new Audio('/sounds/ding.mp3');
audio.play().catch((err) => console.warn('Could not play ding sound:', err));
}
} else if (event.type === "plan_approval_required") {
} else if (event.type === 'plan_approval_required') {
// Reload features when plan is generated and requires approval
// This ensures the feature card shows the "Approve Plan" button
console.log("[Board] Plan approval required, reloading features...");
console.log('[Board] Plan approval required, reloading features...');
loadFeatures();
} else if (event.type === "auto_mode_error") {
} else if (event.type === 'auto_mode_error') {
// Reload features when an error occurs (feature moved to waiting_approval)
console.log(
"[Board] Feature error, reloading features...",
event.error
);
console.log('[Board] Feature error, reloading features...', event.error);
// Remove from running tasks so it moves to the correct column
if (event.featureId) {
@@ -231,20 +216,20 @@ export function useBoardFeatures({ currentProject }: UseBoardFeaturesProps) {
// Check for authentication errors and show a more helpful message
const isAuthError =
event.errorType === "authentication" ||
event.errorType === 'authentication' ||
(event.error &&
(event.error.includes("Authentication failed") ||
event.error.includes("Invalid API key")));
(event.error.includes('Authentication failed') ||
event.error.includes('Invalid API key')));
if (isAuthError) {
toast.error("Authentication Failed", {
toast.error('Authentication Failed', {
description:
"Your API key is invalid or expired. Please check Settings or run 'claude login' in terminal.",
duration: 10000,
});
} else {
toast.error("Agent encountered an error", {
description: event.error || "Check the logs for details",
toast.error('Agent encountered an error', {
description: event.error || 'Check the logs for details',
});
}
}

View File

@@ -1,10 +1,10 @@
import { useMemo, useRef, useEffect } from "react";
import { useMemo, useRef, useEffect } from 'react';
import {
useKeyboardShortcuts,
useKeyboardShortcutsConfig,
KeyboardShortcut,
} from "@/hooks/use-keyboard-shortcuts";
import { Feature } from "@/store/app-store";
} from '@/hooks/use-keyboard-shortcuts';
import { Feature } from '@/store/app-store';
interface UseBoardKeyboardShortcutsProps {
features: Feature[];
@@ -27,7 +27,7 @@ export function useBoardKeyboardShortcuts({
const inProgressFeaturesForShortcuts = useMemo(() => {
return features.filter((f) => {
const isRunning = runningAutoTasks.includes(f.id);
return isRunning || f.status === "in_progress";
return isRunning || f.status === 'in_progress';
});
}, [features, runningAutoTasks]);
@@ -45,19 +45,19 @@ export function useBoardKeyboardShortcuts({
{
key: shortcuts.addFeature,
action: onAddFeature,
description: "Add new feature",
description: 'Add new feature',
},
{
key: shortcuts.startNext,
action: () => startNextFeaturesRef.current(),
description: "Start next features from backlog",
description: 'Start next features from backlog',
},
];
// Add shortcuts for in-progress cards (1-9 and 0 for 10th)
inProgressFeaturesForShortcuts.slice(0, 10).forEach((feature, index) => {
// Keys 1-9 for first 9 cards, 0 for 10th card
const key = index === 9 ? "0" : String(index + 1);
const key = index === 9 ? '0' : String(index + 1);
shortcutsList.push({
key,
action: () => {

View File

@@ -1,15 +1,13 @@
import { useCallback } from "react";
import { Feature } from "@/store/app-store";
import { getElectronAPI } from "@/lib/electron";
import { useAppStore } from "@/store/app-store";
import { useCallback } from 'react';
import { Feature } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
interface UseBoardPersistenceProps {
currentProject: { path: string; id: string } | null;
}
export function useBoardPersistence({
currentProject,
}: UseBoardPersistenceProps) {
export function useBoardPersistence({ currentProject }: UseBoardPersistenceProps) {
const { updateFeature } = useAppStore();
// Persist feature update to API (replaces saveFeatures)
@@ -20,20 +18,16 @@ export function useBoardPersistence({
try {
const api = getElectronAPI();
if (!api.features) {
console.error("[BoardView] Features API not available");
console.error('[BoardView] Features API not available');
return;
}
const result = await api.features.update(
currentProject.path,
featureId,
updates
);
const result = await api.features.update(currentProject.path, featureId, updates);
if (result.success && result.feature) {
updateFeature(result.feature.id, result.feature);
}
} catch (error) {
console.error("Failed to persist feature update:", error);
console.error('Failed to persist feature update:', error);
}
},
[currentProject, updateFeature]
@@ -47,7 +41,7 @@ export function useBoardPersistence({
try {
const api = getElectronAPI();
if (!api.features) {
console.error("[BoardView] Features API not available");
console.error('[BoardView] Features API not available');
return;
}
@@ -56,7 +50,7 @@ export function useBoardPersistence({
updateFeature(result.feature.id, result.feature);
}
} catch (error) {
console.error("Failed to persist feature creation:", error);
console.error('Failed to persist feature creation:', error);
}
},
[currentProject, updateFeature]
@@ -70,13 +64,13 @@ export function useBoardPersistence({
try {
const api = getElectronAPI();
if (!api.features) {
console.error("[BoardView] Features API not available");
console.error('[BoardView] Features API not available');
return;
}
await api.features.delete(currentProject.path, featureId);
} catch (error) {
console.error("Failed to persist feature deletion:", error);
console.error('Failed to persist feature deletion:', error);
}
},
[currentProject]

View File

@@ -1,32 +1,35 @@
import { useState, useCallback } from "react";
import { Feature } from "@/store/app-store";
import { useState, useCallback } from 'react';
import { Feature } from '@/store/app-store';
import {
FeatureImagePath as DescriptionImagePath,
ImagePreviewMap,
} from "@/components/ui/description-image-dropzone";
} from '@/components/ui/description-image-dropzone';
export function useFollowUpState() {
const [showFollowUpDialog, setShowFollowUpDialog] = useState(false);
const [followUpFeature, setFollowUpFeature] = useState<Feature | null>(null);
const [followUpPrompt, setFollowUpPrompt] = useState("");
const [followUpPrompt, setFollowUpPrompt] = useState('');
const [followUpImagePaths, setFollowUpImagePaths] = useState<DescriptionImagePath[]>([]);
const [followUpPreviewMap, setFollowUpPreviewMap] = useState<ImagePreviewMap>(() => new Map());
const resetFollowUpState = useCallback(() => {
setShowFollowUpDialog(false);
setFollowUpFeature(null);
setFollowUpPrompt("");
setFollowUpPrompt('');
setFollowUpImagePaths([]);
setFollowUpPreviewMap(new Map());
}, []);
const handleFollowUpDialogChange = useCallback((open: boolean) => {
if (!open) {
resetFollowUpState();
} else {
setShowFollowUpDialog(open);
}
}, [resetFollowUpState]);
const handleFollowUpDialogChange = useCallback(
(open: boolean) => {
if (!open) {
resetFollowUpState();
} else {
setShowFollowUpDialog(open);
}
},
[resetFollowUpState]
);
return {
// State

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from "react";
import type { FeatureSuggestion } from "@/lib/electron";
import { useState, useCallback } from 'react';
import type { FeatureSuggestion } from '@/lib/electron';
export function useSuggestionsState() {
const [showSuggestionsDialog, setShowSuggestionsDialog] = useState(false);