fix(desktop): prevent window drag from tab whitespace

Closes #10194
This commit is contained in:
t8y2
2026-09-26 14:27:19 +00:00
parent b4f503e1f1
commit ed863e9e59
4 changed files with 67 additions and 1 deletions
@@ -1755,7 +1755,7 @@ watch([() => props.specialPageTabs?.settingsActive, () => props.specialPageTabs?
</div>
</CustomContextMenu>
</template>
<div v-if="!section.pinned" :class="tabTailDragRegionClass" data-tauri-drag-region />
<div v-if="!section.pinned" :class="tabTailDragRegionClass" data-tauri-drag-region="false" />
</div>
</div>
</template>
@@ -149,6 +149,16 @@ describe("always-on-top button visibility", () => {
setActivePinia(pinia);
});
it("keeps the actual title bar on Tauri's intentional native drag path", async () => {
const { host, unmount } = mount(AppToolbar, toolbarProps);
await settled();
expect(host.querySelector(".app-toolbar")?.getAttribute("data-tauri-drag-region")).toBe("");
expect(host.querySelector('.app-toolbar > [data-tauri-drag-region=""]')).not.toBeNull();
unmount();
});
it("hides the main toolbar pin control until the appearance setting opts in", async () => {
const settingsStore = useSettingsStore();
const { host, unmount } = mount(AppToolbar, toolbarProps);
@@ -177,6 +177,33 @@ describe("EditorGroupTabBar behavior", () => {
host.remove();
});
it("keeps a focus-return click on tab-strip whitespace out of native window dragging", async () => {
const store = useQueryStore();
const firstId = store.createTab("pg-1", "app", "Query 1", "query");
const secondId = store.createTab("pg-1", "app", "Query 2", "query");
const mainGroup = store.groups[0];
const activated: string[] = [];
const { app, host } = mountBar(mainGroup.id, [firstId, secondId], firstId, pinia, (tabId) => activated.push(tabId));
await settle();
const tabTail = host.querySelector<HTMLElement>('[data-tauri-drag-region="false"]');
expect(tabTail).not.toBeNull();
window.dispatchEvent(new Event("blur"));
window.dispatchEvent(new Event("focus"));
tabTail!.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, button: 0, detail: 1 }));
tabTail!.click();
await settle();
expect(activated).toEqual([]);
tabPill(host, secondId).click();
await settle();
expect(activated).toEqual([secondId]);
app.unmount();
host.remove();
});
it("closes a tab tooltip when the pointer leaves the tab", async () => {
const store = useQueryStore();
const tabId = store.createTab("pg-1", "app", "Plugin", "query");
@@ -22,6 +22,7 @@ const mocks = vi.hoisted(() => ({
removePluginTrustedKey: vi.fn(),
toast: vi.fn(),
isTauriRuntime: vi.fn(),
openExternal: vi.fn(),
refreshPluginWorkbenches: vi.fn(),
}));
@@ -31,6 +32,7 @@ vi.mock("@/stores/connectionStore", () => ({ useConnectionStore: () => ({ connec
vi.mock("@/stores/queryStore", () => ({ useQueryStore: () => ({}) }));
vi.mock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: mocks.isTauriRuntime }));
vi.mock("@tauri-apps/api/event", () => ({ listen: vi.fn(async () => vi.fn()) }));
vi.mock("@tauri-apps/plugin-shell", () => ({ open: mocks.openExternal }));
vi.mock("vue-i18n", async () => {
const { ref } = await import("vue");
return { useI18n: () => ({ locale: ref("en"), t: (key: string, values = {}) => `${key}:${JSON.stringify(values)}` }) };
@@ -270,6 +272,33 @@ describe("PluginContributionsPanel batch source validation", () => {
});
});
describe("PluginContributionsPanel external links", () => {
it("hands GitHub and homepage clicks to the desktop shell without selecting the card", async () => {
const linkedCatalog = catalog("first", ["linked"]);
Object.assign(linkedCatalog.catalog!.plugins[0], {
source: "https://github.com/dbxio/linked",
homepage: "https://dbxio.com/plugins/linked",
});
state.catalogResults = [linkedCatalog];
state.marketplaceViewMode = "grid";
mocks.isTauriRuntime.mockReturnValue(true);
await nextTick();
const sourceButton = host.querySelector<HTMLButtonElement>('[aria-label^="pluginPlatform.sourceRepository"]')!;
const homepageButton = host.querySelector<HTMLButtonElement>('[aria-label^="pluginPlatform.pluginHomepage"]')!;
const cardClick = vi.fn();
sourceButton.closest("article")!.addEventListener("click", cardClick);
sourceButton.click();
await vi.waitFor(() => expect(mocks.openExternal).toHaveBeenCalledTimes(1));
homepageButton.click();
await vi.waitFor(() => expect(mocks.openExternal).toHaveBeenCalledTimes(2));
expect(mocks.openExternal.mock.calls).toEqual([["https://github.com/dbxio/linked"], ["https://dbxio.com/plugins/linked"]]);
expect(cardClick).not.toHaveBeenCalled();
});
});
describe("PluginContributionsPanel update center synchronization", () => {
it("notifies after a single marketplace update without duplicating per batch item", async () => {
const listener = vi.fn();