From 8ff2709ad2c36a0674be2e443af8228dfe9399d2 Mon Sep 17 00:00:00 2001 From: enaboapps <60785457+enaboapps@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:42:21 +0100 Subject: [PATCH 1/3] Remove recent activity from UI --- src/App.test.tsx | 26 ++++++++++++++++++++++++++ src/App.tsx | 4 +--- src/styles.css | 5 ----- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index 99f01d6..ca0a005 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -13,9 +13,11 @@ function stateWithSettings(settings: AppSettings) { describe("Switchify PC shell", () => { beforeEach(() => { browserState.settings = structuredClone(defaultBrowserSettings); + browserState.bluetooth = "initializing"; browserState.pendingPairings = []; browserState.pairedDevices = []; browserState.connectedDeviceName = null; + browserState.lastActivity = null; browserState.diagnostics = { recentBluetooth: [], lastDisconnect: null, recentErrors: [] }; browserState.telemetry = { consent: "undecided", available: true }; browserState.updater = { status: "unconfigured", version: null, downloadedBytes: 0, totalBytes: null, error: null, retryAction: null }; @@ -58,6 +60,30 @@ describe("Switchify PC shell", () => { checkForUpdates.mockRestore(); }); + it("keeps internal activity out of Home and Support while retaining troubleshooting history", async () => { + browserState.bluetooth = "advertising"; + browserState.lastActivity = { kind: "error", message: "Internal runtime activity" }; + browserState.diagnostics = { + recentBluetooth: [{ sequence: 1, timestamp: 1, category: "bluetooth", status: "advertising" }], + lastDisconnect: null, + recentErrors: [{ sequence: 2, timestamp: 2, category: "runtime", status: "failed", detail: "Bluetooth adapter failed" }], + }; + + render(); + + await screen.findByRole("heading", { name: "Switchify PC" }); + expect(screen.getByText("Waiting for a nearby Android device.")).toBeInTheDocument(); + expect(screen.queryByText("Recent activity")).not.toBeInTheDocument(); + expect(screen.queryByText("Internal runtime activity")).not.toBeInTheDocument(); + + fireEvent.click(screen.getByRole("button", { name: "Support" })); + fireEvent.click(await screen.findByRole("tab", { name: "Troubleshooting" })); + expect(screen.queryByText("Recent activity")).not.toBeInTheDocument(); + expect(screen.queryByText("Internal runtime activity")).not.toBeInTheDocument(); + expect(screen.getByRole("heading", { name: "Recent errors" })).toBeInTheDocument(); + expect(screen.getByText("Bluetooth adapter failed")).toBeInTheDocument(); + }); + it("shows update progress and exposes cancellation in Settings", async () => { browserState.updater = { status: "downloading", version: "1.0.0-beta.2", downloadedBytes: 50, totalBytes: 200, error: null, retryAction: null }; const cancel = vi.spyOn(api, "cancelUpdateDownload").mockResolvedValue(structuredClone(browserState)); diff --git a/src/App.tsx b/src/App.tsx index 9ec932f..584f439 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -47,7 +47,7 @@ function HomeView({ state, onDisconnect, onAccessibility, onSetup }: { state: Ap

Switchify PC

Android control for this computer

{bluetoothOk ? : } -

{bluetoothLabels[state.bluetooth]}

{state.connectedDeviceName ?? state.lastActivity?.message ?? "Waiting for a nearby Android device."}

+

{bluetoothLabels[state.bluetooth]}

{state.connectedDeviceName ?? "Waiting for a nearby Android device."}

{state.bluetooth === "connected" ? : }
@@ -55,7 +55,6 @@ function HomeView({ state, onDisconnect, onAccessibility, onSetup }: { state: Ap

Input access

{state.accessibility === "required" && }

Secure pairing

{state.pairedDevices.length === 0 ? "No saved devices" : `${state.pairedDevices.length} saved device${state.pairedDevices.length === 1 ? "" : "s"}`}

-
Recent activity

{state.lastActivity?.message ?? "No recent activity."}

; } @@ -406,7 +405,6 @@ function SupportView({ state, busy, perform, openSetup }: { state: AppState; bus

Last disconnect

{state.diagnostics.lastDisconnect ? `${state.diagnostics.lastDisconnect.detail ?? state.diagnostics.lastDisconnect.status}` : "No disconnect recorded yet"}

Recent errors

{state.diagnostics.recentErrors.length > 0 ? state.diagnostics.recentErrors.map((event) => event.detail ?? event.status).join(" ยท ") : "No recent errors"}

} - {state.lastActivity &&
Recent activity

{state.lastActivity.message}

} ; } diff --git a/src/styles.css b/src/styles.css index 40d7141..0892fc4 100644 --- a/src/styles.css +++ b/src/styles.css @@ -69,11 +69,6 @@ main { min-width: 0; } .status-list h3 { font-size: 14px; } .status-list p { margin-top: 3px; color: var(--muted); font-size: 13px; } .status-list .status-icon { width: 34px; height: 34px; } -.activity-panel { margin-top: 24px; border-left: 3px solid var(--inactive); padding: 12px 15px; background: var(--surface-muted); } -.activity-panel span { color: var(--muted); font-size: 11px; font-weight: 700; text-transform: uppercase; } -.activity-panel p { margin-top: 5px; font-size: 13px; } -.activity-panel p[data-kind="success"] { color: var(--status-ok); } -.activity-panel p[data-kind="error"] { color: var(--status-error); } .primary, .secondary, .text-button { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 36px; border-radius: 6px; padding: 0 14px; cursor: pointer; } .primary { border: 1px solid var(--brand); color: var(--on-brand); background: var(--brand); font-weight: 650; } From 6f144c08dbe6edb838fa3c9e68f69867ccd21949 Mon Sep 17 00:00:00 2001 From: enaboapps <60785457+enaboapps@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:51:24 +0100 Subject: [PATCH 2/3] Clarify Bluetooth recovery states --- src/App.test.tsx | 22 +++++++++++++++++++++- src/App.tsx | 13 ++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index ca0a005..82086a9 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -2,7 +2,7 @@ import { act, fireEvent, render, screen, waitFor, within } from "@testing-librar import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { App } from "./App"; import { api, browserState } from "./api"; -import type { AppSettings, SwitchProfile } from "./types"; +import type { AppSettings, BluetoothState, SwitchProfile } from "./types"; const defaultBrowserSettings = structuredClone(browserState.settings); @@ -84,6 +84,26 @@ describe("Switchify PC shell", () => { expect(screen.getByText("Bluetooth adapter failed")).toBeInTheDocument(); }); + it.each([ + ["initializing", "Preparing this computer for nearby devices."], + ["advertising", "Waiting for a nearby Android device."], + ["connected", "Android device connected."], + ["poweredOff", "Turn on Bluetooth to connect an Android device."], + ["unauthorized", "Allow Bluetooth access in System Settings to connect."], + ["conflict", "Quit the other Switchify PC instance, then reopen this app."], + ["unsupported", "This computer does not support the required Bluetooth features."], + ["error", "Bluetooth could not start. Try restarting Switchify PC."], + ] satisfies Array<[BluetoothState, string]>)('uses recovery-appropriate Home copy for Bluetooth state "%s"', async (bluetooth, description) => { + browserState.bluetooth = bluetooth; + browserState.lastActivity = { kind: "error", message: "Internal runtime activity" }; + + render(); + + await screen.findByRole("heading", { name: "Switchify PC" }); + expect(screen.getByText(description)).toBeInTheDocument(); + expect(screen.queryByText("Internal runtime activity")).not.toBeInTheDocument(); + }); + it("shows update progress and exposes cancellation in Settings", async () => { browserState.updater = { status: "downloading", version: "1.0.0-beta.2", downloadedBytes: 50, totalBytes: 200, error: null, retryAction: null }; const cancel = vi.spyOn(api, "cancelUpdateDownload").mockResolvedValue(structuredClone(browserState)); diff --git a/src/App.tsx b/src/App.tsx index 584f439..d0eebe5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -19,6 +19,17 @@ const bluetoothLabels: Record = { conflict: "Current Switchify PC is running", unsupported: "Bluetooth unavailable", error: "Bluetooth unavailable", }; +const bluetoothDescriptions: Record = { + initializing: "Preparing this computer for nearby devices.", + advertising: "Waiting for a nearby Android device.", + connected: "Android device connected.", + poweredOff: "Turn on Bluetooth to connect an Android device.", + unauthorized: "Allow Bluetooth access in System Settings to connect.", + conflict: "Quit the other Switchify PC instance, then reopen this app.", + unsupported: "This computer does not support the required Bluetooth features.", + error: "Bluetooth could not start. Try restarting Switchify PC.", +}; + function NavButton({ active, icon, children, onClick }: { active: boolean; icon: ReactNode; children: ReactNode; onClick: () => void }) { return ; } @@ -47,7 +58,7 @@ function HomeView({ state, onDisconnect, onAccessibility, onSetup }: { state: Ap

Switchify PC

Android control for this computer

{bluetoothOk ? : } -

{bluetoothLabels[state.bluetooth]}

{state.connectedDeviceName ?? "Waiting for a nearby Android device."}

+

{bluetoothLabels[state.bluetooth]}

{state.connectedDeviceName ?? bluetoothDescriptions[state.bluetooth]}

{state.bluetooth === "connected" ? : }
From 207623173428405bb773019c0c7db42476e311c8 Mon Sep 17 00:00:00 2001 From: enaboapps <60785457+enaboapps@users.noreply.github.com> Date: Mon, 10 Aug 2026 12:59:24 +0100 Subject: [PATCH 3/3] Ignore stale device names during recovery --- src/App.test.tsx | 21 ++++++++++++--------- src/App.tsx | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index 82086a9..7be69cc 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -85,22 +85,25 @@ describe("Switchify PC shell", () => { }); it.each([ - ["initializing", "Preparing this computer for nearby devices."], - ["advertising", "Waiting for a nearby Android device."], - ["connected", "Android device connected."], - ["poweredOff", "Turn on Bluetooth to connect an Android device."], - ["unauthorized", "Allow Bluetooth access in System Settings to connect."], - ["conflict", "Quit the other Switchify PC instance, then reopen this app."], - ["unsupported", "This computer does not support the required Bluetooth features."], - ["error", "Bluetooth could not start. Try restarting Switchify PC."], - ] satisfies Array<[BluetoothState, string]>)('uses recovery-appropriate Home copy for Bluetooth state "%s"', async (bluetooth, description) => { + ["initializing", "Stale tablet", "Preparing this computer for nearby devices."], + ["advertising", "Stale tablet", "Waiting for a nearby Android device."], + ["connected", null, "Android device connected."], + ["connected", "Pixel Tablet", "Pixel Tablet"], + ["poweredOff", "Stale tablet", "Turn on Bluetooth to connect an Android device."], + ["unauthorized", "Stale tablet", "Allow Bluetooth access in System Settings to connect."], + ["conflict", "Stale tablet", "Quit the other Switchify PC instance, then reopen this app."], + ["unsupported", "Stale tablet", "This computer does not support the required Bluetooth features."], + ["error", "Stale tablet", "Bluetooth could not start. Try restarting Switchify PC."], + ] satisfies Array<[BluetoothState, string | null, string]>)('uses recovery-appropriate Home copy for Bluetooth state "%s"', async (bluetooth, deviceName, description) => { browserState.bluetooth = bluetooth; + browserState.connectedDeviceName = deviceName; browserState.lastActivity = { kind: "error", message: "Internal runtime activity" }; render(); await screen.findByRole("heading", { name: "Switchify PC" }); expect(screen.getByText(description)).toBeInTheDocument(); + if (bluetooth !== "connected") expect(screen.queryByText("Stale tablet")).not.toBeInTheDocument(); expect(screen.queryByText("Internal runtime activity")).not.toBeInTheDocument(); }); diff --git a/src/App.tsx b/src/App.tsx index d0eebe5..a6493f3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -58,7 +58,7 @@ function HomeView({ state, onDisconnect, onAccessibility, onSetup }: { state: Ap

Switchify PC

Android control for this computer

{bluetoothOk ? : } -

{bluetoothLabels[state.bluetooth]}

{state.connectedDeviceName ?? bluetoothDescriptions[state.bluetooth]}

+

{bluetoothLabels[state.bluetooth]}

{state.bluetooth === "connected" ? state.connectedDeviceName ?? bluetoothDescriptions.connected : bluetoothDescriptions[state.bluetooth]}

{state.bluetooth === "connected" ? : }