diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 16d371b..2e21673 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -63,7 +63,7 @@ Polling continues while a prompt is open. The poll is the server's only sign tha - A select or multi field with `allowCustomInput` gets a text field for values outside the list. For multi, the text field takes comma-separated values. - As in QuickAdd's one-page form, a single-note picker (`picker: "file"`) starts with no note picked, and the form won't submit until a required one has a pick. -Text fields are controlled. When an edit ends in a new `[[`, or types `#` at the start of a word, the field pushes `LinkPicker` or `TagPicker` (`src/completion-pickers.tsx`), which loads its items from `quickadd:suggest`. A pick replaces the trigger with `[[text]]` or `#tag `, and the field takes focus again when the picker closes. The trigger rules are pure functions in `src/lib/completion.ts`. +Text and number fields are controlled, because Raycast clears an uncontrolled text field when the form re-renders with its error. When an edit to a text field ends in a new `[[`, or types `#` at the start of a word, the field pushes `LinkPicker` or `TagPicker` (`src/completion-pickers.tsx`), which loads its items from `quickadd:suggest`. A pick replaces the trigger with `[[text]]` or `#tag `, and the field takes focus again when the picker closes. The trigger rules are pure functions in `src/lib/completion.ts`. The suggester, confirm, checkbox, and info prompts keep their own views. A suggester is a searchable list, which suits a single pick from many items. @@ -79,6 +79,6 @@ Form item ids are positional (`field-0`), not field ids, because QuickAdd field The `verify` flag that **Run in Obsidian** and the capture commands pass needs **QuickAdd >= 2.14**. Older versions ignore it, and some captures can report success without writing. -`[[` and `#` completion needs **QuickAdd with `quickadd:suggest`**, which is not released yet. Without it, the picker shows the CLI's error. +`[[` and `#` completion needs **QuickAdd >= 2.31**, which added `quickadd:suggest`. Before it, the picker says which version it needs. In the one-page form, note pickers start empty only with **QuickAdd >= 2.31**, which marks them with `picker: "file"`. Older versions send them as plain suggesters, so they keep the first note picked. diff --git a/README.md b/README.md index 5142355..449545a 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ The form renders each input from the field metadata QuickAdd sends. To get a lar ### Links and tags -Typing `[[` in a text field opens a searchable list of the vault's notes and aliases. Typing `#` at the start of a word opens the vault's tags, most used first. Picking one inserts the link or tag where you typed. This needs QuickAdd with `quickadd:suggest`. +Typing `[[` in a text field opens a searchable list of the vault's notes and aliases. Typing `#` at the start of a word opens the vault's tags, most used first. Picking one inserts the link or tag where you typed. This needs QuickAdd 2.31 or later. ## Requirements diff --git a/src/form-field.tsx b/src/form-field.tsx index e671fd2..b901a11 100644 --- a/src/form-field.tsx +++ b/src/form-field.tsx @@ -39,13 +39,7 @@ export function FieldControl({ case "text": return ; case "number": - return ( - - ); + return ; case "date": return ( ; + id: string; + title: string; + info?: string; + error?: string; + onChange: () => void; +}) { + const [value, setValue] = useState(spec.defaultValue ?? ""); + return ( + { + onChange(); + setValue(next); + }} + /> + ); +} + function TextControl({ spec, vault, diff --git a/src/lib/suggest.test.ts b/src/lib/suggest.test.ts new file mode 100644 index 0000000..bff7bf2 --- /dev/null +++ b/src/lib/suggest.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, it, vi } from "vitest"; +import { suggestTags } from "./suggest"; +import { vaultAt } from "./vaults"; + +const cli = vi.hoisted(() => ({ stdout: "" })); + +vi.mock("@raycast/api", () => ({ + getPreferenceValues: () => ({ cliPath: process.execPath }), +})); + +vi.mock("node:child_process", () => ({ + execFile: (...args: unknown[]) => { + const callback = args.at(-1) as (error: null, out: object) => void; + callback(null, { stdout: cli.stdout }); + }, +})); + +describe("suggestTags", () => { + const vault = vaultAt("/Users/me/notes"); + + it("says which QuickAdd it needs when the CLI lacks quickadd:suggest", async () => { + cli.stdout = + 'Error: Command "quickadd:suggest" not found. It may require a plugin to be enabled.\n'; + await expect(suggestTags(vault)).rejects.toThrow( + /^Link and tag completion needs QuickAdd 2\.31 or later\.$/, + ); + }); + + it("passes other CLI errors through unchanged", async () => { + cli.stdout = "Vault not found.\n"; + await expect(suggestTags(vault)).rejects.toThrow(/^Vault not found\.$/); + }); +}); diff --git a/src/lib/suggest.ts b/src/lib/suggest.ts index 180fc2b..6cff0f9 100644 --- a/src/lib/suggest.ts +++ b/src/lib/suggest.ts @@ -11,9 +11,22 @@ interface SuggestResponse { } async function suggest(vault: Vault, kind: "links" | "tags"): Promise { - const response = await invoke>(vault, "quickadd:suggest", { - kind, - }); + let response: SuggestResponse; + try { + response = await invoke>(vault, "quickadd:suggest", { + kind, + }); + } catch (error) { + if ( + error instanceof ObsidianCliError && + error.message.includes('Command "quickadd:suggest" not found') + ) { + throw new ObsidianCliError( + "Link and tag completion needs QuickAdd 2.31 or later.", + ); + } + throw error; + } if (!response.ok || !response.items) { throw new ObsidianCliError( response.error ?? "QuickAdd returned no suggestions.",