Merge remote-tracking branch 'origin/main' into paseo-cloud-worktree-plan

This commit is contained in:
Mohamed Boudra
2026-07-13 18:44:36 +02:00
18 changed files with 428 additions and 70 deletions
+100
View File
@@ -0,0 +1,100 @@
import { expect, test, type Page } from "./fixtures";
import { gotoAppShell, openSettings } from "./helpers/app";
import { openSettingsSection } from "./helpers/settings";
const DISCORD_DESTINATION =
/^https:\/\/(?:discord\.gg\/jz8T2uahpH|discord\.com\/invite\/jz8T2uahpH)(?:[/?#]|$)/;
const GITHUB_ISSUE_DESTINATION =
/^https:\/\/github\.com\/(?:getpaseo\/paseo\/issues\/new(?:\/choose)?(?:[/?#]|$)|login\?return_to=https%3A%2F%2Fgithub\.com%2Fgetpaseo%2Fpaseo%2Fissues%2Fnew$)/;
const APP_VERSION = /^Paseo v\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
async function openHelpMenu(page: Page): Promise<void> {
await page.getByTestId("sidebar-help").click();
await expect(page.getByTestId("sidebar-help-menu")).toBeVisible();
}
async function expectDiagnosticReport(page: Page): Promise<void> {
const sheet = page.getByTestId("app-diagnostic-sheet");
await expect(sheet).toBeVisible();
await expect(sheet.getByRole("button", { name: "Copy diagnostic" })).toBeEnabled();
await expect(page.getByText(/App version:/).first()).toBeVisible();
}
async function closeSheet(page: Page, testID: string): Promise<void> {
const sheet = page.getByTestId(testID);
await sheet.getByLabel("Close").click();
await expect(sheet).not.toBeVisible();
}
async function expectExternalPage(
page: Page,
actionTestID: string,
expectedUrl: RegExp,
): Promise<void> {
const popupPromise = page.waitForEvent("popup");
await page.getByTestId(actionTestID).click();
const popup = await popupPromise;
expect(popup.url()).toMatch(expectedUrl);
await popup.close();
}
test("opens troubleshooting tools from the sidebar help menu", async ({ page }) => {
await gotoAppShell(page);
await expect(page.getByTestId("sidebar-help")).toBeVisible();
await openHelpMenu(page);
const triggerBox = await page.getByTestId("sidebar-help").evaluate((element) => {
const { y, height } = element.getBoundingClientRect();
return { y, height };
});
const menuBox = await page.getByTestId("sidebar-help-menu").evaluate((element) => {
const { y, height } = element.getBoundingClientRect();
return { y, height };
});
expect(menuBox.y + menuBox.height).toBeLessThanOrEqual(triggerBox.y);
await expect(page.getByText("Troubleshoot", { exact: true })).toBeVisible();
await expect(page.getByText("Report an issue", { exact: true })).toBeVisible();
await expect(page.getByTestId("sidebar-help-version")).toHaveText(APP_VERSION);
await page.getByTestId("sidebar-help-diagnostics").click();
await expectDiagnosticReport(page);
await closeSheet(page, "app-diagnostic-sheet");
await openHelpMenu(page);
await page.getByTestId("sidebar-help-shortcuts").click();
await expect(page.getByTestId("keyboard-shortcuts-dialog")).toBeVisible();
await closeSheet(page, "keyboard-shortcuts-dialog");
});
test("opens the preferred issue-reporting destinations", async ({ page }) => {
await gotoAppShell(page);
await openHelpMenu(page);
await expectExternalPage(page, "sidebar-help-discord", DISCORD_DESTINATION);
await openHelpMenu(page);
await expectExternalPage(page, "sidebar-help-github", GITHUB_ISSUE_DESTINATION);
});
test("keeps diagnostics available from Settings after globalizing the sheet", async ({ page }) => {
await gotoAppShell(page);
await openSettings(page);
await openSettingsSection(page, "diagnostics");
await page.getByRole("button", { name: "Run", exact: true }).click();
await expectDiagnosticReport(page);
});
test.describe("compact sidebar help", () => {
test.use({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true });
test("offers diagnostics without advertising disabled keyboard shortcuts", async ({ page }) => {
await gotoAppShell(page);
await page.getByRole("button", { name: "Open menu", exact: true }).click();
await openHelpMenu(page);
await expect(page.getByTestId("sidebar-help-shortcuts")).toHaveCount(0);
await page.getByTestId("sidebar-help-diagnostics").click();
await expectDiagnosticReport(page);
});
});
+2
View File
@@ -26,6 +26,7 @@ import { WorktreeSetupCalloutSource } from "@/components/worktree-setup-callout-
import { DownloadToast } from "@/components/download-toast";
import { QuittingOverlay } from "@/components/quitting-overlay";
import { KeyboardShortcutsDialog } from "@/components/keyboard-shortcuts-dialog";
import { AppDiagnosticHost } from "@/components/app-diagnostic-host";
import { LeftSidebar } from "@/components/left-sidebar";
import { SidebarModelProvider } from "@/components/sidebar/sidebar-model";
import { CompactExplorerSidebarHost } from "@/components/compact-explorer-sidebar-host";
@@ -487,6 +488,7 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
<ProviderSettingsHost />
<WorkspaceSetupDialog />
<KeyboardShortcutsDialog />
<AppDiagnosticHost />
<QuittingOverlay />
</View>
);
@@ -0,0 +1,18 @@
import { AppDiagnosticSheet } from "@/components/app-diagnostic-sheet";
import { isElectronRuntime } from "@/desktop/host";
import { useAppDiagnosticStore } from "@/diagnostics/store";
import { resolveAppVersion } from "@/utils/app-version";
export function AppDiagnosticHost() {
const visible = useAppDiagnosticStore((state) => state.visible);
const close = useAppDiagnosticStore((state) => state.close);
return (
<AppDiagnosticSheet
visible={visible}
onClose={close}
appVersion={resolveAppVersion()}
isDesktopApp={isElectronRuntime()}
/>
);
}
@@ -28,6 +28,7 @@ import { TitlebarDragRegion } from "@/components/desktop/titlebar-drag-region";
import { HostPicker } from "@/components/hosts/host-picker";
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
import { SidebarDisplayPreferencesMenu } from "@/components/sidebar/sidebar-display-preferences-menu";
import { SidebarHelpMenu } from "@/components/sidebar/sidebar-help-menu";
import { Shortcut } from "@/components/ui/shortcut";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useIsCompactFormFactor } from "@/constants/layout";
@@ -532,6 +533,7 @@ function SidebarFooter({
shortcutKeys={settingsKeys}
theme={theme}
/>
<SidebarHelpMenu />
</View>
</View>
);
@@ -0,0 +1,156 @@
import { useCallback, useState } from "react";
import { Text, View } from "react-native";
import { Activity, CircleHelp, Keyboard } from "lucide-react-native";
import { useTranslation } from "react-i18next";
import { StyleSheet, withUnistyles } from "react-native-unistyles";
import { DiscordIcon } from "@/components/icons/discord-icon";
import { GitHubIcon } from "@/components/icons/github-icon";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuHint,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { useIsCompactFormFactor } from "@/constants/layout";
import { isNative } from "@/constants/platform";
import { useAppDiagnosticStore } from "@/diagnostics/store";
import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { ICON_SIZE, type Theme } from "@/styles/theme";
import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates";
import { resolveAppVersion } from "@/utils/app-version";
import { openExternalUrl } from "@/utils/open-external-url";
const DISCORD_URL = "https://discord.gg/jz8T2uahpH";
const GITHUB_ISSUE_URL = "https://github.com/getpaseo/paseo/issues/new";
const ThemedActivity = withUnistyles(Activity);
const ThemedCircleHelp = withUnistyles(CircleHelp);
const ThemedKeyboard = withUnistyles(Keyboard);
const ThemedDiscordIcon = withUnistyles(DiscordIcon);
const ThemedGitHubIcon = withUnistyles(GitHubIcon);
const foregroundColorMapping = (theme: Theme) => ({ color: theme.colors.foreground });
const foregroundMutedColorMapping = (theme: Theme) => ({
color: theme.colors.foregroundMuted,
});
const diagnosticLeadingIcon = (
<ThemedActivity size={ICON_SIZE.sm} uniProps={foregroundMutedColorMapping} />
);
const shortcutsLeadingIcon = (
<ThemedKeyboard size={ICON_SIZE.sm} uniProps={foregroundMutedColorMapping} />
);
const discordLeadingIcon = (
<ThemedDiscordIcon size={ICON_SIZE.sm} uniProps={foregroundMutedColorMapping} />
);
const githubLeadingIcon = (
<ThemedGitHubIcon size={ICON_SIZE.sm} uniProps={foregroundMutedColorMapping} />
);
export function SidebarHelpMenu() {
const { t } = useTranslation();
const isCompactLayout = useIsCompactFormFactor();
const openAppDiagnostic = useAppDiagnosticStore((state) => state.open);
const setShortcutsDialogOpen = useKeyboardShortcutsStore((state) => state.setShortcutsDialogOpen);
const [open, setOpen] = useState(false);
const showKeyboardShortcuts = !isNative && !isCompactLayout;
const version = formatVersionWithPrefix(resolveAppVersion());
const openKeyboardShortcuts = useCallback(() => {
setShortcutsDialogOpen(true);
}, [setShortcutsDialogOpen]);
const openDiscord = useCallback(() => {
void openExternalUrl(DISCORD_URL);
}, []);
const openGitHubIssue = useCallback(() => {
void openExternalUrl(GITHUB_ISSUE_URL);
}, []);
return (
<DropdownMenu open={open} onOpenChange={setOpen}>
<Tooltip delayDuration={300} enabledOnDesktop={!open}>
<TooltipTrigger asChild>
<View>
<DropdownMenuTrigger
style={styles.trigger}
testID="sidebar-help"
accessibilityRole="button"
accessibilityLabel={t("sidebar.help.trigger")}
>
{({ hovered }) => (
<ThemedCircleHelp
size={ICON_SIZE.md}
uniProps={hovered ? foregroundColorMapping : foregroundMutedColorMapping}
/>
)}
</DropdownMenuTrigger>
</View>
</TooltipTrigger>
<TooltipContent side="top" align="center" offset={8}>
<Text style={styles.tooltipText}>{t("sidebar.help.trigger")}</Text>
</TooltipContent>
</Tooltip>
<DropdownMenuContent side="top" align="end" offset={8} width={280} testID="sidebar-help-menu">
<DropdownMenuLabel>{t("sidebar.help.troubleshoot")}</DropdownMenuLabel>
<DropdownMenuItem
testID="sidebar-help-diagnostics"
description={t("sidebar.help.diagnosticsDescription")}
leading={diagnosticLeadingIcon}
onSelect={openAppDiagnostic}
>
{t("sidebar.help.diagnostics")}
</DropdownMenuItem>
{showKeyboardShortcuts ? (
<DropdownMenuItem
testID="sidebar-help-shortcuts"
description={t("sidebar.help.shortcutsDescription")}
leading={shortcutsLeadingIcon}
onSelect={openKeyboardShortcuts}
>
{t("sidebar.help.shortcuts")}
</DropdownMenuItem>
) : null}
<DropdownMenuSeparator />
<DropdownMenuLabel>{t("sidebar.help.reportIssue")}</DropdownMenuLabel>
<DropdownMenuItem
testID="sidebar-help-discord"
description={t("sidebar.help.discordDescription")}
leading={discordLeadingIcon}
onSelect={openDiscord}
>
{t("sidebar.help.discord")}
</DropdownMenuItem>
<DropdownMenuItem
testID="sidebar-help-github"
description={t("sidebar.help.githubDescription")}
leading={githubLeadingIcon}
onSelect={openGitHubIssue}
>
{t("sidebar.help.github")}
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuHint testID="sidebar-help-version">
{t("sidebar.help.version", { version })}
</DropdownMenuHint>
</DropdownMenuContent>
</DropdownMenu>
);
}
const styles = StyleSheet.create((theme) => ({
trigger: {
width: 28,
height: 28,
alignItems: "center",
justifyContent: "center",
paddingVertical: theme.spacing[1],
paddingHorizontal: theme.spacing[1],
},
tooltipText: {
fontSize: theme.fontSize.sm,
color: theme.colors.popoverForeground,
},
}));
+13
View File
@@ -0,0 +1,13 @@
import { create } from "zustand";
interface AppDiagnosticState {
visible: boolean;
open: () => void;
close: () => void;
}
export const useAppDiagnosticStore = create<AppDiagnosticState>((set) => ({
visible: false,
open: () => set({ visible: true }),
close: () => set({ visible: false }),
}));
+8 -4
View File
@@ -6,6 +6,8 @@ import {
type EmptyProjectDescriptor as ProjectWithoutWorkspacesDescriptor,
type WorkspaceDescriptor,
} from "@/stores/session-store";
import { generateDraftId } from "@/stores/draft-keys";
import type { NavigateToWorkspaceInput } from "@/stores/navigation-active-workspace-store";
import { buildWorkspaceTabPersistenceKey } from "@/stores/workspace-tabs-store";
type OpenProjectPayload = ProjectAddResponse["payload"];
@@ -57,8 +59,7 @@ interface WorkspaceOpenCallbacks {
isConnected: boolean;
mergeWorkspaces: (serverId: string, workspaces: Iterable<WorkspaceDescriptor>) => void;
setHasHydratedWorkspaces: (serverId: string, hydrated: boolean) => void;
openDraftTab: (workspaceKey: string) => string | null;
navigateToWorkspace: (serverId: string, workspaceId: string) => void;
navigateToWorkspace: (input: NavigateToWorkspaceInput) => string;
}
export interface OpenGithubRepoDirectlyInput extends WorkspaceOpenCallbacks {
@@ -122,8 +123,11 @@ function finishWorkspaceOpen(
input.mergeWorkspaces(normalizedServerId, [workspace]);
input.setHasHydratedWorkspaces(normalizedServerId, true);
input.openDraftTab(workspaceKey);
input.navigateToWorkspace(normalizedServerId, workspace.id);
input.navigateToWorkspace({
serverId: normalizedServerId,
workspaceId: workspace.id,
target: { kind: "draft", draftId: generateDraftId() },
});
return true;
}
+12 -30
View File
@@ -8,6 +8,7 @@ import type {
EmptyProjectDescriptor as ProjectWithoutWorkspacesDescriptor,
WorkspaceDescriptor,
} from "@/stores/session-store";
import type { NavigateToWorkspaceInput } from "@/stores/navigation-active-workspace-store";
const SERVER_ID = "server-1";
const PROJECT_PATH = "/repo/project";
@@ -55,15 +56,6 @@ interface RecordedHydrated {
hydrated: boolean;
}
interface RecordedOpenDraftTab {
workspaceKey: string;
}
interface RecordedNavigate {
serverId: string;
workspaceId: string;
}
interface RecordedClone {
repo: string;
targetDirectory: string;
@@ -90,23 +82,13 @@ function createFakeSession() {
};
}
function createFakeWorkspaceLayout() {
const openedTabs: RecordedOpenDraftTab[] = [];
return {
openedTabs,
openDraftTab: (workspaceKey: string) => {
openedTabs.push({ workspaceKey });
return "tab-1";
},
};
}
function createFakeNavigator() {
const navigations: RecordedNavigate[] = [];
const navigations: NavigateToWorkspaceInput[] = [];
return {
navigations,
navigateToWorkspace: (serverId: string, workspaceId: string) => {
navigations.push({ serverId, workspaceId });
navigateToWorkspace: (input: NavigateToWorkspaceInput) => {
navigations.push(input);
return `/hosts/${input.serverId}/workspaces/${input.workspaceId}`;
},
};
}
@@ -226,7 +208,6 @@ describe("openProjectDirectly", () => {
describe("openGithubRepoDirectly", () => {
it("opens a cloned GitHub workspace and seeds a draft tab", async () => {
const session = createFakeSession();
const layout = createFakeWorkspaceLayout();
const navigator = createFakeNavigator();
const workspacePayload = buildWorkspacePayload();
const github = createFakeGithubCloneClient(workspacePayload);
@@ -240,7 +221,6 @@ describe("openGithubRepoDirectly", () => {
client: github,
mergeWorkspaces: session.mergeWorkspaces,
setHasHydratedWorkspaces: session.setHasHydratedWorkspaces,
openDraftTab: layout.openDraftTab,
navigateToWorkspace: navigator.navigateToWorkspace,
});
@@ -261,13 +241,17 @@ describe("openGithubRepoDirectly", () => {
workspaceDirectory: PROJECT_PATH,
});
expect(session.hydrated).toEqual([{ serverId: SERVER_ID, hydrated: true }]);
expect(layout.openedTabs).toEqual([{ workspaceKey: `${SERVER_ID}:1` }]);
expect(navigator.navigations).toEqual([{ serverId: SERVER_ID, workspaceId: "1" }]);
expect(navigator.navigations).toEqual([
{
serverId: SERVER_ID,
workspaceId: "1",
target: { kind: "draft", draftId: expect.any(String) },
},
]);
});
it("rejects a workspace without an identity before changing app state", async () => {
const session = createFakeSession();
const layout = createFakeWorkspaceLayout();
const navigator = createFakeNavigator();
const github = createFakeGithubCloneClient({ ...buildWorkspacePayload(), id: " " });
@@ -280,14 +264,12 @@ describe("openGithubRepoDirectly", () => {
client: github,
mergeWorkspaces: session.mergeWorkspaces,
setHasHydratedWorkspaces: session.setHasHydratedWorkspaces,
openDraftTab: layout.openDraftTab,
navigateToWorkspace: navigator.navigateToWorkspace,
});
expect(result).toBe(false);
expect(session.merges).toEqual([]);
expect(session.hydrated).toEqual([]);
expect(layout.openedTabs).toEqual([]);
expect(navigator.navigations).toEqual([]);
});
});
+2 -19
View File
@@ -1,8 +1,6 @@
import { useCallback } from "react";
import { useHostRuntimeClient, useHostRuntimeIsConnected } from "@/runtime/host-runtime";
import { useSessionStore } from "@/stores/session-store";
import { useWorkspaceLayoutStore } from "@/stores/workspace-layout-store";
import { generateDraftId } from "@/stores/draft-keys";
import { navigateToWorkspace } from "@/stores/navigation-active-workspace-store";
import {
openGithubRepoDirectly,
@@ -61,12 +59,6 @@ export function useOpenGithubRepo(
const isConnected = useHostRuntimeIsConnected(normalizedServerId);
const mergeWorkspaces = useSessionStore((state) => state.mergeWorkspaces);
const setHasHydratedWorkspaces = useSessionStore((state) => state.setHasHydratedWorkspaces);
const openDraftTab = useCallback((workspaceKey: string) => {
return useWorkspaceLayoutStore.getState().openTabFocused(workspaceKey, {
kind: "draft",
draftId: generateDraftId(),
});
}, []);
return useCallback(
async (repo: string, targetDirectory: string, cloneProtocol?: WorkspaceGithubCloneProtocol) => {
@@ -79,18 +71,9 @@ export function useOpenGithubRepo(
client,
mergeWorkspaces,
setHasHydratedWorkspaces,
openDraftTab,
navigateToWorkspace: (targetServerId, workspaceId) =>
navigateToWorkspace({ serverId: targetServerId, workspaceId }),
navigateToWorkspace,
});
},
[
client,
isConnected,
mergeWorkspaces,
normalizedServerId,
openDraftTab,
setHasHydratedWorkspaces,
],
[client, isConnected, mergeWorkspaces, normalizedServerId, setHasHydratedWorkspaces],
);
}
+14
View File
@@ -790,6 +790,20 @@ export const ar: TranslationResources = {
settings: "إعدادات",
closeSidebar: "إغلاق الشريط الجانبي",
},
help: {
trigger: "المساعدة والدعم",
troubleshoot: "استكشاف الأخطاء وإصلاحها",
diagnostics: "تشغيل التشخيص",
diagnosticsDescription: "جمع تفاصيل التطبيق والمضيفين المتصلين",
shortcuts: "اختصارات لوحة المفاتيح",
shortcutsDescription: "عرض اختصارات لوحة المفاتيح المتاحة",
reportIssue: "الإبلاغ عن مشكلة",
discord: "Discord",
discordDescription: "الأفضل للمساعدة السريعة والنقاش",
github: "إنشاء مشكلة على GitHub",
githubDescription: "الإبلاغ عن خطأ يمكن إعادة إنتاجه",
version: "Paseo {{version}}",
},
sections: {
sessions: "السجل",
schedules: "الجداول",
+14
View File
@@ -797,6 +797,20 @@ export const en = {
settings: "Settings",
closeSidebar: "Close sidebar",
},
help: {
trigger: "Help and support",
troubleshoot: "Troubleshoot",
diagnostics: "Run diagnostics",
diagnosticsDescription: "Collect app and connected host details",
shortcuts: "Keyboard shortcuts",
shortcutsDescription: "View available keyboard shortcuts",
reportIssue: "Report an issue",
discord: "Discord",
discordDescription: "Best for quick help and discussion",
github: "Create GitHub issue",
githubDescription: "Report a reproducible bug",
version: "Paseo {{version}}",
},
sections: {
sessions: "History",
schedules: "Schedules",
+14
View File
@@ -817,6 +817,20 @@ export const es: TranslationResources = {
settings: "Ajustes",
closeSidebar: "Cerrar barra lateral",
},
help: {
trigger: "Ayuda y soporte",
troubleshoot: "Solucionar problemas",
diagnostics: "Ejecutar diagnóstico",
diagnosticsDescription: "Recopila datos de la app y los hosts conectados",
shortcuts: "Atajos de teclado",
shortcutsDescription: "Ver los atajos de teclado disponibles",
reportIssue: "Informar de un problema",
discord: "Discord",
discordDescription: "La mejor opción para ayuda rápida y conversación",
github: "Crear incidencia en GitHub",
githubDescription: "Informar de un error reproducible",
version: "Paseo {{version}}",
},
sections: {
sessions: "Historial",
schedules: "Horarios",
+14
View File
@@ -816,6 +816,20 @@ export const fr: TranslationResources = {
settings: "Paramètres",
closeSidebar: "Fermer la barre latérale",
},
help: {
trigger: "Aide et assistance",
troubleshoot: "Dépannage",
diagnostics: "Lancer le diagnostic",
diagnosticsDescription: "Collecter les détails de l’app et des hôtes connectés",
shortcuts: "Raccourcis clavier",
shortcutsDescription: "Afficher les raccourcis clavier disponibles",
reportIssue: "Signaler un problème",
discord: "Discord",
discordDescription: "Idéal pour obtenir une aide rapide et échanger",
github: "Créer un ticket GitHub",
githubDescription: "Signaler un bug reproductible",
version: "Paseo {{version}}",
},
sections: {
sessions: "Historique",
schedules: "Planifications",
+14
View File
@@ -802,6 +802,20 @@ export const ja: TranslationResources = {
settings: "設定",
closeSidebar: "サイドバーを閉じる",
},
help: {
trigger: "ヘルプとサポート",
troubleshoot: "トラブルシューティング",
diagnostics: "診断を実行",
diagnosticsDescription: "アプリと接続中のホストの詳細を収集",
shortcuts: "キーボードショートカット",
shortcutsDescription: "利用可能なキーボードショートカットを表示",
reportIssue: "問題を報告",
discord: "Discord",
discordDescription: "すばやいサポートや相談に最適",
github: "GitHub Issueを作成",
githubDescription: "再現可能なバグを報告",
version: "Paseo {{version}}",
},
sections: {
sessions: "履歴",
schedules: "スケジュール",
+14
View File
@@ -808,6 +808,20 @@ export const ptBR: TranslationResources = {
settings: "Configurações",
closeSidebar: "Fechar barra lateral",
},
help: {
trigger: "Ajuda e suporte",
troubleshoot: "Resolver problemas",
diagnostics: "Executar diagnóstico",
diagnosticsDescription: "Coletar detalhes do app e dos hosts conectados",
shortcuts: "Atalhos de teclado",
shortcutsDescription: "Ver os atalhos de teclado disponíveis",
reportIssue: "Relatar um problema",
discord: "Discord",
discordDescription: "Ideal para ajuda rápida e conversa",
github: "Criar issue no GitHub",
githubDescription: "Relatar um bug reproduzível",
version: "Paseo {{version}}",
},
sections: {
sessions: "Histórico",
schedules: "Agendamentos",
+14
View File
@@ -809,6 +809,20 @@ export const ru: TranslationResources = {
settings: "Настройки",
closeSidebar: "Закрыть боковую панель",
},
help: {
trigger: "Помощь и поддержка",
troubleshoot: "Устранение неполадок",
diagnostics: "Запустить диагностику",
diagnosticsDescription: "Собрать данные приложения и подключённых хостов",
shortcuts: "Сочетания клавиш",
shortcutsDescription: "Показать доступные сочетания клавиш",
reportIssue: "Сообщить о проблеме",
discord: "Discord",
discordDescription: "Для быстрой помощи и обсуждения",
github: "Создать issue в GitHub",
githubDescription: "Сообщить о воспроизводимой ошибке",
version: "Paseo {{version}}",
},
sections: {
sessions: "История",
schedules: "Расписания",
+14
View File
@@ -785,6 +785,20 @@ export const zhCN: TranslationResources = {
settings: "设置",
closeSidebar: "关闭侧边栏",
},
help: {
trigger: "帮助与支持",
troubleshoot: "问题排查",
diagnostics: "运行诊断",
diagnosticsDescription: "收集应用和已连接 Host 的详细信息",
shortcuts: "键盘快捷键",
shortcutsDescription: "查看可用的键盘快捷键",
reportIssue: "报告问题",
discord: "Discord",
discordDescription: "适合快速求助和讨论",
github: "创建 GitHub Issue",
githubDescription: "报告可复现的 bug",
version: "Paseo {{version}}",
},
sections: {
sessions: "历史",
schedules: "计划",
+3 -17
View File
@@ -35,7 +35,6 @@ import {
SquareTerminal,
} from "lucide-react-native";
import { DropdownTrigger } from "@/components/ui/dropdown-trigger";
import { AppDiagnosticSheet } from "@/components/app-diagnostic-sheet";
import { ComboboxTrigger } from "@/components/ui/combobox-trigger";
import { SidebarHeaderRow } from "@/components/sidebar/sidebar-header-row";
import { SidebarSeparator } from "@/components/sidebar/sidebar-separator";
@@ -76,6 +75,7 @@ import { isElectronRuntime } from "@/desktop/host";
import { useDesktopAppUpdater } from "@/desktop/updates/use-desktop-app-updater";
import { formatVersionWithPrefix } from "@/desktop/updates/desktop-updates";
import { resolveAppVersion } from "@/utils/app-version";
import { useAppDiagnosticStore } from "@/diagnostics/store";
import { settingsStyles } from "@/styles/settings";
import { THINKING_TONE_NATIVE_PCM_BASE64 } from "@/utils/thinking-tone.native-pcm";
import { useVoiceAudioEngineOptional } from "@/contexts/voice-context";
@@ -445,8 +445,6 @@ interface DiagnosticsSectionProps {
isPlaybackTestRunning: boolean;
playbackTestResult: string | null;
handlePlaybackTest: () => Promise<void>;
appVersion: string | null;
isDesktopApp: boolean;
}
function DiagnosticsSection({
@@ -454,16 +452,12 @@ function DiagnosticsSection({
isPlaybackTestRunning,
playbackTestResult,
handlePlaybackTest,
appVersion,
isDesktopApp,
}: DiagnosticsSectionProps) {
const { t } = useTranslation();
const [diagnosticSheetOpen, setDiagnosticSheetOpen] = useState(false);
const openAppDiagnostic = useAppDiagnosticStore((state) => state.open);
const handlePlayPress = useCallback(() => {
void handlePlaybackTest();
}, [handlePlaybackTest]);
const handleOpenDiagnostic = useCallback(() => setDiagnosticSheetOpen(true), []);
const handleCloseDiagnostic = useCallback(() => setDiagnosticSheetOpen(false), []);
return (
<SettingsSection title={t("settings.diagnostics.title")}>
<View style={settingsStyles.card}>
@@ -472,7 +466,7 @@ function DiagnosticsSection({
<Text style={settingsStyles.rowTitle}>{t("settings.diagnostics.app.rowTitle")}</Text>
<Text style={settingsStyles.rowHint}>{t("settings.diagnostics.app.rowHint")}</Text>
</View>
<Button variant="secondary" size="sm" onPress={handleOpenDiagnostic}>
<Button variant="secondary" size="sm" onPress={openAppDiagnostic}>
{t("settings.diagnostics.app.run")}
</Button>
</View>
@@ -495,12 +489,6 @@ function DiagnosticsSection({
</Button>
</View>
</View>
<AppDiagnosticSheet
visible={diagnosticSheetOpen}
onClose={handleCloseDiagnostic}
appVersion={appVersion}
isDesktopApp={isDesktopApp}
/>
</SettingsSection>
);
}
@@ -1429,8 +1417,6 @@ export default function SettingsScreen({ view, openAddHostIntent = null }: Setti
isPlaybackTestRunning={isPlaybackTestRunning}
playbackTestResult={playbackTestResult}
handlePlaybackTest={handlePlaybackTest}
appVersion={appVersion}
isDesktopApp={isDesktopApp}
/>
);
case "about":