remove unused exports, components and hooks (#819)

This commit is contained in:
Gabriel Gordon-Hall
2025-09-25 14:48:12 +01:00
committed by GitHub
parent 2dba0713c8
commit 0cbb8cd057
20 changed files with 13 additions and 516 deletions

View File

@@ -9,7 +9,6 @@ import {
} from 'shared/types.ts';
import type { ProcessStartPayload } from '@/types/logs';
import FileChangeRenderer from './FileChangeRenderer';
import { renderJson } from './ToolDetails';
import { useExpandable } from '@/stores/useExpandableStore';
import {
AlertCircle,
@@ -41,6 +40,11 @@ type Props = {
};
type FileEditAction = Extract<ActionType, { action: 'file_edit' }>;
type JsonValue = any;
const renderJson = (v: JsonValue) => (
<pre className="whitespace-pre-wrap">{JSON.stringify(v, null, 2)}</pre>
);
const getEntryIcon = (entryType: NormalizedEntryType) => {
const iconSize = 'h-3 w-3';

View File

@@ -1,88 +0,0 @@
import MarkdownRenderer from '@/components/ui/markdown-renderer.tsx';
import RawLogText from '@/components/common/RawLogText';
import { Braces, FileText } from 'lucide-react';
type JsonValue = any;
type ToolResult = {
type: 'markdown' | 'json';
value: JsonValue;
};
type Props = {
arguments?: JsonValue | null;
result?: ToolResult | null;
commandOutput?: string | null; // presence => command mode
commandExit?:
| { type: 'success'; success: boolean }
| { type: 'exit_code'; code: number }
| null;
};
export const renderJson = (v: JsonValue) => (
<pre className="whitespace-pre-wrap">{JSON.stringify(v, null, 2)}</pre>
);
export default function ToolDetails({
arguments: args,
result,
commandOutput,
}: Props) {
const isCommandMode = commandOutput !== undefined;
return (
<div className="space-y-3">
{args && (
<section>
{!isCommandMode ? (
<>
<div className="flex items-center gap-2 text-xs text-zinc-500">
<Braces className="h-3 w-3" />
<span>Arguments</span>
</div>
{renderJson(args)}
</>
) : (
<>
<RawLogText
content={
typeof args === 'string'
? args
: JSON.stringify(args, null, 2)
}
/>
</>
)}
</section>
)}
{result && !isCommandMode && (
<section>
<div className="flex items-center gap-2 text-xs text-zinc-500">
{result.type === 'json' ? (
<Braces className="h-3 w-3" />
) : (
<FileText className="h-3 w-3" />
)}
<span>Result</span>
</div>
<div className="mt-1">
{result.type === 'markdown' ? (
<MarkdownRenderer content={String(result.value ?? '')} />
) : (
renderJson(result.value)
)}
</div>
</section>
)}
{isCommandMode && (
<section>
<div className="mt-1">
<RawLogText content={commandOutput ?? ''} />
</div>
</section>
)}
</div>
);
}

View File

@@ -26,5 +26,3 @@ export function ProfileVariantBadge({
</span>
);
}
export default ProfileVariantBadge;

View File

@@ -115,4 +115,3 @@ const PrivacyOptInDialog = NiceModal.create(() => {
});
export { PrivacyOptInDialog };
export default PrivacyOptInDialog;

View File

@@ -196,4 +196,3 @@ const CreatePrDialog = NiceModal.create(() => {
});
export { CreatePrDialog as CreatePRDialog };
export default CreatePrDialog;

View File

@@ -94,4 +94,3 @@ const DeleteTaskConfirmationDialog =
});
export { DeleteTaskConfirmationDialog };
export default DeleteTaskConfirmationDialog;

View File

@@ -1,96 +0,0 @@
import { Label } from '@/components/ui/label';
import BranchSelector from '@/components/tasks/BranchSelector';
import ExecutorProfileSelector from './ExecutorProfileSelector';
import type {
GitBranch,
ExecutorConfig,
ExecutorProfileId,
} from 'shared/types';
type Props = {
// Branch selector props
branches?: GitBranch[];
selectedBranch?: string | null;
onBranchSelect?: (branch: string) => void;
showBranchSelector?: boolean;
branchSelectorProps?: {
placeholder?: string;
className?: string;
excludeCurrentBranch?: boolean;
};
// Executor profile selector props
profiles?: Record<string, ExecutorConfig> | null;
selectedProfile?: ExecutorProfileId | null;
onProfileSelect?: (profile: ExecutorProfileId) => void;
showExecutorSelector?: boolean;
executorSelectorProps?: {
showLabel?: boolean;
showVariantSelector?: boolean;
className?: string;
};
// Common props
disabled?: boolean;
className?: string;
};
function TaskSettings({
// Branch selector props
branches = [],
selectedBranch,
onBranchSelect,
showBranchSelector = true,
branchSelectorProps = {},
// Executor profile selector props
profiles,
selectedProfile,
onProfileSelect,
showExecutorSelector = true,
executorSelectorProps = {},
// Common props
disabled = false,
className = '',
}: Props) {
return (
<div className={`space-y-3 ${className}`}>
{/* Executor Profile Selector */}
{showExecutorSelector &&
profiles &&
selectedProfile &&
onProfileSelect && (
<ExecutorProfileSelector
profiles={profiles}
selectedProfile={selectedProfile}
onProfileSelect={onProfileSelect}
disabled={disabled}
{...executorSelectorProps}
/>
)}
{/* Branch Selector */}
{showBranchSelector &&
branches.length > 0 &&
selectedBranch !== undefined &&
onBranchSelect && (
<div>
<Label htmlFor="base-branch" className="text-sm font-medium">
Branch
</Label>
<BranchSelector
branches={branches}
selectedBranch={selectedBranch}
onBranchSelect={onBranchSelect}
placeholder="Select branch"
className="mt-1.5"
{...branchSelectorProps}
/>
</div>
)}
</div>
);
}
export default TaskSettings;

View File

@@ -1,2 +1 @@
export { default as ExecutorProfileSelector } from './ExecutorProfileSelector';
export { default as TaskSettings } from './TaskSettings';

View File

@@ -12,7 +12,7 @@ function getStatusIcon(status?: string) {
return <Circle aria-hidden className="h-4 w-4 text-muted-foreground" />;
}
export function TodoPanel() {
function TodoPanel() {
const { entries } = useEntries();
const { todos } = usePinnedTodos(entries);

View File

@@ -1,8 +1,4 @@
import type {
ExecutionProcessRunReason,
ExecutionProcessStatus,
ExecutionProcess,
} from 'shared/types';
import type { ExecutionProcessRunReason } from 'shared/types';
// Process run reasons
export const PROCESS_RUN_REASONS = {
@@ -12,51 +8,14 @@ export const PROCESS_RUN_REASONS = {
DEV_SERVER: 'devserver' as ExecutionProcessRunReason,
} as const;
// Process statuses
export const PROCESS_STATUSES = {
RUNNING: 'running' as ExecutionProcessStatus,
COMPLETED: 'completed' as ExecutionProcessStatus,
FAILED: 'failed' as ExecutionProcessStatus,
KILLED: 'killed' as ExecutionProcessStatus,
} as const;
// Helper functions
export const isAutoCollapsibleProcess = (
runReason: ExecutionProcessRunReason
): boolean => {
return (
runReason === PROCESS_RUN_REASONS.SETUP_SCRIPT ||
runReason === PROCESS_RUN_REASONS.CLEANUP_SCRIPT
);
};
export const isCodingAgent = (
runReason: ExecutionProcessRunReason
): boolean => {
return runReason === PROCESS_RUN_REASONS.CODING_AGENT;
};
export const isProcessCompleted = (status: ExecutionProcessStatus): boolean => {
return (
status === PROCESS_STATUSES.COMPLETED || status === PROCESS_STATUSES.FAILED
);
};
export const shouldShowInLogs = (
runReason: ExecutionProcessRunReason
): boolean => {
return runReason !== PROCESS_RUN_REASONS.DEV_SERVER;
};
export const getLatestCodingAgent = (
processes: ExecutionProcess[]
): string | null => {
const codingAgents = processes.filter((p) => isCodingAgent(p.run_reason));
if (codingAgents.length === 0) return null;
return codingAgents.sort((a, b) =>
a.started_at === b.started_at
? a.id.localeCompare(b.id) // tie-break for same timestamp
: new Date(b.started_at).getTime() - new Date(a.started_at).getTime()
)[0].id;
};

View File

@@ -3,8 +3,6 @@ export { useAttemptExecution } from './useAttemptExecution';
export { useOpenInEditor } from './useOpenInEditor';
export { useDevServer } from './useDevServer';
export { useRebase } from './useRebase';
export { useCreatePR } from './useCreatePR';
export { useMerge } from './useMerge';
export { usePush } from './usePush';
export { useProjectBranches } from './useProjectBranches';
export { useKeyboardShortcut } from './useKeyboardShortcut';

View File

@@ -1,39 +0,0 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { attemptsApi, type Result } from '@/lib/api';
import type { CreateGitHubPrRequest, GitHubServiceError } from 'shared/types';
export function useCreatePR(
attemptId: string | undefined,
onSuccess?: (prUrl?: string) => void,
onError?: (err: unknown) => void
) {
const queryClient = useQueryClient();
return useMutation<
Result<string, GitHubServiceError>,
Error,
CreateGitHubPrRequest
>({
mutationFn: async (prData: CreateGitHubPrRequest) => {
if (!attemptId)
return { success: false, error: undefined, message: 'No attempt ID' };
return attemptsApi.createPR(attemptId, prData);
},
onSuccess: (result) => {
if (result.success) {
queryClient.invalidateQueries({
queryKey: ['branchStatus', attemptId],
});
onSuccess?.(result.data);
} else {
throw (
result.error || new Error(result.message || 'Failed to create PR')
);
}
},
onError: (err) => {
console.error('Failed to create PR:', err);
onError?.(err);
},
});
}

View File

@@ -1,186 +0,0 @@
import { useEffect, useState, useRef } from 'react';
import { applyPatch } from 'rfc6902';
import type { ExecutionProcess } from 'shared/types';
import type { ProcessStartPayload } from '@/types/logs';
interface ProcessData {
[processId: string]: any;
}
interface UseEventSourceManagerParams {
processes: ExecutionProcess[];
enabled: boolean;
getEndpoint: (process: ExecutionProcess) => string;
initialData?: any;
}
interface UseEventSourceManagerResult {
processData: ProcessData;
isConnected: boolean;
error: string | null;
}
export const useEventSourceManager = ({
processes,
enabled,
getEndpoint,
initialData = null,
}: UseEventSourceManagerParams): UseEventSourceManagerResult => {
const [processData, setProcessData] = useState<ProcessData>({});
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState<string | null>(null);
const eventSourcesRef = useRef<Map<string, EventSource>>(new Map());
const processDataRef = useRef<ProcessData>({});
const processedEntriesRef = useRef<Map<string, Set<number>>>(new Map());
const processesRef = useRef<ExecutionProcess[]>([]);
const enabledRef = useRef<boolean>(enabled);
const getEndpointRef = useRef(getEndpoint);
const retryCountsRef = useRef<Map<string, number>>(new Map());
const retryTimersRef = useRef<Map<string, ReturnType<typeof setTimeout>>>(
new Map()
);
// Keep latest values in refs for retry handlers
useEffect(() => {
processesRef.current = processes;
}, [processes]);
useEffect(() => {
enabledRef.current = enabled;
}, [enabled]);
useEffect(() => {
getEndpointRef.current = getEndpoint;
}, [getEndpoint]);
useEffect(() => {
if (!enabled || !processes.length) {
// Close all connections and reset state
eventSourcesRef.current.forEach((es) => es.close());
eventSourcesRef.current.clear();
setProcessData({});
setIsConnected(false);
setError(null);
processDataRef.current = {};
processedEntriesRef.current.clear();
return;
}
const currentIds = new Set(processes.map((p) => p.id));
// Remove old connections
eventSourcesRef.current.forEach((es, id) => {
if (!currentIds.has(id)) {
es.close();
eventSourcesRef.current.delete(id);
delete processDataRef.current[id];
processedEntriesRef.current.delete(id);
}
});
// Helper to open an EventSource with auto-retry on transient failures (e.g., race before store is ready)
const openEventSource = (process: ExecutionProcess) => {
// If disabled or process no longer present, don't connect
if (!enabledRef.current) return;
if (!processesRef.current.find((p) => p.id === process.id)) return;
const endpoint = getEndpointRef.current(process);
// Reinitialize process data on each (re)connect to avoid duplicating history
processDataRef.current[process.id] = initialData
? structuredClone(initialData)
: { entries: [] };
processedEntriesRef.current.delete(process.id);
// Inject process start marker as the first entry (client-side only)
const processStartPayload: ProcessStartPayload = {
processId: process.id,
runReason: process.run_reason,
startedAt: process.started_at,
status: process.status,
};
const processStartEntry = {
type: 'PROCESS_START' as const,
content: processStartPayload,
};
processDataRef.current[process.id].entries.push(processStartEntry);
const eventSource = new EventSource(endpoint);
eventSource.onopen = () => {
setError(null);
setIsConnected(true);
retryCountsRef.current.set(process.id, 0);
};
eventSource.addEventListener('json_patch', (event) => {
try {
const patches = JSON.parse(event.data);
if (!processedEntriesRef.current.has(process.id)) {
processedEntriesRef.current.set(process.id, new Set());
}
applyPatch(processDataRef.current[process.id], patches);
setProcessData({ ...processDataRef.current });
} catch (err) {
console.error('Failed to apply JSON patch:', err);
setError('Failed to process log update');
}
});
eventSource.addEventListener('finished', () => {
eventSource.close();
eventSourcesRef.current.delete(process.id);
retryCountsRef.current.delete(process.id);
const t = retryTimersRef.current.get(process.id);
if (t) {
clearTimeout(t);
retryTimersRef.current.delete(process.id);
}
setIsConnected(eventSourcesRef.current.size > 0);
});
eventSource.onerror = () => {
setError('Connection failed');
eventSource.close();
eventSourcesRef.current.delete(process.id);
const nextAttempt = (retryCountsRef.current.get(process.id) || 0) + 1;
retryCountsRef.current.set(process.id, nextAttempt);
const maxAttempts = 6;
if (
nextAttempt <= maxAttempts &&
enabledRef.current &&
processesRef.current.find((p) => p.id === process.id)
) {
const delay = Math.min(1500, 250 * 2 ** (nextAttempt - 1));
const timer = setTimeout(() => openEventSource(process), delay);
const prevTimer = retryTimersRef.current.get(process.id);
if (prevTimer) clearTimeout(prevTimer);
retryTimersRef.current.set(process.id, timer);
} else {
setIsConnected(eventSourcesRef.current.size > 0);
}
};
eventSourcesRef.current.set(process.id, eventSource);
};
// Add new connections
processes.forEach((process) => {
if (eventSourcesRef.current.has(process.id)) return;
openEventSource(process);
});
setIsConnected(eventSourcesRef.current.size > 0);
return () => {
// Cleanup all event sources and any pending retry timers
eventSourcesRef.current.forEach((es) => es.close());
eventSourcesRef.current.clear();
retryTimersRef.current.forEach((t) => clearTimeout(t));
retryTimersRef.current.clear();
};
}, [processes, enabled, getEndpoint, initialData]);
return { processData, isConnected, error };
};

View File

@@ -1,12 +0,0 @@
import { useQuery } from '@tanstack/react-query';
import { projectsApi } from '@/lib/api';
export function useProjectBranches(projectId?: string) {
return useQuery({
queryKey: ['projectBranches', projectId],
queryFn: () => projectsApi.getBranches(projectId!),
enabled: !!projectId,
staleTime: 30_000,
refetchOnWindowFocus: false,
});
}

View File

@@ -12,7 +12,7 @@ export const UI_TO_I18N = {
ES: 'es',
} as const;
export const SUPPORTED_UI_LANGUAGES = ['BROWSER', 'EN', 'JA', 'ES'] as const;
const SUPPORTED_UI_LANGUAGES = ['BROWSER', 'EN', 'JA', 'ES'] as const;
export const SUPPORTED_I18N_CODES = Object.values(UI_TO_I18N);
const FALLBACK_ENDONYMS = {

View File

@@ -52,7 +52,7 @@ export type {
UpdateFollowUpDraftRequest,
} from 'shared/types';
export class ApiError<E = unknown> extends Error {
class ApiError<E = unknown> extends Error {
public status?: number;
public error_data?: E;
@@ -69,7 +69,7 @@ export class ApiError<E = unknown> extends Error {
}
}
export const makeRequest = async (url: string, options: RequestInit = {}) => {
const makeRequest = async (url: string, options: RequestInit = {}) => {
const headers = {
'Content-Type': 'application/json',
...(options.headers || {}),
@@ -641,17 +641,6 @@ export const githubApi = {
const response = await makeRequest(`/api/github/repositories?page=${page}`);
return handleApiResponse<RepositoryInfo[]>(response);
},
// createProjectFromRepository: async (
// data: CreateProjectFromGitHub
// ): Promise<Project> => {
// const response = await makeRequest('/api/projects/from-github', {
// method: 'POST',
// body: JSON.stringify(data, (_key, value) =>
// typeof value === 'bigint' ? Number(value) : value
// ),
// });
// return handleApiResponse<Project>(response);
// },
};
// Task Templates APIs

View File

@@ -14,7 +14,7 @@ export function displayConflictOpLabel(op?: ConflictOp | null): string {
}
}
export function formatConflictHeader(
function formatConflictHeader(
op: ConflictOp | null | undefined,
sourceBranch: string,
baseBranch?: string

View File

@@ -16,4 +16,3 @@ export const useDiffViewStore = create<State>((set) => ({
}));
export const useDiffViewMode = () => useDiffViewStore((s) => s.mode);
export const useToggleDiffViewMode = () => useDiffViewStore((s) => s.toggle);

View File

@@ -7,7 +7,7 @@ type State = {
clear: () => void;
};
export const useExpandableStore = create<State>((set) => ({
const useExpandableStore = create<State>((set) => ({
expanded: {},
setKey: (key, value) =>
set((s) =>

View File

@@ -26,7 +26,7 @@ const defaultUiState: TaskUiState = {
fileToDelete: null,
};
export const useTaskDetailsUiStore = create<TaskDetailsUiStore>((set, get) => ({
const useTaskDetailsUiStore = create<TaskDetailsUiStore>((set, get) => ({
ui: {},
getUiState: (taskId: string) => {
@@ -59,17 +59,6 @@ export const useTaskDetailsUiStore = create<TaskDetailsUiStore>((set, get) => ({
},
}));
// Convenience hooks for specific UI state
export const useTaskLoading = (taskId: string) => {
const { getUiState, setUiState } = useTaskDetailsUiStore();
const { loading } = getUiState(taskId);
return {
loading,
setLoading: (value: boolean) => setUiState(taskId, { loading: value }),
};
};
export const useTaskStopping = (taskId: string) => {
const { getUiState, setUiState } = useTaskDetailsUiStore();
const { isStopping } = getUiState(taskId);
@@ -80,17 +69,3 @@ export const useTaskStopping = (taskId: string) => {
setUiState(taskId, { isStopping: value }),
};
};
export const useTaskDeletingFiles = (taskId: string) => {
const { getUiState, setUiState } = useTaskDetailsUiStore();
const { deletingFiles, fileToDelete } = getUiState(taskId);
return {
deletingFiles,
fileToDelete,
setFileToDelete: (value: string | null) =>
setUiState(taskId, { fileToDelete: value }),
setDeletingFiles: (value: Set<string>) =>
setUiState(taskId, { deletingFiles: value }),
};
};