fix(desktop): scope dark styles and restore dialog blur
This commit is contained in:
parent
3143683b5a
commit
e78a510485
|
|
@ -34,6 +34,7 @@ import { useVisibilityChange } from "@/composables/useVisibilityChange";
|
|||
import { useWebDavAutoUpload } from "@/composables/useWebDavAutoUpload";
|
||||
import { useScheduledDatabaseBackups } from "@/composables/useScheduledDatabaseBackups";
|
||||
import { shouldDrawDesktopWindowFrame } from "@/composables/useWindowControls";
|
||||
import { startDialogBackdropSync, stopDialogBackdropSync } from "@/lib/app/dialogBackdrop";
|
||||
import { useSaveSqlFolderSelection } from "@/composables/useSaveSqlFolderSelection";
|
||||
import "@/i18n";
|
||||
import { translateBackendError } from "@/i18n/backend-errors";
|
||||
|
|
@ -2087,6 +2088,7 @@ onMounted(async () => {
|
|||
});
|
||||
applyTheme();
|
||||
void applyUiScale(settingsStore.editorSettings.uiScale);
|
||||
startDialogBackdropSync();
|
||||
window.addEventListener("keydown", handleKeydown);
|
||||
window.addEventListener("dbx-open-driver-store", openDriverStoreFromEvent);
|
||||
if (isDesktop) {
|
||||
|
|
@ -2147,6 +2149,7 @@ onMounted(async () => {
|
|||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopDialogBackdropSync();
|
||||
cleanupTauriListeners();
|
||||
cleanupCloseActionPromptListener();
|
||||
if (updateCheckTimer) {
|
||||
|
|
|
|||
|
|
@ -570,15 +570,15 @@ defineExpose({ focus, dismiss: editor.dismiss, rememberHistory: editor.rememberH
|
|||
0 1px color-mix(in oklab, var(--background) 96%, var(--muted) 4%);
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-label--where {
|
||||
:global(.dark .data-grid-topbar-condition-label--where) {
|
||||
color: rgb(96 165 250);
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-label--order {
|
||||
:global(.dark .data-grid-topbar-condition-label--order) {
|
||||
color: rgb(251 146 60);
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-label--floating {
|
||||
:global(.dark .data-grid-topbar-condition-label--floating) {
|
||||
text-shadow:
|
||||
-1px 0 rgb(24, 24, 27),
|
||||
1px 0 rgb(24, 24, 27),
|
||||
|
|
@ -617,7 +617,7 @@ defineExpose({ focus, dismiss: editor.dismiss, rememberHistory: editor.rememberH
|
|||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-input {
|
||||
:global(.dark .data-grid-topbar-condition-input) {
|
||||
color: rgb(244, 244, 245);
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
|
@ -657,7 +657,7 @@ defineExpose({ focus, dismiss: editor.dismiss, rememberHistory: editor.rememberH
|
|||
--data-grid-condition-suffix-width: 0px;
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-pane--expanded {
|
||||
:global(.dark .data-grid-topbar-condition-pane--expanded) {
|
||||
background: rgb(24, 24, 27) !important;
|
||||
color: rgb(244, 244, 245);
|
||||
box-shadow:
|
||||
|
|
@ -706,11 +706,11 @@ defineExpose({ focus, dismiss: editor.dismiss, rememberHistory: editor.rememberH
|
|||
color: rgb(249 115 22 / 70%);
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-input--where.data-grid-topbar-condition-input--compact::placeholder {
|
||||
:global(.dark .data-grid-topbar-condition-input--where.data-grid-topbar-condition-input--compact::placeholder) {
|
||||
color: rgb(147 197 253 / 70%);
|
||||
}
|
||||
|
||||
:global(.dark) .data-grid-topbar-condition-input--order.data-grid-topbar-condition-input--compact::placeholder {
|
||||
:global(.dark .data-grid-topbar-condition-input--order.data-grid-topbar-condition-input--compact::placeholder) {
|
||||
color: rgb(253 186 116 / 70%);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -124,6 +124,14 @@ describe("DataGridConditionEditor quote completion", () => {
|
|||
expect(floatingLabelCss).not.toContain("box-shadow:");
|
||||
});
|
||||
|
||||
it("keeps dark condition styles scoped to their target elements", () => {
|
||||
const source = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/grid/DataGridConditionEditor.vue"), "utf8");
|
||||
|
||||
expect(source).not.toContain(":global(.dark) .data-grid");
|
||||
expect(source).toContain(":global(.dark .data-grid-topbar-condition-label--floating)");
|
||||
expect(source).toContain(":global(.dark .data-grid-topbar-condition-pane--expanded)");
|
||||
});
|
||||
|
||||
it("scrolls the caret into view after accepting a long completion", () => {
|
||||
const source = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/grid/DataGridConditionEditor.vue"), "utf8");
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
const DIALOG_BACKDROP_CLASS = "dbx-dialog-backdrop-active";
|
||||
|
||||
let dialogBackdropObserver: MutationObserver | undefined;
|
||||
|
||||
function hasDialogOverlay(): boolean {
|
||||
return Array.from(document.body.children).some((child) => child.matches('[data-slot="dialog-overlay"]') || child.querySelector('[data-slot="dialog-overlay"]'));
|
||||
}
|
||||
|
||||
function syncDialogBackdrop() {
|
||||
document.documentElement.classList.toggle(DIALOG_BACKDROP_CLASS, hasDialogOverlay());
|
||||
}
|
||||
|
||||
export function startDialogBackdropSync() {
|
||||
if (typeof document === "undefined" || dialogBackdropObserver) return;
|
||||
dialogBackdropObserver = new MutationObserver(syncDialogBackdrop);
|
||||
dialogBackdropObserver.observe(document.body, { childList: true });
|
||||
syncDialogBackdrop();
|
||||
}
|
||||
|
||||
export function stopDialogBackdropSync() {
|
||||
dialogBackdropObserver?.disconnect();
|
||||
dialogBackdropObserver = undefined;
|
||||
document.documentElement.classList.remove(DIALOG_BACKDROP_CLASS);
|
||||
}
|
||||
|
|
@ -6,6 +6,8 @@ const dialogContentSource = readFileSync(new URL("../../components/ui/dialog/Dia
|
|||
const dialogScrollContentSource = readFileSync(new URL("../../components/ui/dialog/DialogScrollContent.vue", import.meta.url), "utf8");
|
||||
const dialogOverlaySource = readFileSync(new URL("../../components/ui/dialog/DialogOverlay.vue", import.meta.url), "utf8");
|
||||
const connectionDialogSource = readFileSync(new URL("../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
|
||||
const dialogBackdropSource = readFileSync(new URL("../../lib/app/dialogBackdrop.ts", import.meta.url), "utf8");
|
||||
const appSource = readFileSync(new URL("../../App.vue", import.meta.url), "utf8");
|
||||
const desktopIndexSource = readFileSync(new URL("../../../index.html", import.meta.url), "utf8");
|
||||
const connectionDialogLegacyCss = readFileSync(new URL("../../../public/connection-dialog-legacy.css", import.meta.url), "utf8");
|
||||
|
||||
|
|
@ -44,9 +46,15 @@ describe("legacy WebView CSS fallbacks", () => {
|
|||
expect(connectionDialogSource).toContain("max-height: calc(var(--dbx-viewport-height) - 2rem);");
|
||||
});
|
||||
|
||||
it("avoids full-window backdrop filters that can blank WebKit after a dialog closes", () => {
|
||||
it("blurs the stable app root instead of the transient dialog overlay", () => {
|
||||
expect(dialogOverlaySource).not.toContain("backdrop-filter");
|
||||
expect(dialogOverlaySource).toContain("bg-black/10");
|
||||
expect(globalsCss).toContain("html.dbx-dialog-backdrop-active #root");
|
||||
expect(globalsCss).toContain("filter: blur(4px);");
|
||||
expect(dialogBackdropSource).toContain("dialogBackdropObserver.observe(document.body, { childList: true })");
|
||||
expect(dialogBackdropSource).not.toContain("subtree: true");
|
||||
expect(appSource).toContain("startDialogBackdropSync();");
|
||||
expect(appSource).toContain("stopDialogBackdropSync();");
|
||||
});
|
||||
|
||||
it("keeps legacy tab triggers connected to the configured corner style", () => {
|
||||
|
|
|
|||
|
|
@ -270,6 +270,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
html.dbx-dialog-backdrop-active #root {
|
||||
filter: blur(4px);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: rgb(19 20 22);
|
||||
--foreground: rgb(215 215 219);
|
||||
|
|
|
|||
Loading…
Reference in New Issue