Make provider diagnostics easier to share (#1611)

* Show PATH matches in provider diagnostics

* Add copyable provider command probes

* Fix provider diagnostic path reporting

* Clean up command probe diagnostics
This commit is contained in:
Mohamed Boudra
2026-06-19 12:46:39 +08:00
committed by GitHub
parent f9660c7e89
commit cda66ae5f3
14 changed files with 453 additions and 21 deletions
@@ -1,4 +1,5 @@
import { AlertTriangle, FileText, Plus, RotateCw, Trash2 } from "lucide-react-native";
import * as Clipboard from "expo-clipboard";
import { AlertTriangle, Copy, FileText, Plus, RotateCw, Trash2 } from "lucide-react-native";
import type { TFunction } from "i18next";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -20,6 +21,7 @@ import { Button } from "@/components/ui/button";
import { LoadingSpinner } from "@/components/ui/loading-spinner";
import { useIsCompactFormFactor } from "@/constants/layout";
import { isWeb } from "@/constants/platform";
import { useToast } from "@/contexts/toast-context";
import { CODE_SURFACE_DATASET } from "@/styles/code-surface";
import { useDaemonConfig } from "@/hooks/use-daemon-config";
import { useProvidersSnapshot } from "@/hooks/use-providers-snapshot";
@@ -248,6 +250,7 @@ function DiagnosticSubSheet({
}) {
const { t } = useTranslation();
const { theme } = useUnistyles();
const toast = useToast();
const client = useHostRuntimeClient(serverId);
const [diagnostic, setDiagnostic] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
@@ -288,31 +291,62 @@ function DiagnosticSubSheet({
void fetchDiagnostic();
}, [fetchDiagnostic]);
const copyButtonStyle = useCallback(
({ hovered, pressed }: PressableStateCallbackType & { hovered?: boolean }) => [
sheetStyles.iconButton,
(Boolean(hovered) || pressed) && Boolean(diagnostic) && sheetStyles.iconButtonHovered,
diagnostic ? null : sheetStyles.disabled,
],
[diagnostic],
);
const handleCopyPress = useCallback(() => {
if (!diagnostic) return;
void Clipboard.setStringAsync(diagnostic)
.then(() => toast.copied(t("settings.providers.diagnostic.copyLabel")))
.catch(() => toast.error(t("settings.providers.diagnostic.copyFailed")));
}, [diagnostic, t, toast]);
const header = useMemo<SheetHeader>(
() => ({
title: t("settings.providers.diagnostic.title"),
actions: (
<Pressable
onPress={handleRefreshPress}
disabled={loading}
hitSlop={8}
style={refreshButtonStyle}
accessibilityRole="button"
accessibilityLabel={
loading
? t("settings.providers.diagnostic.refreshingAccessibility")
: t("settings.providers.diagnostic.refreshAccessibility")
}
>
{loading ? (
<LoadingSpinner size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
) : (
<RotateCw size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
)}
</Pressable>
<View style={sheetStyles.headerActions}>
<Pressable
onPress={handleCopyPress}
disabled={!diagnostic}
hitSlop={8}
style={copyButtonStyle}
accessibilityRole="button"
accessibilityLabel={t("settings.providers.diagnostic.copyAccessibility")}
>
<Copy size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
</Pressable>
<Pressable
onPress={handleRefreshPress}
disabled={loading}
hitSlop={8}
style={refreshButtonStyle}
accessibilityRole="button"
accessibilityLabel={
loading
? t("settings.providers.diagnostic.refreshingAccessibility")
: t("settings.providers.diagnostic.refreshAccessibility")
}
>
{loading ? (
<LoadingSpinner size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
) : (
<RotateCw size={theme.iconSize.sm} color={theme.colors.foregroundMuted} />
)}
</Pressable>
</View>
),
}),
[
copyButtonStyle,
diagnostic,
handleCopyPress,
handleRefreshPress,
loading,
refreshButtonStyle,
@@ -733,6 +767,11 @@ const sheetStyles = StyleSheet.create((theme) => ({
iconButtonHovered: {
backgroundColor: theme.colors.surface2,
},
headerActions: {
flexDirection: "row",
alignItems: "center",
gap: theme.spacing[1],
},
disabled: {
opacity: 0.5,
},
+3
View File
@@ -1778,6 +1778,9 @@ export const ar: TranslationResources = {
button: "التشخيص",
refresh: "ينعش",
refreshing: "منعش...",
copyLabel: "التشخيص",
copyAccessibility: "نسخ التشخيص",
copyFailed: "فشل نسخ التشخيص",
refreshAccessibility: "تحديث التشخيص",
refreshingAccessibility: "تحديث التشخيص",
running: "تشغيل التشخيص...",
+3
View File
@@ -1786,6 +1786,9 @@ export const en = {
button: "Diagnostic",
refresh: "Refresh",
refreshing: "Refreshing...",
copyLabel: "diagnostic",
copyAccessibility: "Copy diagnostic",
copyFailed: "Failed to copy diagnostic",
refreshAccessibility: "Refresh diagnostic",
refreshingAccessibility: "Refreshing diagnostic",
running: "Running diagnostic...",
+3
View File
@@ -1819,6 +1819,9 @@ export const es: TranslationResources = {
button: "Diagnóstico",
refresh: "Refrescar",
refreshing: "Refrescante...",
copyLabel: "diagnóstico",
copyAccessibility: "Copiar diagnóstico",
copyFailed: "No se pudo copiar el diagnóstico",
refreshAccessibility: "Actualizar diagnóstico",
refreshingAccessibility: "Diagnóstico refrescante",
running: "Ejecutando diagnóstico...",
+3
View File
@@ -1824,6 +1824,9 @@ export const fr: TranslationResources = {
button: "Diagnostique",
refresh: "Rafraîchir",
refreshing: "Rafraîchissant...",
copyLabel: "diagnostic",
copyAccessibility: "Copier le diagnostic",
copyFailed: "Échec de la copie du diagnostic",
refreshAccessibility: "Actualiser le diagnostic",
refreshingAccessibility: "Diagnostic rafraîchissant",
running: "Exécution du diagnostic...",
+3
View File
@@ -1810,6 +1810,9 @@ export const ru: TranslationResources = {
button: "Диагностика",
refresh: "Обновить",
refreshing: "Освежающий...",
copyLabel: "диагностика",
copyAccessibility: "Скопировать диагностику",
copyFailed: "Не удалось скопировать диагностику",
refreshAccessibility: "Обновить диагностику",
refreshingAccessibility: "Обновление диагностики",
running: "Запускаю диагностику...",
+3
View File
@@ -1755,6 +1755,9 @@ export const zhCN: TranslationResources = {
button: "诊断",
refresh: "刷新",
refreshing: "正在刷新...",
copyLabel: "诊断",
copyAccessibility: "复制诊断",
copyFailed: "复制诊断失败",
refreshAccessibility: "刷新诊断",
refreshingAccessibility: "正在刷新诊断",
running: "正在运行诊断...",