mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-07 08:54:42 +00:00
Merge remote-tracking branch 'origin/main' into paseo-cloud-worktree-plan
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
},
|
||||
}));
|
||||
@@ -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 }),
|
||||
}));
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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: "الجداول",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: "スケジュール",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: "Расписания",
|
||||
|
||||
@@ -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: "计划",
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user