From b205b14c5fe8762e83deabb16c2a3baff57667c6 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 09:41:12 +0000 Subject: [PATCH] Avoid keyboard-height padding inside setup dialogues --- .../services/LiveSyncUI/DialogHost.svelte | 2 +- test/e2e-obsidian/README.md | 2 + test/e2e-obsidian/scripts/dialog-mounts.ts | 42 +++++++++++++++++++ updates.md | 2 + 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/modules/services/LiveSyncUI/DialogHost.svelte b/src/modules/services/LiveSyncUI/DialogHost.svelte index dde0aaf7..79862b17 100644 --- a/src/modules/services/LiveSyncUI/DialogHost.svelte +++ b/src/modules/services/LiveSyncUI/DialogHost.svelte @@ -67,7 +67,7 @@ gap: 0.5em; display: flex; flex-direction: column; - padding-bottom: var(--keyboard-height, 0px); + padding-bottom: 0; user-select: text; -webkit-user-select: text; } diff --git a/test/e2e-obsidian/README.md b/test/e2e-obsidian/README.md index f47ad6e6..8420fab2 100644 --- a/test/e2e-obsidian/README.md +++ b/test/e2e-obsidian/README.md @@ -50,6 +50,8 @@ Set `showVerboseLog: true` only in isolated plug-in data when a focused investig Collect evidence before cleanup, and keep process, Vault, profile, and remote-fixture cleanup in `finally`. After `app.emulateMobile(true)`, use the active CDP renderer for fixture operations because Obsidian may remove desktop-only CLI commands. Visually inspect screenshots before copying selected images into user documentation; a passing locator assertion does not establish that a dialogue is readable or unobstructed. +The dialogue scenario simulates the host keyboard-height variable while the mobile CouchDB setup dialogue is open. It verifies that the space after the final action does not grow with the keyboard height and restores the original variable and scroll position afterwards. This layout fixture does not open a physical software keyboard. + ## Local Setup Set `OBSIDIAN_BINARY` when Obsidian is not installed in a standard location. Set `OBSIDIAN_CLI` as well when its companion executable is outside the built-in discovery paths. diff --git a/test/e2e-obsidian/scripts/dialog-mounts.ts b/test/e2e-obsidian/scripts/dialog-mounts.ts index 79dc2dae..fd8ba265 100644 --- a/test/e2e-obsidian/scripts/dialog-mounts.ts +++ b/test/e2e-obsidian/scripts/dialog-mounts.ts @@ -431,6 +431,47 @@ async function assertDialogueActionAlignment(modal: Locator, mode: DialogueMode, } } +async function assertNoExtraKeyboardSpace(page: Page, modal: Locator): Promise { + const original = await page.evaluate(() => ({ + value: document.body.style.getPropertyValue("--keyboard-height"), + priority: document.body.style.getPropertyPriority("--keyboard-height"), + })); + const originalScroll = await modal.locator(".modal-content").evaluate((element) => element.scrollTop); + const keyboardHeight = await page.evaluate(() => Math.round(innerHeight * 0.4)); + let baseline: number | undefined; + try { + for (const height of [0, keyboardHeight, 0]) { + await page.evaluate((value) => document.body.style.setProperty("--keyboard-height", `${value}px`), height); + await modal.locator(".modal-content").evaluate((element) => { + element.scrollTop = element.scrollHeight; + }); + const gap = await modal.locator(".dialog-host").evaluate((host) => { + const buttons = Array.from(host.querySelectorAll("button")).filter( + (button) => button.getClientRects().length > 0 + ); + const last = buttons[buttons.length - 1]; + if (last === undefined) throw new Error("The dialogue has no action button."); + return host.getBoundingClientRect().bottom - last.getBoundingClientRect().bottom; + }); + console.log(JSON.stringify({ event: "dialogue-keyboard-spacing", height, gap, baseline })); + baseline ??= gap; + if (Math.abs(gap - baseline) > 1) { + throw new Error( + `Expected the keyboard not to add blank content after the final action: ${JSON.stringify({ height, gap, baseline })}` + ); + } + } + } finally { + await page.evaluate(({ value, priority }) => { + if (value === "") document.body.style.removeProperty("--keyboard-height"); + else document.body.style.setProperty("--keyboard-height", value, priority); + }, original); + await modal.locator(".modal-content").evaluate((element, top) => { + element.scrollTop = top; + }, originalScroll); + } +} + async function verifyRemoteSelectionDialogue(mode: DialogueMode): Promise { await openRemoteSelectionDialogue(); const screenshotPath = await captureObsidianDialogue( @@ -561,6 +602,7 @@ async function verifyCouchDBSettingsDialogue(mode: DialogueMode): Promise