mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
chore(lint): no-nested-ternary in app batch 2 (26 files)
This commit is contained in:
@@ -114,12 +114,14 @@ export function createLocalFileAttachmentStore(params: {
|
||||
await ensureDirectory(baseDirectory);
|
||||
|
||||
const id = input.id ?? generateAttachmentId();
|
||||
const mimeTypeFromSource =
|
||||
input.source.kind === "data_url"
|
||||
? parseDataUrl(input.source.dataUrl).mimeType
|
||||
: input.source.kind === "blob"
|
||||
? input.source.blob.type
|
||||
: undefined;
|
||||
let mimeTypeFromSource: string | undefined;
|
||||
if (input.source.kind === "data_url") {
|
||||
mimeTypeFromSource = parseDataUrl(input.source.dataUrl).mimeType;
|
||||
} else if (input.source.kind === "blob") {
|
||||
mimeTypeFromSource = input.source.blob.type;
|
||||
} else {
|
||||
mimeTypeFromSource = undefined;
|
||||
}
|
||||
const mimeType = normalizeMimeType(input.mimeType ?? mimeTypeFromSource);
|
||||
const fileName = input.fileName ?? null;
|
||||
const extension = extensionForAttachment({ fileName, mimeType });
|
||||
|
||||
@@ -222,12 +222,14 @@ export function AddHostModal({ visible, onClose, onCancel, onSaved }: AddHostMod
|
||||
handleClose();
|
||||
} catch (error) {
|
||||
const { title, detail, raw: rawDetail } = buildConnectionFailureCopy(endpoint, error);
|
||||
const combined =
|
||||
rawDetail && detail && rawDetail !== detail
|
||||
? `${title}\n${detail}\nDetails: ${rawDetail}`
|
||||
: detail
|
||||
? `${title}\n${detail}`
|
||||
: title;
|
||||
let combined: string;
|
||||
if (rawDetail && detail && rawDetail !== detail) {
|
||||
combined = `${title}\n${detail}\nDetails: ${rawDetail}`;
|
||||
} else if (detail) {
|
||||
combined = `${title}\n${detail}`;
|
||||
} else {
|
||||
combined = title;
|
||||
}
|
||||
setErrorMessage(combined);
|
||||
if (!isMobile) {
|
||||
// Desktop/web: also surface it as a dialog for quick visibility.
|
||||
|
||||
@@ -8,12 +8,10 @@ export function lineNumberGutterWidth(maxLineNumber: number): number {
|
||||
}
|
||||
|
||||
export function getCodeInsets(theme: any) {
|
||||
const padding =
|
||||
typeof theme.spacing?.[3] === "number"
|
||||
? theme.spacing[3]
|
||||
: typeof theme.spacing?.[4] === "number"
|
||||
? theme.spacing[4]
|
||||
: 12;
|
||||
let padding: number;
|
||||
if (typeof theme.spacing?.[3] === "number") padding = theme.spacing[3];
|
||||
else if (typeof theme.spacing?.[4] === "number") padding = theme.spacing[4];
|
||||
else padding = 12;
|
||||
const extraRight = theme.spacing[4];
|
||||
const extraBottom = theme.spacing[3];
|
||||
|
||||
|
||||
@@ -217,19 +217,19 @@ function ModelRow({
|
||||
accessibilityLabel={isFavorite ? "Unfavorite model" : "Favorite model"}
|
||||
testID={`favorite-model-${row.provider}-${row.modelId}`}
|
||||
>
|
||||
{({ hovered }) => (
|
||||
<Star
|
||||
size={16}
|
||||
color={
|
||||
isFavorite
|
||||
? theme.colors.palette.amber[500]
|
||||
: hovered
|
||||
? theme.colors.foregroundMuted
|
||||
: theme.colors.border
|
||||
}
|
||||
fill={isFavorite ? theme.colors.palette.amber[500] : "transparent"}
|
||||
/>
|
||||
)}
|
||||
{({ hovered }) => {
|
||||
let starColor: string;
|
||||
if (isFavorite) starColor = theme.colors.palette.amber[500];
|
||||
else if (hovered) starColor = theme.colors.foregroundMuted;
|
||||
else starColor = theme.colors.border;
|
||||
return (
|
||||
<Star
|
||||
size={16}
|
||||
color={starColor}
|
||||
fill={isFavorite ? theme.colors.palette.amber[500] : "transparent"}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</Pressable>
|
||||
) : null,
|
||||
[
|
||||
|
||||
@@ -101,12 +101,12 @@ function CommandCenterActionRow({
|
||||
const { theme } = useUnistyles();
|
||||
const handlePress = useCallback(() => onSelect(item), [onSelect, item]);
|
||||
const action = item.action;
|
||||
const actionIcon =
|
||||
action.icon === "plus" ? (
|
||||
<Plus size={16} strokeWidth={2.4} color={theme.colors.foregroundMuted} />
|
||||
) : action.icon === "settings" ? (
|
||||
<Settings size={16} strokeWidth={2.2} color={theme.colors.foregroundMuted} />
|
||||
) : null;
|
||||
let actionIcon: React.ReactNode = null;
|
||||
if (action.icon === "plus") {
|
||||
actionIcon = <Plus size={16} strokeWidth={2.4} color={theme.colors.foregroundMuted} />;
|
||||
} else if (action.icon === "settings") {
|
||||
actionIcon = <Settings size={16} strokeWidth={2.2} color={theme.colors.foregroundMuted} />;
|
||||
}
|
||||
const titleStyle = useMemo(
|
||||
() => [styles.title, { color: theme.colors.foreground }],
|
||||
[theme.colors.foreground],
|
||||
|
||||
@@ -782,8 +782,10 @@ export function Composer({
|
||||
}
|
||||
void voice.startVoice(serverId, agentId).catch((error) => {
|
||||
console.error("[Composer] Failed to start voice mode", 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) {
|
||||
toastErrorRef.current(message);
|
||||
}
|
||||
|
||||
@@ -1238,12 +1238,14 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi
|
||||
const hasChanges = files.length > 0;
|
||||
const diffErrorMessage = diffPayloadError?.message ?? null;
|
||||
const prErrorMessage = githubFeaturesEnabled ? (prPayloadError?.message ?? null) : null;
|
||||
const branchLabel =
|
||||
gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD"
|
||||
? gitStatus.currentBranch
|
||||
: notGit
|
||||
? "Not a git repository"
|
||||
: "Unknown";
|
||||
let branchLabel: string;
|
||||
if (gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD") {
|
||||
branchLabel = gitStatus.currentBranch;
|
||||
} else if (notGit) {
|
||||
branchLabel = "Not a git repository";
|
||||
} else {
|
||||
branchLabel = "Unknown";
|
||||
}
|
||||
const actionsDisabled = !isGit || Boolean(status?.error) || isStatusLoading;
|
||||
const aheadCount = gitStatus?.aheadBehind?.ahead ?? 0;
|
||||
const behindBaseCount = gitStatus?.aheadBehind?.behind ?? 0;
|
||||
@@ -1313,15 +1315,17 @@ export function GitDiffPane({ serverId, workspaceId, cwd, hideHeaderRow }: GitDi
|
||||
</View>
|
||||
);
|
||||
} else if (!hasChanges) {
|
||||
let emptyMessage: string;
|
||||
if (changesPreferences.hideWhitespace) {
|
||||
emptyMessage = "No visible changes after hiding whitespace";
|
||||
} else if (diffMode === "uncommitted") {
|
||||
emptyMessage = "No uncommitted changes";
|
||||
} else {
|
||||
emptyMessage = `No changes vs ${baseRefLabel}`;
|
||||
}
|
||||
bodyContent = (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>
|
||||
{changesPreferences.hideWhitespace
|
||||
? "No visible changes after hiding whitespace"
|
||||
: diffMode === "uncommitted"
|
||||
? "No uncommitted changes"
|
||||
: `No changes vs ${baseRefLabel}`}
|
||||
</Text>
|
||||
<Text style={styles.emptyText}>{emptyMessage}</Text>
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -145,12 +145,11 @@ export const LeftSidebar = memo(function LeftSidebar({
|
||||
const activeHostStatus = activeServerId
|
||||
? (activeHostSnapshot?.connectionStatus ?? "connecting")
|
||||
: "idle";
|
||||
const activeHostStatusColor =
|
||||
activeHostStatus === "online"
|
||||
? theme.colors.palette.green[400]
|
||||
: activeHostStatus === "connecting"
|
||||
? theme.colors.palette.amber[500]
|
||||
: theme.colors.palette.red[500];
|
||||
let activeHostStatusColor: string;
|
||||
if (activeHostStatus === "online") activeHostStatusColor = theme.colors.palette.green[400];
|
||||
else if (activeHostStatus === "connecting")
|
||||
activeHostStatusColor = theme.colors.palette.amber[500];
|
||||
else activeHostStatusColor = theme.colors.palette.red[500];
|
||||
const hostOptions = useMemo(
|
||||
() =>
|
||||
daemons.map((daemon) => ({
|
||||
@@ -523,7 +522,10 @@ function MobileSidebar({
|
||||
pointerEvents: backdropOpacity.value > 0.01 ? "auto" : "none",
|
||||
}));
|
||||
|
||||
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";
|
||||
|
||||
const backdropStyle = useMemo(
|
||||
() => [staticStyles.backdrop, backdropAnimatedStyle],
|
||||
|
||||
@@ -215,7 +215,8 @@ export function ProviderDiagnosticSheet({
|
||||
<ActivityIndicator size="small" color={theme.colors.foregroundMuted} />
|
||||
<Text style={sheetStyles.mutedText}>Running diagnostic…</Text>
|
||||
</View>
|
||||
) : diagnostic ? (
|
||||
) : null}
|
||||
{!(loading && !diagnostic) && diagnostic ? (
|
||||
<ScrollView
|
||||
style={sheetStyles.codeScroll}
|
||||
contentContainerStyle={sheetStyles.codeContent}
|
||||
@@ -227,11 +228,12 @@ export function ProviderDiagnosticSheet({
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
) : (
|
||||
) : null}
|
||||
{!(loading && !diagnostic) && !diagnostic ? (
|
||||
<View style={sheetStyles.codeBlockLoading}>
|
||||
<Text style={sheetStyles.mutedText}>No diagnostic available.</Text>
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
|
||||
@@ -196,6 +196,16 @@ interface WorkspaceRowInnerProps {
|
||||
archiveShortcutKeys?: ShortcutKey[][] | null;
|
||||
}
|
||||
|
||||
function getWorkspaceArchiveStatus(
|
||||
isWorktree: boolean,
|
||||
archiveStatus: "idle" | "pending" | "success",
|
||||
isArchivingWorkspace: boolean,
|
||||
): "idle" | "pending" | "success" {
|
||||
if (isWorktree) return archiveStatus;
|
||||
if (isArchivingWorkspace) return "pending";
|
||||
return "idle";
|
||||
}
|
||||
|
||||
function useSidebarWorkspaceEntry(
|
||||
serverId: string | null,
|
||||
workspaceId: string | null,
|
||||
@@ -369,8 +379,10 @@ function WorkspaceStatusIndicator({
|
||||
);
|
||||
}
|
||||
|
||||
const KindIcon =
|
||||
workspaceKind === "local_checkout" ? Monitor : workspaceKind === "worktree" ? FolderGit2 : null;
|
||||
let KindIcon: typeof Monitor | typeof FolderGit2 | null;
|
||||
if (workspaceKind === "local_checkout") KindIcon = Monitor;
|
||||
else if (workspaceKind === "worktree") KindIcon = FolderGit2;
|
||||
else KindIcon = null;
|
||||
if (!KindIcon) return null;
|
||||
|
||||
const dotColor = getStatusDotColor({ theme, bucket, showDoneAsInactive: false });
|
||||
@@ -1187,7 +1199,8 @@ function WorkspaceRowInner({
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
) : workspace.diffStat ? (
|
||||
) : null}
|
||||
{!(onArchive && (isHovered || isTouchPlatform)) && workspace.diffStat ? (
|
||||
<DiffStat
|
||||
additions={workspace.diffStat.additions}
|
||||
deletions={workspace.diffStat.deletions}
|
||||
@@ -1413,7 +1426,7 @@ function WorkspaceRowWithMenu({
|
||||
dragHandleProps={dragHandleProps}
|
||||
menuController={null}
|
||||
archiveLabel={isWorktree ? "Archive worktree" : "Hide from sidebar"}
|
||||
archiveStatus={isWorktree ? archiveStatus : isArchivingWorkspace ? "pending" : "idle"}
|
||||
archiveStatus={getWorkspaceArchiveStatus(isWorktree, archiveStatus, isArchivingWorkspace)}
|
||||
archivePendingLabel={isWorktree ? "Archiving..." : "Hiding..."}
|
||||
onArchive={isWorktree ? handleArchiveWorktree : handleArchiveWorkspace}
|
||||
onCopyBranchName={canCopyBranchName ? handleCopyBranchName : undefined}
|
||||
|
||||
@@ -27,6 +27,11 @@ const restrictToHorizontalAxis: Modifier = ({ transform }) => ({
|
||||
|
||||
const DND_MODIFIERS: Modifier[] = [restrictToHorizontalAxis];
|
||||
|
||||
function computeDragOpacity(hasExternalContext: boolean, isDragging: boolean): number {
|
||||
if (!isDragging) return 1;
|
||||
return hasExternalContext ? 0.3 : 0.9;
|
||||
}
|
||||
|
||||
function SortableItem<T>({
|
||||
id,
|
||||
item,
|
||||
@@ -77,7 +82,7 @@ function SortableItem<T>({
|
||||
() => ({
|
||||
transform: combinedTransform || undefined,
|
||||
transition,
|
||||
opacity: externalDndContext && isDragging ? 0.3 : isDragging ? 0.9 : 1,
|
||||
opacity: computeDragOpacity(Boolean(externalDndContext), isDragging),
|
||||
zIndex: isDragging ? 1000 : 1,
|
||||
}),
|
||||
[combinedTransform, transition, externalDndContext, isDragging],
|
||||
|
||||
@@ -583,13 +583,11 @@ export default function TerminalEmulator({
|
||||
|
||||
const handleVisible =
|
||||
scrollbarGeometry.isVisible && (isDraggingScrollbar || isScrollVisible || isHandleHovered);
|
||||
const handleOpacity = isDraggingScrollbar
|
||||
? SCROLLBAR_HANDLE_OPACITY_DRAGGING
|
||||
: isHandleHovered
|
||||
? SCROLLBAR_HANDLE_OPACITY_HOVERED
|
||||
: isScrollVisible
|
||||
? SCROLLBAR_HANDLE_OPACITY_VISIBLE
|
||||
: 0;
|
||||
let handleOpacity: number;
|
||||
if (isDraggingScrollbar) handleOpacity = SCROLLBAR_HANDLE_OPACITY_DRAGGING;
|
||||
else if (isHandleHovered) handleOpacity = SCROLLBAR_HANDLE_OPACITY_HOVERED;
|
||||
else if (isScrollVisible) handleOpacity = SCROLLBAR_HANDLE_OPACITY_VISIBLE;
|
||||
else handleOpacity = 0;
|
||||
const handleWidth =
|
||||
isDraggingScrollbar || isHandleHovered
|
||||
? SCROLLBAR_HANDLE_WIDTH_ACTIVE
|
||||
|
||||
@@ -200,17 +200,22 @@ export function ComboboxItem({
|
||||
}: ComboboxItemProps): ReactElement {
|
||||
const { theme } = useUnistyles();
|
||||
|
||||
const leadingContent = leadingSlot ? (
|
||||
<View style={styles.comboboxItemLeadingSlot}>{leadingSlot}</View>
|
||||
) : kind === "directory" || kind === "file" ? (
|
||||
<View style={styles.comboboxItemLeadingSlot}>
|
||||
{kind === "directory" ? (
|
||||
let leadingContent: ReactElement | null = null;
|
||||
if (leadingSlot) {
|
||||
leadingContent = <View style={styles.comboboxItemLeadingSlot}>{leadingSlot}</View>;
|
||||
} else if (kind === "directory") {
|
||||
leadingContent = (
|
||||
<View style={styles.comboboxItemLeadingSlot}>
|
||||
<Folder size={16} color={theme.colors.foregroundMuted} />
|
||||
) : (
|
||||
</View>
|
||||
);
|
||||
} else if (kind === "file") {
|
||||
leadingContent = (
|
||||
<View style={styles.comboboxItemLeadingSlot}>
|
||||
<File size={16} color={theme.colors.foregroundMuted} />
|
||||
)}
|
||||
</View>
|
||||
) : null;
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const itemPressableStyle = useCallback(
|
||||
({ pressed, hovered = false }: PressableStateCallbackType & { hovered?: boolean }) => [
|
||||
@@ -482,30 +487,36 @@ export function Combobox({
|
||||
const measuredTopStartBottom = useMeasuredTopStartPosition
|
||||
? Math.max(windowHeight - referenceTop + 5, collisionPadding)
|
||||
: null;
|
||||
let resolvedPositionReady: boolean;
|
||||
if (isDesktopAboveSearch) {
|
||||
resolvedPositionReady = floatingLeft !== null && desktopAboveSearchBottom !== null;
|
||||
} else if (useMeasuredTopStartPosition) {
|
||||
resolvedPositionReady = clampedMeasuredTopStartLeft !== null && measuredTopStartBottom !== null;
|
||||
} else {
|
||||
resolvedPositionReady =
|
||||
floatingLeft !== null &&
|
||||
floatingTop !== null &&
|
||||
(hasNonZeroFloatingPosition || referenceAtOrigin);
|
||||
}
|
||||
const hasResolvedDesktopPosition =
|
||||
referenceWidth !== null &&
|
||||
referenceWidth > 0 &&
|
||||
(isDesktopAboveSearch
|
||||
? floatingLeft !== null && desktopAboveSearchBottom !== null
|
||||
: useMeasuredTopStartPosition
|
||||
? clampedMeasuredTopStartLeft !== null && measuredTopStartBottom !== null
|
||||
: floatingLeft !== null &&
|
||||
floatingTop !== null &&
|
||||
(hasNonZeroFloatingPosition || referenceAtOrigin));
|
||||
referenceWidth !== null && referenceWidth > 0 && resolvedPositionReady;
|
||||
const shouldHideDesktopContent = desktopPreventInitialFlash && !hasResolvedDesktopPosition;
|
||||
const shouldUseDesktopFade = !desktopPreventInitialFlash;
|
||||
|
||||
const desktopPositionStyle = isDesktopAboveSearch
|
||||
? {
|
||||
left: floatingLeft ?? 0,
|
||||
bottom: desktopAboveSearchBottom ?? 0,
|
||||
}
|
||||
: useMeasuredTopStartPosition
|
||||
? {
|
||||
left: clampedMeasuredTopStartLeft ?? 0,
|
||||
bottom: measuredTopStartBottom ?? 0,
|
||||
}
|
||||
: floatingStyles;
|
||||
let desktopPositionStyle: { left: number; bottom: number } | typeof floatingStyles;
|
||||
if (isDesktopAboveSearch) {
|
||||
desktopPositionStyle = {
|
||||
left: floatingLeft ?? 0,
|
||||
bottom: desktopAboveSearchBottom ?? 0,
|
||||
};
|
||||
} else if (useMeasuredTopStartPosition) {
|
||||
desktopPositionStyle = {
|
||||
left: clampedMeasuredTopStartLeft ?? 0,
|
||||
bottom: measuredTopStartBottom ?? 0,
|
||||
};
|
||||
} else {
|
||||
desktopPositionStyle = floatingStyles;
|
||||
}
|
||||
|
||||
const { sheetRef: bottomSheetRef, handleSheetChange } = useIsolatedBottomSheetVisibility({
|
||||
visible: isOpen,
|
||||
|
||||
@@ -695,20 +695,22 @@ export function ContextMenuItem({
|
||||
}, [isDisabled, closeOnSelect, setOpen, onSelect]);
|
||||
|
||||
const itemPressableStyle = useCallback(
|
||||
({ pressed, hovered = false }: PressableStateCallbackType & { hovered?: boolean }) => [
|
||||
styles.item,
|
||||
selected
|
||||
? selectedVariant === "accent"
|
||||
? styles.itemSelectedAccent
|
||||
: styles.itemSelected
|
||||
: null,
|
||||
selected && (hovered || pressed) && selectedVariant !== "accent"
|
||||
? styles.itemSelectedInteractive
|
||||
: null,
|
||||
isDisabled ? styles.itemDisabled : null,
|
||||
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 && (hovered || pressed) && selectedVariant !== "accent"
|
||||
? styles.itemSelectedInteractive
|
||||
: null,
|
||||
isDisabled ? styles.itemDisabled : null,
|
||||
hovered && !pressed && !isDisabled ? styles.itemHovered : null,
|
||||
pressed && !isDisabled ? styles.itemPressed : null,
|
||||
];
|
||||
},
|
||||
[selected, selectedVariant, isDisabled],
|
||||
);
|
||||
|
||||
|
||||
@@ -112,12 +112,10 @@ function buildAudioPlaybackSource(chunks: BufferedAudioChunk[]): AudioPlaybackSo
|
||||
}
|
||||
|
||||
const format = chunks[0]?.format ?? "pcm";
|
||||
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}`;
|
||||
|
||||
const bytes = output.slice();
|
||||
return {
|
||||
|
||||
@@ -21,6 +21,20 @@ import {
|
||||
import { useDaemonStatus } from "@/desktop/hooks/use-daemon-status";
|
||||
import { resolveAppVersion } from "@/utils/app-version";
|
||||
|
||||
function getDaemonManagementButtonLabel(isUpdating: boolean, isPaused: boolean): string {
|
||||
if (isUpdating) return isPaused ? "Resuming..." : "Pausing...";
|
||||
return isPaused ? "Resume" : "Pause";
|
||||
}
|
||||
|
||||
function getDaemonRestartButtonLabel(
|
||||
isRestarting: boolean,
|
||||
daemonStatus: string | undefined,
|
||||
actionLabel: string,
|
||||
): string {
|
||||
if (!isRestarting) return actionLabel;
|
||||
return daemonStatus === "running" ? "Restarting..." : "Starting...";
|
||||
}
|
||||
|
||||
export function LocalDaemonSection() {
|
||||
const { theme } = useUnistyles();
|
||||
const showSection = shouldUseDesktopDaemon();
|
||||
@@ -338,13 +352,10 @@ export function LocalDaemonSection() {
|
||||
onPress={handleToggleDaemonManagement}
|
||||
disabled={isUpdatingDaemonManagement}
|
||||
>
|
||||
{isUpdatingDaemonManagement
|
||||
? isDaemonManagementPaused
|
||||
? "Resuming..."
|
||||
: "Pausing..."
|
||||
: isDaemonManagementPaused
|
||||
? "Resume"
|
||||
: "Pause"}
|
||||
{getDaemonManagementButtonLabel(
|
||||
isUpdatingDaemonManagement,
|
||||
isDaemonManagementPaused,
|
||||
)}
|
||||
</Button>
|
||||
</View>
|
||||
<View style={ROW_WITH_BORDER_STYLE}>
|
||||
@@ -360,11 +371,11 @@ export function LocalDaemonSection() {
|
||||
onPress={handleUpdateLocalDaemon}
|
||||
disabled={isRestartingDaemon}
|
||||
>
|
||||
{isRestartingDaemon
|
||||
? daemonStatus?.status === "running"
|
||||
? "Restarting..."
|
||||
: "Starting..."
|
||||
: daemonActionLabel}
|
||||
{getDaemonRestartButtonLabel(
|
||||
isRestartingDaemon,
|
||||
daemonStatus?.status,
|
||||
daemonActionLabel,
|
||||
)}
|
||||
</Button>
|
||||
</View>
|
||||
<View style={ROW_WITH_BORDER_STYLE}>
|
||||
|
||||
@@ -79,7 +79,8 @@ export function PairDeviceSection() {
|
||||
<ActivityIndicator size="small" />
|
||||
<Text style={styles.hint}>Loading pairing offer…</Text>
|
||||
</View>
|
||||
) : pairingQuery.isError ? (
|
||||
) : null}
|
||||
{!pairingQuery.isPending && pairingQuery.isError ? (
|
||||
<View style={styles.centered}>
|
||||
<Text style={styles.hint}>
|
||||
{pairingQuery.error instanceof Error
|
||||
@@ -90,7 +91,8 @@ export function PairDeviceSection() {
|
||||
Retry
|
||||
</Button>
|
||||
</View>
|
||||
) : !pairingQuery.data?.url ? (
|
||||
) : null}
|
||||
{!pairingQuery.isPending && !pairingQuery.isError && !pairingQuery.data?.url ? (
|
||||
<View style={styles.centered}>
|
||||
<Text style={styles.hint}>
|
||||
{pairingQuery.data?.relayEnabled === false
|
||||
@@ -101,7 +103,8 @@ export function PairDeviceSection() {
|
||||
Retry
|
||||
</Button>
|
||||
</View>
|
||||
) : (
|
||||
) : null}
|
||||
{!pairingQuery.isPending && !pairingQuery.isError && pairingQuery.data?.url ? (
|
||||
<View style={styles.content}>
|
||||
<Text style={styles.hint}>
|
||||
Scan this QR code with Paseo on your phone, or copy the link below.
|
||||
@@ -109,11 +112,11 @@ export function PairDeviceSection() {
|
||||
<View style={styles.qrContainer}>
|
||||
{qrImageSource ? (
|
||||
<Image source={qrImageSource} style={styles.qrImage} resizeMode="contain" />
|
||||
) : qrQuery.isError ? (
|
||||
) : null}
|
||||
{!qrImageSource && qrQuery.isError ? (
|
||||
<Text style={styles.hint}>QR code unavailable.</Text>
|
||||
) : (
|
||||
<ActivityIndicator size="small" />
|
||||
)}
|
||||
) : null}
|
||||
{!qrImageSource && !qrQuery.isError ? <ActivityIndicator size="small" /> : null}
|
||||
</View>
|
||||
<View style={styles.linkRow}>
|
||||
<View style={styles.inputWrapper}>
|
||||
@@ -135,7 +138,7 @@ export function PairDeviceSection() {
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -158,13 +158,11 @@ export function deriveAgentScreenViewState({
|
||||
};
|
||||
}
|
||||
|
||||
const source: "authoritative" | "optimistic" | "stale" = useOptimisticCreateFlowAgent
|
||||
? "optimistic"
|
||||
: input.agent
|
||||
? "authoritative"
|
||||
: input.shouldUseOptimisticStream
|
||||
? "optimistic"
|
||||
: "stale";
|
||||
let source: "authoritative" | "optimistic" | "stale";
|
||||
if (useOptimisticCreateFlowAgent) source = "optimistic";
|
||||
else if (input.agent) source = "authoritative";
|
||||
else if (input.shouldUseOptimisticStream) source = "optimistic";
|
||||
else source = "stale";
|
||||
|
||||
let sync: AgentScreenReadySyncState;
|
||||
if (!input.isConnected) {
|
||||
|
||||
@@ -50,15 +50,15 @@ function selectWorkspacePrHint(payload: CheckoutPrStatusPayload): PrHint | null
|
||||
return null;
|
||||
}
|
||||
|
||||
let state: "merged" | "open" | "closed";
|
||||
if (status.isMerged || status.state === "merged") state = "merged";
|
||||
else if (status.state === "open") state = "open";
|
||||
else state = "closed";
|
||||
|
||||
return {
|
||||
url: status.url,
|
||||
number,
|
||||
state:
|
||||
status.isMerged || status.state === "merged"
|
||||
? "merged"
|
||||
: status.state === "open"
|
||||
? "open"
|
||||
: "closed",
|
||||
state,
|
||||
checks: status.checks,
|
||||
checksStatus: status.checksStatus as PrHint["checksStatus"],
|
||||
reviewDecision: status.reviewDecision as PrHint["reviewDecision"],
|
||||
|
||||
@@ -280,12 +280,14 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
|
||||
const pushDisabled = actionsDisabled || pushStatus === "pending";
|
||||
const archiveDisabled = actionsDisabled || archiveStatus === "pending";
|
||||
|
||||
const branchLabel =
|
||||
gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD"
|
||||
? gitStatus.currentBranch
|
||||
: notGit
|
||||
? "Not a git repository"
|
||||
: "Unknown";
|
||||
let branchLabel: string;
|
||||
if (gitStatus?.currentBranch && gitStatus.currentBranch !== "HEAD") {
|
||||
branchLabel = gitStatus.currentBranch;
|
||||
} else if (notGit) {
|
||||
branchLabel = "Not a git repository";
|
||||
} else {
|
||||
branchLabel = "Unknown";
|
||||
}
|
||||
|
||||
// Build actions
|
||||
const gitActions: GitActions = useMemo(() => {
|
||||
|
||||
@@ -171,14 +171,11 @@ function SetupPanel() {
|
||||
return null;
|
||||
})();
|
||||
|
||||
const statusLabel =
|
||||
snapshot?.status === "running"
|
||||
? "Running"
|
||||
: snapshot?.status === "completed"
|
||||
? "Completed"
|
||||
: snapshot?.status === "failed"
|
||||
? "Failed"
|
||||
: "Waiting for setup output";
|
||||
let statusLabel: string;
|
||||
if (snapshot?.status === "running") statusLabel = "Running";
|
||||
else if (snapshot?.status === "completed") statusLabel = "Completed";
|
||||
else if (snapshot?.status === "failed") statusLabel = "Failed";
|
||||
else statusLabel = "Waiting for setup output";
|
||||
|
||||
return (
|
||||
<ScrollView
|
||||
@@ -196,7 +193,8 @@ function SetupPanel() {
|
||||
<ActivityIndicator size="large" color={theme.colors.foregroundMuted} />
|
||||
<Text style={styles.waitingText}>Setting up workspace...</Text>
|
||||
</View>
|
||||
) : hasNoSetupCommands ? (
|
||||
) : null}
|
||||
{!isWaiting && hasNoSetupCommands ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text
|
||||
style={styles.emptyText}
|
||||
@@ -206,7 +204,8 @@ function SetupPanel() {
|
||||
No setup commands ran for this workspace.
|
||||
</Text>
|
||||
</View>
|
||||
) : (
|
||||
) : null}
|
||||
{!isWaiting && !hasNoSetupCommands ? (
|
||||
<View style={styles.commandList}>
|
||||
{commands.map((command) => {
|
||||
const isExpanded = expandedIndices.has(command.index);
|
||||
@@ -275,7 +274,7 @@ function SetupPanel() {
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -70,14 +70,16 @@ function SessionsScreenContent({ serverId }: { serverId: string }) {
|
||||
<View style={styles.loadingContainer}>
|
||||
<LoadingSpinner size="large" color={theme.colors.foregroundMuted} />
|
||||
</View>
|
||||
) : sortedAgents.length === 0 ? (
|
||||
) : null}
|
||||
{!isInitialLoad && sortedAgents.length === 0 ? (
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyText}>No sessions yet</Text>
|
||||
<Button variant="ghost" leftIcon={ChevronLeft} onPress={handleBack}>
|
||||
Back
|
||||
</Button>
|
||||
</View>
|
||||
) : (
|
||||
) : null}
|
||||
{!isInitialLoad && sortedAgents.length > 0 ? (
|
||||
<AgentList
|
||||
agents={sortedAgents}
|
||||
showCheckoutInfo={false}
|
||||
@@ -86,7 +88,7 @@ function SessionsScreenContent({ serverId }: { serverId: string }) {
|
||||
listFooterComponent={listFooterComponent}
|
||||
showAttentionIndicator={false}
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -344,6 +344,15 @@ function AboutSection({ appVersionText, isDesktopApp }: AboutSectionProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function getUpdateButtonLabel(
|
||||
isInstalling: boolean,
|
||||
latestVersion: string | null | undefined,
|
||||
): string {
|
||||
if (isInstalling) return "Installing...";
|
||||
if (latestVersion) return `Update to ${formatVersionWithPrefix(latestVersion)}`;
|
||||
return "Update";
|
||||
}
|
||||
|
||||
function DesktopAppUpdateRow() {
|
||||
const { settings, updateSettings } = useAppSettings();
|
||||
const {
|
||||
@@ -451,11 +460,7 @@ function DesktopAppUpdateRow() {
|
||||
onPress={handleInstallUpdate}
|
||||
disabled={isChecking || isInstalling || !availableUpdate}
|
||||
>
|
||||
{isInstalling
|
||||
? "Installing..."
|
||||
: availableUpdate?.latestVersion
|
||||
? `Update to ${formatVersionWithPrefix(availableUpdate.latestVersion)}`
|
||||
: "Update"}
|
||||
{getUpdateButtonLabel(isInstalling, availableUpdate?.latestVersion)}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -140,11 +140,13 @@ export function ProvidersSection({ serverId }: ProvidersSectionProps) {
|
||||
<View style={EMPTY_CARD_STYLE}>
|
||||
<Text style={styles.emptyText}>Connect to this host to see providers</Text>
|
||||
</View>
|
||||
) : isLoading ? (
|
||||
) : null}
|
||||
{hasServer && isConnected && isLoading ? (
|
||||
<View style={EMPTY_CARD_STYLE}>
|
||||
<Text style={styles.emptyText}>Loading...</Text>
|
||||
</View>
|
||||
) : (
|
||||
) : null}
|
||||
{hasServer && isConnected && !isLoading ? (
|
||||
<View style={settingsStyles.card}>
|
||||
{providerDefinitions.map((def, index) => (
|
||||
<ProviderRow
|
||||
@@ -156,7 +158,7 @@ export function ProvidersSection({ serverId }: ProvidersSectionProps) {
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
)}
|
||||
) : null}
|
||||
</SettingsSection>
|
||||
|
||||
{diagnosticProvider ? (
|
||||
|
||||
@@ -2183,33 +2183,41 @@ function WorkspaceScreenContent({
|
||||
},
|
||||
[buildPaneContentModel],
|
||||
);
|
||||
const content = shouldRenderMissingWorkspaceDescriptor({
|
||||
const showMissingWorkspaceDescriptor = shouldRenderMissingWorkspaceDescriptor({
|
||||
workspace: workspaceDescriptor,
|
||||
hasHydratedWorkspaces,
|
||||
}) ? (
|
||||
<View style={styles.emptyState}>
|
||||
<ActivityIndicator color={theme.colors.foregroundMuted} />
|
||||
</View>
|
||||
) : isMissingWorkspaceExecutionAuthority ? (
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateText}>
|
||||
Workspace execution directory is missing. Reload workspace data before opening tabs.
|
||||
</Text>
|
||||
</View>
|
||||
) : !activeTabDescriptor ? (
|
||||
!hasHydratedAgents ? (
|
||||
});
|
||||
let content: React.ReactNode;
|
||||
if (showMissingWorkspaceDescriptor) {
|
||||
content = (
|
||||
<View style={styles.emptyState}>
|
||||
<ActivityIndicator color={theme.colors.foregroundMuted} />
|
||||
</View>
|
||||
) : (
|
||||
);
|
||||
} else if (isMissingWorkspaceExecutionAuthority) {
|
||||
content = (
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateText}>
|
||||
Workspace execution directory is missing. Reload workspace data before opening tabs.
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (!activeTabDescriptor && !hasHydratedAgents) {
|
||||
content = (
|
||||
<View style={styles.emptyState}>
|
||||
<ActivityIndicator color={theme.colors.foregroundMuted} />
|
||||
</View>
|
||||
);
|
||||
} else if (!activeTabDescriptor) {
|
||||
content = (
|
||||
<View style={styles.emptyState}>
|
||||
<Text style={styles.emptyStateText}>
|
||||
No tabs are available yet. Use New tab to create an agent or terminal.
|
||||
</Text>
|
||||
</View>
|
||||
)
|
||||
) : (
|
||||
mountedFocusedPaneTabIds.map((tabId) => {
|
||||
);
|
||||
} else {
|
||||
content = mountedFocusedPaneTabIds.map((tabId) => {
|
||||
const tabDescriptor = focusedPaneTabDescriptorMap.get(tabId);
|
||||
if (!tabDescriptor) {
|
||||
return null;
|
||||
@@ -2226,8 +2234,8 @@ function WorkspaceScreenContent({
|
||||
buildPaneContentModel={buildMobilePaneContentModel}
|
||||
/>
|
||||
);
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const buildDesktopPaneContentModel = useCallback(
|
||||
function buildDesktopPaneContentModel(input: { paneId: string; tab: WorkspaceTabDescriptor }) {
|
||||
|
||||
@@ -216,11 +216,10 @@ export const useDownloadStore = create<DownloadState>()((set, get) => ({
|
||||
updated.delete(id);
|
||||
}
|
||||
}
|
||||
const newActiveId = state.activeDownloadId
|
||||
? updated.has(state.activeDownloadId)
|
||||
? state.activeDownloadId
|
||||
: findMostRecentDownloadId(updated)
|
||||
: null;
|
||||
let newActiveId: string | null;
|
||||
if (!state.activeDownloadId) newActiveId = null;
|
||||
else if (updated.has(state.activeDownloadId)) newActiveId = state.activeDownloadId;
|
||||
else newActiveId = findMostRecentDownloadId(updated);
|
||||
return { downloads: updated, activeDownloadId: newActiveId };
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user