mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-09-03 17:52:00 +03:00
fix(webui): honor native host chrome
This commit is contained in:
+12
-5
@@ -79,6 +79,7 @@ import {
|
|||||||
} from "@/lib/api";
|
} from "@/lib/api";
|
||||||
import {
|
import {
|
||||||
createRuntimeHost,
|
createRuntimeHost,
|
||||||
|
isNativeRuntime,
|
||||||
toRuntimeSurface,
|
toRuntimeSurface,
|
||||||
} from "@/lib/runtime";
|
} from "@/lib/runtime";
|
||||||
import { projectNameFromPath, scopeWithAccessMode } from "@/lib/workspace";
|
import { projectNameFromPath, scopeWithAccessMode } from "@/lib/workspace";
|
||||||
@@ -778,6 +779,14 @@ function formatPairingExpiry(seconds: number | null | undefined): string {
|
|||||||
return `${Math.ceil(seconds / 60)} min`;
|
return `${Math.ceil(seconds / 60)} min`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveRuntimeSurface(
|
||||||
|
surface: RuntimeSurface | null | undefined,
|
||||||
|
fallback: RuntimeSurface,
|
||||||
|
): RuntimeSurface {
|
||||||
|
if (isNativeRuntime(surface)) return "native";
|
||||||
|
return surface ? toRuntimeSurface(surface) : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [state, setState] = useState<BootState>({ status: "loading" });
|
const [state, setState] = useState<BootState>({ status: "loading" });
|
||||||
@@ -787,9 +796,7 @@ export default function App() {
|
|||||||
async (client: NanobotClient, fallbackSurface: RuntimeSurface) => {
|
async (client: NanobotClient, fallbackSurface: RuntimeSurface) => {
|
||||||
const boot = await fetchBootstrap("", bootstrapSecretRef.current);
|
const boot = await fetchBootstrap("", bootstrapSecretRef.current);
|
||||||
const url = deriveWsUrl(boot.ws_path, boot.token, boot.ws_url);
|
const url = deriveWsUrl(boot.ws_path, boot.token, boot.ws_url);
|
||||||
const runtimeSurface = boot.runtime_surface
|
const runtimeSurface = resolveRuntimeSurface(boot.runtime_surface, fallbackSurface);
|
||||||
? toRuntimeSurface(boot.runtime_surface)
|
|
||||||
: fallbackSurface;
|
|
||||||
const runtimeHost = createRuntimeHost(runtimeSurface, boot.runtime_capabilities);
|
const runtimeHost = createRuntimeHost(runtimeSurface, boot.runtime_capabilities);
|
||||||
const tokenExpiresAt = boot.expires_in
|
const tokenExpiresAt = boot.expires_in
|
||||||
? bootstrapTokenExpiresAt(boot.expires_in)
|
? bootstrapTokenExpiresAt(boot.expires_in)
|
||||||
@@ -827,7 +834,7 @@ export default function App() {
|
|||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
if (secret) saveSecret(secret);
|
if (secret) saveSecret(secret);
|
||||||
const url = deriveWsUrl(boot.ws_path, boot.token, boot.ws_url);
|
const url = deriveWsUrl(boot.ws_path, boot.token, boot.ws_url);
|
||||||
const runtimeSurface = toRuntimeSurface(boot.runtime_surface);
|
const runtimeSurface = resolveRuntimeSurface(boot.runtime_surface, "browser");
|
||||||
const runtimeHost = createRuntimeHost(runtimeSurface, boot.runtime_capabilities);
|
const runtimeHost = createRuntimeHost(runtimeSurface, boot.runtime_capabilities);
|
||||||
const client = new NanobotClient({
|
const client = new NanobotClient({
|
||||||
url,
|
url,
|
||||||
@@ -1086,7 +1093,7 @@ function Shell({
|
|||||||
const temporarySessionsRef = useRef<Record<string, ChatSummary>>({});
|
const temporarySessionsRef = useRef<Record<string, ChatSummary>>({});
|
||||||
const effectiveRuntimeSurface =
|
const effectiveRuntimeSurface =
|
||||||
settingsSnapshot?.surface ?? settingsSnapshot?.runtime_surface ?? runtimeSurface;
|
settingsSnapshot?.surface ?? settingsSnapshot?.runtime_surface ?? runtimeSurface;
|
||||||
const showHostChrome = effectiveRuntimeSurface === "native";
|
const showHostChrome = isNativeRuntime(effectiveRuntimeSurface);
|
||||||
const showMainSidebar = view !== "settings";
|
const showMainSidebar = view !== "settings";
|
||||||
const activeTemporarySession = activeKey ? temporarySessions[activeKey] ?? null : null;
|
const activeTemporarySession = activeKey ? temporarySessions[activeKey] ?? null : null;
|
||||||
const temporaryChatId = activeTemporarySession?.chatId ?? null;
|
const temporaryChatId = activeTemporarySession?.chatId ?? null;
|
||||||
|
|||||||
@@ -310,6 +310,7 @@ describe("App layout", () => {
|
|||||||
sessionUpdateHandlers.clear();
|
sessionUpdateHandlers.clear();
|
||||||
sidebarStateUpdateHandlers.clear();
|
sidebarStateUpdateHandlers.clear();
|
||||||
window.history.replaceState(null, "", "/");
|
window.history.replaceState(null, "", "/");
|
||||||
|
Reflect.deleteProperty(window, "nanobotHost");
|
||||||
setNavigatorPlatform("Linux x86_64");
|
setNavigatorPlatform("Linux x86_64");
|
||||||
localStorage.removeItem("nanobot-webui.sidebar");
|
localStorage.removeItem("nanobot-webui.sidebar");
|
||||||
localStorage.removeItem("nanobot-webui.sidebar.completed-runs.v1");
|
localStorage.removeItem("nanobot-webui.sidebar.completed-runs.v1");
|
||||||
@@ -335,6 +336,7 @@ describe("App layout", () => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
cleanup();
|
cleanup();
|
||||||
|
Reflect.deleteProperty(window, "nanobotHost");
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
vi.unstubAllGlobals();
|
vi.unstubAllGlobals();
|
||||||
});
|
});
|
||||||
@@ -1655,6 +1657,32 @@ describe("App layout", () => {
|
|||||||
await waitFor(() => expect(flowSidebar).toHaveStyle({ width: "272px" }));
|
await waitFor(() => expect(flowSidebar).toHaveStyle({ width: "272px" }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses native chrome when the host bridge overrides browser gateway metadata", async () => {
|
||||||
|
Reflect.set(window, "nanobotHost", { pickFolder: vi.fn() });
|
||||||
|
vi.mocked(fetchBootstrap).mockResolvedValue({
|
||||||
|
token: "tok",
|
||||||
|
api_token: "api-tok",
|
||||||
|
ws_path: "/",
|
||||||
|
expires_in: 300,
|
||||||
|
runtime_surface: "browser",
|
||||||
|
});
|
||||||
|
mockFetchRoutes({
|
||||||
|
"/api/settings": {
|
||||||
|
...baseSettingsPayload(),
|
||||||
|
surface: "browser",
|
||||||
|
runtime_surface: "browser",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
await waitFor(() => expect(connectSpy).toHaveBeenCalled());
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId("sidebar-brand-mark")).toHaveClass("mt-5");
|
||||||
|
});
|
||||||
|
expect(document.documentElement).toHaveClass("native-host");
|
||||||
|
});
|
||||||
|
|
||||||
it("switches to the next session when deleting the active chat", async () => {
|
it("switches to the next session when deleting the active chat", async () => {
|
||||||
mockSessions = [
|
mockSessions = [
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user