perf(desktop): defer startup dialog chunks
This commit is contained in:
parent
b16393f68c
commit
c29bd98d94
|
|
@ -1,10 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from "vue";
|
||||
import { ref, computed, watch, onMounted, onUnmounted, nextTick, defineAsyncComponent } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import AiAssistant from "@/components/editor/AiAssistant.vue";
|
||||
import QueryHistory from "@/components/editor/QueryHistory.vue";
|
||||
import AppToolbar from "@/components/layout/AppToolbar.vue";
|
||||
import AppTabBar from "@/components/layout/AppTabBar.vue";
|
||||
import AppSidebar from "@/components/layout/AppSidebar.vue";
|
||||
|
|
@ -12,9 +10,6 @@ import EditorToolbar from "@/components/layout/EditorToolbar.vue";
|
|||
import ContentArea from "@/components/layout/ContentArea.vue";
|
||||
import AppDialogs from "@/components/layout/AppDialogs.vue";
|
||||
import WelcomeScreen from "@/components/layout/WelcomeScreen.vue";
|
||||
import DriverStorePage from "@/components/config/DriverStoreDialog.vue";
|
||||
import UpdateDialog from "@/components/layout/UpdateDialog.vue";
|
||||
import LoginPage from "@/components/auth/LoginPage.vue";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
|
|
@ -56,6 +51,17 @@ import { Button } from "@/components/ui/button";
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import type { HistoryEntry } from "@/lib/tauri";
|
||||
import type { AiAction } from "@/lib/ai";
|
||||
|
||||
const AiAssistant = defineAsyncComponent(() => import("@/components/editor/AiAssistant.vue"));
|
||||
const QueryHistory = defineAsyncComponent(() => import("@/components/editor/QueryHistory.vue"));
|
||||
const DriverStorePage = defineAsyncComponent(() => import("@/components/config/DriverStoreDialog.vue"));
|
||||
const UpdateDialog = defineAsyncComponent(() => import("@/components/layout/UpdateDialog.vue"));
|
||||
const LoginPage = defineAsyncComponent(() => import("@/components/auth/LoginPage.vue"));
|
||||
|
||||
type AiAssistantHandle = {
|
||||
triggerAction: (action: AiAction, instruction?: string) => void;
|
||||
};
|
||||
|
||||
const { t } = useI18n();
|
||||
const connectionStore = useConnectionStore();
|
||||
|
|
@ -94,9 +100,10 @@ const showDriverStore = ref(false);
|
|||
const agentDriverUpdateCount = ref(0);
|
||||
const showHistory = ref(false);
|
||||
const showAiPanel = ref(localStorage.getItem("dbx-ai-panel-open") !== "false");
|
||||
const aiPanelReady = ref(false);
|
||||
const { sidebarWidth, aiPanelWidth, historyWidth, startSidebarResize, startAiPanelResize, startHistoryResize } =
|
||||
usePanelResize();
|
||||
const aiAssistantRef = ref<InstanceType<typeof AiAssistant> | null>(null);
|
||||
const aiAssistantRef = ref<AiAssistantHandle | null>(null);
|
||||
const appSidebarRef = ref<InstanceType<typeof AppSidebar> | null>(null);
|
||||
const contentAreaRef = ref<InstanceType<typeof ContentArea> | null>(null);
|
||||
|
||||
|
|
@ -629,6 +636,9 @@ function openDriverStoreFromEvent() {
|
|||
onMounted(async () => {
|
||||
console.log("[STARTUP] onMounted begin");
|
||||
const mountStart = performance.now();
|
||||
requestAnimationFrame(() => {
|
||||
aiPanelReady.value = true;
|
||||
});
|
||||
applyTheme();
|
||||
window.addEventListener("keydown", handleKeydown, true);
|
||||
window.addEventListener("dbx-open-driver-store", openDriverStoreFromEvent);
|
||||
|
|
@ -857,6 +867,7 @@ onUnmounted(() => {
|
|||
<div class="panel-resize-handle panel-resize-handle--left" @mousedown="startAiPanelResize" />
|
||||
<div class="h-full min-h-0 overflow-hidden">
|
||||
<AiAssistant
|
||||
v-if="aiPanelReady"
|
||||
ref="aiAssistantRef"
|
||||
:tab="activeTab"
|
||||
:connection="activeConnection"
|
||||
|
|
@ -910,6 +921,7 @@ onUnmounted(() => {
|
|||
@open-database-search-target="openDatabaseSearchTarget"
|
||||
/>
|
||||
<UpdateDialog
|
||||
v-if="showUpdateDialog"
|
||||
v-model:open="showUpdateDialog"
|
||||
:update-info="updateInfo"
|
||||
:update-check-message="updateCheckMessage"
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { computed, watch, defineAsyncComponent } from "vue";
|
|||
import { useI18n } from "vue-i18n";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import ConnectionDialog from "@/components/connection/ConnectionDialog.vue";
|
||||
import EditorSettingsDialog from "@/components/editor/EditorSettingsDialog.vue";
|
||||
import DangerConfirmDialog from "@/components/editor/DangerConfirmDialog.vue";
|
||||
const ConnectionDialog = defineAsyncComponent(() => import("@/components/connection/ConnectionDialog.vue"));
|
||||
const EditorSettingsDialog = defineAsyncComponent(() => import("@/components/editor/EditorSettingsDialog.vue"));
|
||||
const DangerConfirmDialog = defineAsyncComponent(() => import("@/components/editor/DangerConfirmDialog.vue"));
|
||||
const DataTransferDialog = defineAsyncComponent(() => import("@/components/transfer/DataTransferDialog.vue"));
|
||||
const SchemaDiffDialog = defineAsyncComponent(() => import("@/components/diff/SchemaDiffDialog.vue"));
|
||||
const DataCompareDialog = defineAsyncComponent(() => import("@/components/diff/DataCompareDialog.vue"));
|
||||
|
|
@ -95,6 +95,7 @@ watch(
|
|||
|
||||
<template>
|
||||
<ConnectionDialog
|
||||
v-if="showConnectionDialog || editConfig"
|
||||
:open="showConnectionDialog"
|
||||
:edit-config="editConfig"
|
||||
@update:open="emit('update:showConnectionDialog', $event)"
|
||||
|
|
@ -104,29 +105,34 @@ watch(
|
|||
@open-driver-store="emit('openDriverStore')"
|
||||
/>
|
||||
<EditorSettingsDialog
|
||||
v-if="showSettingsDialog"
|
||||
:open="showSettingsDialog"
|
||||
:initial-tab="settingsInitialTab || 'editor'"
|
||||
:app-version="appVersion"
|
||||
@update:open="emit('update:showSettingsDialog', $event)"
|
||||
/>
|
||||
<DangerConfirmDialog
|
||||
v-if="showDangerDialog"
|
||||
:open="showDangerDialog"
|
||||
:sql="dangerSql"
|
||||
@update:open="emit('update:showDangerDialog', $event)"
|
||||
@confirm="emit('dangerConfirm')"
|
||||
/>
|
||||
<DataTransferDialog
|
||||
v-if="dialogs.showTransferDialog.value"
|
||||
v-model:open="dialogs.showTransferDialog.value"
|
||||
:prefill-connection-id="dialogs.transferPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.transferPrefillDatabase.value"
|
||||
/>
|
||||
<SchemaDiffDialog
|
||||
v-if="dialogs.showSchemaDiffDialog.value"
|
||||
v-model:open="dialogs.showSchemaDiffDialog.value"
|
||||
:prefill-connection-id="dialogs.schemaDiffPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.schemaDiffPrefillDatabase.value"
|
||||
:prefill-schema="dialogs.schemaDiffPrefillSchema.value"
|
||||
/>
|
||||
<DataCompareDialog
|
||||
v-if="dialogs.showDataCompareDialog.value"
|
||||
v-model:open="dialogs.showDataCompareDialog.value"
|
||||
:prefill-connection-id="dialogs.dataComparePrefillConnectionId.value"
|
||||
:prefill-database="dialogs.dataComparePrefillDatabase.value"
|
||||
|
|
@ -134,11 +140,13 @@ watch(
|
|||
:prefill-table="dialogs.dataComparePrefillTable.value"
|
||||
/>
|
||||
<SqlFileExecutionDialog
|
||||
v-if="dialogs.showSqlFileDialog.value"
|
||||
v-model:open="dialogs.showSqlFileDialog.value"
|
||||
:prefill-connection-id="dialogs.sqlFilePrefillConnectionId.value"
|
||||
:prefill-database="dialogs.sqlFilePrefillDatabase.value"
|
||||
/>
|
||||
<SchemaDiagramDialog
|
||||
v-if="dialogs.showDiagramDialog.value"
|
||||
v-model:open="dialogs.showDiagramDialog.value"
|
||||
:prefill-connection-id="dialogs.diagramPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.diagramPrefillDatabase.value"
|
||||
|
|
@ -146,6 +154,7 @@ watch(
|
|||
:focus-table-name="dialogs.diagramFocusTableName.value"
|
||||
/>
|
||||
<TableImportDialog
|
||||
v-if="dialogs.showTableImportDialog.value"
|
||||
v-model:open="dialogs.showTableImportDialog.value"
|
||||
:prefill-connection-id="dialogs.tableImportPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.tableImportPrefillDatabase.value"
|
||||
|
|
@ -153,6 +162,7 @@ watch(
|
|||
:prefill-table="dialogs.tableImportPrefillTable.value"
|
||||
/>
|
||||
<TableStructureEditorDialog
|
||||
v-if="dialogs.showStructureEditorDialog.value"
|
||||
v-model:open="dialogs.showStructureEditorDialog.value"
|
||||
:prefill-connection-id="dialogs.structurePrefillConnectionId.value"
|
||||
:prefill-database="dialogs.structurePrefillDatabase.value"
|
||||
|
|
@ -161,6 +171,7 @@ watch(
|
|||
@saved="emit('structureEditorSaved')"
|
||||
/>
|
||||
<FieldLineageDialog
|
||||
v-if="dialogs.showFieldLineageDialog.value"
|
||||
v-model:open="dialogs.showFieldLineageDialog.value"
|
||||
:prefill-connection-id="dialogs.lineagePrefillConnectionId.value"
|
||||
:prefill-database="dialogs.lineagePrefillDatabase.value"
|
||||
|
|
@ -170,6 +181,7 @@ watch(
|
|||
@open-target="emit('openLineageTarget', $event)"
|
||||
/>
|
||||
<DatabaseSearchDialog
|
||||
v-if="dialogs.showDatabaseSearchDialog.value"
|
||||
v-model:open="dialogs.showDatabaseSearchDialog.value"
|
||||
:prefill-connection-id="dialogs.databaseSearchPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.databaseSearchPrefillDatabase.value"
|
||||
|
|
@ -177,6 +189,7 @@ watch(
|
|||
@open-target="emit('openDatabaseSearchTarget', $event)"
|
||||
/>
|
||||
<DatabaseExportDialog
|
||||
v-if="dialogs.showDatabaseExportDialog.value"
|
||||
v-model:open="dialogs.showDatabaseExportDialog.value"
|
||||
:prefill-connection-id="dialogs.databaseExportPrefillConnectionId.value"
|
||||
:prefill-database="dialogs.databaseExportPrefillDatabase.value"
|
||||
|
|
@ -184,6 +197,7 @@ watch(
|
|||
:prefill-table="dialogs.databaseExportPrefillTable.value"
|
||||
/>
|
||||
<ConfigPassphraseDialog
|
||||
v-if="dialogs.showConfigPassphraseDialog.value"
|
||||
v-model:open="dialogs.showConfigPassphraseDialog.value"
|
||||
:mode="dialogs.configPassphraseMode.value"
|
||||
:external-error="dialogs.configPassphraseError.value"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
import { readFileSync } from "node:fs";
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
const appSource = readFileSync("apps/desktop/src/App.vue", "utf8");
|
||||
const appDialogsSource = readFileSync("apps/desktop/src/components/layout/AppDialogs.vue", "utf8");
|
||||
|
||||
test("app defers cold-start side panels and modal pages behind async components", () => {
|
||||
assert.match(appSource, /defineAsyncComponent/);
|
||||
for (const component of ["AiAssistant", "QueryHistory", "DriverStorePage", "UpdateDialog", "LoginPage"]) {
|
||||
assert.doesNotMatch(appSource, new RegExp(`import ${component} from`));
|
||||
assert.match(appSource, new RegExp(`const ${component} = defineAsyncComponent`));
|
||||
}
|
||||
});
|
||||
|
||||
test("app dialogs keep non-primary dialogs out of the startup chunk", () => {
|
||||
for (const component of ["ConnectionDialog", "EditorSettingsDialog", "DangerConfirmDialog"]) {
|
||||
assert.doesNotMatch(appDialogsSource, new RegExp(`import ${component} from`));
|
||||
assert.match(appDialogsSource, new RegExp(`const ${component} = defineAsyncComponent`));
|
||||
}
|
||||
});
|
||||
|
||||
test("app dialogs only render async dialogs when their open state needs them", () => {
|
||||
assert.match(appDialogsSource, /<ConnectionDialog\s+v-if="showConnectionDialog \|\| editConfig"/);
|
||||
assert.match(appDialogsSource, /<EditorSettingsDialog\s+v-if="showSettingsDialog"/);
|
||||
assert.match(appDialogsSource, /<DangerConfirmDialog\s+v-if="showDangerDialog"/);
|
||||
assert.match(appDialogsSource, /<DataTransferDialog\s+v-if="dialogs\.showTransferDialog\.value"/);
|
||||
assert.match(appDialogsSource, /<SchemaDiagramDialog\s+v-if="dialogs\.showDiagramDialog\.value"/);
|
||||
assert.match(appDialogsSource, /<DatabaseExportDialog\s+v-if="dialogs\.showDatabaseExportDialog\.value"/);
|
||||
});
|
||||
Loading…
Reference in New Issue