From 77226216a1b9851f25d49c5aa522e796411a4edb Mon Sep 17 00:00:00 2001 From: Christian Bager Bach Houmann Date: Sat, 26 Sep 2026 11:18:28 +0200 Subject: [PATCH 1/2] feat(gui): add stable CSS classes to QuickAdd's pickers Core styles every picker as .prompt, so a CSS snippet couldn't tell QuickAdd's apart from the command palette. The launcher and Multi picker now carry qa-choice-suggester, and pick lists and api.suggester carry qa-suggester. Documents both with a snippet that centers them. Refs #1796 --- .../content/docs/docs/ControllingPrompts.md | 19 +++++++++++ src/gui/GenericSuggester/SuggesterModal.ts | 9 +++++ src/gui/suggesters/choiceSuggester.ts | 6 ++++ tests/e2e/suggester-classes.test.ts | 33 +++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 tests/e2e/suggester-classes.test.ts diff --git a/docs/src/content/docs/docs/ControllingPrompts.md b/docs/src/content/docs/docs/ControllingPrompts.md index 0e345e9f1..69a38625a 100644 --- a/docs/src/content/docs/docs/ControllingPrompts.md +++ b/docs/src/content/docs/docs/ControllingPrompts.md @@ -115,6 +115,25 @@ Rather than answering prompts one at a time, QuickAdd can collect everything in The full behavior - what is collected, date parsing, defaults, and script-declared inputs - is documented in [One-page Inputs](/docs/Advanced/onePageInputs/). +## Style pickers with CSS {#style-pickers-with-css} + +QuickAdd's pickers are standard Obsidian pickers, so they open near the top of the window like the command palette, while text prompts open in the middle. To change that, target these classes from a [CSS snippet](https://obsidian.md/help/snippets): + +| Class | Picker | +| --- | --- | +| `qa-choice-suggester` | The QuickAdd launcher and Multi choices | +| `qa-suggester` | Pick lists such as `{{VALUE:a,b,c}}`, and `quickAddApi.suggester` in scripts | + +For example, this centers QuickAdd's pickers on desktop and leaves the command palette and other plugins alone: + +```css +body:not(.is-mobile) .prompt:is(.qa-choice-suggester, .qa-suggester) { + top: auto; +} +``` + +A centered picker re-centers as you type, so the search box moves a little when the list gets shorter. + ## For script authors {#for-script-authors} User scripts can declare their inputs so they appear in the one-page form (`quickadd.inputs`), and can open a one-page form of their own at runtime with [`quickAddApi.requestInputs`](/docs/QuickAddAPI/). Both are covered in [One-page Inputs](/docs/Advanced/onePageInputs/#user-scripts-declare-inputs-optional). diff --git a/src/gui/GenericSuggester/SuggesterModal.ts b/src/gui/GenericSuggester/SuggesterModal.ts index 633b0b201..0159787d3 100644 --- a/src/gui/GenericSuggester/SuggesterModal.ts +++ b/src/gui/GenericSuggester/SuggesterModal.ts @@ -83,6 +83,15 @@ export class SuggesterModal extends FuzzySuggestModal { } } + /** + * `qa-suggester` is a stable hook for user CSS snippets; core styles every + * picker as `.prompt`, so without it a snippet can't tell QuickAdd's apart. + */ + onOpen(): void { + super.onOpen(); + this.modalEl.addClass("qa-suggester"); + } + getItemText(item: T): string { const index = this.items.indexOf(item); const displayItem = index >= 0 ? this.displayItems[index] : undefined; diff --git a/src/gui/suggesters/choiceSuggester.ts b/src/gui/suggesters/choiceSuggester.ts index f247af85f..83f5079ac 100644 --- a/src/gui/suggesters/choiceSuggester.ts +++ b/src/gui/suggesters/choiceSuggester.ts @@ -288,6 +288,12 @@ export default class ChoiceSuggester extends FuzzySuggestModal { } } + /** `qa-choice-suggester` is a stable hook for user CSS snippets (see SuggesterModal). */ + onOpen(): void { + super.onOpen(); + this.modalEl.addClass("qa-choice-suggester"); + } + onClose(): void { super.onClose(); this.markdownComponent.unload(); diff --git a/tests/e2e/suggester-classes.test.ts b/tests/e2e/suggester-classes.test.ts new file mode 100644 index 000000000..84b98a229 --- /dev/null +++ b/tests/e2e/suggester-classes.test.ts @@ -0,0 +1,33 @@ +import { it } from "vitest"; +import { MultiChoice } from "../../src/types/choices/MultiChoice"; +import { TemplateChoice } from "../../src/types/choices/TemplateChoice"; +import type IChoice from "../../src/types/choices/IChoice"; +import { createQuickAddE2EHarness } from "./e2eVault"; +import { pressKey, waitForElement } from "./uiHelpers"; + +const getContext = createQuickAddE2EHarness("suggester-classes"); + +// Users target these classes from CSS snippets (docs: ControllingPrompts +// "Style pickers with CSS"), so renaming one breaks their themes. +it("marks QuickAdd's pickers with the documented CSS classes", async () => { + const { obsidian, plugin } = getContext(); + const folder = new MultiChoice("Styled folder").addChoice(new TemplateChoice("Styled template")); + await plugin.data<{ choices: IChoice[] }>().patch((data) => { + data.choices = [folder]; + }); + await plugin.reload({ waitUntilReady: true }); + + await obsidian.dev.evalJson(`(() => { + void app.plugins.plugins.quickadd.api.executeChoice(${JSON.stringify(folder.name)}).catch(() => {}); + return true; + })()`); + await waitForElement(obsidian, ".prompt.qa-choice-suggester .suggestion-item"); + await pressKey(obsidian, "Escape"); + + await obsidian.dev.evalJson(`(() => { + void app.plugins.plugins.quickadd.api.suggester(["low", "high"], ["low", "high"]).catch(() => {}); + return true; + })()`); + await waitForElement(obsidian, ".prompt.qa-suggester .suggestion-item"); + await pressKey(obsidian, "Escape"); +}); From 7721a13edea8342d60abffdcaa38794c4599f58e Mon Sep 17 00:00:00 2001 From: Christian Bager Bach Houmann Date: Sat, 26 Sep 2026 11:28:30 +0200 Subject: [PATCH 2/2] feat(gui): add qa-suggester to the template note discovery picker (#1802) --- .../src/content/docs/docs/ControllingPrompts.md | 2 +- src/gui/TemplateNoteDiscoveryModal.ts | 6 ++++++ tests/e2e/suggester-classes.test.ts | 17 ++++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/src/content/docs/docs/ControllingPrompts.md b/docs/src/content/docs/docs/ControllingPrompts.md index 69a38625a..9bec632e6 100644 --- a/docs/src/content/docs/docs/ControllingPrompts.md +++ b/docs/src/content/docs/docs/ControllingPrompts.md @@ -122,7 +122,7 @@ QuickAdd's pickers are standard Obsidian pickers, so they open near the top of t | Class | Picker | | --- | --- | | `qa-choice-suggester` | The QuickAdd launcher and Multi choices | -| `qa-suggester` | Pick lists such as `{{VALUE:a,b,c}}`, and `quickAddApi.suggester` in scripts | +| `qa-suggester` | Single-value pick lists such as `{{VALUE:a,b,c}}`, the note picker for Template choices that look for existing notes, and `quickAddApi.suggester` in scripts | For example, this centers QuickAdd's pickers on desktop and leaves the command palette and other plugins alone: diff --git a/src/gui/TemplateNoteDiscoveryModal.ts b/src/gui/TemplateNoteDiscoveryModal.ts index 9d9a6add5..a72577854 100644 --- a/src/gui/TemplateNoteDiscoveryModal.ts +++ b/src/gui/TemplateNoteDiscoveryModal.ts @@ -37,6 +37,12 @@ export class TemplateNoteDiscoveryModal extends FuzzySuggestModal this.open(); } + /** `qa-suggester` is a stable hook for user CSS snippets (see SuggesterModal). */ + onOpen(): void { + super.onOpen(); + this.modalEl.addClass("qa-suggester"); + } + getItems(): DiscoveryRow[] { return this.rows; } getItemText(row: DiscoveryRow): string { diff --git a/tests/e2e/suggester-classes.test.ts b/tests/e2e/suggester-classes.test.ts index 84b98a229..ee553904f 100644 --- a/tests/e2e/suggester-classes.test.ts +++ b/tests/e2e/suggester-classes.test.ts @@ -2,7 +2,7 @@ import { it } from "vitest"; import { MultiChoice } from "../../src/types/choices/MultiChoice"; import { TemplateChoice } from "../../src/types/choices/TemplateChoice"; import type IChoice from "../../src/types/choices/IChoice"; -import { createQuickAddE2EHarness } from "./e2eVault"; +import { createQuickAddE2EHarness, seedVaultFile } from "./e2eVault"; import { pressKey, waitForElement } from "./uiHelpers"; const getContext = createQuickAddE2EHarness("suggester-classes"); @@ -10,10 +10,14 @@ const getContext = createQuickAddE2EHarness("suggester-classes"); // Users target these classes from CSS snippets (docs: ControllingPrompts // "Style pickers with CSS"), so renaming one breaks their themes. it("marks QuickAdd's pickers with the documented CSS classes", async () => { - const { obsidian, plugin } = getContext(); + const { obsidian, plugin, sandbox } = getContext(); const folder = new MultiChoice("Styled folder").addChoice(new TemplateChoice("Styled template")); + const discovery = new TemplateChoice("Styled discovery"); + discovery.discoverExistingNotesBeforeCreate = true; + discovery.templatePath = await seedVaultFile(obsidian, sandbox, "Styled discovery template.md"); + discovery.fileNameFormat = { enabled: true, format: "{{VALUE}}" }; await plugin.data<{ choices: IChoice[] }>().patch((data) => { - data.choices = [folder]; + data.choices = [folder, discovery]; }); await plugin.reload({ waitUntilReady: true }); @@ -30,4 +34,11 @@ it("marks QuickAdd's pickers with the documented CSS classes", async () => { })()`); await waitForElement(obsidian, ".prompt.qa-suggester .suggestion-item"); await pressKey(obsidian, "Escape"); + + await obsidian.dev.evalJson(`(() => { + void app.plugins.plugins.quickadd.api.executeChoice(${JSON.stringify(discovery.name)}).catch(() => {}); + return true; + })()`); + await waitForElement(obsidian, '.prompt.qa-suggester input[placeholder^="Search notes or create"]'); + await pressKey(obsidian, "Escape"); });