Skip to content

feat: support .base templates and guard capture targets#1122

Merged
chhoumann merged 9 commits intomasterfrom
1121-feature-request-allow-creating-and-templating-base-base-files
Feb 24, 2026
Merged

feat: support .base templates and guard capture targets#1122
chhoumann merged 9 commits intomasterfrom
1121-feature-request-allow-creating-and-templating-base-base-files

Conversation

@chhoumann
Copy link
Owner

@chhoumann chhoumann commented Feb 24, 2026

Summary

  • add first-class .base template support for Template choices (creation, overwrite, increment, and path normalization)
  • preserve explicit file extensions in Capture targets for supported capture types (.md / .canvas)
  • explicitly reject Capture targets ending in .base with a clear abort message directing users to Template choices
  • update one-page preflight/template token parsing to recognize .base
  • include docs updates for Template and Capture extension behavior
  • add a docs example for capturing from a .base template into the active markdown file

Issue

Testing

  • bun run test
  • bun run build-with-lint
  • dev vault verification via Obsidian CLI:
    • Template choice with .base template creates .base output
    • Capture choice with captureTo: *.base is blocked with MacroAbortError and guidance message
    • Capture-to-active-file with {{TEMPLATE:...base}} inserts resolved .base content into an active .md note
    • obsidian vault=dev dev:errors reported no errors

Summary by CodeRabbit

  • New Features

    • Support for .base templates alongside .md and .canvas; explicit extensions are preserved, names without extensions default to .md.
    • Capture can insert .base template content into the active note via a template token with Capture-to-active-file enabled.
  • Bug Fixes

    • Capture flow rejects creating .base files as direct capture targets.
  • Documentation

    • Clarified filename/extension behavior and added an example for inserting .base templates.
  • Tests

    • Expanded coverage for .base handling across capture, template, preflight, and naming flows.

@chhoumann chhoumann linked an issue Feb 24, 2026 that may be closed by this pull request
@vercel
Copy link

vercel bot commented Feb 24, 2026

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

Project Deployment Actions Updated (UTC)
quickadd Ready Ready Preview Feb 24, 2026 6:08pm

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds first-class support for the .base file extension across QuickAdd: regexes, path normalization, template/capture detection, title extraction, preflight scanning, tests, and docs updated to treat .base alongside .md and .canvas.

Changes

Cohort / File(s) Summary
Documentation
docs/docs/Choices/CaptureChoice.md, docs/docs/Choices/TemplateChoice.md, docs/docs/Examples/Capture_InsertBaseTemplateIntoActiveFile.md
Documented .base handling, guidance for Capture format / basename behavior, and an example for inserting .base template content into the active file.
Constants & Regex
src/constants.ts
Added BASE_FILE_EXTENSION_REGEX and expanded TEMPLATE_REGEX to accept .base (now matches `.md
Capture Engine
src/engine/CaptureChoiceEngine.ts, src/engine/CaptureChoiceEngine.selection.test.ts
New normalizeCaptureFilePath, switched detection to extension-regexes (md/canvas/base), use basenameWithoutMdOrCanvas for titles; tests updated to cover .base rejection and canvas/extensionless behaviors.
Template Engine & Tests
src/engine/TemplateEngine.ts, src/engine/templateEngine-increment-canvas.test.ts, src/engine/templateEngine-title.test.ts, src/engine/canvas-integration.test.ts
Treat .base as a valid template extension for resolution, normalization, title extraction, incremental naming, and related tests.
Template Choice Engine & Tests
src/engine/TemplateChoiceEngine.ts, src/engine/TemplateChoiceEngine.notice.test.ts
Recognize .base files as resolvable/overwritable; added test for overwrite behavior with .base.
Preflight & Scanning
src/preflight/runOnePagePreflight.ts, src/preflight/runOnePagePreflight.selection.test.ts, src/preflight/RequirementCollector.test.ts
Prevent appending .md for .base/.canvas template paths; ensure .base TEMPLATE references are discovered during preflight scanning.
Path Utilities
src/utils/pathUtils.ts
Strip .base (case-insensitive) from basenames alongside .md and .canvas.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

released

Poem

🐇 I hopped through code with whiskers bright,
Found .base tucked in templates at night,
Titles trimmed, captures tuned, tests that sing,
Boards and notes now neatly spring,
A carrot-coded, joyful byte!

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (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 specifically describes the main changes: adding support for .base templates and preventing capture to .base files.
Linked Issues check ✅ Passed All primary coding requirements from issue #1121 are met: .base template support via Template choices, explicit extension preservation in Capture, .base capture rejection with guidance, and template token parsing updates.
Out of Scope Changes check ✅ Passed All changes are directly related to .base template support and capture target guarding as specified in the PR objectives and linked issue #1121.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 1121-feature-request-allow-creating-and-templating-base-base-files

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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 24, 2026

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1ca1085
Status: ✅  Deploy successful!
Preview URL: https://a52ff9c8.quickadd.pages.dev
Branch Preview URL: https://1121-feature-request-allow-c.quickadd.pages.dev

View logs

devin-ai-integration[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/engine/CaptureChoiceEngine.ts (1)

472-474: ⚠️ Potential issue | 🟠 Major

Strip non‑.md extensions when deriving the capture title.
With .base / .canvas targets now preserved, fileBasename keeps the extension and leaks it into {{TITLE}} and front‑matter variables. Please strip all known extensions here.

🔧 Suggested fix
-		const fileBasename = filePath.split("/").pop()?.replace(/\.md$/, "") || "";
+		const fileBasename =
+			filePath
+				.split("/")
+				.pop()
+				?.replace(/\.(md|canvas|base)$/i, "") || "";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/CaptureChoiceEngine.ts` around lines 472 - 474, The current
extraction for fileBasename (in CaptureChoiceEngine where filePath is split and
then passed to this.formatter.setTitle) only strips ".md" and therefore leaves
other known extensions like ".base" and ".canvas" in the title; update the
derivation to remove all known capture-related extensions by replacing trailing
extensions (e.g. .md, .base, .canvas — case insensitive) from the popped
filename before calling this.formatter.setTitle so {{TITLE}} and front‑matter
variables never include those extensions.
🧹 Nitpick comments (3)
src/utils/pathUtils.ts (1)

3-6: Function name is now misleading.
basenameWithoutMdOrCanvas also strips .base; consider renaming (e.g., basenameWithoutKnownExtension) or adding a brief comment to avoid confusion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/pathUtils.ts` around lines 3 - 6, The function
basenameWithoutMdOrCanvas incorrectly implies it only removes .md and .canvas
while it also strips .base; either rename the function (e.g.,
basenameWithoutKnownExtension) and update all call sites to the new name, or add
a concise JSDoc comment above basenameWithoutMdOrCanvas explaining that it
removes .md, .canvas, and .base extensions; ensure you update any tests or
imports referencing basenameWithoutMdOrCanvas to match the chosen change and
keep the implementation in basenameWithoutMdOrCanvas (or the new name)
unchanged.
src/engine/canvas-integration.test.ts (1)

6-8: Consider importing regex constants from src/constants.ts rather than redefining them locally.

The test file defines MARKDOWN_REGEX, CANVAS_REGEX, and BASE_REGEX inline in several describe blocks. If the canonical constants in src/constants.ts are ever changed (e.g., a flag added), these tests won't catch the divergence and could give false assurance.

♻️ Example for the first describe block
+import {
+  MARKDOWN_FILE_EXTENSION_REGEX,
+  CANVAS_FILE_EXTENSION_REGEX,
+  BASE_FILE_EXTENSION_REGEX,
+} from '../constants';

 describe('Canvas Template Integration', () => {
   describe('Regex patterns for canvas support', () => {
-    const MARKDOWN_REGEX = new RegExp(/\.md$/);
-    const CANVAS_REGEX = new RegExp(/\.canvas$/);
-    const BASE_REGEX = new RegExp(/\.base$/);
+    const MARKDOWN_REGEX = MARKDOWN_FILE_EXTENSION_REGEX;
+    const CANVAS_REGEX = CANVAS_FILE_EXTENSION_REGEX;
+    const BASE_REGEX = BASE_FILE_EXTENSION_REGEX;

Also applies to: 40-41, 78-80, 130-132

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/canvas-integration.test.ts` around lines 6 - 8, Replace the local
regex redefinitions in the test with the canonical exported regex constants by
importing MARKDOWN_REGEX, CANVAS_REGEX, and BASE_REGEX from the project's
constants module and use those imports in each describe block (instead of
redeclaring new RegExp instances); update the top of canvas-integration.test.ts
to import the exported constants and remove the inline new RegExp(...)
declarations so tests always reference the single source of truth.
src/engine/TemplateEngine.ts (1)

517-518: basenameWithoutMdOrCanvas name is now stale — consider renaming in a follow-up.

pathUtils.ts was updated in this PR to also strip .base (via /\.(md|canvas|base)$/i), but the function is still called basenameWithoutMdOrCanvas. A name like basenameWithoutKnownExtension would more accurately reflect its current behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/TemplateEngine.ts` around lines 517 - 518, The callsite in
TemplateEngine.ts still uses the outdated function name
basenameWithoutMdOrCanvas; rename the utility to a clearer name (e.g.,
basenameWithoutKnownExtension) and update this usage and any other
imports/usages accordingly: change references to basenameWithoutMdOrCanvas ->
basenameWithoutKnownExtension, update the import in TemplateEngine.ts and the
exported function name in pathUtils.ts (or wherever it's defined), and run a
project-wide search to update all occurrences so the runtime and types remain
consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/engine/CaptureChoiceEngine.ts`:
- Around line 472-474: The current extraction for fileBasename (in
CaptureChoiceEngine where filePath is split and then passed to
this.formatter.setTitle) only strips ".md" and therefore leaves other known
extensions like ".base" and ".canvas" in the title; update the derivation to
remove all known capture-related extensions by replacing trailing extensions
(e.g. .md, .base, .canvas — case insensitive) from the popped filename before
calling this.formatter.setTitle so {{TITLE}} and front‑matter variables never
include those extensions.

---

Nitpick comments:
In `@src/engine/canvas-integration.test.ts`:
- Around line 6-8: Replace the local regex redefinitions in the test with the
canonical exported regex constants by importing MARKDOWN_REGEX, CANVAS_REGEX,
and BASE_REGEX from the project's constants module and use those imports in each
describe block (instead of redeclaring new RegExp instances); update the top of
canvas-integration.test.ts to import the exported constants and remove the
inline new RegExp(...) declarations so tests always reference the single source
of truth.

In `@src/engine/TemplateEngine.ts`:
- Around line 517-518: The callsite in TemplateEngine.ts still uses the outdated
function name basenameWithoutMdOrCanvas; rename the utility to a clearer name
(e.g., basenameWithoutKnownExtension) and update this usage and any other
imports/usages accordingly: change references to basenameWithoutMdOrCanvas ->
basenameWithoutKnownExtension, update the import in TemplateEngine.ts and the
exported function name in pathUtils.ts (or wherever it's defined), and run a
project-wide search to update all occurrences so the runtime and types remain
consistent.

In `@src/utils/pathUtils.ts`:
- Around line 3-6: The function basenameWithoutMdOrCanvas incorrectly implies it
only removes .md and .canvas while it also strips .base; either rename the
function (e.g., basenameWithoutKnownExtension) and update all call sites to the
new name, or add a concise JSDoc comment above basenameWithoutMdOrCanvas
explaining that it removes .md, .canvas, and .base extensions; ensure you update
any tests or imports referencing basenameWithoutMdOrCanvas to match the chosen
change and keep the implementation in basenameWithoutMdOrCanvas (or the new
name) unchanged.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7cbbf2 and 652737d.

📒 Files selected for processing (15)
  • docs/docs/Choices/CaptureChoice.md
  • docs/docs/Choices/TemplateChoice.md
  • src/constants.ts
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/engine/CaptureChoiceEngine.ts
  • src/engine/TemplateChoiceEngine.notice.test.ts
  • src/engine/TemplateChoiceEngine.ts
  • src/engine/TemplateEngine.ts
  • src/engine/canvas-integration.test.ts
  • src/engine/templateEngine-increment-canvas.test.ts
  • src/engine/templateEngine-title.test.ts
  • src/preflight/RequirementCollector.test.ts
  • src/preflight/runOnePagePreflight.selection.test.ts
  • src/preflight/runOnePagePreflight.ts
  • src/utils/pathUtils.ts

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.

🧹 Nitpick comments (2)
src/engine/CaptureChoiceEngine.ts (2)

473-475: basenameWithoutMdOrCanvas now also strips .base — name is stale.

The helper in src/utils/pathUtils.ts (line 4) uses /\.(md|canvas|base)$/i, so the function name no longer reflects its behavior. Consider renaming to something like basenameWithoutExtension or fileBasename to avoid confusion as more extensions are added.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/CaptureChoiceEngine.ts` around lines 473 - 475, The helper
function basenameWithoutMdOrCanvas now strips .base as well, so rename the
function to a more accurate name (e.g., basenameWithoutExtension or
fileBasename) and update all usages: rename the function declaration in the util
module (basenameWithoutMdOrCanvas -> basenameWithoutExtension), update the
import and call in CaptureChoiceEngine (the fileBasename assignment that calls
basenameWithoutMdOrCanvas), and adjust any other references/tests to the new
identifier to keep names consistent with behavior.

318-324: Extract a shared helper for the triple-regex extension check.

The same three-regex test appears in both resolveCaptureTarget (Lines 318-322) and normalizeCaptureFilePath (Lines 565-568). A small predicate keeps them in sync and makes it trivial to add future extensions.

Also note: the regex constants are case-sensitive (/\.base$/) while basenameWithoutMdOrCanvas in src/utils/pathUtils.ts uses a case-insensitive pattern (/\.(md|canvas|base)$/i). Consider aligning the casing behavior.

♻️ Suggested helper
+private hasKnownFileExtension(path: string): boolean {
+	return (
+		MARKDOWN_FILE_EXTENSION_REGEX.test(path) ||
+		CANVAS_FILE_EXTENSION_REGEX.test(path) ||
+		BASE_FILE_EXTENSION_REGEX.test(path)
+	);
+}

Then in both call sites:

-		if (
-			MARKDOWN_FILE_EXTENSION_REGEX.test(normalizedCaptureTo) ||
-			CANVAS_FILE_EXTENSION_REGEX.test(normalizedCaptureTo) ||
-			BASE_FILE_EXTENSION_REGEX.test(normalizedCaptureTo)
-		) {
+		if (this.hasKnownFileExtension(normalizedCaptureTo)) {
-		if (
-			MARKDOWN_FILE_EXTENSION_REGEX.test(normalizedPath) ||
-			CANVAS_FILE_EXTENSION_REGEX.test(normalizedPath) ||
-			BASE_FILE_EXTENSION_REGEX.test(normalizedPath)
-		) {
+		if (this.hasKnownFileExtension(normalizedPath)) {

Also applies to: 563-574

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/engine/CaptureChoiceEngine.ts` around lines 318 - 324, Extract a shared
predicate (e.g., isMdCanvasOrBaseExtension) that encapsulates the triple-regex
test currently duplicated in resolveCaptureTarget and normalizeCaptureFilePath;
have the helper accept the normalized path and return boolean by testing against
MARKDOWN_FILE_EXTENSION_REGEX, CANVAS_FILE_EXTENSION_REGEX and
BASE_FILE_EXTENSION_REGEX in one place. Replace the inline tests in
resolveCaptureTarget and normalizeCaptureFilePath with calls to this predicate,
and update the regex constants or the helper to use case-insensitive matching
(or to lower-case the input) so behavior aligns with basenameWithoutMdOrCanvas
in src/utils/pathUtils.ts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/engine/CaptureChoiceEngine.ts`:
- Around line 473-475: The helper function basenameWithoutMdOrCanvas now strips
.base as well, so rename the function to a more accurate name (e.g.,
basenameWithoutExtension or fileBasename) and update all usages: rename the
function declaration in the util module (basenameWithoutMdOrCanvas ->
basenameWithoutExtension), update the import and call in CaptureChoiceEngine
(the fileBasename assignment that calls basenameWithoutMdOrCanvas), and adjust
any other references/tests to the new identifier to keep names consistent with
behavior.
- Around line 318-324: Extract a shared predicate (e.g.,
isMdCanvasOrBaseExtension) that encapsulates the triple-regex test currently
duplicated in resolveCaptureTarget and normalizeCaptureFilePath; have the helper
accept the normalized path and return boolean by testing against
MARKDOWN_FILE_EXTENSION_REGEX, CANVAS_FILE_EXTENSION_REGEX and
BASE_FILE_EXTENSION_REGEX in one place. Replace the inline tests in
resolveCaptureTarget and normalizeCaptureFilePath with calls to this predicate,
and update the regex constants or the helper to use case-insensitive matching
(or to lower-case the input) so behavior aligns with basenameWithoutMdOrCanvas
in src/utils/pathUtils.ts.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 652737d and 964d672.

📒 Files selected for processing (2)
  • src/engine/CaptureChoiceEngine.selection.test.ts
  • src/engine/CaptureChoiceEngine.ts

coderabbitai[bot]

This comment was marked as resolved.

@chhoumann chhoumann changed the title feat: support .base files for template and capture workflows feat: support .base templates and guard capture targets Feb 24, 2026
@chhoumann chhoumann merged commit f2daeed into master Feb 24, 2026
5 checks passed
@chhoumann chhoumann deleted the 1121-feature-request-allow-creating-and-templating-base-base-files branch February 24, 2026 19:37
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.

[FEATURE REQUEST] Allow creating and templating base (.base) files

1 participant