mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(desktop): add legacy webview fallbacks for snapshots and dialogs
This commit is contained in:
@@ -158,7 +158,7 @@ const previewVisible = computed(() => open.value && !!props.source);
|
||||
<Dialog v-model:open="open">
|
||||
<!-- 对话框整体限高:高度上限跟随 --dbx-viewport-height(兼容旧版 WebView 中 vh 不准的情况);
|
||||
内部采用纵向 flex 布局,header/footer 固定,只有中间预览区滚动,保证底部按钮始终可见 -->
|
||||
<DialogContent class="flex max-h-[calc(var(--dbx-viewport-height)-2rem)] flex-col overflow-hidden border border-border !bg-background text-foreground shadow-2xl !backdrop-blur-none sm:max-w-[860px]">
|
||||
<DialogContent class="dbx-code-snapshot-dialog flex max-h-[calc(var(--dbx-viewport-height)-2rem)] flex-col overflow-hidden border border-border !bg-background text-foreground shadow-2xl !backdrop-blur-none sm:max-w-[860px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle class="flex items-center gap-2">
|
||||
<Camera class="h-5 w-5 text-primary" />
|
||||
@@ -209,7 +209,7 @@ const previewVisible = computed(() => open.value && !!props.source);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<DialogFooter class="dbx-code-snapshot-dialog__footer">
|
||||
<Button variant="outline" @click="open = false">{{ t("codeSnapshot.close") }}</Button>
|
||||
<Button variant="outline" :disabled="exporting" @click="exportSnapshot('clipboard')">
|
||||
<Check v-if="copied" class="mr-1.5 h-4 w-4 text-green-500" />
|
||||
@@ -224,3 +224,21 @@ const previewVisible = computed(() => open.value && !!props.source);
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-code-snapshot-dialog {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
width: calc(100vw - 2rem) !important;
|
||||
max-width: 860px !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
html.dbx-legacy-webview .dbx-code-snapshot-dialog__footer {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-end !important;
|
||||
flex-wrap: nowrap !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -86,7 +86,7 @@ const emit = defineEmits<{
|
||||
<span class="text-background/70">{{ columnNameLabel }}</span>
|
||||
<span class="flex min-w-0 items-center gap-2">
|
||||
<span class="min-w-0 flex-1 truncate font-mono">{{ name }}</span>
|
||||
<button type="button" class="flex h-5 w-5 shrink-0 items-center justify-center rounded hover:bg-background/10" :title="copyColumnNameLabel" @click.stop="emit('copyName')">
|
||||
<button data-column-header-copy-name type="button" class="flex h-5 w-5 shrink-0 items-center justify-center rounded hover:bg-background/10" :title="copyColumnNameLabel" @click.stop="emit('copyName')">
|
||||
<Copy class="h-3 w-3" />
|
||||
</button>
|
||||
</span>
|
||||
@@ -157,3 +157,13 @@ const emit = defineEmits<{
|
||||
border-right: 2px solid rgb(100, 116, 139) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
html.dbx-legacy-webview [data-column-header-copy-name]:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
html.dbx-legacy-webview.dark [data-column-header-copy-name]:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -90,7 +90,7 @@ watch(
|
||||
<template>
|
||||
<Dialog :open="open" @update:open="handleOpenChange">
|
||||
<DialogContent
|
||||
class="sm:max-w-[520px]"
|
||||
class="dbx-update-dialog sm:max-w-[520px]"
|
||||
:show-close-button="!isCloseBlocked"
|
||||
@interact-outside="
|
||||
(e: Event) => {
|
||||
@@ -167,3 +167,14 @@ watch(
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-update-dialog[class~="max-w-sm"] {
|
||||
max-width: 520px !important;
|
||||
}
|
||||
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-update-dialog [data-slot="dialog-footer"] {
|
||||
flex-direction: row !important;
|
||||
justify-content: flex-end !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -224,7 +224,7 @@ function onClose() {
|
||||
|
||||
<template>
|
||||
<Dialog :open="props.open" @update:open="onClose">
|
||||
<DialogContent class="sm:max-w-190" @close-auto-focus="onDdlDialogCloseAutoFocus">
|
||||
<DialogContent class="dbx-ddl-view-dialog sm:max-w-190" @close-auto-focus="onDdlDialogCloseAutoFocus">
|
||||
<DialogHeader>
|
||||
<DialogTitle>DDL - {{ props.tableName }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -276,4 +276,13 @@ function onClose() {
|
||||
.ddl-view-editor :deep(.cm-content ::selection) {
|
||||
background: var(--dbx-editor-selection-background, rgba(59, 130, 246, 0.35)) !important;
|
||||
}
|
||||
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-ddl-view-dialog[class~="max-w-sm"] {
|
||||
max-width: 47.5rem !important;
|
||||
}
|
||||
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-ddl-view-dialog [data-slot="dialog-footer"] {
|
||||
flex-direction: row !important;
|
||||
justify-content: flex-end !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1353,10 +1353,8 @@ async function saveConfigTask() {
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@media (min-width: 640px) {
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-transfer-dialog[class~="max-w-sm"] {
|
||||
/* Override the legacy default cap without pinning width, so native resize remains effective. */
|
||||
max-width: calc(100vw - 2rem) !important;
|
||||
}
|
||||
html.dbx-legacy-webview [data-slot="dialog-content"].dbx-transfer-dialog[class~="max-w-sm"] {
|
||||
/* Override the legacy default cap without pinning width, so native resize remains effective. */
|
||||
max-width: calc(100vw - 2rem) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { CODE_SNAPSHOT_CSS, renderCodeSnapshotHtml, savePngDataUrlToFile, snapshotElementToPng } from "@/lib/codeSnapshot/codeSnapshot";
|
||||
import { CODE_SNAPSHOT_CSS, materializeSnapshotCloneLineNumbers, renderCodeSnapshotHtml, savePngDataUrlToFile, snapshotElementToPng } from "@/lib/codeSnapshot/codeSnapshot";
|
||||
import { LEGACY_WEBVIEW_CLASS } from "@/lib/ui/legacyWebView";
|
||||
|
||||
const codeSnapshotDialogSource = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/codeSnapshot/CodeSnapshotDialog.vue"), "utf8");
|
||||
|
||||
const { createCodeHighlighter, highlightCode, toPng, isTauriRuntime, save, writeFile } = vi.hoisted(() => {
|
||||
const highlightCode = vi.fn((content: string, _lang: string) => `<span class="line">${content}</span>`);
|
||||
@@ -78,6 +85,7 @@ describe("renderCodeSnapshotHtml", () => {
|
||||
expect(html).toContain(".dbx-code-snapshot *");
|
||||
expect(html).toContain("border: 0");
|
||||
expect(html).toContain("outline: 0");
|
||||
expect(html).toContain(".dbx-code-snapshot__line-number");
|
||||
});
|
||||
|
||||
it("renders macOS traffic lights and an optional title by default", async () => {
|
||||
@@ -124,8 +132,10 @@ describe("renderCodeSnapshotHtml", () => {
|
||||
|
||||
expect(dark).toContain("background:#0d1117");
|
||||
expect(dark).toContain("--dbx-snapshot-line-number:#484f58");
|
||||
expect(dark).toContain("color:#e1e4e8");
|
||||
expect(light).toContain("background:#ffffff");
|
||||
expect(light).toContain("--dbx-snapshot-line-number:#d0d7de");
|
||||
expect(light).toContain("color:#24292f");
|
||||
});
|
||||
|
||||
it("applies custom padding and font size", async () => {
|
||||
@@ -135,12 +145,35 @@ describe("renderCodeSnapshotHtml", () => {
|
||||
expect(html).toContain("padding:0 24px 24px");
|
||||
});
|
||||
|
||||
it("keeps the snapshot dialog responsive layout classes in source", () => {
|
||||
expect(codeSnapshotDialogSource).toContain("sm:max-w-[860px]");
|
||||
expect(codeSnapshotDialogSource).toContain("md:flex-row");
|
||||
expect(codeSnapshotDialogSource).toContain("md:w-52");
|
||||
});
|
||||
|
||||
it("materializes real line-number nodes in the export clone", () => {
|
||||
document.body.innerHTML = '<div class="dbx-code-snapshot"><pre class="dbx-code-snapshot__pre dbx-code-snapshot__pre--numbered"><code><span class="line">SELECT 1</span><span class="line">FROM dual</span></code></pre></div>';
|
||||
const root = document.body.firstElementChild as HTMLElement;
|
||||
|
||||
materializeSnapshotCloneLineNumbers(root);
|
||||
|
||||
const pre = root.querySelector(".dbx-code-snapshot__pre") as HTMLElement;
|
||||
const lines = Array.from(root.querySelectorAll<HTMLElement>("code > .line"));
|
||||
expect(pre.classList.contains("dbx-code-snapshot__pre--numbered")).toBe(false);
|
||||
expect(lines).toHaveLength(2);
|
||||
expect(lines[0]?.querySelector(".dbx-code-snapshot__line-number")?.textContent).toBe("1");
|
||||
expect(lines[1]?.querySelector(".dbx-code-snapshot__line-number")?.textContent).toBe("2");
|
||||
expect(lines[0]?.querySelector(".dbx-code-snapshot__line-content")?.textContent).toBe("SELECT 1");
|
||||
expect(lines[1]?.querySelector(".dbx-code-snapshot__line-content")?.textContent).toBe("FROM dual");
|
||||
});
|
||||
|
||||
it("exports at a capped device-pixel ratio for crisp high-DPI snapshots", async () => {
|
||||
toPng.mockResolvedValue("data:image/png;base64,test");
|
||||
vi.stubGlobal("window", { devicePixelRatio: 3 });
|
||||
try {
|
||||
await snapshotElementToPng({ offsetWidth: 320, offsetHeight: 160, scrollWidth: 640, scrollHeight: 240 } as HTMLElement);
|
||||
|
||||
const options = toPng.mock.calls.at(-1)?.[1];
|
||||
expect(toPng).toHaveBeenLastCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({
|
||||
@@ -153,6 +186,8 @@ describe("renderCodeSnapshotHtml", () => {
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(options?.adjustPseudoElement).toBeUndefined();
|
||||
expect(options?.onclone).toBeUndefined();
|
||||
|
||||
vi.stubGlobal("window", { devicePixelRatio: 0 });
|
||||
await snapshotElementToPng({ offsetWidth: 320, offsetHeight: 160, scrollWidth: 320, scrollHeight: 160 } as HTMLElement);
|
||||
@@ -162,6 +197,31 @@ describe("renderCodeSnapshotHtml", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("enables the legacy WebView export fallback only when the runtime is marked legacy", async () => {
|
||||
toPng.mockResolvedValue("data:image/png;base64,test");
|
||||
document.documentElement.classList.add(LEGACY_WEBVIEW_CLASS);
|
||||
try {
|
||||
await snapshotElementToPng({ offsetWidth: 320, offsetHeight: 160, scrollWidth: 320, scrollHeight: 160 } as HTMLElement);
|
||||
|
||||
const options = toPng.mock.calls.at(-1)?.[1];
|
||||
expect(typeof options?.adjustPseudoElement).toBe("function");
|
||||
expect(typeof options?.onclone).toBe("function");
|
||||
expect(options.adjustPseudoElement?.(document.createElement("span"), ":after", {} as CSSStyleDeclaration)).toBeUndefined();
|
||||
|
||||
const clone = document.createElement("div");
|
||||
clone.innerHTML = '<div class="dbx-code-snapshot"><pre class="dbx-code-snapshot__pre dbx-code-snapshot__pre--numbered" style="--dbx-snapshot-line-number:#d0d7de"><code><span class="line">SELECT 1</span><span class="line">FROM dual</span></code></pre></div>';
|
||||
options.onclone?.(clone);
|
||||
|
||||
const line = clone.querySelector<HTMLElement>(".dbx-code-snapshot__line-number");
|
||||
expect(line?.textContent).toBe("1");
|
||||
expect(line?.getAttribute("style")).toContain("display: inline-block");
|
||||
expect(line?.getAttribute("style")).toContain("margin-right: 1.4em");
|
||||
expect(line?.getAttribute("style")).toContain("color: #d0d7de");
|
||||
} finally {
|
||||
document.documentElement.classList.remove(LEGACY_WEBVIEW_CLASS);
|
||||
}
|
||||
});
|
||||
|
||||
it("reduces the export scale when a wide snapshot reaches the canvas edge limit", async () => {
|
||||
toPng.mockResolvedValue("data:image/png;base64,test");
|
||||
vi.stubGlobal("window", { devicePixelRatio: 2 });
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import type { AppThemeAppearance } from "@/lib/app/appTheme";
|
||||
import { createAiShikiBlockCodeHighlighter, type AiCodeHighlighter } from "@/lib/ai/aiCodeHighlighter";
|
||||
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
|
||||
import { LEGACY_WEBVIEW_CLASS } from "@/lib/ui/legacyWebView";
|
||||
|
||||
export interface CodeSnapshotSource {
|
||||
code: string;
|
||||
@@ -27,7 +28,7 @@ export interface CodeSnapshotStyleOptions {
|
||||
appearance: AppThemeAppearance;
|
||||
/** Render the macOS-style traffic light dots. Defaults to true. */
|
||||
showTrafficLights?: boolean;
|
||||
/** Render line numbers via CSS counters. Defaults to true. */
|
||||
/** Render line numbers. Defaults to true. */
|
||||
showLineNumbers?: boolean;
|
||||
/** Padding (px) around the code area. Defaults to 16. */
|
||||
padding?: number;
|
||||
@@ -55,6 +56,11 @@ const SNAPSHOT_LINE_NUMBER: Record<AppThemeAppearance, string> = {
|
||||
dark: "#484f58",
|
||||
};
|
||||
|
||||
const SNAPSHOT_CODE_TEXT: Record<AppThemeAppearance, string> = {
|
||||
light: "#24292f",
|
||||
dark: "#e1e4e8",
|
||||
};
|
||||
|
||||
const TRAFFIC_LIGHT_COLORS = ["#ff5f57", "#febc2e", "#28c840"] as const;
|
||||
const MAX_EXPORT_PIXEL_RATIO = 2;
|
||||
const MAX_EXPORT_CANVAS_EDGE = 16_384;
|
||||
@@ -112,6 +118,16 @@ export const CODE_SNAPSHOT_CSS = `
|
||||
display: block;
|
||||
min-height: 1.6em;
|
||||
}
|
||||
.dbx-code-snapshot__line-number {
|
||||
display: inline-block;
|
||||
min-width: 2.2em;
|
||||
margin-right: 1.4em;
|
||||
text-align: right;
|
||||
color: var(--dbx-snapshot-line-number, #8b949e);
|
||||
font-variant-numeric: tabular-nums;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
.dbx-code-snapshot__pre--numbered {
|
||||
counter-reset: dbx-snapshot-line;
|
||||
}
|
||||
@@ -160,6 +176,48 @@ function renderSnapshotBar(title: string | undefined, appearance: AppThemeAppear
|
||||
return `<div class="dbx-code-snapshot__bar" style="background:${SNAPSHOT_BAR_BACKGROUND[appearance]};color:${SNAPSHOT_BAR_TEXT[appearance]}">${dots}${titleHtml}</div>`;
|
||||
}
|
||||
|
||||
function isSnapshotNumberedLineNode(node: Node): node is Element {
|
||||
return node instanceof Element && node.classList.contains("line") && node.closest(".dbx-code-snapshot__pre--numbered") !== null;
|
||||
}
|
||||
|
||||
function hasMaterializedLineNumber(line: Element): boolean {
|
||||
return Array.from(line.children).some((child) => child.classList.contains("dbx-code-snapshot__line-number"));
|
||||
}
|
||||
|
||||
function legacyWebViewExportFallbackEnabled(): boolean {
|
||||
return typeof document !== "undefined" && document.documentElement.classList.contains(LEGACY_WEBVIEW_CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep preview rendering on CSS counters, but rewrite the cloned export DOM to
|
||||
* use real line-number nodes. Older WebKit foreignObject exports can flatten
|
||||
* every counter() value to "1", while the live preview still looks correct.
|
||||
*/
|
||||
export function materializeSnapshotCloneLineNumbers(root: ParentNode): void {
|
||||
for (const block of Array.from(root.querySelectorAll<HTMLElement>(".dbx-code-snapshot__pre--numbered"))) {
|
||||
block.classList.remove("dbx-code-snapshot__pre--numbered");
|
||||
const lineNumberColor = block.style.getPropertyValue("--dbx-snapshot-line-number").trim() || SNAPSHOT_LINE_NUMBER.dark;
|
||||
const lines = Array.from(block.querySelectorAll<HTMLElement>("code > .line"));
|
||||
for (const [index, line] of lines.entries()) {
|
||||
if (hasMaterializedLineNumber(line)) continue;
|
||||
const lineNumber = block.ownerDocument.createElement("span");
|
||||
lineNumber.className = "dbx-code-snapshot__line-number";
|
||||
lineNumber.setAttribute("aria-hidden", "true");
|
||||
lineNumber.textContent = String(index + 1);
|
||||
lineNumber.style.cssText = ["display:inline-block", "min-width:2.2em", "margin-right:1.4em", "text-align:right", `color:${lineNumberColor}`, "font-variant-numeric:tabular-nums", "user-select:none", "-webkit-user-select:none"].join(";");
|
||||
|
||||
const lineContent = block.ownerDocument.createElement("span");
|
||||
lineContent.className = "dbx-code-snapshot__line-content";
|
||||
lineContent.style.display = "inline";
|
||||
while (line.firstChild) {
|
||||
lineContent.append(line.firstChild);
|
||||
}
|
||||
|
||||
line.append(lineNumber, lineContent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a self-contained snapshot DOM (as an HTML string) for the given code.
|
||||
* The returned string includes its own `<style>` tag and can be injected via
|
||||
@@ -189,7 +247,7 @@ export async function renderCodeSnapshotHtml(source: CodeSnapshotSource, options
|
||||
`<style>${CODE_SNAPSHOT_CSS}</style>` +
|
||||
`<div class="dbx-code-snapshot" data-snapshot-appearance="${appearance}" style="background:${SNAPSHOT_BACKGROUND[appearance]};font-size:${fontSize}px">` +
|
||||
bar +
|
||||
`<pre class="${preClass}" style="--dbx-snapshot-line-number:${lineNumberColor};padding:0 ${padding}px ${padding}px"><code>${body}</code></pre>` +
|
||||
`<pre class="${preClass}" style="--dbx-snapshot-line-number:${lineNumberColor};color:${SNAPSHOT_CODE_TEXT[appearance]};padding:0 ${padding}px ${padding}px"><code>${body}</code></pre>` +
|
||||
`</div>`
|
||||
);
|
||||
}
|
||||
@@ -207,12 +265,28 @@ export async function snapshotElementToPng(element: HTMLElement): Promise<string
|
||||
|
||||
const targetScale = Math.min(MAX_EXPORT_PIXEL_RATIO, Math.max(1, typeof window === "undefined" ? 1 : window.devicePixelRatio || 1));
|
||||
const scale = Math.min(targetScale, MAX_EXPORT_CANVAS_EDGE / width, MAX_EXPORT_CANVAS_EDGE / height, Math.sqrt(MAX_EXPORT_CANVAS_PIXELS / basePixels));
|
||||
const useLegacyLineNumberFallback = legacyWebViewExportFallbackEnabled();
|
||||
return domtoimage.toPng(element, {
|
||||
quality: 1,
|
||||
width,
|
||||
height,
|
||||
// dom-to-image-more 使用 scale 放大实际画布;pixelRatio 不是该库支持的参数。
|
||||
scale,
|
||||
adjustPseudoElement: useLegacyLineNumberFallback
|
||||
? (node: Node, pseudoElement: ":before" | ":after") => {
|
||||
if (pseudoElement === ":before" && isSnapshotNumberedLineNode(node)) {
|
||||
return false;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
: undefined,
|
||||
onclone: useLegacyLineNumberFallback
|
||||
? (clone: Node) => {
|
||||
if (typeof (clone as ParentNode).querySelectorAll === "function") {
|
||||
materializeSnapshotCloneLineNumbers(clone as ParentNode);
|
||||
}
|
||||
}
|
||||
: undefined,
|
||||
style: {
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
|
||||
@@ -6,7 +6,11 @@ const globalsCss = readCascadeCss();
|
||||
const dialogContentSource = readFileSync(new URL("../../components/ui/dialog/DialogContent.vue", import.meta.url), "utf8");
|
||||
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 codeSnapshotDialogSource = readFileSync(new URL("../../components/codeSnapshot/CodeSnapshotDialog.vue", import.meta.url), "utf8");
|
||||
const dataTransferDialogSource = readFileSync(new URL("../../components/transfer/DataTransferDialog.vue", import.meta.url), "utf8");
|
||||
const updateDialogSource = readFileSync(new URL("../../components/layout/UpdateDialog.vue", import.meta.url), "utf8");
|
||||
const dataGridColumnHeaderSource = readFileSync(new URL("../../components/grid/DataGridColumnHeader.vue", import.meta.url), "utf8");
|
||||
const ddlViewDialogSource = readFileSync(new URL("../../components/objects/DdlViewDialog.vue", import.meta.url), "utf8");
|
||||
const connectionDialogSource = readFileSync(new URL("../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
|
||||
const connectionTreeSource = readFileSync(new URL("../../components/sidebar/ConnectionTree.vue", import.meta.url), "utf8");
|
||||
const activeConnectionFilterSource = readFileSync(new URL("../../components/sidebar/ActiveConnectionFilterButton.vue", import.meta.url), "utf8");
|
||||
@@ -86,10 +90,45 @@ describe("legacy WebView CSS fallbacks", () => {
|
||||
expect(dataTransferDialogSource).toContain('width: "min(1120px, calc(100vw - 2rem))"');
|
||||
expect(dataTransferDialogSource).toContain('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-transfer-dialog[class~="max-w-sm"]');
|
||||
expect(dataTransferDialogSource).toContain("max-width: calc(100vw - 2rem) !important;");
|
||||
expect(dataTransferDialogSource).not.toContain("@media (min-width: 640px)");
|
||||
expect(dataTransferDialogSource).not.toMatch(/^\s+width: calc\(100vw - 2rem\) !important;$/m);
|
||||
expect(globalsCss).not.toContain(".dbx-transfer-dialog");
|
||||
});
|
||||
|
||||
it("keeps the code snapshot dialog layout scoped to legacy WebViews without media queries", () => {
|
||||
expect(codeSnapshotDialogSource).toContain('class="dbx-code-snapshot-dialog flex max-h-[calc(var(--dbx-viewport-height)-2rem)] flex-col overflow-hidden border border-border !bg-background text-foreground shadow-2xl !backdrop-blur-none sm:max-w-[860px]"');
|
||||
expect(codeSnapshotDialogSource).toContain('class="dbx-code-snapshot-dialog__footer"');
|
||||
expect(codeSnapshotDialogSource).toContain('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-code-snapshot-dialog');
|
||||
expect(codeSnapshotDialogSource).toContain("width: calc(100vw - 2rem) !important;");
|
||||
expect(codeSnapshotDialogSource).toContain("flex-direction: row !important;");
|
||||
expect(codeSnapshotDialogSource).toContain("flex-wrap: nowrap !important;");
|
||||
expect(codeSnapshotDialogSource).toContain("justify-content: flex-end !important;");
|
||||
expect(codeSnapshotDialogSource).not.toContain("@media");
|
||||
});
|
||||
|
||||
it("keeps the update dialog's wide layout fallback scoped to legacy WebViews without media queries", () => {
|
||||
expect(updateDialogSource).toContain('class="dbx-update-dialog sm:max-w-[520px]"');
|
||||
expect(updateDialogSource).toContain('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-update-dialog[class~="max-w-sm"]');
|
||||
expect(updateDialogSource).toContain('dbx-update-dialog [data-slot="dialog-footer"]');
|
||||
expect(updateDialogSource).not.toContain("@media");
|
||||
});
|
||||
|
||||
it("keeps the DDL dialog's desktop layout scoped to legacy WebViews without media queries", () => {
|
||||
expect(ddlViewDialogSource).toContain('class="dbx-ddl-view-dialog sm:max-w-190"');
|
||||
expect(ddlViewDialogSource).toContain('html.dbx-legacy-webview [data-slot="dialog-content"].dbx-ddl-view-dialog[class~="max-w-sm"]');
|
||||
expect(ddlViewDialogSource).toContain("max-width: 47.5rem !important;");
|
||||
expect(ddlViewDialogSource).toContain('dbx-ddl-view-dialog [data-slot="dialog-footer"]');
|
||||
expect(ddlViewDialogSource).not.toContain("@media");
|
||||
});
|
||||
|
||||
it("uses an explicit tooltip copy-button hover color in legacy WebViews", () => {
|
||||
expect(dataGridColumnHeaderSource).toContain("data-column-header-copy-name");
|
||||
expect(dataGridColumnHeaderSource).toContain("html.dbx-legacy-webview [data-column-header-copy-name]:hover");
|
||||
expect(dataGridColumnHeaderSource).toContain("background-color: rgba(255, 255, 255, 0.1);");
|
||||
expect(dataGridColumnHeaderSource).toContain("html.dbx-legacy-webview.dark [data-column-header-copy-name]:hover");
|
||||
expect(dataGridColumnHeaderSource).toContain("background-color: rgba(0, 0, 0, 0.1);");
|
||||
});
|
||||
|
||||
it("keeps primary alpha utilities readable in legacy WebViews", () => {
|
||||
expect(globalsCss).toContain("--dbx-primary-rgb: 23, 23, 23;");
|
||||
expect(globalsCss).toContain("--dbx-primary-rgb: 46, 95, 166;");
|
||||
|
||||
Reference in New Issue
Block a user