Skip to content

feat(capture): run a QuickAdd choice for captures, picked or created in QuickAdd - #511

Open
chhoumann wants to merge 12 commits into
masterfrom
feat/quickadd-capture
Open

chhoumann wants to merge 12 commits into
masterfrom
feat/quickadd-capture

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Why

Captures can run a QuickAdd choice instead of writing the timestamp themselves, so the choice decides where each timestamp goes. Typing the choice's name was error-prone, broke on a rename, and left a new user to build the choice by hand. Now the user picks the choice in QuickAdd's own picker, or creates a ready-made one in one click. PodNotes stores the choice's id.

What changed

  • Settings → PodNotes → Notes → QuickAdd choice has buttons instead of a text field. Choose... opens QuickAdd's choice picker and Create adds QuickAdd's Podcast capture choice. Once set, the button shows the choice's name in QuickAdd, and x clears the setting.
  • A long choice name stays on one line inside its button and ends in an ellipsis, so the row's name and x keep their place on a phone and in a narrow window. Choose... keeps its label while QuickAdd's picker is open.
  • Every capture runs the choice by its id. When QuickAdd can't run it, the capture writes nothing and one notice names the fix.
  • src/quickAdd.ts is the one place that knows QuickAdd's API and its state: not installed, disabled, too old, or ready.
State Row A capture
Nothing set Choose... Create Inserts the timestamp, as before
Set the choice's name in QuickAdd, x Runs the choice
Choice deleted in QuickAdd Missing choice, x "The QuickAdd choice no longer exists. Choose another in PodNotes settings."
QuickAdd not installed Install QuickAdd (and x when set) "Install QuickAdd to capture with a QuickAdd choice"
QuickAdd disabled Enable QuickAdd (and x when set) "Enable QuickAdd to capture with a QuickAdd choice"
QuickAdd before choice picking Update QuickAdd (and x when set) "Update QuickAdd to capture with a QuickAdd choice"

Install, Enable and Update open Community plugins. The row has no helper text in any state.

Create hands QuickAdd a one-choice package through importPackage. The choice is a Capture named Podcast capture with the fixed id podnotes-capture. It captures to {{VALUE:episodeNote}}, creates the note when it is missing, and inserts after ## Notes at the end of the section, creating the heading at the bottom. Its format is {{VALUE:timestamp}} alone, because the timestamp template already starts with - . QuickAdd skips an id it already has, so a second Create selects the existing choice and overwrites nothing. If the import fails, a notice says "Could not create the QuickAdd choice".

Scope

  • Needs the QuickAdd release that adds choice picking (getChoice, pickChoice, executeChoice({ id }), importPackage). That API is feat(api): pick, look up, run by id and install choices from other plugins quickadd#2247. This PR merges only after that release is public. Its e2e then pins the release instead of the local build it reads from OBSIDIAN_E2E_QUICKADD_DIR.
  • Keeps the variables, private-feed redaction, and error containment from this PR's first commits.
  • PodNotes reads none of QuickAdd's settings and builds no picker of its own.

Tradeoffs

  • Id only, no stored name. The row asks QuickAdd for the name, so a rename shows at once. While QuickAdd is missing, the row shows the fix instead of a name.
  • Create imports with no review screen. The package is a fixed, code-free Capture from trusted plugin code, so a JSON review would only get in the way.
  • The ready-made choice creates a missing note without the episode note template, because QuickAdd creates it. Users who want the template create the note first with Create episode note.

Blast Radius

The QuickAdd row, and captures with a QuickAdd choice set. timestamp.quickAddChoice was never released, so it becomes timestamp.quickAddChoiceId with no migration. A 2.22.0 device keeps the id when it saves, because its codec copies timestamp fields it does not know. Captures with no choice set are unchanged. Bundle: 102,534 bytes gzip, +737 against 101,797 at 43b1aa6, 466 left of 103,000.

Verification

Node v22.23.2, Linux, at e4ccd04:

  • npm run lint: 0 errors, "Svelte policies: 0 errors, 0 warnings."
  • npm run format:check: "All matched files use the correct format."
  • npm run typecheck and npm run build: exit 0
  • npm run test: svelte-check clean, 127 files, 2,813 tests passed
  • npm run test:tooling: 41 tests passed
  • npm run docs:build: Complete
  • npm run size: 102,534 bytes gzip, budget 103,000
  • npm run test:e2e -- tests/e2e/quickadd-capture.test.ts tests/e2e/settings-tab.test.ts on a fresh vault, Obsidian 1.14.4, with OBSIDIAN_E2E_QUICKADD_DIR set to the QuickAdd build from feat(api): pick, look up, run by id and install choices from other plugins quickadd#2247 at 4804745: 2 files, 18 tests passed.
  • tests/e2e/quickadd-capture.test.ts ten more times in a row: 70 of 70 passed.

The full e2e suite last ran at 971f16f:

  • npm run lint: 0 errors, "Svelte policies: 0 errors, 0 warnings."
  • npm run format:check: "All matched files use the correct format."
  • npm run typecheck and npm run build: exit 0
  • npm run test: svelte-check clean, 127 files, 2,813 tests passed
  • npm run test:tooling: 41 tests passed
  • npm run docs:build: Complete
  • npm run test:e2e on a fresh vault, Obsidian 1.14.4, with OBSIDIAN_E2E_QUICKADD_DIR set to the QuickAdd build from feat(api): pick, look up, run by id and install choices from other plugins quickadd#2247 at 4804745 (digests checked against its build.json): 33 files, 249 tests passed.

tests/e2e/quickadd-capture.test.ts drives real QuickAdd in real Obsidian:

  • Choose... opens QuickAdd's picker. Typing Log and Enter stores the choice's id, and the row shows its name.
  • Create adds Podcast capture and selects it. With the default timestamp template, the first capture creates the note as ## Notes and one single-bullet line. The second adds its own line at the end of the section.
  • After a rename in QuickAdd, the capture still lands and the row shows the new name. After a delete, the row says Missing choice, and a capture shows the notice and writes nothing.
  • The real QuickAdd 2.32.1 release shows Update QuickAdd. Without QuickAdd the row says Install QuickAdd, and installed but disabled it says Enable QuickAdd. None of the three captures writes anything.
  • The command, the player's button and the headset all run the choice, as before.
  • With the choice renamed to "Podcast research notes for my weekly engineering and product review", the row is checked in a 900 px window (name beside the controls), a 720 px window (stacked) and on a phone: the row doesn't overflow, its name isn't squeezed, the choice's name is cut inside its button and starts inside it, and x sits inside the row.
  • While QuickAdd's picker is open, Choose... behind it shows its label, and a click at its center lands on the picker, not the button.

A flaky test, fixed

The 2.32.1 case timed out on the PodNotes settings tab about one run in ten (1 of 10 here, and once for the verifier). Obsidian closes Settings when it focuses a note (workspace.setActiveLeaf calls focusLeaf, which closes it). PodNotes opens the new episode note after creating it, and noteAt returned once the file existed, so the note's focus sometimes landed just after the test opened Settings. noteAt now waits until the note has focus, the same synchronous step that closes Settings. With PodNotes' note open delayed by 100 or 200 ms, the old wait fails every time with the same timeout and the new one passes the whole file.

Mutations

Each wrong alternative was applied, the suites run, and the change reverted.

Mutant Failing test
The capture runs the choice by its name, executeChoice(choice.name) commands.test.ts "runs it by its id once with the capture, and writes nothing itself", the segment case, and the headset case
No too-old check: the capture calls executeChoice on any loaded QuickAdd, 2.32.1 included commands.test.ts "says Update QuickAdd, and writes nothing, until QuickAdd can run the choice"; e2e "QuickAdd 2.32.1, from before choice picking, asks for an update and runs nothing" (no update notice)
Create's format - {{VALUE:timestamp}} e2e "Create adds QuickAdd's ready-made choice, whose captures create the note and add one bullet each" (- - [00:00:30](...))
No layout fix e2e "a long choice name stays on one line in its button..." at 900 px (row name squeezed, name not cut); with the window steps skipped, on the phone (row overflows, x outside the row, name starts left of the button)
Ellipsis styles on the button, but the control keeps its automatic min-width same test, 900 px: row name squeezed
Button stays a flex box with overflow: hidden and text-overflow: ellipsis, so the name is clipped at both ends with no ellipsis same test: name starts left of the button
overflow: hidden on the row's control instead of the button same test: the name isn't cut inside its button
Choose...'s click handler returns the picker's promise e2e "Choose... picks a choice in QuickAdd's own picker": the label behind the picker is transparent (Obsidian's spinner)

Docs at release

docs/src/content/docs/QuickAdd.md: add this section before "Capture a timestamp with a comment":

## Capture with QuickAdd

Set a QuickAdd choice in PodNotes, and each capture runs it instead of inserting the timestamp at your cursor. The choice decides where the timestamp goes. Every capture runs it: **Capture Timestamp**, **Capture Last 10 Seconds**, **Capture Last 20 Seconds**, the full player's **Timestamp** button, and the headset or lock-screen capture. You need the QuickAdd release that adds choice picking.

To add each timestamp to the end of a `## Notes` section in the episode's note:

1. In **Settings → PodNotes**, under **Notes**, select **Create** next to **QuickAdd choice**.
2. Play an episode, and capture a timestamp.

**Create** adds a Capture choice named **Podcast capture** to QuickAdd, and you can change it there like any other choice. When the episode's note doesn't exist yet, the first capture creates it with only the `## Notes` heading and the timestamp. To start from your episode note template, create the note with **PodNotes: Create episode note** first.

To use a choice you built yourself, select **Choose...** and pick it. A choice reads these values as `{{VALUE:<name>}}`:

| Name | Value |
| --- | --- |
| `timestamp` | The text a capture inserts without QuickAdd, from **Timestamp template**. For a range, the range. |
| `time` | The playback position as the player shows it, such as `12:34`. For a range, `12:24-12:34`. |
| `seconds` | The position in whole seconds. For a range, its start. |
| `link` | The `obsidian://podnotes` link that plays the episode from that position. |
| `episode` | The episode title. |
| `podcast` | The podcast name. |
| `url` | The episode's URL, as `{{url}}` writes it in a note. |
| `episodeNote` | The path of the episode's note, from **Episode note path**. |

A value PodNotes doesn't have is empty, and QuickAdd doesn't ask for it. No value contains a private feed's address. `link` is empty for an episode PodNotes can't link to.

PodNotes shows no **Undo** for these captures, because QuickAdd writes the text. QuickAdd shows its own notice. When QuickAdd can't run the choice, the capture writes nothing, and a notice says what to do: install, enable, or update QuickAdd, or choose another choice. To capture at your cursor again, select **x** next to the choice.

docs/src/content/docs/settings.md: in the Notes table, add after Timestamp offset (s):

| **QuickAdd choice** | None | Runs this QuickAdd choice for each capture, instead of inserting the timestamp. **Choose...** picks one of your QuickAdd choices, and **Create** adds a ready-made one. See [Capture with QuickAdd](/QuickAdd/#capture-with-quickadd). |

docs/src/content/docs/timestamps.md: at the end of "Choose where a timestamp goes", add:

To choose where every capture goes, such as under a heading in the episode note, set **QuickAdd choice**. See [Capture with QuickAdd](/QuickAdd/#capture-with-quickadd).

docs/src/content/docs/commands.md: after "After each capture, a notice says where the timestamp went and offers Undo.", add "With QuickAdd choice set, the choice decides where it goes, and there is no Undo."

Screenshots

A long choice name, before (971f16f) and after, on a phone (Obsidian's mobile emulation, 390 x 844):

Before: the name runs past the button and both screen edges

After: the name ends in an ellipsis, x inside the row

In a 720 px window, where the row stacks:

Before: the name overflows, x outside the row

After

In a 900 px window, where the name sits beside the controls:

Before: the row's name is squeezed away

After: QuickAdd choice wraps, the name ends in an ellipsis

QuickAdd's picker open over the row, before and after:

Before: Choose... shows a spinner

After: Choose... keeps its label

The rest are from tests/e2e/quickadd-capture.test.ts, Obsidian 1.14.4.

QuickAdd's picker, opened by Choose..., then after typing Log:

QuickAdd's choice picker over PodNotes settings

The picker filtered to Log timestamp

Nothing set:

Choose... and Create

Set, then renamed in QuickAdd, then deleted in QuickAdd:

Log timestamp and the clear button

Podcast log after the rename

Missing choice

QuickAdd not installed, disabled, and 2.32.1:

Install QuickAdd

Enable QuickAdd

Update QuickAdd

Note

Add QuickAdd choice support for captures

  • Captures now run through a QuickAdd choice when timestamp.quickAddChoiceId is set in settings; the choice receives variables for timestamp text, formatted time, timestamp link, episode metadata, sanitized episode URL, and episode-note path. See CaptureService.ts.
  • The Notes settings tab gains a QuickAdd row with Choose, Create, and Clear controls, plus a ready-made capture choice that writes the timestamp to the episode note after the Notes heading. See PodNotesSettingsTab.ts.
  • QuickAdd availability is detected at runtime and the user is told to Install, Enable, or Update as needed; captures are refused with a notice instead of written when QuickAdd or the choice is unusable. See quickAdd.ts.
  • Settings decode/encode persists the choice id and drops non-string values with a warning. See settingsCodec.ts.
  • Risk (Behavioral Change): captures with a non-empty quickAddChoiceId no longer write directly to an editor or episode note; they invoke QuickAdd and return no direct Capture result. An empty id keeps the old direct-write behavior. Check CaptureService.capture and CaptureService.runQuickAdd.

Macroscope summarized e4ccd04.


Note

Medium Risk
Configured captures change behavior (external QuickAdd writes, no undo) and depend on QuickAdd’s API/version; unset choice leaves the existing capture path unchanged.

Overview
Adds an optional QuickAdd choice for timestamp captures: when timestamp.quickAddChoiceId is set, every capture (commands, player button, headset) calls QuickAdd’s executeChoice with template-derived variables instead of inserting into the focused editor or episode note—no PodNotes undo on that path.

Settings → Notes gains a QuickAdd choice row: Choose... (QuickAdd’s picker), Create (imports a built-in “Podcast capture” package via importPackage), and clear; the UI surfaces Install/Enable/Update/Missing choice when QuickAdd isn’t usable. Choice id is persisted in settingsCodec as an optional string.

New src/quickAdd.ts centralizes QuickAdd detection and the capture package. CaptureService builds {{VALUE:…}} payloads (time, link, episode note path, etc.) with private-feed redaction; helpers resolveEpisodeUrl, getPodcastNotePath, and episodeTimestampLink are exported/shared for that. Unit, settings, command, and Obsidian e2e tests cover the integration and layout.

Reviewed by Cursor Bugbot for commit e4ccd04. Bugbot is set up for automated code reviews on this repo. Configure here.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Deploying podnotes with  Cloudflare Pages  Cloudflare Pages

Latest commit: e4ccd04
Status: ✅  Deploy successful!
Preview URL: https://494a88dd.podnotes.pages.dev
Branch Preview URL: https://feat-quickadd-capture.podnotes.pages.dev

View logs

@chhoumann
chhoumann marked this pull request as ready for review October 18, 2026 12:20
The QuickAdd choice row no longer takes a typed name. Choose... opens
QuickAdd's choice picker, and PodNotes stores the picked choice's id, so a
choice renamed in QuickAdd keeps working and the row shows its current name.
The x button clears the setting and leaves QuickAdd alone.

A capture runs the choice by its id. When QuickAdd is not installed, is
disabled, predates choice picking, or no longer has the choice, the capture
shows one notice that names the fix and writes nothing. The row's button
says Install, Enable or Update QuickAdd, or Missing choice, and the first
three open Community plugins.

The unreleased timestamp.quickAddChoice (a name) becomes
timestamp.quickAddChoiceId. src/quickAdd.ts is the one place that knows
QuickAdd's API.
With no QuickAdd choice set, Create sits next to Choose.... It hands QuickAdd
a one-choice package, Podcast capture with the fixed id podnotes-capture, and
selects it. The choice captures to the episode note, creates the note when it
is missing, and adds each timestamp at the end of its ## Notes section,
creating the heading at the bottom. Its format is the timestamp alone, since
the timestamp template already starts with a bullet.

QuickAdd skips a choice id it already has, so a second Create selects the
existing choice under its current name and overwrites nothing.
@chhoumann chhoumann changed the title feat(capture): run a QuickAdd choice for captures when one is set feat(capture): run a QuickAdd choice for captures, picked or created in QuickAdd Oct 18, 2026
A screen reader read "Install QuickAdd QuickAdd choice". The QuickAdd choice
button takes its row's name only while QuickAdd is ready, where it reads
Choose... or the choice's name; Install, Enable and Update QuickAdd read as
they are.
…te's text

QuickAdd's picker is a prompt, not a .modal. A capture into a note QuickAdd
creates ends without a trailing newline, and the next one starts on a new
line. Both journeys now pass against the QuickAdd build with choice picking.
…d Choose...'s label while the picker is open

On a phone or in a narrow window, a long choice name drew past its button and pushed Clear out of the row; beside the row's name it squeezed the name away. The button now ends the name in an ellipsis, and the row's control may shrink.

Choose... no longer hands Obsidian the picker's promise, which swapped its label for a spinner while QuickAdd's picker was open. The picker covers the button, so it still takes no second click.

The e2e checks the row's bounds at 900 px, 720 px and on a phone, and the label behind the open picker.
Obsidian closes Settings when it focuses a note, and PodNotes opens the episode note after creating it. A test that opened Settings once the file existed lost Settings to the note's focus about one run in ten, timing out on the PodNotes tab. noteAt now waits for the note's focus.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant