chore(lint): no-nested-ternary in app batch 1 (18 files)

This commit is contained in:
Mohamed Boudra
2026-04-24 06:19:52 +07:00
parent 40aae0b314
commit c61753f143
18 changed files with 230 additions and 193 deletions
@@ -649,17 +649,14 @@ const AgentStreamViewComponent = forwardRef<AgentStreamViewHandle, AgentStreamVi
renderStreamItem(item, index, items, index === 0 ? lastHistoryItem : null),
[lastHistoryItem, renderStreamItem],
);
const liveAuxiliaryHeaderStyle = useMemo(
() => [
stylesheet.listHeaderContent,
boundary.hasLiveHead
? streamRenderStrategy.getFlatListInverted()
? { paddingBottom: looseGap }
: { paddingTop: looseGap }
: null,
],
[boundary.hasLiveHead, streamRenderStrategy, looseGap],
);
const liveAuxiliaryHeaderStyle = useMemo(() => {
let headerPadding: { paddingBottom: number } | { paddingTop: number } | null;
if (!boundary.hasLiveHead) headerPadding = null;
else if (streamRenderStrategy.getFlatListInverted())
headerPadding = { paddingBottom: looseGap };
else headerPadding = { paddingTop: looseGap };
return [stylesheet.listHeaderContent, headerPadding];
}, [boundary.hasLiveHead, streamRenderStrategy, looseGap]);
const renderLiveAuxiliary = useCallback<StreamSegmentRenderers["renderLiveAuxiliary"]>(() => {
if (!auxiliary.pendingPermissions && !auxiliary.workingIndicator) {
return null;
@@ -1080,12 +1077,11 @@ function PermissionRequestCard({
const textColor = isPrimary ? theme.colors.foreground : theme.colors.foregroundMuted;
const iconColor = textColor;
const Icon = action.behavior === "allow" ? Check : X;
const testID =
action.behavior === "deny"
? "permission-request-deny"
: action.id === "accept" || action.id === "implement"
? "permission-request-accept"
: `permission-request-action-${action.id}`;
let testID: string;
if (action.behavior === "deny") testID = "permission-request-deny";
else if (action.id === "accept" || action.id === "implement")
testID = "permission-request-accept";
else testID = `permission-request-action-${action.id}`;
return (
<PermissionActionButton
+5 -6
View File
@@ -83,12 +83,11 @@ export function CalloutCard({
</View>
) : null}
{hasDescription ? (
typeof description === "string" ? (
<CalloutDescriptionText>{description}</CalloutDescriptionText>
) : (
<View style={styles.descriptionSlot}>{description}</View>
)
{hasDescription && typeof description === "string" ? (
<CalloutDescriptionText>{description}</CalloutDescriptionText>
) : null}
{hasDescription && typeof description !== "string" ? (
<View style={styles.descriptionSlot}>{description}</View>
) : null}
{visibleActions.length > 0 ? (
@@ -98,7 +98,8 @@ export function DictationControls({
<View style={styles.loadingContainer}>
<ActivityIndicator size="small" color={theme.colors.foreground} />
</View>
) : isFailed ? (
) : null}
{!actionsDisabled && isFailed ? (
<Pressable
onPress={onRetry}
accessibilityLabel="Retry dictation"
@@ -106,7 +107,8 @@ export function DictationControls({
>
<RefreshCcw size={theme.iconSize.sm} color={theme.colors.surface0} />
</Pressable>
) : (
) : null}
{!actionsDisabled && !isFailed ? (
<>
<Pressable
onPress={onAccept}
@@ -123,7 +125,7 @@ export function DictationControls({
<ArrowUp size={theme.iconSize.sm} color={theme.colors.surface0} />
</Pressable>
</>
)}
) : null}
</View>
</View>
);
@@ -216,7 +218,8 @@ export function DictationOverlay({
<View style={overlayStyles.loadingContainer}>
<ActivityIndicator size="small" color={theme.colors.palette.white} />
</View>
) : isFailed ? (
) : null}
{!actionsDisabled && isFailed ? (
<Pressable
onPress={onRetry}
accessibilityRole="button"
@@ -225,7 +228,8 @@ export function DictationOverlay({
>
<RefreshCcw size={theme.iconSize.lg} color={theme.colors.accent} strokeWidth={2.5} />
</Pressable>
) : (
) : null}
{!actionsDisabled && !isFailed ? (
<>
<Pressable
onPress={onAccept}
@@ -248,7 +252,7 @@ export function DictationOverlay({
<ArrowUp size={theme.iconSize.lg} color={theme.colors.accent} strokeWidth={2.5} />
</Pressable>
</>
)}
) : null}
</View>
</View>
);
+18 -13
View File
@@ -3,10 +3,21 @@ import { ActivityIndicator, Pressable, Text, View } from "react-native";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { Check, X, XCircle } from "lucide-react-native";
import { useDownloadStore, formatSpeed, formatEta } from "@/stores/download-store";
import { useDownloadStore, formatSpeed, formatEta, type Download } from "@/stores/download-store";
const AUTO_DISMISS_DELAY = 3000;
function getDownloadStatusText(download: Download): string {
if (download.status === "downloading") {
if (download.progress) {
return `${Math.round(download.progress.percent * 100)}% · ${formatSpeed(download.progress.speed)} · ${formatEta(download.progress.eta)}`;
}
return "Starting...";
}
if (download.status === "complete") return "Download complete";
return download.message ?? "Download failed";
}
export function DownloadToast() {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
@@ -56,24 +67,18 @@ export function DownloadToast() {
<View style={styles.toast}>
{activeDownload.status === "downloading" ? (
<ActivityIndicator size="small" color={theme.colors.foreground} />
) : activeDownload.status === "complete" ? (
) : null}
{activeDownload.status === "complete" ? (
<Check size={18} color={theme.colors.primary} />
) : (
) : null}
{activeDownload.status !== "downloading" && activeDownload.status !== "complete" ? (
<XCircle size={18} color={theme.colors.destructive} />
)}
) : null}
<View style={styles.textContainer}>
<Text style={styles.fileName} numberOfLines={1}>
{activeDownload.fileName}
</Text>
<Text style={styles.status}>
{activeDownload.status === "downloading"
? activeDownload.progress
? `${Math.round(activeDownload.progress.percent * 100)}% · ${formatSpeed(activeDownload.progress.speed)} · ${formatEta(activeDownload.progress.eta)}`
: "Starting..."
: activeDownload.status === "complete"
? "Download complete"
: (activeDownload.message ?? "Download failed")}
</Text>
<Text style={styles.status}>{getDownloadStatusText(activeDownload)}</Text>
{activeDownload.status === "downloading" && activeDownload.progress && (
<View style={styles.progressBar}>
<ProgressFill percent={activeDownload.progress.percent} />
@@ -286,7 +286,10 @@ export function ExplorerSidebar({
// Mobile: full-screen overlay with gesture.
// On web, keep it interactive only while open so closed sidebars don't eat taps.
const overlayPointerEvents = isWeb ? (isOpen ? "auto" : "none") : "box-none";
let overlayPointerEvents: "auto" | "none" | "box-none";
if (!isWeb) overlayPointerEvents = "box-none";
else if (isOpen) overlayPointerEvents = "auto";
else overlayPointerEvents = "none";
// Navigation stacks can keep previous screens mounted; hide sidebars for unfocused
// screens so only the active screen exposes explorer/terminal surfaces.
+58 -48
View File
@@ -183,7 +183,10 @@ function getElementDescriptor(element: HTMLElement | null): string | null {
const id = element.id ? `#${element.id}` : "";
const testId = element.getAttribute?.("data-testid");
const label = element.getAttribute?.("aria-label");
const suffix = testId ? `[data-testid="${testId}"]` : label ? `[aria-label="${label}"]` : "";
let suffix: string;
if (testId) suffix = `[data-testid="${testId}"]`;
else if (label) suffix = `[aria-label="${label}"]`;
else suffix = "";
return `${tag}${id}${suffix}`;
}
@@ -576,8 +579,10 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
}
void voice.startVoice(voiceServerId, voiceAgentId).catch((error) => {
console.error("[MessageInput] Failed to start realtime voice", error);
const message =
error instanceof Error ? error.message : typeof error === "string" ? error : null;
let message: string | null;
if (error instanceof Error) message = error.message;
else if (typeof error === "string") message = error;
else message = null;
if (message && message.trim().length > 0) {
toast.error(message);
}
@@ -675,11 +680,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const getWebElement = useCallback((target: "root" | "wrapper"): HTMLElement | null => {
const current = target === "root" ? rootRef.current : inputWrapperRef.current;
if (!current) return null;
return current instanceof HTMLElement
? current
: (current as unknown as { getBoundingClientRect?: () => DOMRect }).getBoundingClientRect
? (current as unknown as HTMLElement)
: null;
if (current instanceof HTMLElement) return current;
if ((current as unknown as { getBoundingClientRect?: () => DOMRect }).getBoundingClientRect) {
return current as unknown as HTMLElement;
}
return null;
}, []);
useEffect(() => {
@@ -924,16 +929,30 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const isSendButtonDisabled =
disabled || (!canPressLoadingButton && (isSubmitDisabled || isSubmitLoading));
const defaultActionQueues = defaultSendBehavior === "queue" && isAgentRunning;
const defaultSubmitAccessibilityLabel = canPressLoadingButton
? "Interrupt agent"
: defaultActionQueues
? "Queue message"
: isAgentRunning
? "Send and interrupt"
: "Send message";
let defaultSubmitAccessibilityLabel: string;
if (canPressLoadingButton) defaultSubmitAccessibilityLabel = "Interrupt agent";
else if (defaultActionQueues) defaultSubmitAccessibilityLabel = "Queue message";
else if (isAgentRunning) defaultSubmitAccessibilityLabel = "Send and interrupt";
else defaultSubmitAccessibilityLabel = "Send message";
const submitAccessibilityLabel =
submitButtonAccessibilityLabel ?? defaultSubmitAccessibilityLabel;
let voiceButtonAccessibilityLabel: string;
if (isRealtimeVoiceForCurrentAgent) {
voiceButtonAccessibilityLabel = voice?.isMuted ? "Unmute Voice mode" : "Mute Voice mode";
} else if (isDictating) {
voiceButtonAccessibilityLabel = "Stop dictation";
} else {
voiceButtonAccessibilityLabel = "Start dictation";
}
let voiceTooltipText: string;
if (isRealtimeVoiceForCurrentAgent) {
voiceTooltipText = voice?.isMuted ? "Unmute voice" : "Mute voice";
} else {
voiceTooltipText = "Dictation";
}
const handleInputChange = useCallback(
(nextValue: string) => {
markScrollInvestigationEvent(investigationComponentId, "inputChange");
@@ -1107,42 +1126,32 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
onPress={handleVoicePress}
disabled={!isDictationStartEnabled}
accessibilityRole="button"
accessibilityLabel={
isRealtimeVoiceForCurrentAgent
? voice?.isMuted
? "Unmute Voice mode"
: "Mute Voice mode"
: isDictating
? "Stop dictation"
: "Start dictation"
}
accessibilityLabel={voiceButtonAccessibilityLabel}
style={voiceButtonStyle}
>
{({ hovered }) =>
isDictating ? (
<Square size={buttonIconSize} color="white" fill="white" />
) : isRealtimeVoiceForCurrentAgent && voice?.isMuted ? (
<MicOff
size={buttonIconSize}
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
/>
) : (
{({ hovered }) => {
if (isDictating) {
return <Square size={buttonIconSize} color="white" fill="white" />;
}
if (isRealtimeVoiceForCurrentAgent && voice?.isMuted) {
return (
<MicOff
size={buttonIconSize}
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
/>
);
}
return (
<Mic
size={buttonIconSize}
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
/>
)
}
);
}}
</TooltipTrigger>
<TooltipContent side="top" align="center" offset={8}>
<View style={styles.tooltipRow}>
<Text style={styles.tooltipText}>
{isRealtimeVoiceForCurrentAgent
? voice?.isMuted
? "Unmute voice"
: "Mute voice"
: "Dictation"}
</Text>
<Text style={styles.tooltipText}>{voiceTooltipText}</Text>
{(isRealtimeVoiceForCurrentAgent ? voiceMuteToggleKeys : dictationToggleKeys) ? (
<Shortcut
chord={
@@ -1166,13 +1175,13 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
accessibilityRole="button"
style={sendButtonCombinedStyle}
>
{isSubmitLoading ? (
<ActivityIndicator size="small" color="white" />
) : submitIcon === "return" ? (
{isSubmitLoading ? <ActivityIndicator size="small" color="white" /> : null}
{!isSubmitLoading && submitIcon === "return" ? (
<CornerDownLeft size={buttonIconSize} color="white" />
) : (
) : null}
{!isSubmitLoading && submitIcon !== "return" ? (
<ArrowUp size={buttonIconSize} color="white" />
)}
) : null}
</TooltipTrigger>
<TooltipContent side="top" align="center" offset={8}>
<View style={styles.tooltipRow}>
@@ -1204,7 +1213,8 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
onRetry={dictationStatus === "failed" ? handleRetryFailedRecording : undefined}
onDiscard={dictationStatus === "failed" ? handleDiscardFailedRecording : undefined}
/>
) : showRealtimeOverlay && voice ? (
) : null}
{!showDictationOverlay && showRealtimeOverlay && voice ? (
<RealtimeVoiceOverlay
isMuted={voice.isMuted}
isSwitching={voice.isVoiceSwitching}
@@ -248,11 +248,11 @@ export function ProjectPickerModal() {
keyboardShouldPersistTaps="always"
showsVerticalScrollIndicator={false}
>
{isSubmitting ? (
<Text style={emptyTextStyle}>Opening project...</Text>
) : options.length === 0 && !query.trim() ? (
{isSubmitting ? <Text style={emptyTextStyle}>Opening project...</Text> : null}
{!isSubmitting && options.length === 0 && !query.trim() ? (
<Text style={emptyTextStyle}>Start typing a path</Text>
) : (
) : null}
{!isSubmitting && !(options.length === 0 && !query.trim()) ? (
<>
{options.map((path, index) => (
<PathRow
@@ -263,7 +263,7 @@ export function ProjectPickerModal() {
/>
))}
</>
)}
) : null}
</ScrollView>
</View>
</View>
+7 -7
View File
@@ -242,13 +242,13 @@ export function ToastViewport({
return null;
}
const icon =
toast.icon ??
(toast.variant === "success" ? (
<CheckCircle2 size={18} color={theme.colors.primary} />
) : toast.variant === "error" ? (
<AlertTriangle size={18} color={theme.colors.destructive} />
) : null);
let defaultIcon: ReactNode = null;
if (toast.variant === "success") {
defaultIcon = <CheckCircle2 size={18} color={theme.colors.primary} />;
} else if (toast.variant === "error") {
defaultIcon = <AlertTriangle size={18} color={theme.colors.destructive} />;
}
const icon = toast.icon ?? defaultIcon;
const content = (
<View style={styles.container} pointerEvents="box-none">
@@ -285,8 +285,14 @@ export function DropdownMenuTrigger({
}
function getTransformOrigin(placement: Placement, alignment: Alignment): string {
const vertical = placement === "bottom" ? "top" : placement === "top" ? "bottom" : "center";
const horizontal = alignment === "start" ? "left" : alignment === "end" ? "right" : "center";
let vertical: string;
if (placement === "bottom") vertical = "top";
else if (placement === "top") vertical = "bottom";
else vertical = "center";
let horizontal: string;
if (alignment === "start") horizontal = "left";
else if (alignment === "end") horizontal = "right";
else horizontal = "center";
return `${vertical} ${horizontal}`;
}
@@ -610,21 +616,23 @@ export function DropdownMenuItem({
}, [isDisabled, selectItem, onSelect, closeOnSelect]);
const itemPressableStyle = useCallback(
({ pressed, hovered = false }: PressableStateCallbackType & { hovered?: boolean }) => [
styles.item,
selected
? selectedVariant === "accent"
? styles.itemSelectedAccent
: styles.itemSelected
: null,
selected && (Boolean(hovered) || pressed) && selectedVariant !== "accent"
? styles.itemSelectedInteractive
: null,
isDisabled ? styles.itemDisabled : null,
muted && !isDisabled ? styles.itemMuted : null,
Boolean(hovered) && !pressed && !isDisabled ? styles.itemHovered : null,
pressed && !isDisabled ? styles.itemPressed : null,
],
({ pressed, hovered = false }: PressableStateCallbackType & { hovered?: boolean }) => {
let selectedStyle: typeof styles.itemSelectedAccent | typeof styles.itemSelected | null;
if (!selected) selectedStyle = null;
else if (selectedVariant === "accent") selectedStyle = styles.itemSelectedAccent;
else selectedStyle = styles.itemSelected;
return [
styles.item,
selectedStyle,
selected && (Boolean(hovered) || pressed) && selectedVariant !== "accent"
? styles.itemSelectedInteractive
: null,
isDisabled ? styles.itemDisabled : null,
muted && !isDisabled ? styles.itemMuted : null,
Boolean(hovered) && !pressed && !isDisabled ? styles.itemHovered : null,
pressed && !isDisabled ? styles.itemPressed : null,
];
},
[selected, selectedVariant, isDisabled, muted],
);
+12 -3
View File
@@ -119,7 +119,10 @@ export function VolumeMeter({
// Create animated styles unconditionally at top level
const line1Style = useAnimatedStyle(() => {
const isActive = isSpeaking;
const baseOpacity = isMuted ? 0.3 : isActive ? 0.9 : 0.5;
let baseOpacity: number;
if (isMuted) baseOpacity = 0.3;
else if (isActive) baseOpacity = 0.9;
else baseOpacity = 0.5;
const currentVolume = isMuted ? 0 : animatedVolume.value;
const currentHeight = MIN_HEIGHT + MAX_HEIGHT * currentVolume * 1.2;
const volumeBoost = isMuted || !isActive ? 0 : currentVolume * 0.3;
@@ -131,7 +134,10 @@ export function VolumeMeter({
const line2Style = useAnimatedStyle(() => {
const isActive = isSpeaking;
const baseOpacity = isMuted ? 0.3 : isActive ? 0.9 : 0.5;
let baseOpacity: number;
if (isMuted) baseOpacity = 0.3;
else if (isActive) baseOpacity = 0.9;
else baseOpacity = 0.5;
const currentVolume = isMuted ? 0 : animatedVolume.value;
const currentHeight = MIN_HEIGHT + MAX_HEIGHT * currentVolume * 1.05;
const volumeBoost = isMuted || !isActive ? 0 : currentVolume * 0.3;
@@ -143,7 +149,10 @@ export function VolumeMeter({
const line3Style = useAnimatedStyle(() => {
const isActive = isSpeaking;
const baseOpacity = isMuted ? 0.3 : isActive ? 0.9 : 0.5;
let baseOpacity: number;
if (isMuted) baseOpacity = 0.3;
else if (isActive) baseOpacity = 0.9;
else baseOpacity = 0.5;
const currentVolume = isMuted ? 0 : animatedVolume.value;
const currentHeight = MIN_HEIGHT + MAX_HEIGHT * currentVolume * 0.9;
const volumeBoost = isMuted || !isActive ? 0 : currentVolume * 0.3;
@@ -338,13 +338,11 @@ export function WebDesktopScrollbarOverlay({
}
const handleVisible = isDragging || isScrollVisible || isHandleHovered;
const handleOpacity = isDragging
? HANDLE_OPACITY_DRAGGING
: isHandleHovered
? HANDLE_OPACITY_HOVERED
: isScrollVisible
? HANDLE_OPACITY_VISIBLE
: 0;
let handleOpacity: number;
if (isDragging) handleOpacity = HANDLE_OPACITY_DRAGGING;
else if (isHandleHovered) handleOpacity = HANDLE_OPACITY_HOVERED;
else if (isScrollVisible) handleOpacity = HANDLE_OPACITY_VISIBLE;
else handleOpacity = 0;
const handleWidth = isDragging || isHandleHovered ? HANDLE_WIDTH_ACTIVE : HANDLE_WIDTH_IDLE;
const handleColor = theme.colors.scrollbarHandle;
const handleCursor = isDragging ? "grabbing" : "grab";
@@ -229,22 +229,22 @@ export function StartupSplashScreen({ bootstrapState }: StartupSplashScreenProps
};
}, [isError]);
const progressSteps =
phase === "starting-daemon"
? [{ key: "starting-daemon", label: "Starting local server...", status: "active" as const }]
: phase === "connecting"
? [
{ key: "starting-daemon", label: "Started local server", status: "complete" as const },
{
key: "connecting",
label: "Connecting to local server...",
status: "active" as const,
},
]
: [
{ key: "starting-daemon", label: "Started local server", status: "complete" as const },
{ key: "connecting", label: "Connected to local server", status: "complete" as const },
];
let progressSteps: { key: string; label: string; status: "active" | "complete" }[];
if (phase === "starting-daemon") {
progressSteps = [
{ key: "starting-daemon", label: "Starting local server...", status: "active" },
];
} else if (phase === "connecting") {
progressSteps = [
{ key: "starting-daemon", label: "Started local server", status: "complete" },
{ key: "connecting", label: "Connecting to local server...", status: "active" },
];
} else {
progressSteps = [
{ key: "starting-daemon", label: "Started local server", status: "complete" },
{ key: "connecting", label: "Connected to local server", status: "complete" },
];
}
const logsText = useMemo(() => {
if (isLoadingLogs) {
@@ -342,19 +342,18 @@ function TabChip({
<View style={styles.tabIcon}>
<WorkspaceTabIcon presentation={presentation} active={isHighlighted} />
</View>
{showLabel ? (
presentation.titleState === "loading" ? (
<View style={tabLabelSkeletonStyle} />
) : (
<Text
style={tabLabelStyle}
selectable={false}
numberOfLines={1}
ellipsizeMode="tail"
>
{presentation.label}
</Text>
)
{showLabel && presentation.titleState === "loading" ? (
<View style={tabLabelSkeletonStyle} />
) : null}
{showLabel && presentation.titleState !== "loading" ? (
<Text
style={tabLabelStyle}
selectable={false}
numberOfLines={1}
ellipsizeMode="tail"
>
{presentation.label}
</Text>
) : null}
</View>
@@ -192,14 +192,18 @@ export function WorkspaceDraftAgentTab({
if (!provider) {
throw new Error("Select a model");
}
let modeIdOverride: { modeId: string } | Record<string, never>;
if (autoSubmitConfig?.modeId) {
modeIdOverride = { modeId: autoSubmitConfig.modeId };
} else if (composerState.modeOptions.length > 0 && composerState.selectedMode !== "") {
modeIdOverride = { modeId: composerState.selectedMode };
} else {
modeIdOverride = {};
}
const config = buildWorkspaceDraftAgentConfig({
provider,
cwd: workspaceDirectory,
...(autoSubmitConfig?.modeId
? { modeId: autoSubmitConfig.modeId }
: composerState.modeOptions.length > 0 && composerState.selectedMode !== ""
? { modeId: composerState.selectedMode }
: {}),
...modeIdOverride,
model: autoSubmitConfig?.model ?? (composerState.effectiveModelId || undefined),
thinkingOptionId:
autoSubmitConfig?.thinkingOptionId ??
+18 -18
View File
@@ -205,25 +205,25 @@ export const useWorkspaceTabsStore = create<WorkspaceTabsState>()(
const currentOrder = state.tabOrderByWorkspace[key] ?? [];
const nextOrder = currentOrder.filter((value) => value !== normalizedTabId);
const nextUiTabsByWorkspace =
nextTabs.length === 0
? (() => {
const { [key]: _removed, ...rest } = state.uiTabsByWorkspace;
return rest;
})()
: nextTabs.length === currentTabs.length
? state.uiTabsByWorkspace
: { ...state.uiTabsByWorkspace, [key]: nextTabs };
let nextUiTabsByWorkspace: typeof state.uiTabsByWorkspace;
if (nextTabs.length === 0) {
const { [key]: _removed, ...rest } = state.uiTabsByWorkspace;
nextUiTabsByWorkspace = rest;
} else if (nextTabs.length === currentTabs.length) {
nextUiTabsByWorkspace = state.uiTabsByWorkspace;
} else {
nextUiTabsByWorkspace = { ...state.uiTabsByWorkspace, [key]: nextTabs };
}
const nextTabOrderByWorkspace =
nextOrder.length === 0
? (() => {
const { [key]: _removed, ...rest } = state.tabOrderByWorkspace;
return rest;
})()
: nextOrder.length === currentOrder.length
? state.tabOrderByWorkspace
: { ...state.tabOrderByWorkspace, [key]: nextOrder };
let nextTabOrderByWorkspace: typeof state.tabOrderByWorkspace;
if (nextOrder.length === 0) {
const { [key]: _removed, ...rest } = state.tabOrderByWorkspace;
nextTabOrderByWorkspace = rest;
} else if (nextOrder.length === currentOrder.length) {
nextTabOrderByWorkspace = state.tabOrderByWorkspace;
} else {
nextTabOrderByWorkspace = { ...state.tabOrderByWorkspace, [key]: nextOrder };
}
const currentFocused = state.focusedTabIdByWorkspace[key] ?? null;
const nextFocused =
@@ -43,9 +43,14 @@ export function buildSidebarProjectRowModel(input: {
const collapsible = input.project.projectKind === "git" || input.project.workspaces.length > 1;
let chevron: "expand" | "collapse" | null;
if (!collapsible) chevron = null;
else if (input.collapsed) chevron = "expand";
else chevron = "collapse";
return {
kind: "project_section",
chevron: collapsible ? (input.collapsed ? "expand" : "collapse") : null,
chevron,
trailingAction: input.project.projectKind === "git" ? "new_worktree" : "none",
};
}
@@ -59,10 +59,9 @@ export async function buildClientConfig(
suppressSendErrors: true,
reconnect: { enabled: false },
...(isDev ? { runtimeMetricsIntervalMs: 10_000 } : {}),
...(connection.type === "directSocket" || connection.type === "directPipe"
? localTransportFactory
? { transportFactory: localTransportFactory }
: {}
...((connection.type === "directSocket" || connection.type === "directPipe") &&
localTransportFactory
? { transportFactory: localTransportFactory }
: {}),
};
+4 -6
View File
@@ -271,12 +271,10 @@ export function createVoiceRuntime(deps: VoiceRuntimeDeps): VoiceRuntime {
bytes: Uint8Array,
format: string,
): { arrayBuffer(): Promise<ArrayBuffer>; size: number; type: string } {
const mimeType =
format === "pcm"
? "audio/pcm;rate=24000;bits=16"
: format === "mp3"
? "audio/mpeg"
: `audio/${format}`;
let mimeType: string;
if (format === "pcm") mimeType = "audio/pcm;rate=24000;bits=16";
else if (format === "mp3") mimeType = "audio/mpeg";
else mimeType = `audio/${format}`;
return {
size: bytes.byteLength,