Skip to content

Conversation

@chhoumann
Copy link
Owner

@chhoumann chhoumann commented Dec 20, 2025

Summary

  • add per-choice blank-line mode (auto/skip/none) for Insert After
  • default auto skips blank lines only for ATX headings
  • update insert-after logic, tests, and docs/UI copy

Testing

  • bun run test

Summary by CodeRabbit

  • New Features

    • Added configurable "Blank lines after match" to "Insert After" with modes: Auto (headings only), Skip (always skip), None (never skip). Default: Auto.
  • Documentation

    • Updated the "Insert After" guidance and examples to show Auto, Skip, and None behaviors and explain heading-specific handling.

✏️ Tip: You can customize this high-level summary in your review settings.

@vercel
Copy link

vercel bot commented Dec 20, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
quickadd Ready Ready Preview Dec 20, 2025 0:54am

@coderabbitai
Copy link

coderabbitai bot commented Dec 20, 2025

Walkthrough

Adds a configurable blank-line handling mode for the "Insert After" option in CaptureChoice: a new blankLineAfterMatchMode with values "auto" (headings-only), "skip", and "none", implemented across types, formatter logic, UI, tests, and documentation.

Changes

Cohort / File(s) Summary
Type Definitions
src/types/choices/ICaptureChoice.ts, src/types/choices/CaptureChoice.ts
Added `BlankLineAfterMatchMode = "auto"
Formatter Implementation & Tests
src/formatters/captureChoiceFormatter.ts, src/formatters/captureChoiceFormatter-frontmatter.test.ts
Imported new type; added helpers shouldSkipBlankLinesAfterMatch, isAtxHeading, and findInsertAfterPositionWithBlankLines; integrated mode-aware logic into insertAfterHandler; updated tests to exercise auto/skip/none behaviors and edge cases (headings, whitespace, EOF, CRLF).
UI Configuration
src/gui/ChoiceBuilder/captureChoiceBuilder.ts
Updated Insert After description; added "Blank lines after match" dropdown (auto/skip/none), defaulting to "auto", disabled when "Insert at end" is active, and persisted to choice.insertAfter.blankLineAfterMatchMode.
Documentation
docs/docs/Choices/CaptureChoice.md
Revised Insert After behavior text to describe heading-aware default and added the new "Blank lines after match" control with an example illustrating auto behavior.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as ChoiceBuilder UI
    participant Choice as CaptureChoice (model)
    participant Formatter as CaptureChoiceFormatter
    participant Inserter as Insert After Handler

    User->>UI: Select mode (auto|skip|none)
    UI->>Choice: Save insertAfter.blankLineAfterMatchMode
    User->>Formatter: Trigger Insert After operation
    Formatter->>Choice: Read insertAfter.blankLineAfterMatchMode
    Formatter->>Formatter: isAtxHeading(matchLine)?
    alt mode == "skip" or (mode == "auto" and heading)
        Formatter->>Formatter: skip following blank lines (scan forward)
    else
        Formatter->>Formatter: do not skip blank lines (insert after match)
    end
    Formatter->>Inserter: Provide insertion index
    Inserter->>Inserter: Insert content at computed position
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Inspect captureChoiceFormatter.ts helpers for correct ATX heading detection and robust newline/CRLF handling.
  • Verify tests in captureChoiceFormatter-frontmatter.test.ts cover all modes and edge cases (EOF, mixed newlines).
  • Confirm UI defaulting, enable/disable logic, and persistence in captureChoiceBuilder.ts.

Possibly related PRs

Poem

🐰
I hopped through lines both blank and neat,
Auto, skip, or never skip the beat.
I nudge the text and mark the place,
Let headings breathe or lose their space.
A tiny hop for tidy prose — hooray!

Pre-merge checks and finishing touches

✅ Passed checks (3 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 a blank-line mode configuration for the insert-after feature.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 555

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

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/formatters/captureChoiceFormatter.ts (1)

9-10: Consider combining type imports from the same module.

Both imports are from the same module and could be combined for cleaner code.

🔎 Proposed refactor
-import type ICaptureChoice from "../types/choices/ICaptureChoice";
-import type { BlankLineAfterMatchMode } from "../types/choices/ICaptureChoice";
+import type ICaptureChoice, { BlankLineAfterMatchMode } from "../types/choices/ICaptureChoice";
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 818c036 and bdeea79.

📒 Files selected for processing (6)
  • docs/docs/Choices/CaptureChoice.md (1 hunks)
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts (2 hunks)
  • src/formatters/captureChoiceFormatter.ts (3 hunks)
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2 hunks)
  • src/types/choices/CaptureChoice.ts (4 hunks)
  • src/types/choices/ICaptureChoice.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Source code lives in src/: core logic under engine/, services/, and utils/; Svelte UI in src/gui; shared types in src/types; settings entry in src/quickAddSettingsTab.ts

Files:

  • src/types/choices/ICaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
  • src/types/choices/CaptureChoice.ts
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts
  • src/formatters/captureChoiceFormatter.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Biome enforces tab indentation (width 2), LF endings, and an 80-character line guide; align editor settings
Use camelCase for variables and functions
Prefer type-only imports in TypeScript files
Route logging through the logger utilities for consistent output
Structure production code so Obsidian dependencies are injected behind interfaces; unit tests target pure logic and swap in adapters or tests/obsidian-stub.ts

Files:

  • src/types/choices/ICaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
  • src/types/choices/CaptureChoice.ts
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts
  • src/formatters/captureChoiceFormatter.ts
src/**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components

Files:

  • src/types/choices/ICaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
  • src/types/choices/CaptureChoice.ts
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts
  • src/formatters/captureChoiceFormatter.ts
docs/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Keep user-facing docs in docs/ directory

Files:

  • docs/docs/Choices/CaptureChoice.md
🧬 Code graph analysis (3)
src/gui/ChoiceBuilder/captureChoiceBuilder.ts (1)
tests/obsidian-stub.ts (1)
  • Setting (151-233)
src/types/choices/CaptureChoice.ts (1)
src/types/choices/ICaptureChoice.ts (1)
  • BlankLineAfterMatchMode (5-5)
src/formatters/captureChoiceFormatter.ts (1)
src/types/choices/ICaptureChoice.ts (1)
  • BlankLineAfterMatchMode (5-5)
🔇 Additional comments (13)
src/types/choices/ICaptureChoice.ts (1)

5-6: LGTM!

The new BlankLineAfterMatchMode type and optional blankLineAfterMatchMode property are well-designed. The optional property ensures backward compatibility with existing saved choices, and the type alias is properly exported for use across the codebase.

Also applies to: 33-33

docs/docs/Choices/CaptureChoice.md (1)

60-75: LGTM!

The documentation clearly explains the three blank line handling modes and provides a helpful example. The terminology ("Auto (headings only)", "Always skip", "Never skip") matches the UI labels, and the example accurately reflects the behavior tested in captureChoiceFormatter-frontmatter.test.ts.

src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2)

475-504: LGTM!

The blank line mode dropdown is well-implemented:

  • Proper default initialization to "auto" when not set
  • Correct disabling of the control when "Insert at end of section" is enabled
  • Clear description that changes based on insertAtEnd state
  • Type assertion on the value is safe given the constrained dropdown options

469-472: Good addition of reload for UI refresh.

The reload() call on toggle change ensures the blank line mode setting's disabled state updates correctly when toggling "Insert at end of section".

src/formatters/captureChoiceFormatter-frontmatter.test.ts (2)

182-358: Excellent test coverage for the new blank line handling feature.

The test suite comprehensively covers:

  • All three modes (auto, skip, none)
  • Edge cases: EOF, CRLF line endings, whitespace-only lines, multiple consecutive blank lines
  • The distinction between heading and non-heading matches in auto mode

114-114: LGTM!

The default choice factory correctly includes blankLineAfterMatchMode: 'auto' to match the production default, ensuring tests reflect real-world behavior.

src/types/choices/CaptureChoice.ts (3)

3-3: LGTM!

Correct use of type-only import for BlankLineAfterMatchMode, following TypeScript best practices as per coding guidelines.


25-25: LGTM!

The optional property declaration and constructor default value are correctly implemented, ensuring new choices start with "auto" as the default mode.

Also applies to: 64-64


94-96: Good backward compatibility handling.

The Load() method properly migrates existing choices that lack the new blankLineAfterMatchMode property by defaulting to "auto". The guard checking loaded.insertAfter before accessing the property is defensive and handles potential edge cases where insertAfter might be undefined.

src/formatters/captureChoiceFormatter.ts (4)

202-209: LGTM! Clean mode-based logic.

The three-way branch correctly implements the blank line handling modes: explicit skip, explicit none, and auto (heading-only).


211-213: LGTM! Regex correctly implements CommonMark ATX heading detection.

The pattern accurately validates ATX headings per the CommonMark specification: 0-3 spaces of indentation, 1-6 hash marks, mandatory whitespace, and content.


215-243: LGTM! Robust blank-line scanning with proper EOF handling.

The method correctly skips consecutive blank lines after the match, with careful handling of the trailing empty element from split("\n") when files end with a newline. The defensive coding (nullish coalescing on line 223) and clear comment on lines 227-228 enhance maintainability.


278-287: LGTM! Proper integration of blank-line mode.

The else block correctly applies the blank-line adjustment only when not using insertAtEnd, with a sensible default of "auto". The flow properly adjusts targetPosition before the final insertion.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bdeea79 and 8de9cb0.

📒 Files selected for processing (6)
  • docs/docs/Choices/CaptureChoice.md (1 hunks)
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts (9 hunks)
  • src/formatters/captureChoiceFormatter.ts (3 hunks)
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2 hunks)
  • src/types/choices/CaptureChoice.ts (4 hunks)
  • src/types/choices/ICaptureChoice.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/docs/Choices/CaptureChoice.md
  • src/types/choices/ICaptureChoice.ts
  • src/formatters/captureChoiceFormatter-frontmatter.test.ts
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Source code lives in src/: core logic under engine/, services/, and utils/; Svelte UI in src/gui; shared types in src/types; settings entry in src/quickAddSettingsTab.ts

Files:

  • src/formatters/captureChoiceFormatter.ts
  • src/types/choices/CaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Biome enforces tab indentation (width 2), LF endings, and an 80-character line guide; align editor settings
Use camelCase for variables and functions
Prefer type-only imports in TypeScript files
Route logging through the logger utilities for consistent output
Structure production code so Obsidian dependencies are injected behind interfaces; unit tests target pure logic and swap in adapters or tests/obsidian-stub.ts

Files:

  • src/formatters/captureChoiceFormatter.ts
  • src/types/choices/CaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
src/**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components

Files:

  • src/formatters/captureChoiceFormatter.ts
  • src/types/choices/CaptureChoice.ts
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
🧬 Code graph analysis (3)
src/formatters/captureChoiceFormatter.ts (1)
src/types/choices/ICaptureChoice.ts (1)
  • BlankLineAfterMatchMode (5-5)
src/types/choices/CaptureChoice.ts (1)
src/types/choices/ICaptureChoice.ts (1)
  • BlankLineAfterMatchMode (5-5)
src/gui/ChoiceBuilder/captureChoiceBuilder.ts (1)
tests/obsidian-stub.ts (1)
  • Setting (151-233)
🪛 GitHub Actions: Build With Lint
src/gui/ChoiceBuilder/captureChoiceBuilder.ts

[error] 526-526: Command failed: tsc -noEmit -skipLibCheck && bun lint && node esbuild.config.mjs production — TypeScript error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.

🪛 GitHub Check: Build With Lint
src/gui/ChoiceBuilder/captureChoiceBuilder.ts

[failure] 526-526:
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.

🔇 Additional comments (4)
src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2)

500-503: Verify the reload behavior aligns with user expectations.

Adding this.reload() to the insertAtEnd toggle ensures the blank-line mode setting updates its disabled state dynamically. Confirm this doesn't cause unexpected side effects, such as losing unsaved changes in other fields or disrupting the user's workflow.


506-535: LGTM: Blank line mode UI control is well-structured.

The implementation correctly:

  • Initializes the default value to "auto" for backward compatibility
  • Adapts the description based on insertAtEnd state
  • Disables the control when insertAtEnd is active (both the dropdown and the setting itself)

Note: Lines 533 and 535 both disable UI elements when insertAtEnd is true. While this is redundant, it ensures consistent visual feedback and doesn't cause issues.

src/types/choices/CaptureChoice.ts (1)

3-3: LGTM: Type definition and backward compatibility are well-handled.

The implementation correctly:

  • Uses a type-only import as per coding guidelines
  • Declares blankLineAfterMatchMode as optional for flexibility
  • Sets a sensible default ("auto") in both the constructor and the Load method
  • Ensures existing configurations without this field will default to "auto" behavior

Also applies to: 26-26, 65-65, 95-97

src/formatters/captureChoiceFormatter.ts (1)

216-223: LGTM: Blank line handling logic is sound.

The implementation correctly:

  • Distinguishes between three modes: always skip ("skip"), never skip ("none"), and conditional skip ("auto" for ATX headings only)
  • Preserves existing EOF behavior by respecting the scan limit
  • Defaults to "auto" mode when not explicitly configured
  • Integrates cleanly into the existing insertion flow

The defensive nullish coalescing on Line 237 (lines[matchIndex] ?? "") is unnecessary but harmless, as matchIndex >= 0 is already verified on Line 235.

Also applies to: 229-257, 293-299

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2)

506-508: Initialization looks good; consider a more precise null check.

The defensive initialization ensures blankLineAfterMatchMode has a default value, which helps address the TypeScript type safety concern from the previous review. The current falsy check works fine since valid values are "auto", "skip", or "none".

🔎 Optional: more precise null check

If you want to be more explicit about checking for undefined/null only:

-if (!this.choice.insertAfter?.blankLineAfterMatchMode) {
+if (this.choice.insertAfter?.blankLineAfterMatchMode == null) {
 	this.choice.insertAfter.blankLineAfterMatchMode = "auto";
 }

This prevents treating other falsy values (like empty string) as missing, though in practice the current code works correctly.


510-537: Consider importing the BlankLineAfterMatchMode type.

The new blank line mode setting is well-structured and follows patterns from the rest of the file. The conditional description and disabled state logic are correct.

Based on the coding guidelines preference for type-only imports and the AI summary indicating that BlankLineAfterMatchMode is defined in the types, consider importing it:

🔎 Suggested type import

At the top of the file, add:

 import type QuickAdd from "../../main";
 import type ICaptureChoice from "../../types/choices/ICaptureChoice";
+import type { BlankLineAfterMatchMode } from "../../types/choices/ICaptureChoice";
 import type { LinkPlacement, LinkType } from "../../types/linkPlacement";

Then on lines 530-533, use the imported type:

 .onChange((value) => {
-	this.choice.insertAfter.blankLineAfterMatchMode = value as
-		| "auto"
-		| "skip"
-	| "none";
+	this.choice.insertAfter.blankLineAfterMatchMode = value as BlankLineAfterMatchMode;
 });

This improves type safety and maintainability by reusing the canonical type definition.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8de9cb0 and 2016f73.

📒 Files selected for processing (1)
  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Source code lives in src/: core logic under engine/, services/, and utils/; Svelte UI in src/gui; shared types in src/types; settings entry in src/quickAddSettingsTab.ts

Files:

  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

src/**/*.{ts,tsx}: Biome enforces tab indentation (width 2), LF endings, and an 80-character line guide; align editor settings
Use camelCase for variables and functions
Prefer type-only imports in TypeScript files
Route logging through the logger utilities for consistent output
Structure production code so Obsidian dependencies are injected behind interfaces; unit tests target pure logic and swap in adapters or tests/obsidian-stub.ts

Files:

  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
src/**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components

Files:

  • src/gui/ChoiceBuilder/captureChoiceBuilder.ts
🔇 Additional comments (3)
src/gui/ChoiceBuilder/captureChoiceBuilder.ts (3)

441-444: LGTM! Description accurately reflects the new feature.

The updated description text appropriately informs users that blank line handling is now configurable, which aligns with the new blank-line mode setting added below.


500-503: LGTM! The reload() call is necessary.

Adding this.reload() ensures the blank line mode setting properly updates its disabled state when the "Insert at end" toggle changes, maintaining correct UI behavior.


526-528: Past TypeScript error appears to be resolved.

The ?? "auto" fallback ensures setValue always receives a string, addressing the compilation error flagged in the previous review. Combined with the initialization at lines 506-508, this properly handles the optional blankLineAfterMatchMode field.

@chhoumann chhoumann merged commit 231e908 into master Dec 20, 2025
4 checks passed
@chhoumann chhoumann deleted the 555 branch December 20, 2025 13:03
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.

2 participants