From b6de3969ac04b54b786dcd8f660b831bb04dfb99 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 06:13:07 +0000 Subject: [PATCH] Align vertically stacked dialogue action buttons --- .../services/LiveSyncUI/DialogHost.svelte | 4 +++ test/e2e-obsidian/scripts/dialog-mounts.ts | 35 ++++++++++++++++++- updates.md | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/modules/services/LiveSyncUI/DialogHost.svelte b/src/modules/services/LiveSyncUI/DialogHost.svelte index 6265c7bf..dde0aaf7 100644 --- a/src/modules/services/LiveSyncUI/DialogHost.svelte +++ b/src/modules/services/LiveSyncUI/DialogHost.svelte @@ -76,6 +76,10 @@ margin-left: 10px; } + .dialog-host :global(.button-group > button) { + margin-left: 0; + } + .dialog-host :global(.button-group) { display: flex; flex-direction: column; diff --git a/test/e2e-obsidian/scripts/dialog-mounts.ts b/test/e2e-obsidian/scripts/dialog-mounts.ts index 60d7cf1e..79dc2dae 100644 --- a/test/e2e-obsidian/scripts/dialog-mounts.ts +++ b/test/e2e-obsidian/scripts/dialog-mounts.ts @@ -1,6 +1,6 @@ import { readFile } from "node:fs/promises"; import { join } from "node:path"; -import type { Page } from "playwright"; +import type { Locator, Page } from "playwright"; import { decodeSettingsFromSetupURI } from "@vrtmrz/livesync-commonlib/setup-uri"; import { decryptString } from "@vrtmrz/livesync-commonlib/compat/encryption/stringEncryption"; import { SETTING_KEY_P2P_DEVICE_NAME } from "@vrtmrz/livesync-commonlib/compat/common/types"; @@ -400,6 +400,37 @@ async function verifyRemoteSizeNoticeAndDialogue(): Promise<{ }; } +async function assertDialogueActionAlignment(modal: Locator, mode: DialogueMode, name: string): Promise { + const groups = await modal.locator(".dialog-host .button-group").evaluateAll((elements) => { + return elements + .filter((element) => element.getClientRects().length > 0) + .map((element) => { + const group = element.getBoundingClientRect(); + const buttons = Array.from(element.children) + .filter((child) => child instanceof HTMLButtonElement && child.getClientRects().length > 0) + .map((child) => { + const rect = child.getBoundingClientRect(); + return { left: rect.left, right: rect.right, width: rect.width }; + }); + return { group: { left: group.left, right: group.right, width: group.width }, buttons }; + }) + .filter((group) => group.buttons.length > 0); + }); + if (groups.length === 0) { + throw new Error(`Expected visible action buttons in ${name} (${mode}).`); + } + console.log(JSON.stringify({ event: "dialogue-action-alignment", name, mode, groups })); + for (const { group, buttons } of groups) { + for (const button of buttons) { + if (Math.abs(button.left - group.left) > 1 || Math.abs(button.right - group.right) > 1) { + throw new Error( + `Expected ${name} (${mode}) action buttons to align with both edges of their content column: ${JSON.stringify({ group, button })}` + ); + } + } + } +} + async function verifyRemoteSelectionDialogue(mode: DialogueMode): Promise { await openRemoteSelectionDialogue(); const screenshotPath = await captureObsidianDialogue( @@ -446,6 +477,7 @@ async function verifyRemoteSelectionDialogue(mode: DialogueMode): Promise