Skip to content

Commit d0750a0

Browse files
authored
fix: ai panel hidden web
1 parent f38ba34 commit d0750a0

4 files changed

Lines changed: 53 additions & 8 deletions

File tree

‎packages/web-proxy/src/WebProxySurface.vue‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,10 @@ let unlistenRecordingState: (() => void) | undefined;
9292
let overlayObserver: MutationObserver | undefined;
9393
9494
// Nuxt UI teleports interactive overlays into the main webview, but desktop child
95-
// webviews always render above that DOM. Tooltips are intentionally excluded.
95+
// webviews always render above that DOM. Custom floating panels opt in with
96+
// data-native-view-overlay. Tooltips are intentionally excluded.
9697
const OVERLAY_SELECTOR = [
98+
"[data-native-view-overlay]",
9799
'[role="menu"][data-state="open"]',
98100
'[role="dialog"][data-state="open"]',
99101
'[role="alertdialog"][data-state="open"]',

‎ui/components/RightPanel/AiOverlayPanel.vue‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ onDeactivated(resetPosition);
6363
<div
6464
id="workspace-ai-overlay"
6565
ref="host"
66+
data-native-view-overlay
6667
data-ai-context="preserve"
6768
class="pointer-events-none absolute inset-0 z-50"
6869
>

‎ui/koko/components/SftpIde/index.vue‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const explorerRef = useTemplateRef<{
7474
openUploadPicker: () => void;
7575
scrollQuickOpenActive: () => void;
7676
}>("explorer");
77-
const editorLayout = ref<HTMLElement | null>(null);
77+
const editorLayout = shallowRef<HTMLElement | null>(null);
7878
const alertDialogOpen = ref(false);
7979
const alertTarget = ref<AlertTarget | null>(null);
8080
const alertSubmitting = ref(false);

‎ui/workspaces/WebProxySessionSurface.test.ts‎

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import ts from "typescript";
22
import { expect, it, vi } from "vitest";
33
import { computed, effectScope, nextTick, reactive, ref, toRaw, watch } from "vue";
44
import source from "../../packages/web-proxy/src/WebProxySurface.vue?raw";
5+
import aiPanelSource from "../components/RightPanel/AiOverlayPanel.vue?raw";
56

6-
function setupSurface(safeMode = false, observe = false) {
7+
function setupSurface(safeMode = false, observe = false, dom?: Document) {
78
const desktopWebProxy = {
89
create: vi.fn(async (request: Record<string, unknown>) => structuredClone(request)),
910
onState: vi.fn(async () => vi.fn()),
@@ -70,16 +71,18 @@ function setupSurface(safeMode = false, observe = false) {
7071
useWorkspaceTabs: () => ({ activeTabId: ref("tab"), tabs: ref([]), markSessionConnected, closeSession }),
7172
usePlatform: () => ({}),
7273
registerWorkspaceSessionCloseGuard: vi.fn(),
73-
document: {
74+
document: dom ?? {
7475
visibilityState: "visible",
7576
querySelector: vi.fn(),
7677
addEventListener: vi.fn(),
7778
removeEventListener: vi.fn()
7879
},
79-
MutationObserver: class {
80-
observe() {}
81-
disconnect() {}
82-
},
80+
MutationObserver: dom
81+
? MutationObserver
82+
: class {
83+
observe() {}
84+
disconnect() {}
85+
},
8386
ResizeObserver: class {
8487
observe() {}
8588
disconnect() {}
@@ -114,6 +117,45 @@ function setupSurface(safeMode = false, observe = false) {
114117
};
115118
}
116119

120+
it.skipIf(typeof document === "undefined")(
121+
"backgrounds the native website for AI and menus, restoring only the active tab",
122+
async () => {
123+
const { surface, desktopWebProxy, props, mount, stop } = setupSurface(false, true, document);
124+
const host = document.createElement("div");
125+
document.body.append(host);
126+
try {
127+
await mount();
128+
expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, true);
129+
// Use the actual AI host markup so losing its overlay marker regresses this check.
130+
host.innerHTML = `${aiPanelSource.match(/<div\s+id="workspace-ai-overlay"[^>]*>/)![0]}</div>`;
131+
await vi.waitFor(() => expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, false));
132+
133+
host.insertAdjacentHTML("beforeend", '<div role="menu" data-state="open"></div>');
134+
host.firstElementChild!.remove();
135+
await nextTick();
136+
expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, false);
137+
host.replaceChildren();
138+
await vi.waitFor(() => expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, true));
139+
140+
props.active = false;
141+
await nextTick();
142+
expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, false);
143+
host.innerHTML = "<div data-native-view-overlay></div>";
144+
await nextTick();
145+
host.replaceChildren();
146+
await nextTick();
147+
expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, false);
148+
props.active = true;
149+
await vi.waitFor(() => expect(desktopWebProxy.setActive).toHaveBeenLastCalledWith(surface.viewLabel, true));
150+
expect(desktopWebProxy.create).toHaveBeenCalledOnce();
151+
expect(desktopWebProxy.close).not.toHaveBeenCalled();
152+
} finally {
153+
stop();
154+
host.remove();
155+
}
156+
}
157+
);
158+
117159
it.each([undefined, [], ["https://sso.test", "https://asset.test:8443"]].map((allowedUrls) => ({ allowedUrls })))(
118160
"creates a view from reactive session data with allowed URLs $allowedUrls",
119161
async ({ allowedUrls }) => {

0 commit comments

Comments
 (0)