fix(plugins): restore workbench refresh and bootstrap
This commit is contained in:
4 files changed
+48
-7
No files matched your search
@@ -421,6 +421,8 @@ const activeTab = computed(() => queryStore.tabs.find((t) => t.id === queryStore
|
||||
// iframe moved out of the DOM reloads from scratch, so KeepAlive/ContentArea
|
||||
// remounts flash the whole webview and drop its live session state.
|
||||
const mountedPluginWorkbenchTabs = computed(() => queryStore.tabs.filter((tab) => tab.mode === "plugin-workbench" && tab.pluginWorkbench));
|
||||
type PluginWorkbenchTabHandle = { refresh: () => Promise<unknown> };
|
||||
const pluginWorkbenchTabRefs = new Map<string, PluginWorkbenchTabHandle>();
|
||||
const tabNavigationHistory = ref(createTabNavigationHistory());
|
||||
let pendingTabHistoryNavigationId: string | null = null;
|
||||
let detachedEventUnlisteners: Array<() => void> = [];
|
||||
@@ -3144,6 +3146,23 @@ function handleNativeSelectAll(e: KeyboardEvent) {
|
||||
if (shouldBlockAppNativeSelectAll(e)) e.preventDefault();
|
||||
}
|
||||
|
||||
function setPluginWorkbenchTabRef(tabId: string, element: unknown) {
|
||||
if (element && typeof element === "object" && "refresh" in element && typeof element.refresh === "function") {
|
||||
pluginWorkbenchTabRefs.set(tabId, element as PluginWorkbenchTabHandle);
|
||||
} else {
|
||||
pluginWorkbenchTabRefs.delete(tabId);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshActivePluginWorkbench(): boolean {
|
||||
const tab = activeTab.value;
|
||||
if (tab?.mode !== "plugin-workbench") return false;
|
||||
const pluginWorkbench = pluginWorkbenchTabRefs.get(tab.id);
|
||||
if (!pluginWorkbench) return false;
|
||||
void pluginWorkbench.refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
async function closeActiveSurface() {
|
||||
if (showSettingsPage.value) {
|
||||
closeSettingsPage();
|
||||
@@ -3303,6 +3322,11 @@ async function handleKeydown(e: KeyboardEvent) {
|
||||
if (selectedSql.value.trim()) sendSelectionToAi(selectedSql.value);
|
||||
return;
|
||||
}
|
||||
if (isModRShortcut(e) && refreshActivePluginWorkbench()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (isModRShortcut(e) && e.target instanceof Element && contentAreaRef.value?.handleModRTarget(e.target)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -3876,7 +3900,13 @@ onUnmounted(() => {
|
||||
yields the layout (display:none) while a plugin tab is
|
||||
active, or both flex-1 siblings would split the column. -->
|
||||
<div v-for="workbenchTab in mountedPluginWorkbenchTabs" :key="workbenchTab.id" v-show="activeTab && workbenchTab.id === activeTab.id" class="flex min-h-0 flex-1 flex-col">
|
||||
<PluginWorkbenchTab :plugin-id="workbenchTab.pluginWorkbench!.pluginId" :contribution-id="workbenchTab.pluginWorkbench!.contributionId" :context="workbenchTab.pluginWorkbench!.context" @close-tab="queryStore.closeTab(workbenchTab.id)" />
|
||||
<PluginWorkbenchTab
|
||||
:ref="(element) => setPluginWorkbenchTabRef(workbenchTab.id, element)"
|
||||
:plugin-id="workbenchTab.pluginWorkbench!.pluginId"
|
||||
:contribution-id="workbenchTab.pluginWorkbench!.contributionId"
|
||||
:context="workbenchTab.pluginWorkbench!.context"
|
||||
@close-tab="queryStore.closeTab(workbenchTab.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -205,9 +205,6 @@ function handleCancelClose() {
|
||||
<div v-show="driverStoreActive || pluginCenterActive || settingsPageActive" data-special-page-workspace class="flex min-h-0 min-w-0 flex-1 overflow-hidden" :class="layoutClass">
|
||||
<div data-special-page-navigation class="flex min-h-0 min-w-0 shrink-0 flex-col overflow-auto" :style="navigationStyle">
|
||||
<div v-for="group in queryStore.groups" :key="group.id" :ref="(element) => setTabBarTarget(group.id, element)" :data-special-page-tab-target="group.id" class="flex min-h-0 min-w-0" :class="isVerticalLayout ? 'flex-1' : 'shrink-0'" />
|
||||
<button v-if="pluginCenterOpen" type="button" data-plugin-center-tab class="shrink-0 rounded-md px-2 py-1 text-left text-sm" :class="pluginCenterActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground'" @click="emit('activate-plugin-center')">
|
||||
{{ t("toolbar.pluginCenter") }}
|
||||
</button>
|
||||
</div>
|
||||
<div data-special-page-content class="flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
|
||||
<slot />
|
||||
|
||||
+17
-2
@@ -384,12 +384,19 @@ describe("plugin SDK source", () => {
|
||||
dbxPlugin?: { invoke: (method: string, params?: unknown, options?: { timeoutMs?: number }) => Promise<unknown> };
|
||||
}
|
||||
|
||||
function loadSdk(posted: unknown[]): SdkWindow {
|
||||
function loadSdk(posted: unknown[], initialTheme?: { appearance: "dark" | "light"; tokens: Record<string, string> }): SdkWindow {
|
||||
const sandbox = {} as SdkWindow;
|
||||
const document = {
|
||||
documentElement: {
|
||||
dataset: {} as Record<string, string>,
|
||||
style: { colorScheme: "", setProperty: vi.fn() },
|
||||
},
|
||||
dispatchEvent: vi.fn(),
|
||||
} as unknown as Document;
|
||||
// The SDK IIFE only touches window and the bare-global addEventListener at
|
||||
// boot; parent.postMessage is captured for later request() calls, so a
|
||||
// stub window/parent is enough here.
|
||||
new Function("window", "parent", "addEventListener", pluginSdkSource())(sandbox, { postMessage: (message: unknown) => posted.push(message) }, () => {});
|
||||
new Function("window", "parent", "addEventListener", "document", pluginSdkSource(initialTheme))(sandbox, { postMessage: (message: unknown) => posted.push(message) }, () => {}, document);
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
@@ -421,6 +428,14 @@ describe("plugin SDK source", () => {
|
||||
expect(JSON.stringify(message.params.params.sessionIds)).toBe(JSON.stringify(["session-a", "session-b"]));
|
||||
});
|
||||
|
||||
it("boots successfully when the host provides the initial theme", () => {
|
||||
const posted: unknown[] = [];
|
||||
const { dbxPlugin } = loadSdk(posted, { appearance: "dark", tokens: { "--color-background": "#131416" } });
|
||||
|
||||
expect(dbxPlugin).toBeDefined();
|
||||
expect(posted[0]).toMatchObject({ source: "dbx-plugin", type: "ready" });
|
||||
});
|
||||
|
||||
it("keeps cloneable values intact and plain scalars by reference", () => {
|
||||
const posted: unknown[] = [];
|
||||
const { dbxPlugin } = loadSdk(posted);
|
||||
|
||||
@@ -349,7 +349,6 @@ export function pluginSdkSource(initialTheme?: PluginBridgeTheme): string {
|
||||
const applyTheme = (value) => {
|
||||
if (!value || typeof value !== 'object') return;
|
||||
theme = value;
|
||||
const theme = value;
|
||||
const root = document.documentElement;
|
||||
root.dataset.dbxTheme = theme.appearance === "dark" ? "dark" : "light";
|
||||
root.style.colorScheme = theme.appearance === "dark" ? "dark" : "light";
|
||||
|
||||
Reference in new issue
Block a user