Skip to content

fix: backfill missing fileOpening defaults#1075

Merged
chhoumann merged 4 commits intomasterfrom
focus
Jan 12, 2026
Merged

fix: backfill missing fileOpening defaults#1075
chhoumann merged 4 commits intomasterfrom
focus

Conversation

@chhoumann
Copy link
Copy Markdown
Owner

@chhoumann chhoumann commented Jan 11, 2026

Summary

  • backfill missing fileOpening defaults for Capture/Template choices, including nested/macro/legacy paths
  • centralize file opening defaults + legacy mapping + traversal helpers for migrations
  • expand migration tests for conditional else branches and legacy macros

Testing

  • bun run test

Migration/Release Impact

  • adds new migration backfillFileOpeningDefaults to populate missing file opening settings

Summary by CodeRabbit

  • New Features

    • Migration to backfill missing file-opening preferences across choices.
    • New utilities to normalize and represent file-opening settings.
    • Traversal helper to walk all choices (including nested and legacy paths).
    • New setting flag to control the migration.
  • Improvements

    • Consistent defaulting for file-opening (location, split direction, mode, focus).
    • Updated engines and builders to use normalized file-opening values.
  • Tests

    • New and expanded tests covering migration and defaulting behavior.

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

@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 11, 2026

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

Project Deployment Review Updated (UTC)
quickadd Ready Ready Preview Jan 11, 2026 9:48pm

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 11, 2026

📝 Walkthrough

Walkthrough

Adds centralized file-opening defaults and normalization, a migration to backfill missing fileOpening settings (including legacy mappings), traversal helpers for nested choices/macros, updates constructors/engines to use normalization, and corresponding tests and registration in the migrations map.

Changes

Cohort / File(s) Summary
Core Normalization Utility
src/utils/fileOpeningDefaults.ts
New type FileOpeningSettings, DEFAULT_FILE_OPENING, and normalizeFileOpening() to produce/sanitize full fileOpening objects.
Migration Implementation
src/migrations/backfillFileOpeningDefaults.ts, src/migrations/migrate.ts
New migration backfillFileOpeningDefaults that walks choices, coerces legacy fields, backfills/normalizes fileOpening, logs progress, and is registered in migrations map.
Migration Helpers
src/migrations/helpers/choice-traversal.ts, src/migrations/helpers/file-opening-legacy.ts
New helpers: walkAllChoices() to recursively visit all choices (including nested/macro/conditional) and legacy coercion/translation utilities (coerceLegacyOpenFileInNewTab, createFileOpeningFromLegacy).
Migration Usage Refactor
src/migrations/migrateFileOpeningSettings.ts
Replaced in-file traversal/legacy coercion with imports of walkAllChoices and legacy helper functions; updated control flow to use them.
Engine Updates
src/engine/CaptureChoiceEngine.ts, src/engine/TemplateChoiceEngine.ts
Engines now call normalizeFileOpening() before deriving focus and before invoking openFile/templater navigation, passing normalized settings.
Choice Constructors
src/types/choices/CaptureChoice.ts, src/types/choices/TemplateChoice.ts
Constructors now call normalizeFileOpening() instead of assigning inline hard-coded fileOpening defaults.
Builder and Settings
src/gui/ChoiceBuilder/choiceBuilder.ts, src/settings.ts
ChoiceBuilder uses normalizeFileOpening() and exported FileOpeningSettings type; QuickAddSettings.migrations.backfillFileOpeningDefaults added (default false).
Tests — Engine Mocks & New Case
src/engine/CaptureChoiceEngine.notice.test.ts, src/engine/CaptureChoiceEngine.selection.test.ts, src/engine/CaptureChoiceEngine.template-property-types.test.ts, src/engine/MacroChoiceEngine.notice.test.ts, src/engine/TemplateChoiceEngine.notice.test.ts
Test settings mocks updated to include migrations.backfillFileOpeningDefaults: false. Added test verifying defaulting behavior when fileOpening is missing and that openFile is called with normalized settings.
Migration Tests
src/migrations/backfillFileOpeningDefaults.test.ts
New comprehensive test validating backfill across Capture/Template/Macro/Multi choices (including legacy fields) and ensuring saveSettings() is called.

Sequence Diagram(s)

sequenceDiagram
  participant Plugin
  participant Migration as backfillFileOpeningDefaults
  participant Walker as walkAllChoices
  participant Visitor as choice visitor
  participant Legacy as file-opening legacy helpers
  participant Storage as plugin.saveSettings
  participant Logger

  Plugin->>Migration: migrate(plugin)
  Migration->>Logger: log "starting migration"
  Migration->>Walker: walkAllChoices(plugin, visitor)
  Walker->>Visitor: visit(choice)
  Visitor->>Legacy: coerce legacy fields / createFileOpeningFromLegacy
  Visitor->>Visitor: normalizeFileOpening and set choice.fileOpening
  Visitor-->>Migration: indicate changed (increment)
  Migration->>Storage: plugin.saveSettings()
  Migration->>Logger: log "completed" with migratedCount
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through choices, old and new,
I filled the gaps and set the view.
Legacy tabs find their place,
Focus true, and paths embraced.
Saved and tidy — a rabbit's grace.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: backfill missing fileOpening defaults' accurately and concisely describes the primary change: adding migration logic to populate missing file opening settings across choices.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54e03d6 and 7a9946a.

📒 Files selected for processing (1)
  • src/utils/fileOpeningDefaults.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,mts,mjs,js,json}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,mts,mjs,js,json}: Use tab indentation with width 2 in TypeScript and configuration files (enforced by Biome).
Follow an 80-character line guide (enforced by Biome).

Files:

  • src/utils/fileOpeningDefaults.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use camelCase for variables and functions in TypeScript.
Prefer type-only imports in TypeScript.

Files:

  • src/utils/fileOpeningDefaults.ts
**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components.

Files:

  • src/utils/fileOpeningDefaults.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Route logging through the logger utilities for consistent output.

Files:

  • src/utils/fileOpeningDefaults.ts
src/{engine,services,utils}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Structure production code so Obsidian dependencies are injected behind interfaces to enable unit testing without loading real Obsidian modules.

Files:

  • src/utils/fileOpeningDefaults.ts
🔇 Additional comments (1)
src/utils/fileOpeningDefaults.ts (1)

1-54: Well-structured centralized file-opening defaults utility.

The implementation correctly uses type-only imports, provides clean overloads for type inference with and without extras, and handles both full normalization and fill-missing-only modes appropriately. The == null checks properly catch both null and undefined values, which aligns with the migration/backfill intent.


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
Copy Markdown

@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: 3

🤖 Fix all issues with AI agents
In @src/migrations/backfillFileOpeningDefaults.test.ts:
- Around line 15-191: The test fixtures (captureLegacy, captureMissing,
templatePartial, nestedCapture, nestedTemplate, conditionalCapture,
conditionalElseTemplate, legacyMacroCapture, multiChoice, macroWithNested,
macroWithConditional) are plain objects that the migration mutates by adding
fileOpening, causing TS2339; fix by giving these fixture constants a looser type
(e.g., declare each as any or cast them to a shared interface with an optional
fileOpening like Partial<Choice>), or type the choices array as any[] on the
plugin settings so TypeScript allows dynamic property addition; update the
declarations used by backfillFileOpeningDefaults.migrate (and the plugin object)
accordingly so the compiler accepts the migration mutations.

In @src/migrations/backfillFileOpeningDefaults.ts:
- Around line 30-34: The intermediate cast to Partial<FileOpeningSettings> (the
variable fileOpening) narrows type information and causes a TypeScript error
when calling normalizeFileOpening; remove the intermediate variable/cast and
pass templateOrCaptureChoice.fileOpening directly into normalizeFileOpening so
TypeScript can preserve TExtras generics and infer correctly (apply the same
change for the other occurrence around the second mention at lines ~54).

In @src/utils/fileOpeningDefaults.ts:
- Around line 17-48: The normalizeFileOpening function contains an unused
options parameter and a dead code branch controlled by fillMissingOnly; remove
the options parameter (and any mention of fillMissingOnly) from the signature of
normalizeFileOpening, delete the branch that returns the spread of
DEFAULT_FILE_OPENING when fillMissingOnly is false, and simplify the
implementation to always merge/fill missing fields from DEFAULT_FILE_OPENING
(keep references to DEFAULT_FILE_OPENING and the normalized variable and the
existing null checks for location, direction, mode, focus); update any
JSDoc/typing to reflect the new signature (fileOpening?:
(Partial<FileOpeningSettings> & TExtras) | null) & ensure the function still
returns FileOpeningSettings & TExtras.
🧹 Nitpick comments (4)
src/engine/TemplateChoiceEngine.ts (1)

179-180: Simplify focus extraction.

The normalizeFileOpening function already guarantees that focus is defined (defaults to true if missing). The nullish coalescing on line 180 is redundant.

♻️ Suggested simplification
 const fileOpening = normalizeFileOpening(this.choice.fileOpening);
-const focus = fileOpening.focus ?? true;
+const focus = fileOpening.focus;
src/engine/CaptureChoiceEngine.ts (1)

191-192: Simplify focus extraction.

The normalizeFileOpening function already guarantees that focus is defined (defaults to true if missing). The nullish coalescing on line 192 is redundant.

♻️ Suggested simplification
 const fileOpening = normalizeFileOpening(this.choice.fileOpening);
-const focus = fileOpening.focus ?? true;
+const focus = fileOpening.focus;
src/migrations/backfillFileOpeningDefaults.ts (1)

24-57: Consider simplifying the conditional logic.

The current logic has three cases:

  1. No defaults needed → return early
  2. No fileOpening + legacy fields → create from legacy
  3. Everything else → normalize

The else branch at line 54 catches both "partial fileOpening exists" and "no fileOpening + no legacy fields". In the latter case, you're normalizing undefined, which works but could be clearer.

🔄 Optional refactor for clarity

Consider making the intent more explicit:

 		if (!needsDefaults) return;
 
-		if (!fileOpening && legacyTab) {
+		if (legacyTab && !fileOpening) {
 			templateOrCaptureChoice.fileOpening = createFileOpeningFromLegacy(
 				legacyTab,
 				legacyMode,
 			);
-		} else {
+		} else if (fileOpening) {
 			templateOrCaptureChoice.fileOpening = normalizeFileOpening(fileOpening);
+		} else {
+			// No fileOpening and no legacy fields - create with all defaults
+			templateOrCaptureChoice.fileOpening = normalizeFileOpening(null);
 		}

This makes it explicit that we're creating defaults from scratch in the final case, though the current code works correctly too.

src/migrations/helpers/choice-traversal.ts (1)

12-14: Consider reusing the existing isMultiChoice helper.

An isMultiChoice function already exists at src/migrations/helpers/isMultiChoice.ts with more comprehensive validation. Consider importing and reusing it instead of defining a new type guard here.

♻️ Proposed refactor to reuse existing helper
+import { isMultiChoice } from "./isMultiChoice";
+
-function isMultiChoice(choice: IChoice): choice is MultiChoice {
-	return choice.type === "Multi";
-}
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e1a632 and 7ba726f.

📒 Files selected for processing (18)
  • src/engine/CaptureChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/gui/ChoiceBuilder/choiceBuilder.ts
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/backfillFileOpeningDefaults.ts
  • src/migrations/helpers/choice-traversal.ts
  • src/migrations/helpers/file-opening-legacy.ts
  • src/migrations/migrate.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/settings.ts
  • src/types/choices/CaptureChoice.ts
  • src/types/choices/TemplateChoice.ts
  • src/utils/fileOpeningDefaults.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,mts,mjs,js,json}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,mts,mjs,js,json}: Use tab indentation with width 2 in TypeScript and configuration files (enforced by Biome).
Follow an 80-character line guide (enforced by Biome).

Files:

  • src/types/choices/CaptureChoice.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/migrations/helpers/choice-traversal.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/utils/fileOpeningDefaults.ts
  • src/gui/ChoiceBuilder/choiceBuilder.ts
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/migrations/backfillFileOpeningDefaults.ts
  • src/types/choices/TemplateChoice.ts
  • src/settings.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/migrations/helpers/file-opening-legacy.ts
  • src/migrations/migrate.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use camelCase for variables and functions in TypeScript.
Prefer type-only imports in TypeScript.

Files:

  • src/types/choices/CaptureChoice.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/migrations/helpers/choice-traversal.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/utils/fileOpeningDefaults.ts
  • src/gui/ChoiceBuilder/choiceBuilder.ts
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/migrations/backfillFileOpeningDefaults.ts
  • src/types/choices/TemplateChoice.ts
  • src/settings.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/migrations/helpers/file-opening-legacy.ts
  • src/migrations/migrate.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components.

Files:

  • src/types/choices/CaptureChoice.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/migrations/helpers/choice-traversal.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/utils/fileOpeningDefaults.ts
  • src/gui/ChoiceBuilder/choiceBuilder.ts
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/migrations/backfillFileOpeningDefaults.ts
  • src/types/choices/TemplateChoice.ts
  • src/settings.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/migrations/helpers/file-opening-legacy.ts
  • src/migrations/migrate.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Route logging through the logger utilities for consistent output.

Files:

  • src/types/choices/CaptureChoice.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/migrations/helpers/choice-traversal.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/utils/fileOpeningDefaults.ts
  • src/gui/ChoiceBuilder/choiceBuilder.ts
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/migrations/backfillFileOpeningDefaults.ts
  • src/types/choices/TemplateChoice.ts
  • src/settings.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/migrations/helpers/file-opening-legacy.ts
  • src/migrations/migrate.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
src/{engine,services,utils}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Structure production code so Obsidian dependencies are injected behind interfaces to enable unit testing without loading real Obsidian modules.

Files:

  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/utils/fileOpeningDefaults.ts
  • src/engine/MacroChoiceEngine.notice.test.ts
  • src/engine/CaptureChoiceEngine.template-property-types.test.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/engine/CaptureChoiceEngine.notice.test.ts
🧠 Learnings (4)
📚 Learning: 2025-12-21T07:54:34.875Z
Learnt from: CR
Repo: chhoumann/quickadd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T07:54:34.875Z
Learning: Applies to src/{engine,services,utils}/**/*.{ts,tsx} : Structure production code so Obsidian dependencies are injected behind interfaces to enable unit testing without loading real Obsidian modules.

Applied to files:

  • src/engine/CaptureChoiceEngine.selection.test.ts
📚 Learning: 2025-12-21T07:54:34.875Z
Learnt from: CR
Repo: chhoumann/quickadd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T07:54:34.875Z
Learning: Applies to tests/**/*.{ts,tsx} : Unit tests should target pure logic and swap in adapters or use `tests/obsidian-stub.ts` for Obsidian dependencies.

Applied to files:

  • src/engine/CaptureChoiceEngine.selection.test.ts
📚 Learning: 2025-12-21T07:54:34.875Z
Learnt from: CR
Repo: chhoumann/quickadd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T07:54:34.875Z
Learning: Add regression coverage for bug fixes in tests.

Applied to files:

  • src/migrations/backfillFileOpeningDefaults.test.ts
📚 Learning: 2025-12-21T07:54:34.875Z
Learnt from: CR
Repo: chhoumann/quickadd PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-21T07:54:34.875Z
Learning: Applies to **/*.{ts,tsx} : Prefer type-only imports in TypeScript.

Applied to files:

  • src/types/choices/TemplateChoice.ts
🧬 Code graph analysis (9)
src/types/choices/CaptureChoice.ts (1)
src/utils/fileOpeningDefaults.ts (1)
  • normalizeFileOpening (17-48)
src/engine/TemplateChoiceEngine.ts (2)
src/utils/fileOpeningDefaults.ts (1)
  • normalizeFileOpening (17-48)
src/utilityObsidian.ts (2)
  • openExistingFileTab (874-899)
  • openFile (790-868)
src/migrations/helpers/choice-traversal.ts (7)
src/types/choices/IChoice.ts (1)
  • IChoice (3-10)
src/migrations/helpers/isMultiChoice.ts (1)
  • isMultiChoice (3-14)
src/types/choices/MultiChoice.ts (1)
  • MultiChoice (5-23)
src/types/choices/IMacroChoice.ts (1)
  • IMacroChoice (4-7)
src/types/macros/ICommand.ts (1)
  • ICommand (3-7)
src/types/macros/Conditional/IConditionalCommand.ts (1)
  • IConditionalCommand (4-8)
src/types/macros/QuickCommands/INestedChoiceCommand.ts (1)
  • INestedChoiceCommand (4-6)
src/engine/CaptureChoiceEngine.selection.test.ts (2)
src/types/choices/ICaptureChoice.ts (1)
  • ICaptureChoice (7-56)
src/engine/CaptureChoiceEngine.ts (1)
  • CaptureChoiceEngine (41-537)
src/gui/ChoiceBuilder/choiceBuilder.ts (1)
src/utils/fileOpeningDefaults.ts (2)
  • normalizeFileOpening (17-48)
  • FileOpeningSettings (3-8)
src/migrations/migrateFileOpeningSettings.ts (2)
src/migrations/helpers/file-opening-legacy.ts (1)
  • coerceLegacyOpenFileInNewTab (12-30)
src/migrations/helpers/choice-traversal.ts (1)
  • walkAllChoices (77-90)
src/types/choices/TemplateChoice.ts (1)
src/utils/fileOpeningDefaults.ts (1)
  • normalizeFileOpening (17-48)
src/engine/CaptureChoiceEngine.ts (3)
src/utils/fileOpeningDefaults.ts (1)
  • normalizeFileOpening (17-48)
src/utilityObsidian.ts (2)
  • openExistingFileTab (874-899)
  • openFile (790-868)
tests/obsidian-stub.ts (1)
  • openFile (363-363)
src/migrations/helpers/file-opening-legacy.ts (1)
src/utils/fileOpeningDefaults.ts (2)
  • FileOpeningSettings (3-8)
  • normalizeFileOpening (17-48)
🪛 GitHub Actions: Build With Lint
src/migrations/backfillFileOpeningDefaults.test.ts

[error] 144-144: TypeScript error TS2339: Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; openFileInNewTab: { enabled: boolean; direction: string; focus: boolean; }; openFileInMode: string; }'. (During bun run build-with-lint: tsc -noEmit -skipLibCheck)

🪛 GitHub Check: Build With Lint
src/migrations/backfillFileOpeningDefaults.test.ts

[failure] 186-186:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 180-180:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 174-174:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 168-168:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 162-162:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 150-150:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; }'.


[failure] 144-144:
Property 'fileOpening' does not exist on type '{ id: string; name: string; type: string; openFileInNewTab: { enabled: boolean; direction: string; focus: boolean; }; openFileInMode: string; }'.

src/migrations/backfillFileOpeningDefaults.ts

[failure] 54-54:
Argument of type 'Partial | undefined' is not assignable to parameter of type '(Partial & ({ location: OpenLocation; direction: "vertical" | "horizontal"; mode: FileViewMode2; focus: boolean; } | { ...; })) | null | undefined'.

🔇 Additional comments (18)
src/settings.ts (1)

50-50: LGTM! Migration flag properly added.

The new backfillFileOpeningDefaults migration flag follows the established pattern and is correctly initialized to false in the default settings.

Also applies to: 91-91

src/engine/CaptureChoiceEngine.template-property-types.test.ts (1)

40-40: LGTM! Test mock updated correctly.

The test mock settings correctly include the new migration flag, maintaining alignment with the production settings schema.

src/utils/fileOpeningDefaults.ts (2)

1-8: LGTM! Clean type definition.

The FileOpeningSettings type is well-structured with appropriate imported types and clear property definitions.


10-15: LGTM! Sensible defaults established.

The default file opening settings are reasonable: new files open in tabs with vertical splits in default mode and receive focus.

src/engine/TemplateChoiceEngine.notice.test.ts (1)

35-35: LGTM! Test mock synchronized with settings schema.

The migration flag has been correctly added to the test mock settings.

src/engine/MacroChoiceEngine.notice.test.ts (1)

35-35: LGTM! Test mock properly updated.

The new migration flag is correctly included in the mock settings, maintaining consistency with the broader settings schema changes.

src/engine/TemplateChoiceEngine.ts (1)

188-188: LGTM!

Passing the normalized fileOpening object ensures consistent defaults are applied when opening the file.

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

6-6: LGTM!

Centralizing the file opening defaults through normalizeFileOpening() improves maintainability and ensures consistency across choice types.

Also applies to: 43-43

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

6-6: LGTM!

The normalization pattern is consistently applied across both Template and Capture choice types.

Also applies to: 79-79

src/engine/CaptureChoiceEngine.ts (1)

196-196: LGTM!

Passing the normalized fileOpening object ensures consistent defaults are applied.

src/gui/ChoiceBuilder/choiceBuilder.ts (1)

6-9: LGTM! Well-structured centralization of file opening defaults.

The changes properly delegate default initialization to the centralized normalizeFileOpening utility and adopt the shared FileOpeningSettings type, improving consistency across the codebase.

Also applies to: 137-137, 139-139

src/engine/CaptureChoiceEngine.selection.test.ts (1)

174-210: Excellent test coverage for default file opening behavior.

The test properly validates that when fileOpening is undefined, the engine normalizes it to the expected defaults before passing it to openFile. The test setup and assertions are clear and comprehensive.

src/engine/CaptureChoiceEngine.notice.test.ts (1)

35-35: LGTM! Proper test mock maintenance.

Correctly adds the new migration flag to the test settings mock to keep it aligned with the actual settings structure.

src/migrations/migrate.ts (1)

11-11: LGTM! Migration properly registered.

The new backfillFileOpeningDefaults migration is correctly imported and added to the migrations map, following the established pattern.

Also applies to: 23-23

src/migrations/helpers/file-opening-legacy.ts (1)

1-49: LGTM! Clean legacy mapping implementation.

The helper functions correctly handle legacy file-opening settings conversion:

  • coerceLegacyOpenFileInNewTab safely normalizes various legacy input formats (boolean, object, null/undefined)
  • createFileOpeningFromLegacy properly maps legacy fields to the new FileOpeningSettings structure with appropriate defaults
  • Type validation and fallback logic are sound
src/migrations/helpers/choice-traversal.ts (1)

16-90: Solid traversal implementation with proper cycle detection.

The traversal utilities correctly handle:

  • Cycle prevention via visited set
  • Multi choices and nested structures
  • Macro choices and their commands
  • Conditional and NestedChoice commands with defensive type checks
  • Legacy macro support with type assertions where necessary

The defensive checks at lines 53-56 and 64-69 appropriately handle edge cases in legacy data.

src/migrations/migrateFileOpeningSettings.ts (2)

28-32: Verify migration condition handles failed coercion.

The condition at line 32 checks legacyTabRaw but uses legacyTab (the coerced version). If coerceLegacyOpenFileInNewTab returns null (e.g., when legacyTabRaw is a string), the migration still proceeds with all default values via optional chaining at lines 35-37.

Consider whether the condition should also check legacyTab to skip migration when coercion fails:

if (!templateOrCaptureChoice.fileOpening && legacyTab) {

This ensures migration only occurs when legacy settings are successfully coerced.

Do you want the migration to apply defaults when legacy settings are invalid, or skip migration entirely?


7-11: Excellent refactor to use centralized helpers.

The migration now correctly delegates to:

  • walkAllChoices for comprehensive traversal (including nested and legacy structures)
  • coerceLegacyOpenFileInNewTab and createFileOpeningFromLegacy for consistent legacy handling

This eliminates code duplication and aligns with the shared migration utilities introduced in this PR.

Also applies to: 52-52

Comment thread src/migrations/backfillFileOpeningDefaults.test.ts Outdated
Comment thread src/migrations/backfillFileOpeningDefaults.ts
Comment thread src/utils/fileOpeningDefaults.ts Outdated
Copy link
Copy Markdown

@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 (3)
src/migrations/migrateFileOpeningSettings.ts (1)

28-48: Optional: Redundant optional chaining.

Lines 35-37 use optional chaining (legacyTab?.enabled, etc.) even though line 32's condition ensures legacyTab is truthy. While this defensive pattern is safe and may protect against future refactoring, it's technically redundant.

Consider simplifying if you prefer directness:

const tabSettings = {
	enabled: legacyTab.enabled ?? false,
	direction: legacyTab.direction ?? "vertical",
	focus: legacyTab.focus ?? true,
};

That said, the current code is perfectly safe and may be preferred for resilience.

src/utils/fileOpeningDefaults.ts (2)

30-46: Consider safer type construction pattern.

Line 30's type assertion is aggressive—it asserts a potentially incomplete object as FileOpeningSettings & TExtras before the required fields are populated (lines 32-43). While this works in the current code, it could cause confusion during future modifications.

Consider a safer pattern:

♻️ Suggested refactor for improved type safety
-const normalized = { ...(fileOpening ?? {}) } as FileOpeningSettings & TExtras;
-
-if (normalized.location == null) {
-	normalized.location = DEFAULT_FILE_OPENING.location;
-}
-if (normalized.direction == null) {
-	normalized.direction = DEFAULT_FILE_OPENING.direction;
-}
-if (normalized.mode == null) {
-	normalized.mode = DEFAULT_FILE_OPENING.mode;
-}
-if (normalized.focus == null) {
-	normalized.focus = DEFAULT_FILE_OPENING.focus;
-}
-
-return normalized;
+return {
+	location: fileOpening?.location ?? DEFAULT_FILE_OPENING.location,
+	direction: fileOpening?.direction ?? DEFAULT_FILE_OPENING.direction,
+	mode: fileOpening?.mode ?? DEFAULT_FILE_OPENING.mode,
+	focus: fileOpening?.focus ?? DEFAULT_FILE_OPENING.focus,
+	...(fileOpening ?? {}),
+} as FileOpeningSettings & TExtras;

This builds the complete object in one expression, making the type assertion provably safe.


17-46: Optional: Consider runtime validation for enum-like fields.

The function doesn't validate that direction is "vertical" or "horizontal", or that location/mode match their expected enum values. While TypeScript provides compile-time safety, runtime validation could catch issues from untyped sources (e.g., JSON parsing, external APIs).

This is likely intentional to keep the function lightweight, but consider adding validation if robustness is a priority.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ba726f and 54e03d6.

📒 Files selected for processing (3)
  • src/migrations/backfillFileOpeningDefaults.test.ts
  • src/migrations/migrateFileOpeningSettings.ts
  • src/utils/fileOpeningDefaults.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/migrations/backfillFileOpeningDefaults.test.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx,mts,mjs,js,json}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,mts,mjs,js,json}: Use tab indentation with width 2 in TypeScript and configuration files (enforced by Biome).
Follow an 80-character line guide (enforced by Biome).

Files:

  • src/migrations/migrateFileOpeningSettings.ts
  • src/utils/fileOpeningDefaults.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use camelCase for variables and functions in TypeScript.
Prefer type-only imports in TypeScript.

Files:

  • src/migrations/migrateFileOpeningSettings.ts
  • src/utils/fileOpeningDefaults.ts
**/*.{ts,tsx,svelte}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for classes and Svelte components.

Files:

  • src/migrations/migrateFileOpeningSettings.ts
  • src/utils/fileOpeningDefaults.ts
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Route logging through the logger utilities for consistent output.

Files:

  • src/migrations/migrateFileOpeningSettings.ts
  • src/utils/fileOpeningDefaults.ts
src/{engine,services,utils}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Structure production code so Obsidian dependencies are injected behind interfaces to enable unit testing without loading real Obsidian modules.

Files:

  • src/utils/fileOpeningDefaults.ts
🔇 Additional comments (3)
src/utils/fileOpeningDefaults.ts (2)

1-15: LGTM! Clean type definition and sensible defaults.

The FileOpeningSettings type and DEFAULT_FILE_OPENING constant provide a clear, centralized contract for file-opening behavior. The defaults (tab location, vertical split, default mode, auto-focus) are reasonable choices.


23-28: Correct merge behavior for full override mode.

When fillMissingOnly is false, the function correctly merges defaults first, then applies overrides. The spread operator sequence ensures provided values take precedence.

src/migrations/migrateFileOpeningSettings.ts (1)

52-52: walkAllChoices correctly traverses all choice types and structures.

The implementation properly handles:

  • All root-level choices via plugin.settings.choices
  • Nested Multi-choice children through recursive walkChoice calls
  • Macro-embedded choices and legacy macros via walkCommands
  • Circular references prevention using a visited Set

The migration is safe to use.

@chhoumann chhoumann merged commit bfffd46 into master Jan 12, 2026
4 checks passed
@chhoumann chhoumann deleted the focus branch January 12, 2026 16:03
@github-actions
Copy link
Copy Markdown

🎉 This PR is included in version 2.10.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant