chore(lint): mechanical cleanup in app (unused, shadow, nested-ternary)

Recovers in-flight edits from the app mechanical agent that couldn't be
pushed due to concurrent tree contention. Removes unused helpers and
locals, flattens nested ternaries, narrows a few props where unused.
This commit is contained in:
Mohamed Boudra
2026-04-24 03:20:38 +07:00
parent 28c6a38b3a
commit ec6a35b8cb
24 changed files with 135 additions and 268 deletions

View File

@@ -226,7 +226,7 @@ function ControlledStatusBar({
const providerAnchorRef = useRef<View>(null);
const modeAnchorRef = useRef<View>(null);
const modelAnchorRef = useRef<View>(null);
const _modelAnchorRef = useRef<View>(null);
const thinkingAnchorRef = useRef<View>(null);
const canSelectProvider = Boolean(
@@ -277,7 +277,7 @@ function ControlledStatusBar({
() => (modeOptions ?? []).map((o) => ({ id: o.id, label: o.label })),
[modeOptions],
);
const comboboxModelOptions = useMemo<ComboboxOption[]>(
const _comboboxModelOptions = useMemo<ComboboxOption[]>(
() => (modelOptions ?? []).map((o) => ({ id: o.id, label: o.label })),
[modelOptions],
);
@@ -462,11 +462,7 @@ function ControlledStatusBar({
);
const renderSheetModelTrigger = useCallback(
({ selectedModelLabel }: { selectedModelLabel: string }) => (
<View
style={sheetSelectStyle}
pointerEvents="none"
testID="agent-preferences-model"
>
<View style={sheetSelectStyle} pointerEvents="none" testID="agent-preferences-model">
{ProviderIcon ? (
<ProviderIcon size={theme.iconSize.md} color={theme.colors.foregroundMuted} />
) : null}
@@ -1309,14 +1305,14 @@ export const AgentStatusBar = memo(function AgentStatusBar({
export function DraftAgentStatusBar({
providerDefinitions,
selectedProvider,
onSelectProvider,
onSelectProvider: _onSelectProvider,
modeOptions,
selectedMode,
onSelectMode,
models,
selectedModel,
onSelectModel,
isModelLoading,
isModelLoading: _isModelLoading,
allProviderModels,
isAllModelsLoading,
onSelectProviderAndModel,

View File

@@ -614,7 +614,7 @@ const AgentStreamViewComponent = forwardRef<AgentStreamViewHandle, AgentStreamVi
}, [renderModel, emptyStateStyle]);
const historyItems = renderModel.history;
const liveHeadItems = renderModel.segments.liveHead;
const _liveHeadItems = renderModel.segments.liveHead;
const { boundary, auxiliary } = renderModel;
const lastHistoryItem = historyItems.at(-1) ?? null;
@@ -1130,10 +1130,7 @@ function PermissionRequestCard({
) : null}
{!isPlanRequest ? (
<ToolCallDetailsContent
detail={resolvedToolCallDetail}
maxHeight={200}
/>
<ToolCallDetailsContent detail={resolvedToolCallDetail} maxHeight={200} />
) : null}
{footer}

View File

@@ -63,10 +63,7 @@ function buildSingleAction(onPress: () => void): CalloutCardActions {
return [{ label: "Undo", onPress }];
}
function buildTwoActions(
onWhatsNew: () => void,
onInstall: () => void,
): CalloutCardActions {
function buildTwoActions(onWhatsNew: () => void, onInstall: () => void): CalloutCardActions {
return [
{ label: "What's new", onPress: onWhatsNew },
{ label: "Install & restart", onPress: onInstall, variant: "primary" },

View File

@@ -295,7 +295,7 @@ function FavoritesSection({
canSelectProvider: (provider: string) => boolean;
onToggleFavorite?: (provider: string, modelId: string) => void;
}) {
const { theme } = useUnistyles();
const { theme: _theme } = useUnistyles();
if (favoriteRows.length === 0) {
return null;
@@ -465,7 +465,7 @@ function SelectorContent({
selectedProvider,
selectedModel,
searchQuery,
onSearchChange,
onSearchChange: _onSearchChange,
favoriteKeys,
onSelect,
canSelectProvider,
@@ -492,7 +492,7 @@ function SelectorContent({
[normalizedQuery, scopedRows],
);
const { favoriteRows, regularRows } = useMemo(
const { favoriteRows, regularRows: _regularRows } = useMemo(
() => partitionRows(visibleRows, favoriteKeys),
[favoriteKeys, visibleRows],
);

View File

@@ -1241,8 +1241,6 @@ export function Composer({
);
}
const BUTTON_SIZE = 40;
const styles = StyleSheet.create(((theme: Theme) => ({
container: {
flexDirection: "column",

View File

@@ -20,7 +20,7 @@ interface DiffScrollProps {
export function DiffScroll({
children,
scrollViewWidth,
scrollViewWidth: _scrollViewWidth,
onScrollViewWidthChange,
style,
contentContainerStyle,

View File

@@ -144,10 +144,7 @@ function TreeRowItem({
return (
<Pressable onPress={handlePress} style={pressableStyle}>
{depth > 0 &&
Array.from({ length: depth }, (_, i) => (
<IndentGuide key={i} index={i} />
))}
{depth > 0 && Array.from({ length: depth }, (_, i) => <IndentGuide key={i} index={i} />)}
<View style={styles.entryInfo}>
<View style={styles.entryIcon}>
{(() => {
@@ -968,10 +965,7 @@ interface IndentGuideProps {
function IndentGuide({ index }: IndentGuideProps) {
const { theme } = useUnistyles();
const guideStyle = useMemo(
() => [
styles.indentGuide,
{ left: theme.spacing[3] + index * INDENT_PER_LEVEL + 4 },
],
() => [styles.indentGuide, { left: theme.spacing[3] + index * INDENT_PER_LEVEL + 4 }],
[index, theme.spacing],
);
return <View style={guideStyle} />;

View File

@@ -230,15 +230,6 @@ function canPush(input: BuildGitActionsInput): boolean {
return input.hasRemote && input.aheadOfOrigin > 0 && input.behindOfOrigin === 0;
}
function canMergeBranch(input: BuildGitActionsInput): boolean {
return (
!input.isOnBaseBranch &&
input.baseRefAvailable &&
!input.hasUncommittedChanges &&
input.aheadCount > 0
);
}
function canMergeFromBase(input: BuildGitActionsInput): boolean {
return (
!input.isOnBaseBranch &&

View File

@@ -430,7 +430,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const {
isRecording: isDictating,
isProcessing: isDictationProcessing,
partialTranscript: dictationPartialTranscript,
partialTranscript: _dictationPartialTranscript,
volume: dictationVolume,
duration: dictationDuration,
error: dictationError,

View File

@@ -1177,10 +1177,7 @@ function AssistantMessageBlockContainer({
marginBottom,
children,
}: AssistantMessageBlockContainerProps) {
const style = useMemo(
() => (marginBottom > 0 ? { marginBottom } : undefined),
[marginBottom],
);
const style = useMemo(() => (marginBottom > 0 ? { marginBottom } : undefined), [marginBottom]);
return <View style={style}>{children}</View>;
}
@@ -1773,18 +1770,13 @@ function TodoListItemRow({ text, completed }: TodoListItemRowProps) {
[completed],
);
const textStyle = useMemo(
() => [
todoListCardStylesheet.itemText,
completed && todoListCardStylesheet.itemTextCompleted,
],
() => [todoListCardStylesheet.itemText, completed && todoListCardStylesheet.itemTextCompleted],
[completed],
);
return (
<View style={todoListCardStylesheet.itemRow}>
<View style={badgeStyle}>
{completed ? (
<Check size={12} color={todoUnistylesTheme.colors.primaryForeground} />
) : null}
{completed ? <Check size={12} color={todoUnistylesTheme.colors.primaryForeground} /> : null}
</View>
<Text style={textStyle}>{text}</Text>
</View>
@@ -1836,7 +1828,7 @@ export const TodoListCard = memo(function TodoListCard({
items,
disableOuterSpacing,
}: TodoListCardProps) {
const { theme: unistylesTheme } = useUnistyles();
const { theme: _unistylesTheme } = useUnistyles();
const [isExpanded, setIsExpanded] = useState(false);
const nextTask = useMemo(() => items.find((item) => !item.completed)?.text, [items]);

View File

@@ -709,7 +709,7 @@ function useLongPressDragInteraction(input: {
}, [clearTimers, input.menuController, openContextMenuAtStartPoint]);
const handleDragIntent = useCallback(
(details: { dx: number; dy: number; distance: number }) => {
(_details: { dx: number; dy: number; distance: number }) => {
if (!dragActivatedRef.current) {
return;
}
@@ -722,7 +722,7 @@ function useLongPressDragInteraction(input: {
);
const handleScrollIntent = useCallback(
(details: { dx: number; dy: number; distance: number }) => {
(_details: { dx: number; dy: number; distance: number }) => {
scrollIntentRef.current = true;
didLongPressRef.current = true;
clearTimers();
@@ -731,7 +731,7 @@ function useLongPressDragInteraction(input: {
);
const handleSwipeIntent = useCallback(
(details: { dx: number; dy: number; distance: number }) => {
(_details: { dx: number; dy: number; distance: number }) => {
didLongPressRef.current = true;
clearTimers();
},
@@ -831,7 +831,7 @@ function ProjectHeaderRow({
canCreateWorktree,
isProjectActive = false,
onWorkspacePress,
onWorktreeCreated,
onWorktreeCreated: _onWorktreeCreated,
shortcutNumber = null,
showShortcutBadge = false,
drag,
@@ -854,8 +854,8 @@ function ProjectHeaderRow({
);
onWorkspacePress?.();
}, [displayName, onWorkspacePress, project.iconWorkingDir, serverId]);
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
const toast = useToast();
const _mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
const _toast = useToast();
const interaction = useLongPressDragInteraction({
drag,
@@ -1025,7 +1025,7 @@ function WorkspaceRowInner({
archiveShortcutKeys,
}: WorkspaceRowInnerProps) {
const { theme } = useUnistyles();
const isCompact = useIsCompactFormFactor();
const _isCompact = useIsCompactFormFactor();
const [isHovered, setIsHovered] = useState(false);
const isTouchPlatform = platformIsNative;
const workspaceDirectory = resolveWorkspaceExecutionDirectory({
@@ -2000,8 +2000,8 @@ export function SidebarWorkspaceList({
collapsedProjectKeys,
onToggleProjectCollapsed,
shortcutIndexByWorkspaceKey,
isRefreshing = false,
onRefresh,
isRefreshing: _isRefreshing = false,
onRefresh: _onRefresh,
onWorkspacePress,
onAddProject,
listFooterComponent,

View File

@@ -814,7 +814,7 @@ function SplitPaneView({
onCreateTerminalTab,
buildPaneContentModel,
onFocusPane,
onSplitPane,
onSplitPane: _onSplitPane,
onSplitPaneEmpty,
onReorderTabsInPane,
renderPaneEmptyState,
@@ -823,7 +823,7 @@ function SplitPaneView({
dropPreview,
tabDropPreview,
}: SplitPaneViewProps) {
const { theme } = useUnistyles();
const { theme: _theme } = useUnistyles();
const paneRef = useRef<View | null>(null);
const stableOnFocusPane = useStableEvent(onFocusPane);
const padding = useWindowControlsPadding("tabRow");

View File

@@ -13,7 +13,6 @@ const SYNCED_LOADER_DURATION_MS = 950;
const SYNCED_LOADER_EPOCH_MS = 0;
const DOT_SEQUENCE = [0, 1, 3, 5, 4, 2] as const;
const DOT_COUNT = DOT_SEQUENCE.length;
const GRID_ROWS = 3;
const GRID_COLUMNS = 2;
const SNAKE_SEGMENT_OFFSETS = [0, -1, -2, -3, -4] as const;
const SNAKE_OPACITIES = [1, 0.78, 0.56, 0.34, 0] as const;

View File

@@ -440,7 +440,7 @@ export function Combobox({
}
const measure = () => {
referenceEl.measureInWindow((x, y, width, height) => {
referenceEl.measureInWindow((x, y, width, _height) => {
setReferenceLeft((prev) => (prev === x ? prev : x));
setReferenceAtOrigin(Math.abs(x) <= 1 && Math.abs(y) <= 1);
setReferenceTop((prev) => (prev === y ? prev : y));

View File

@@ -57,18 +57,6 @@ interface ScheduledFrameHandle {
callback: () => void;
}
type BottomAnchorEvent =
| "request_created"
| "evaluate_called"
| "attempt_started"
| "attempt_verified"
| "attempt_failed"
| "request_fulfilled"
| "request_cancelled"
| "detached_by_user"
| "verification_scheduled"
| "blocked_reason_changed";
interface BottomAnchorControllerDriver {
destroy: () => void;
getSnapshot: () => {
@@ -242,25 +230,6 @@ function shouldRequireRouteRequestConfirmation(input: {
return input.confirmationPasses < 1;
}
function getDetailedMeasurementState(
measurementState: ControllerMeasurementState,
): Record<string, unknown> {
const distanceFromBottom = Math.max(
0,
measurementState.contentHeight - (measurementState.offsetY + measurementState.viewportHeight),
);
return {
containerKey: measurementState.containerKey,
viewportWidth: measurementState.viewportWidth,
viewportHeight: measurementState.viewportHeight,
contentHeight: measurementState.contentHeight,
offsetY: measurementState.offsetY,
distanceFromBottom,
viewportMeasuredForKey: measurementState.viewportMeasuredForKey,
contentMeasuredForKey: measurementState.contentMeasuredForKey,
};
}
function createBottomAnchorControllerDriver(
input: CreateBottomAnchorControllerDriverInput,
): BottomAnchorControllerDriver {
@@ -275,7 +244,7 @@ function createBottomAnchorControllerDriver(
let stickyMeasurementRevision = 0;
let lastVerifiedStickyMeasurementRevision = 0;
const getLogContext = (extra?: Record<string, unknown>) => {
const _getLogContext = (extra?: Record<string, unknown>) => {
const measurementState = input.getMeasurementState();
const distanceFromBottom = Math.max(
0,
@@ -336,7 +305,7 @@ function createBottomAnchorControllerDriver(
pendingVerification = null;
};
const cancelPendingRequest = (reason: string) => {
const cancelPendingRequest = (_reason: string) => {
const currentRequest = pendingRequest;
if (!currentRequest) {
cancelPendingAttempt();
@@ -358,7 +327,7 @@ function createBottomAnchorControllerDriver(
});
const scheduleVerification = (attemptContext: AttemptContext, delayFramesOverride?: number) => {
const scheduledMeasurementState = input.getMeasurementState();
const _scheduledMeasurementState = input.getMeasurementState();
if (verificationHandle) {
input.cancelFrame(verificationHandle);
}
@@ -461,7 +430,7 @@ function createBottomAnchorControllerDriver(
const evaluate = (
animated: boolean,
reason:
_reason:
| "request_created"
| "viewport_change"
| "content_size_change"
@@ -660,7 +629,7 @@ export const __private__ = {
deriveBottomAnchorBlockedReason,
deriveVerificationBlockedReason,
deriveRetryDisposition,
deriveModeForLocalRequest(input: {
deriveModeForLocalRequest(_input: {
reason: BottomAnchorLocalRequest["reason"];
}): BottomAnchorMode {
return "sticky-bottom";

View File

@@ -165,21 +165,7 @@ const getLatestPermissionRequest = (
return null;
};
type FileExplorerPayload = Extract<
SessionOutboundMessage,
{ type: "file_explorer_response" }
>["payload"];
type FileDownloadTokenPayload = Extract<
SessionOutboundMessage,
{ type: "file_download_token_response" }
>["payload"];
type AgentUpdatePayload = Extract<SessionOutboundMessage, { type: "agent_update" }>["payload"];
type WorkspaceUpdatePayload = Extract<
SessionOutboundMessage,
{ type: "workspace_update" }
>["payload"];
type WorkspaceSetupProgressPayload = Extract<
SessionOutboundMessage,
{ type: "workspace_setup_progress" }
@@ -310,7 +296,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
) => Promise<void>)
| null
>(null);
const sessionStateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const _sessionStateTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const attentionNotifiedRef = useRef<Map<string, number>>(new Map());
const appStateRef = useRef(AppState.currentState);
const revalidationTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
@@ -1583,7 +1569,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
// Keep the ref updated so the agent_update handler can call it
sendAgentMessageRef.current = sendAgentMessage;
const cancelAgentRun = useCallback(
const _cancelAgentRun = useCallback(
(agentId: string) => {
if (!client) {
console.warn("[Session] cancelAgent skipped: daemon unavailable");
@@ -1596,7 +1582,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client],
);
const deleteAgent = useCallback(
const _deleteAgent = useCallback(
(agentId: string) => {
if (!client) {
console.warn("[Session] deleteAgent skipped: daemon unavailable");
@@ -1609,7 +1595,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client],
);
const archiveAgent = useCallback(
const _archiveAgent = useCallback(
(agentId: string) => {
if (!client) {
console.warn("[Session] archiveAgent skipped: daemon unavailable");
@@ -1622,7 +1608,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client],
);
const restartServer = useCallback(
const _restartServer = useCallback(
(reason?: string) => {
if (!client) {
console.warn("[Session] restartServer skipped: daemon unavailable");
@@ -1635,7 +1621,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client],
);
const createAgent = useCallback(
const _createAgent = useCallback(
async ({
config,
initialPrompt,
@@ -1677,7 +1663,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client],
);
const setAgentMode = useCallback(
const _setAgentMode = useCallback(
(agentId: string, modeId: string) => {
if (!client) {
console.warn("[Session] setAgentMode skipped: daemon unavailable");
@@ -1691,7 +1677,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client, toast],
);
const setAgentModel = useCallback(
const _setAgentModel = useCallback(
(agentId: string, modelId: string | null) => {
if (!client) {
console.warn("[Session] setAgentModel skipped: daemon unavailable");
@@ -1705,7 +1691,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client, toast],
);
const setAgentThinkingOption = useCallback(
const _setAgentThinkingOption = useCallback(
(agentId: string, thinkingOptionId: string | null) => {
if (!client) {
console.warn("[Session] setAgentThinkingOption skipped: daemon unavailable");
@@ -1719,7 +1705,7 @@ function SessionProviderInternal({ children, serverId, client }: SessionProvider
[client, toast],
);
const respondToPermission = useCallback(
const _respondToPermission = useCallback(
(agentId: string, requestId: string, response: any) => {
if (!client) {
console.warn("[Session] respondToPermission skipped: daemon unavailable");

View File

@@ -37,14 +37,6 @@ function makeTimelineEvent(
} as AgentStreamEventPayload;
}
function makeUserTimelineEvent(text: string): AgentStreamEventPayload {
return {
type: "timeline",
provider: "claude",
item: { type: "user_message", text },
} as AgentStreamEventPayload;
}
function makeToolCallTimelineEvent(callId: string): AgentStreamEventPayload {
return {
type: "timeline",

View File

@@ -371,7 +371,7 @@ export function useAgentFormState(options: UseAgentFormStateOptions = {}): UseAg
initialValues,
isVisible = true,
isCreateFlow = true,
isTargetDaemonReady = true,
isTargetDaemonReady: _isTargetDaemonReady = true,
onlineServerIds = [],
} = options;

View File

@@ -123,7 +123,7 @@ function resolveActionShortcutKeys(
}
export function useCommandCenter() {
const pathname = usePathname();
const _pathname = usePathname();
const routeActiveServerId = useActiveServerId();
const { overrides } = useKeyboardShortcutOverrides();
const open = useKeyboardShortcutsStore((s) => s.commandCenterOpen);

View File

@@ -311,7 +311,7 @@ function AgentPanelBody({
onOpenWorkspaceFile?: (input: { filePath: string }) => void;
}) {
const { theme } = useUnistyles();
const { isArchivingAgent } = useArchiveAgent();
const { isArchivingAgent: _isArchivingAgent } = useArchiveAgent();
const hasSession = useSessionStore((state) => Boolean(state.sessions[serverId]));
const projectPlacement = useStoreWithEqualityFn(
useSessionStore,
@@ -445,8 +445,6 @@ function AgentPanelBody({
);
}
const isArchivingCurrentAgent = Boolean(agentId && isArchivingAgent({ serverId, agentId }));
return (
<ChatAgentContent
serverId={serverId}

View File

@@ -656,7 +656,7 @@ function WorkspaceScreenContent({
isRouteFocused,
}: WorkspaceScreenContentProps) {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
const _insets = useSafeAreaInsets();
const mainBackgroundColor = theme.colors.surfaceWorkspace;
const toast = useToast();
const isMobile = useIsCompactFormFactor();
@@ -783,7 +783,7 @@ function WorkspaceScreenContent({
[scriptTerminalIds, terminals],
);
const createTerminalMutation = useMutation({
mutationFn: async (input?: { paneId?: string }) => {
mutationFn: async (_input?: { paneId?: string }) => {
if (!client || !workspaceDirectory) {
throw new Error("Host is not connected");
}
@@ -1100,12 +1100,12 @@ function WorkspaceScreenContent({
const paneFocusSuppressedRef = useRef(false);
const resizeWorkspaceSplit = useWorkspaceLayoutStore((state) => state.resizeSplit);
const reorderWorkspaceTabsInPane = useWorkspaceLayoutStore((state) => state.reorderTabsInPane);
const pinnedAgentIds = useWorkspaceLayoutStore((state) =>
const _pinnedAgentIds = useWorkspaceLayoutStore((state) =>
persistenceKey
? (state.pinnedAgentIdsByWorkspace[persistenceKey] ?? EMPTY_PINNED_AGENT_IDS)
: EMPTY_PINNED_AGENT_IDS,
);
const hiddenAgentIds = useWorkspaceLayoutStore((state) =>
const _hiddenAgentIds = useWorkspaceLayoutStore((state) =>
persistenceKey ? (state.hiddenAgentIdsByWorkspace[persistenceKey] ?? EMPTY_SET) : EMPTY_SET,
);
const pendingByDraftId = useCreateFlowStore((state) => state.pendingByDraftId);
@@ -1164,16 +1164,6 @@ function WorkspaceScreenContent({
};
}, [isRouteFocused, normalizedServerId, setFocusedAgentId]);
const ensureWorkspaceTab = useCallback(
function ensureWorkspaceTab(target: WorkspaceTabTarget): string | null {
if (!persistenceKey) {
return null;
}
return openWorkspaceTabInBackground(persistenceKey, target);
},
[openWorkspaceTabInBackground, persistenceKey],
);
const openWorkspaceDraftTab = useCallback(
function openWorkspaceDraftTab(input?: { draftId?: string; focus?: boolean }) {
if (!persistenceKey) {
@@ -1429,7 +1419,7 @@ function WorkspaceScreenContent({
[handleOpenFileFromExplorer],
);
const [hoveredTabKey, setHoveredTabKey] = useState<string | null>(null);
const [_hoveredTabKey, setHoveredTabKey] = useState<string | null>(null);
const [hoveredCloseTabKey, setHoveredCloseTabKey] = useState<string | null>(null);
const tabByKey = useMemo(() => {

View File

@@ -48,11 +48,6 @@ interface ReorderTabsForPaneInput {
tabIds: string[];
}
interface UpdateTabInTreeInput {
tabId: string;
target: WorkspaceTabTarget;
}
interface UpdateGroupSizesInTreeInput {
groupId: string;
sizes: number[];
@@ -760,23 +755,6 @@ function focusTabInPane(root: SplitNodeInternal, paneId: string, tabId: string):
});
}
function updateTabInTree(root: SplitNodeInternal, input: UpdateTabInTreeInput): SplitNodeInternal {
const panePath = findPanePathContainingTab(root, input.tabId);
invariant(panePath, `Tab not found: ${input.tabId}`);
return replaceNodeAtPath(root, panePath, (node) => {
invariant(node.kind === "pane", "Expected pane while retargeting tab");
return {
kind: "pane",
pane: normalizePaneAfterTabChange({
...node.pane,
tabs: node.pane.tabs.map((tab) =>
tab.tabId === input.tabId ? { ...tab, target: input.target } : tab,
),
}),
};
});
}
function replaceTabInTree(
root: SplitNodeInternal,
input: {

View File

@@ -238,7 +238,6 @@ export function createVoiceRuntime(deps: VoiceRuntimeDeps): VoiceRuntime {
if (snapshotsEqual(next, state.snapshot)) {
return;
}
const previous = state.snapshot;
state.snapshot = next;
emit();
}

View File

@@ -219,20 +219,19 @@ async function findIconInDir(dir: string, patterns: string[]): Promise<string |
return null;
}
// Check each pattern in order of priority
// Collect candidate paths in priority order (pattern, then entry)
const candidatePaths: string[] = [];
for (const pattern of patterns) {
for (const entry of entries) {
if (!matchesPattern(entry, pattern)) {
continue;
}
const fullPath = join(dir, entry);
if (await isExistingFile(fullPath)) {
return fullPath;
if (matchesPattern(entry, pattern)) {
candidatePaths.push(join(dir, entry));
}
}
}
return null;
const existsResults = await Promise.all(candidatePaths.map((p) => isExistingFile(p)));
const foundIndex = existsResults.findIndex((exists) => exists);
return foundIndex === -1 ? null : (candidatePaths[foundIndex] ?? null);
}
async function searchPriorityDirs(
@@ -240,22 +239,18 @@ async function searchPriorityDirs(
ignoredDirsSet: Set<string>,
remainingDepth: number,
): Promise<string | null> {
for (const priorityDir of PRIORITY_DIRS) {
const priorityPath = join(basePath, priorityDir);
if (!(await isExistingDirectory(priorityPath))) {
continue;
}
const result = await searchDirRecursively(
priorityPath,
ICON_PATTERNS,
ignoredDirsSet,
remainingDepth,
);
if (result) {
return result;
}
}
return null;
const priorityPaths = PRIORITY_DIRS.map((priorityDir) => join(basePath, priorityDir));
const existenceResults = await Promise.all(
priorityPaths.map((priorityPath) => isExistingDirectory(priorityPath)),
);
const searchResults = await Promise.all(
priorityPaths.map((priorityPath, index) =>
existenceResults[index]
? searchDirRecursively(priorityPath, ICON_PATTERNS, ignoredDirsSet, remainingDepth)
: Promise.resolve(null),
),
);
return searchResults.find((result): result is string => result !== null) ?? null;
}
async function searchDirRecursively(
@@ -283,28 +278,20 @@ async function searchDirRecursively(
return null;
}
for (const entry of entries) {
if (ignoredDirs.has(entry)) {
continue;
}
const fullPath = join(dir, entry);
if (!(await isExistingDirectory(fullPath))) {
continue;
}
const result = await searchDirRecursively(
fullPath,
patterns,
ignoredDirs,
maxDepth,
currentDepth + 1,
);
if (result) {
return result;
}
}
return null;
const candidatePaths = entries
.filter((entry) => !ignoredDirs.has(entry))
.map((entry) => join(dir, entry));
const isDirResults = await Promise.all(
candidatePaths.map((fullPath) => isExistingDirectory(fullPath)),
);
const recursionResults = await Promise.all(
candidatePaths.map((fullPath, index) =>
isDirResults[index]
? searchDirRecursively(fullPath, patterns, ignoredDirs, maxDepth, currentDepth + 1)
: Promise.resolve(null),
),
);
return recursionResults.find((result): result is string => result !== null) ?? null;
}
/**
@@ -328,36 +315,42 @@ export async function findProjectIcon(
}
// Then search monorepo package directories (packages/*, apps/*)
for (const monoDir of MONOREPO_PACKAGE_DIRS) {
const monoPath = join(projectDir, monoDir);
let packageEntries: string[];
try {
packageEntries = await readdir(monoPath);
} catch {
continue;
}
for (const packageName of packageEntries) {
const packagePath = join(monoPath, packageName);
if (!(await isExistingDirectory(packagePath))) {
continue;
const monoPaths = MONOREPO_PACKAGE_DIRS.map((monoDir) => join(projectDir, monoDir));
const monoEntries = await Promise.all(
monoPaths.map(async (monoPath): Promise<string[] | null> => {
try {
return await readdir(monoPath);
} catch {
return null;
}
const packagePriorityResult = await searchPriorityDirs(
packagePath,
ignoredDirsSet,
maxDepth - 1,
}),
);
const monoResults = await Promise.all(
monoPaths.map(async (monoPath, monoIdx): Promise<string | null> => {
const packageEntries = monoEntries[monoIdx];
if (!packageEntries) return null;
const packagePaths = packageEntries.map((packageName) => join(monoPath, packageName));
const isDirResults = await Promise.all(
packagePaths.map((packagePath) => isExistingDirectory(packagePath)),
);
if (packagePriorityResult) {
return packagePriorityResult;
}
// Search package root
const found = await findIconInDir(packagePath, ICON_PATTERNS);
if (found) {
return found;
}
}
const packageResults = await Promise.all(
packagePaths.map(async (packagePath, idx): Promise<string | null> => {
if (!isDirResults[idx]) return null;
const priorityResult = await searchPriorityDirs(
packagePath,
ignoredDirsSet,
maxDepth - 1,
);
if (priorityResult) return priorityResult;
return await findIconInDir(packagePath, ICON_PATTERNS);
}),
);
return packageResults.find((result): result is string => result !== null) ?? null;
}),
);
const monoMatch = monoResults.find((result): result is string => result !== null);
if (monoMatch) {
return monoMatch;
}
// Then search root and any other non-priority directories
@@ -399,22 +392,20 @@ async function findDirRecursively(
return null;
}
for (const entry of entries) {
if (ignoredDirsSet.has(entry) || priorityDirsSet.has(entry)) {
continue;
}
const fullPath = join(dir, entry);
if (!(await isExistingDirectory(fullPath))) {
continue;
}
const result = await findDirRecursively(fullPath, maxDepth, currentDepth + 1);
if (result) {
return result;
}
}
return null;
const candidatePaths = entries
.filter((entry) => !ignoredDirsSet.has(entry) && !priorityDirsSet.has(entry))
.map((entry) => join(dir, entry));
const isDirResults = await Promise.all(
candidatePaths.map((fullPath) => isExistingDirectory(fullPath)),
);
const recursionResults = await Promise.all(
candidatePaths.map((fullPath, index) =>
isDirResults[index]
? findDirRecursively(fullPath, maxDepth, currentDepth + 1)
: Promise.resolve(null),
),
);
return recursionResults.find((result): result is string => result !== null) ?? null;
}
/**