Skip to content

feat(capture): sort list properties written by property Capture - #2237

Merged
chhoumann merged 2 commits into
masterfrom
issue-2236
Oct 13, 2026
Merged

chhoumann merged 2 commits into
masterfrom
issue-2236

Conversation

@chhoumann

@chhoumann chhoumann commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Property Capture gets a Sort list setting: Keep order (default), Ascending, or Descending. Refs #2236.

Product decision

Sorting: added, as one generic option. Property Capture already merges captured items into a list and removes duplicates. Keeping that list ordered is the last step of the same job, and today it needs a user script. Sorting helps tags, aliases, links, and logs, not only date lists.

  • Sorting applies to every list the Capture writes: Add to list, Set value on a list, and formats with {{PROPERTY}}. It runs after merging and duplicate removal.
  • The order is natural text order (Intl.Collator, numeric). YYYY-MM-DD dates sort chronologically, and item 2 sorts before item 10. There is no "sort by" picker, because QuickAdd writes these items in a format the user controls.

Date-aware duplicate comparison: declined. Obsidian 1.13.7 reads unquoted dates in frontmatter as strings: - 2026-10-10 comes back as "2026-10-10". QuickAdd therefore never sees the non-string date values the issue describes. The remaining case is different spellings of one date (2026-10-11, 2026-10-11T08:00). Handling it would need a date-list mode, format guessing, and a rule for which spelling survives. A consistent {{DATE:YYYY-MM-DD}} format never creates that data. The issue should stay open until a maintainer explains this decision, so this PR uses "Refs", not "Closes".

Changes

  • PropertyCapture.sort ("none" | "asc" | "desc") is required in memory. parsePropertyCapture sets it to "none" when it is missing, so saved Captures work as before.
  • planPropertyUpdate removes duplicates, then sorts, in one helper.
  • The builder has a Sort list dropdown after Action. When sorting is on, the Add to list description no longer says that new items go below the existing ones.
  • The docs section "Capture into a property" includes the issue's example.

Verification

  • pnpm run lint, pnpm run test (6888 passed, 28 skipped), and pnpm run build pass.
  • Unit tests cover the issue example (re-adding does not change the list), Descending, Keep order, numeric order, Set value on a list, {{PROPERTY}}, an empty add, and settings parsing.
  • A new case in tests/e2e/property-capture.test.ts passes in real Obsidian (.agents/run-e2e, 13 of 13).
  • A manual run in Obsidian 1.13.7 with Add to list and Ascending turned [2026-10-10, 2026-10-12] into [2026-10-10, 2026-10-11, 2026-10-12]. A second run returned effect: "unchanged". After Descending was selected in the builder, data.json saved "sort": "desc", and the next run wrote the list in reverse order.

Release and migration impact

No migration is needed. Settings without sort load as Keep order.

Note

Add list-sort setting (Keep order, Ascending, Descending) to property Capture

  • Adds a PropertyListSort setting to property Capture with three modes: Keep order, Ascending, and Descending. Applies to Add to list, typed Set, and composed {{PROPERTY}} writes.
  • Sorting removes duplicate items and uses natural text ordering via Intl.Collator (numeric-aware), so dates and numbers sort correctly. Scalar and multiline text values stay unsorted. See captureProperty.ts and ICaptureChoice.ts.
  • Adds a Sort list dropdown in PropertyCaptureFields.svelte, with Keep order as the default for new and legacy choices.
  • Behavioral Change: list-valued property captures with Ascending or Descending now reorder and dedupe existing note lists; legacy choices without a saved sort field load as Keep order, so existing behavior is unchanged unless the user picks a sort mode.

Macroscope summarized 4965654.

Summary by CodeRabbit

  • New Features
    • Added a Sort list option for captured properties: keep items in their original order, or sort them ascending or descending.
    • Sorting applies after additions and duplicate removal, including when a list is set or composed. Natural ordering keeps dates chronological and places items such as “item 2” before “item 10.”
    • Lists remain in their original order by default, including for existing captures without a sort preference.

Property Capture gets a "Sort list" setting: Keep order (default),
Ascending, or Descending. Sorting applies to every list the Capture
writes (Add to list, Set value on a list, and {{PROPERTY}} formats),
after new items are merged and duplicates removed. Items sort in
natural text order, so YYYY-MM-DD dates sort chronologically and
"item 2" comes before "item 10".

Settings saved without a sort load as Keep order, so existing
Captures behave as before.

Refs #2236
@cloudflare-workers-and-pages

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

Copy link
Copy Markdown

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0bd3b1e
Status: ✅  Deploy successful!
Preview URL: https://3520d7f0.quickadd.pages.dev
Branch Preview URL: https://issue-2236.quickadd.pages.dev

View logs

@coderabbitai

coderabbitai Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 06d6910c-4cf0-4681-ab67-c1e0d13b058d
📥 Commits

Reviewing files that changed from the base of the PR and between 0bd3b1e and 4965654.

📒 Files selected for processing: 2
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
  • src/gui/ChoiceBuilder/choiceForms-1497-legacy-missing-fields.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 4 remain after this review.

📝 Walkthrough

Capture property settings now include a list sort option. List updates remove duplicate items, then preserve their order or sort them naturally in ascending or descending order. The Choice Builder and documentation describe the available options.

Changes: Capture list sorting

Layer / File(s) Summary
Define and expose sort settings
src/types/choices/ICaptureChoice.ts, src/gui/ChoiceBuilder/components/PropertyCaptureFields.svelte, src/gui/ChoiceBuilder/components/WritePositionSetting.svelte, src/gui/ChoiceBuilder/captureChoiceBuilder.ts, src/gui/ChoiceBuilder/CaptureChoiceForm.test.ts, src/gui/ChoiceBuilder/choiceForms-1497-legacy-missing-fields.test.ts, docs/src/content/docs/docs/Choices/CaptureChoice.md
PropertyCapture now has a sort setting with "none" as the default. The Choice Builder provides Keep order, Ascending, and Descending options. The documentation describes natural ordering and duplicate handling.
Sort property list updates
src/engine/captureProperty.ts, src/engine/captureProperty.test.ts, tests/e2e/property-capture.test.ts
List updates remove duplicate items and apply the configured order to added, replaced, and composed lists. Tests cover ascending and descending order, natural numeric ordering, multiline text, and repeated additions.
Set defaults in capture integrations
src/cli/*, src/engine/CaptureChoiceEngine.property.test.ts, src/engine/captureAction.test.ts, src/preflight/collectChoiceRequirements.test.ts, src/services/packagePreview.test.ts, tests/e2e/*
Existing property-capture configurations and test fixtures now set sort: "none". Existing integration expectations remain in place.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  actor User
  participant ChoiceBuilder
  participant parsePropertyCapture
  participant planPropertyUpdate
  User->>ChoiceBuilder: Select list sort option
  ChoiceBuilder->>parsePropertyCapture: Provide property capture settings
  parsePropertyCapture->>planPropertyUpdate: Pass validated sort setting
  planPropertyUpdate->>planPropertyUpdate: Deduplicate items and apply configured ordering
Loading
Merge Risk: ⚪ Minimal · up to 49656

Legacy captures now open with Keep order instead of a blank sort choice. No actionable merge-blocking risk is established in the reviewed changes.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 16 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: adding list sorting for properties written by Property Capture.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit picks a list to sort,
Keep order, or a new resort.
Dates line up from old to new,
Item two comes before ten, too.
Duplicates leave; the list stays neat,
Then hop along on sorted feet.

Comment @coderabbitai help to get the list of available commands.

@chhoumann
chhoumann marked this pull request as ready for review October 13, 2026 20:39
@chhoumann

Copy link
Copy Markdown
Owner Author

Verifier verdict: FAIL for head 0bd3b1e against base 45cd3fb.

Every run-path claim holds in real Obsidian 1.13.7. One user-visible defect blocks: the Capture builder shows an empty Sort list dropdown for any property Capture saved before this change.

Defect (medium). parsePropertyCapture fills in sort: "none" only when the engine runs the choice. The settings loaded from data.json keep the raw object, so for a choice saved without sort the builder binds an undefined value. After a real plugin reload the dropdown rendered with no selected option, and the Action description fell into the sorted branch ("Each line is one item.") instead of the Keep order text. Running such a choice is unaffected, and picking any option persists correctly. Every existing user with a property Capture will see the blank dropdown the first time they open that choice after upgrading. The PR's form test only covers a choice created in the builder, which gets sort: "none" from the write-position defaults. choiceForms-1497-legacy-missing-fields.test.ts exists for exactly this class of problem and has no case for it. A backfill when the property fields mount, plus a legacy-shape test case there, would close it.

Evidence. All scenarios ran through the repo's E2E runner on the isolated vault. Each quickadd:run call reloads the plugin first, so every choice went through the real settings load path.

  1. Backward compatibility. Choice JSON without sort, Add to list of 2026-10-11. Parent 45cd3fb and head 0bd3b1e produced byte-identical files (cmp exit 0).
    Before, both commits:
    ---
    complete_instances:
      - 2026-10-10
      - 2026-10-12
    ---
    
    After, both commits:
    ---
    complete_instances:
      - 2026-10-10
      - 2026-10-12
      - 2026-10-11
    ---
    
  2. Head, same choice with "sort": "asc", run twice. Run 1 reported effect: "changed", run 2 reported effect: "unchanged" and left the file byte-identical.
    ---
    complete_instances:
      - 2026-10-10
      - 2026-10-11
      - 2026-10-12
    ---
    
    With "sort": "desc" the same two runs wrote 2026-10-12, 2026-10-11, 2026-10-10, second run unchanged.
  3. Head, Set value on a list property verify_list: [old] with asc and format {{VALUE:input}}\nzeta\nalpha, input mid, wrote alpha, mid, zeta. Add to list with format {{VALUE:input}}\n{{PROPERTY}} on [d, b], input c, asc, wrote b, c, d.
  4. Head, real Capture builder (Settings, QuickAdd, Configure). The Sort list row sits between Action and Create property if missing, with label and dropdown aligned to the neighbouring rows, options Keep order, Ascending, Descending. Selecting Descending and leaving the page wrote "sort": "desc" to data.json once the 1 s save debounce passed; Ascending likewise. For the pre-upgrade choice the dropdown was blank (defect above).
  5. Edge. Add to list asc of apple into [item 10, Item 2] wrote apple, Item 2, item 10. Reasonable: letters compare case-insensitively at the primary level and 2 sorts before 10.

dev:errors reported nothing after any scenario. pnpm run test: 501 files passed, 6888 tests passed, 28 skipped. pnpm run lint: clean (oxlint, svelte-policy, tsc, svelte-check with 0 errors and 0 warnings).

Commands run: pnpm install --frozen-lockfile; git checkout --detach 45cd3fb0 && pnpm run build; env -u DISPLAY pnpm run start:e2e-obsidian -- --print-env; per scenario one pnpm run obsidian:e2e -- eval code=... to seed the note and the choice, then pnpm run obsidian:e2e -- quickadd:run id=... verify=true vars='{"input":"..."}'; git checkout --detach 0bd3b1e4 && pnpm run build && pnpm run obsidian:e2e -- --reload quickadd:list; the builder scenario as one eval using app.setting.openTabById("quickadd") and capturePage; pnpm run test; pnpm run lint; pnpm run stop:e2e-obsidian.

… list

Saved choices reach the builder without passing through
parsePropertyCapture, so a property Capture saved before this setting
existed had no sort. The Sort list dropdown rendered blank and the Add
to list description dropped its ordering note. The builder now fills
in Keep order when it opens, like its other legacy defaults.
@chhoumann

Copy link
Copy Markdown
Owner Author

Verifier verdict (round 2): PASS+NOTES for head 4965654 against base 45cd3fb.

The round 1 defect is fixed on the real surface, and nothing else regressed. One note on persistence at the end.

Evidence, all in Obsidian 1.13.7 through the repo's E2E runner on the isolated vault:

  1. Round 1 path. A property Capture was seeded into data.json without sort, the plugin was reloaded with --reload, and the loaded choice was confirmed to have no sort key. Opened in the real Capture builder, the Sort list dropdown showed "Keep order" (value none, options Keep order, Ascending, Descending), and the Action description was the full Add to list text ("Each line is one item. New items go below the existing ones. Put {{PROPERTY}} on its own line to choose where the existing items go."). The row sits between Action and Create property if missing. Its label has the same top offset, and its dropdown the same height and right edge, as the Action and Link to captured file dropdowns. Screenshot checked.
  2. Explicit picks on that same legacy choice. Ascending and leaving the page wrote "sort": "asc" to data.json and switched the description to "Each line is one item." Reopening showed Ascending. Picking Keep order wrote "sort": "none" and restored the full description.
  3. Run paths. Add to list with asc of 2026-10-11 into [2026-10-10, 2026-10-12] wrote 2026-10-10, 2026-10-11, 2026-10-12 with effect: "changed". A second run reported effect: "unchanged" and left the file byte-identical. The same choice without sort, run after a plugin reload and without opening the builder, appended 2026-10-11 at the end.
  4. Other paths. A Capture with no propertyCapture, opened in the builder and left untouched, stayed byte-identical in data.json, with no propertyCapture key added.
  5. dev:errors reported nothing after any step. pnpm run test: 501 files passed, 6889 tests passed, 28 skipped. pnpm run lint: clean (oxlint, svelte-policy, tsc, svelte-check with 0 errors and 0 warnings).

Note (low, no action needed in this PR). Opening the legacy choice and leaving without touching anything does not write "sort": "none" to data.json, and the in-memory choice also keeps no sort key. configureChoice takes its base snapshot after the builder constructor has normalized the choice, so saveBuilderEdits sees the edited choice equal to base and keeps the stored value. This is the builder's existing behavior for every legacy default. A Capture seeded without insertBefore also gained no insertBefore on disk after the same untouched open and close. It is not user-visible, because the dropdown shows Keep order, the run path treats a missing sort as Keep order, and an explicit pick persists. The new test's "saves Keep order" holds at the builder's onSave boundary, which is what it asserts.

@chhoumann
chhoumann merged commit 6ba5750 into master Oct 13, 2026
13 checks passed
@chhoumann
chhoumann deleted the issue-2236 branch October 13, 2026 21:07
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