From 375b7e54a9ef1f651e98d7d23b36fcb5aee19df6 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 04:42:24 +0000 Subject: [PATCH 1/9] Align shared confirmation dialogue actions with message text --- package-lock.json | 21 ++++++++++++++++----- package.json | 2 +- updates.md | 2 ++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 717ba6f1..5de010f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@smithy/util-retry": "^4.4.5", "@vrtmrz/browser-ui-kit": "0.1.0", "@vrtmrz/livesync-commonlib": "0.2.2", - "@vrtmrz/obsidian-plugin-kit": "0.1.4", + "@vrtmrz/obsidian-plugin-kit": "0.1.5", "@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/value-store": "0.1.0", "diff-match-patch": "^1.0.5", @@ -4170,12 +4170,14 @@ } }, "node_modules/@vrtmrz/obsidian-plugin-kit": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@vrtmrz/obsidian-plugin-kit/-/obsidian-plugin-kit-0.1.4.tgz", - "integrity": "sha512-MxZgd7UOr8DXk0e+JsAXJmYj/8bfIoEliI4ruvp1Cu4U+mmVI/p63nZ5we3dDW+EaCgRAgF+ztuVDkcMUk1DwA==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@vrtmrz/obsidian-plugin-kit/-/obsidian-plugin-kit-0.1.5.tgz", + "integrity": "sha512-4AzTLmKSzMcDQErZd1NmppsaKnkA9qVjmPXxU3MQlPZlElphz+7n+tjmCxpf5S8eNTycnlpcAD2a7qjTg02+2w==", "license": "MIT", "dependencies": { - "@vrtmrz/ui-interactions": "0.1.2" + "@vrtmrz/secret-store": "0.1.0", + "@vrtmrz/ui-interactions": "0.1.2", + "@vrtmrz/value-store": "0.1.0" }, "peerDependencies": { "obsidian": ">=1.8.7" @@ -4195,6 +4197,15 @@ "playwright": ">=1.50.0" } }, + "node_modules/@vrtmrz/secret-store": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@vrtmrz/secret-store/-/secret-store-0.1.0.tgz", + "integrity": "sha512-9qkJQdt5IRxhPIIQr55bRMaKZjDpF9fLpTtIOsR0Ab90Wl52hj3wsY28EUtgCCRZ1qbXHAgDuplbg0NNMjAolQ==", + "license": "MIT", + "dependencies": { + "@vrtmrz/value-store": "0.1.0" + } + }, "node_modules/@vrtmrz/ui-interactions": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@vrtmrz/ui-interactions/-/ui-interactions-0.1.2.tgz", diff --git a/package.json b/package.json index 84e21ce5..036512e0 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "@smithy/util-retry": "^4.4.5", "@vrtmrz/browser-ui-kit": "0.1.0", "@vrtmrz/livesync-commonlib": "0.2.2", - "@vrtmrz/obsidian-plugin-kit": "0.1.4", + "@vrtmrz/obsidian-plugin-kit": "0.1.5", "@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/value-store": "0.1.0", "diff-match-patch": "^1.0.5", diff --git a/updates.md b/updates.md index 515c88f8..82be282a 100644 --- a/updates.md +++ b/updates.md @@ -14,6 +14,8 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ### Fixes +- Buttons in shared confirmation dialogues now align with the message text, with space for keyboard focus outlines. + - A new Join or scheduled Fetch asks for data-handling choices again, even after a previous Fetch changed the local database suffix. - Fast Setup retains interrupted choices in an editable Markdown code block in the fetch flag. On restart, we confirm the current remote and saved choices, choose again, or cancel; a remaining flag never reapplies choices silently. - If the saved selection cannot be read, we can choose new options for that attempt without repairing or rewriting the flag. Completing or cancelling the operation removes the whole flag. From b6264a9342379a0c5811fc1ecd09849ca03aed5c Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 04:58:07 +0000 Subject: [PATCH 2/9] Clarify confirmation dialogue release notes --- updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updates.md b/updates.md index 82be282a..2904aa14 100644 --- a/updates.md +++ b/updates.md @@ -14,7 +14,7 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi ### Fixes -- Buttons in shared confirmation dialogues now align with the message text, with space for keyboard focus outlines. +- Buttons in confirmation dialogues now align with the message text, with space for keyboard focus outlines. - A new Join or scheduled Fetch asks for data-handling choices again, even after a previous Fetch changed the local database suffix. - Fast Setup retains interrupted choices in an editable Markdown code block in the fetch flag. On restart, we confirm the current remote and saved choices, choose again, or cancel; a remaining flag never reapplies choices silently. From b6de3969ac04b54b786dcd8f660b831bb04dfb99 Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 06:13:07 +0000 Subject: [PATCH 3/9] 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 Date: Fri, 30 Oct 2026 09:41:12 +0000 Subject: [PATCH 4/9] 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 Date: Fri, 30 Oct 2026 11:39:38 +0000 Subject: [PATCH 5/9] Let the CouchDB save warning scroll with the form --- .../dialogs/SetupRemoteCouchDB.svelte | 12 +- test/e2e-obsidian/README.md | 2 +- test/e2e-obsidian/scripts/dialog-mounts.ts | 109 +++++++++++++++++- updates.md | 2 + 4 files changed, 118 insertions(+), 7 deletions(-) diff --git a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte index a163ceee..2c640ee0 100644 --- a/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte +++ b/src/modules/features/SetupWizard/dialogs/SetupRemoteCouchDB.svelte @@ -314,14 +314,16 @@ {#if processing} {translateMessage("Checking connection... Please wait.")} {:else} + {#if setupMode === "settings"} + + {translateMessage( + "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected." + )} + + {/if} checkAndCommit()} /> {#if setupMode === "settings"} - - {translateMessage( - "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected." - )} - { + const originalViewport = await page.evaluate(() => ({ width: innerWidth, height: innerHeight })); + const properties = ["--keyboard-height", "--safe-area-inset-top", "--safe-area-inset-bottom"]; + const original = await page.evaluate( + (names) => + names.map((name) => ({ + name, + value: document.body.style.getPropertyValue(name), + priority: document.body.style.getPropertyPriority(name), + })), + properties + ); + const content = modal.locator(".modal-content"); + const originalScroll = await content.evaluate((element) => element.scrollTop); + const keyboardHeight = Math.round(mobileViewport.height * 0.42); + try { + await page.setViewportSize(mobileViewport); + await page.evaluate( + ({ keyboardHeight, top }) => { + document.body.style.setProperty("--keyboard-height", `${keyboardHeight}px`); + document.body.style.setProperty("--safe-area-inset-bottom", `${keyboardHeight}px`); + document.body.style.setProperty("--safe-area-inset-top", `${top}px`); + }, + { keyboardHeight, top: iPhoneSafeArea.top } + ); + await modal.locator('input[name="couchdb-username"]').focus(); + await page.waitForTimeout(350); + const warning = modal.locator(".sls-info-note").filter({ + hasText: + "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected.", + }); + if ((await warning.count()) !== 1) throw new Error("Expected the untested-save warning to remain available."); + const pinned = await warning.evaluate((element) => { + for (let parent = element.parentElement; parent !== null; parent = parent.parentElement) { + if (parent.classList.contains("modal-content")) break; + const position = getComputedStyle(parent).position; + if (position === "sticky" || position === "fixed") return true; + } + return false; + }); + if (pinned) throw new Error("The untested-save warning still occupies the fixed action area."); + const bounds = await modal.evaluate((element) => { + const host = element.querySelector(".dialog-host"); + const content = element.querySelector(".modal-content"); + const footerElement = host?.querySelector(":scope > .button-group"); + const rowElement = host?.querySelector(".row"); + if (!host || !content || !footerElement || !rowElement) + throw new Error("CouchDB form layout is unavailable."); + const footer = footerElement.getBoundingClientRect(); + const row = rowElement.getBoundingClientRect(); + const modalElement = element.querySelector(".modal"); + if (!modalElement) throw new Error("The CouchDB modal is unavailable."); + const modalRect = modalElement.getBoundingClientRect(); + const contentRect = content.getBoundingClientRect(); + const headerBottom = + element.querySelector(".modal-header")?.getBoundingClientRect().bottom ?? modalRect.top; + const formTop = Math.max(contentRect.top, modalRect.top, headerBottom); + return { + width: innerWidth, + height: innerHeight, + formTop, + footerTop: footer.top, + formHeight: Math.min(footer.top, contentRect.bottom, modalRect.bottom) - formTop, + rowHeight: row.height + (parseFloat(getComputedStyle(host).rowGap) || 0), + footerBottom: footer.bottom, + }; + }); + console.log(JSON.stringify({ event: "couchdb-keyboard-form-space", keyboardHeight, bounds })); + if ( + bounds.width !== mobileViewport.width || + bounds.height !== mobileViewport.height || + bounds.formHeight < bounds.rowHeight * 2 || + bounds.footerBottom > bounds.height - keyboardHeight + 1 + ) { + throw new Error( + `Expected room for at least two input rows above keyboard actions: ${JSON.stringify(bounds)}` + ); + } + await page.screenshot({ + path: join( + process.env.E2E_OBSIDIAN_DIAGNOSTICS_DIR ?? "/tmp/obsidian-livesync-e2e", + "setup-couchdb-keyboard-form.png" + ), + animations: "disabled", + style: ".notice-container { visibility: hidden !important; }", + }); + } finally { + await page.evaluate((entries) => { + for (const { name, value, priority } of entries) { + if (value === "") document.body.style.removeProperty(name); + else document.body.style.setProperty(name, value, priority); + } + }, original); + await page.setViewportSize(originalViewport); + await content.evaluate((element, top) => { + element.scrollTop = top; + }, originalScroll); + } +} + async function verifyRemoteSelectionDialogue(mode: DialogueMode): Promise { await openRemoteSelectionDialogue(); const screenshotPath = await captureObsidianDialogue( @@ -603,6 +709,7 @@ async function verifyCouchDBSettingsDialogue(mode: DialogueMode): Promise Date: Fri, 30 Oct 2026 11:42:50 +0000 Subject: [PATCH 6/9] Measure the visible CouchDB action bounds --- test/e2e-obsidian/scripts/dialog-mounts.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/e2e-obsidian/scripts/dialog-mounts.ts b/test/e2e-obsidian/scripts/dialog-mounts.ts index 3beb9d8b..6c2f5021 100644 --- a/test/e2e-obsidian/scripts/dialog-mounts.ts +++ b/test/e2e-obsidian/scripts/dialog-mounts.ts @@ -528,6 +528,11 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi throw new Error("CouchDB form layout is unavailable."); const footer = footerElement.getBoundingClientRect(); const row = rowElement.getBoundingClientRect(); + const actions = Array.from(footerElement.querySelectorAll("button"), (button) => + button.getBoundingClientRect() + ); + const footerTop = Math.min(footer.top, ...actions.map((action) => action.top)); + const footerBottom = Math.max(footer.bottom, ...actions.map((action) => action.bottom)); const modalElement = element.querySelector(".modal"); if (!modalElement) throw new Error("The CouchDB modal is unavailable."); const modalRect = modalElement.getBoundingClientRect(); @@ -539,10 +544,10 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi width: innerWidth, height: innerHeight, formTop, - footerTop: footer.top, - formHeight: Math.min(footer.top, contentRect.bottom, modalRect.bottom) - formTop, + footerTop, + formHeight: Math.min(footerTop, contentRect.bottom, modalRect.bottom) - formTop, rowHeight: row.height + (parseFloat(getComputedStyle(host).rowGap) || 0), - footerBottom: footer.bottom, + footerBottom, }; }); console.log(JSON.stringify({ event: "couchdb-keyboard-form-space", keyboardHeight, bounds })); From 6e38528a2ffbd7eb7354a0a77e497d8d0436d2bc Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 11:46:00 +0000 Subject: [PATCH 7/9] Settle mobile animations before measuring the form --- test/e2e-obsidian/scripts/dialog-mounts.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/e2e-obsidian/scripts/dialog-mounts.ts b/test/e2e-obsidian/scripts/dialog-mounts.ts index 6c2f5021..6839c2ca 100644 --- a/test/e2e-obsidian/scripts/dialog-mounts.ts +++ b/test/e2e-obsidian/scripts/dialog-mounts.ts @@ -505,6 +505,14 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi ); await modal.locator('input[name="couchdb-username"]').focus(); await page.waitForTimeout(350); + await page.screenshot({ + path: join( + process.env.E2E_OBSIDIAN_DIAGNOSTICS_DIR ?? "/tmp/obsidian-livesync-e2e", + "setup-couchdb-keyboard-form.png" + ), + animations: "disabled", + style: ".notice-container { visibility: hidden !important; }", + }); const warning = modal.locator(".sls-info-note").filter({ hasText: "Saving without a successful connection test keeps this profile, but automatic synchronisation may fail until the connection is corrected.", @@ -548,6 +556,7 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi formHeight: Math.min(footerTop, contentRect.bottom, modalRect.bottom) - formTop, rowHeight: row.height + (parseFloat(getComputedStyle(host).rowGap) || 0), footerBottom, + actionBounds: actions.map((action) => ({ top: action.top, bottom: action.bottom })), }; }); console.log(JSON.stringify({ event: "couchdb-keyboard-form-space", keyboardHeight, bounds })); @@ -561,14 +570,6 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi `Expected room for at least two input rows above keyboard actions: ${JSON.stringify(bounds)}` ); } - await page.screenshot({ - path: join( - process.env.E2E_OBSIDIAN_DIAGNOSTICS_DIR ?? "/tmp/obsidian-livesync-e2e", - "setup-couchdb-keyboard-form.png" - ), - animations: "disabled", - style: ".notice-container { visibility: hidden !important; }", - }); } finally { await page.evaluate((entries) => { for (const { name, value, priority } of entries) { From b11f6501244bde8e80b0c1f743f76de21b6c45ef Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 11:48:19 +0000 Subject: [PATCH 8/9] Select the CouchDB save action group for layout checks --- test/e2e-obsidian/scripts/dialog-mounts.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e-obsidian/scripts/dialog-mounts.ts b/test/e2e-obsidian/scripts/dialog-mounts.ts index 6839c2ca..d65fe153 100644 --- a/test/e2e-obsidian/scripts/dialog-mounts.ts +++ b/test/e2e-obsidian/scripts/dialog-mounts.ts @@ -530,7 +530,8 @@ async function assertCouchDBKeyboardFormSpace(page: Page, modal: Locator): Promi const bounds = await modal.evaluate((element) => { const host = element.querySelector(".dialog-host"); const content = element.querySelector(".modal-content"); - const footerElement = host?.querySelector(":scope > .button-group"); + const actionGroups = host?.querySelectorAll(":scope > .button-group"); + const footerElement = actionGroups?.item(actionGroups.length - 1); const rowElement = host?.querySelector(".row"); if (!host || !content || !footerElement || !rowElement) throw new Error("CouchDB form layout is unavailable."); From 71e6906bc876f6fa11cd755913d2126dcebd721e Mon Sep 17 00:00:00 2001 From: vorotamoroz Date: Fri, 30 Oct 2026 13:04:14 +0000 Subject: [PATCH 9/9] Adopt plug-in kit 0.1.6 for mobile dialogue layouts --- package-lock.json | 8 ++++---- package.json | 2 +- updates.md | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5de010f8..b92503c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@smithy/util-retry": "^4.4.5", "@vrtmrz/browser-ui-kit": "0.1.0", "@vrtmrz/livesync-commonlib": "0.2.2", - "@vrtmrz/obsidian-plugin-kit": "0.1.5", + "@vrtmrz/obsidian-plugin-kit": "0.1.6", "@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/value-store": "0.1.0", "diff-match-patch": "^1.0.5", @@ -4170,9 +4170,9 @@ } }, "node_modules/@vrtmrz/obsidian-plugin-kit": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@vrtmrz/obsidian-plugin-kit/-/obsidian-plugin-kit-0.1.5.tgz", - "integrity": "sha512-4AzTLmKSzMcDQErZd1NmppsaKnkA9qVjmPXxU3MQlPZlElphz+7n+tjmCxpf5S8eNTycnlpcAD2a7qjTg02+2w==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@vrtmrz/obsidian-plugin-kit/-/obsidian-plugin-kit-0.1.6.tgz", + "integrity": "sha512-88HhpN3G53qIQxvsTpe7VE6/unW3YtcZjmqko3Q58yRl/Y+iJDruT1HuEQYEgtkWAB6F2T93z2fhqhvRBXDDWQ==", "license": "MIT", "dependencies": { "@vrtmrz/secret-store": "0.1.0", diff --git a/package.json b/package.json index 036512e0..8b8368cd 100644 --- a/package.json +++ b/package.json @@ -205,7 +205,7 @@ "@smithy/util-retry": "^4.4.5", "@vrtmrz/browser-ui-kit": "0.1.0", "@vrtmrz/livesync-commonlib": "0.2.2", - "@vrtmrz/obsidian-plugin-kit": "0.1.5", + "@vrtmrz/obsidian-plugin-kit": "0.1.6", "@vrtmrz/ui-interactions": "0.1.2", "@vrtmrz/value-store": "0.1.0", "diff-match-patch": "^1.0.5", diff --git a/updates.md b/updates.md index d249bd3a..3f5f82ba 100644 --- a/updates.md +++ b/updates.md @@ -16,6 +16,8 @@ Earlier releases remain available in the 1.0 release history, the 1.0 preview hi - Buttons in confirmation and setup dialogues now align with the content above them. Confirmation dialogue buttons also have space for keyboard focus outlines. +- Mobile confirmation dialogues now retain Obsidian's standard content spacing. Text and password prompts stay above the software keyboard. + - Setup dialogues no longer add a large blank area below their buttons when the software keyboard is open. - CouchDB connection fields have more room above the software keyboard. The warning about saving without a connection test now scrolls with the form.