Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/Choices/CaptureChoice.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ This also supports the [format syntax](/FormatSyntax.md). You can even write a f
For example, you might have a folder called `CRM/people`. In this folder, you have a note for the people in your life. You can type `CRM/people` in the _Capture To_ field, and QuickAdd will ask you which file to capture to. You can then type `John Doe` in the suggester, and QuickAdd will create a file called `John Doe.md` in the `CRM/people` folder.

You could also write nothing - or `/` - in the _Capture To_ field. This will open the suggester with all of your files in it, and you can select or type the name of the file you want to capture to.
Paths are vault-relative. A leading `/` is ignored (except a lone `/`, which opens the file picker for the whole vault).

Capturing to a folder will show all files in that folder. This means that files in nested folders will also appear.

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/Choices/TemplateChoice.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: Template
The template choice type is not meant to be a replacement for [Templater](https://github.com/SilentVoid13/Templater/) plugin or core `Templates`. It's meant to augment them, to add more possibilities. You can use both QuickAdd format syntax in a Templater template - and both will work.

## Mandatory
**Template Path**. This is a path to the template you wish to insert.
**Template Path**. This is a path to the template you wish to insert. Paths are vault-relative; a leading `/` is ignored.

QuickAdd supports both markdown (`.md`) and canvas (`.canvas`) templates. When using a canvas template, the created file will also be a canvas file with the same extension.

Expand Down
26 changes: 26 additions & 0 deletions src/engine/QuickAddEngine.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest";
import { QuickAddEngine } from "./QuickAddEngine";

class TestEngine extends QuickAddEngine {
public constructor() {
super({} as any);
}

public normalize(folderPath: string, fileName: string): string {
return this.normalizeMarkdownFilePath(folderPath, fileName);
}

public run(): void {}
}

describe("QuickAddEngine path normalization", () => {
const engine = new TestEngine();

it("strips leading slashes from folder and file", () => {
expect(engine.normalize("/daily", "/note")).toBe("daily/note.md");
});

it("strips leading slashes from file-only paths", () => {
expect(engine.normalize("", "/review/daily")).toBe("review/daily.md");
});
});
9 changes: 7 additions & 2 deletions src/engine/QuickAddEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,17 @@ export abstract class QuickAddEngine {
}
}

protected stripLeadingSlash(path: string): string {
return path.replace(/^\/+/, "");
}

protected normalizeMarkdownFilePath(
folderPath: string,
fileName: string
): string {
const actualFolderPath: string = folderPath ? `${folderPath}/` : "";
const formattedFileName: string = fileName.replace(
const safeFolderPath = this.stripLeadingSlash(folderPath);
const actualFolderPath: string = safeFolderPath ? `${safeFolderPath}/` : "";
const formattedFileName: string = this.stripLeadingSlash(fileName).replace(
MARKDOWN_FILE_EXTENSION_REGEX,
""
);
Expand Down
12 changes: 6 additions & 6 deletions src/engine/TemplateEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,12 @@ export abstract class TemplateEngine extends QuickAddEngine {
fileName: string,
templatePath: string
): string {
const actualFolderPath: string = folderPath ? `${folderPath}/` : "";
const safeFolderPath = this.stripLeadingSlash(folderPath);
const actualFolderPath: string = safeFolderPath ? `${safeFolderPath}/` : "";
const extension = this.getTemplateExtension(templatePath);
const formattedFileName: string = fileName.replace(
MARKDOWN_FILE_EXTENSION_REGEX,
""
).replace(CANVAS_FILE_EXTENSION_REGEX, "");
const formattedFileName: string = this.stripLeadingSlash(fileName)
.replace(MARKDOWN_FILE_EXTENSION_REGEX, "")
.replace(CANVAS_FILE_EXTENSION_REGEX, "");
return `${actualFolderPath}${formattedFileName}${extension}`;
}

Expand Down Expand Up @@ -284,7 +284,7 @@ export abstract class TemplateEngine extends QuickAddEngine {
}

protected async getTemplateContent(templatePath: string): Promise<string> {
let correctTemplatePath: string = templatePath;
let correctTemplatePath: string = this.stripLeadingSlash(templatePath);
if (!MARKDOWN_FILE_EXTENSION_REGEX.test(templatePath) &&
!CANVAS_FILE_EXTENSION_REGEX.test(templatePath))
correctTemplatePath += ".md";
Expand Down
14 changes: 12 additions & 2 deletions src/engine/canvas-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ describe('Canvas Template Integration', () => {
});

describe('File path normalization', () => {
const stripLeadingSlash = (path: string): string => {
return path.replace(/^\/+/, "");
};

const normalizeTemplateFilePath = (
folderPath: string,
fileName: string,
Expand All @@ -61,9 +65,10 @@ describe('Canvas Template Integration', () => {
const MARKDOWN_REGEX = new RegExp(/\.md$/);
const CANVAS_REGEX = new RegExp(/\.canvas$/);

const actualFolderPath = folderPath ? `${folderPath}/` : "";
const safeFolderPath = stripLeadingSlash(folderPath);
const actualFolderPath = safeFolderPath ? `${safeFolderPath}/` : "";
const extension = CANVAS_REGEX.test(templatePath) ? ".canvas" : ".md";
const formattedFileName = fileName
const formattedFileName = stripLeadingSlash(fileName)
.replace(MARKDOWN_REGEX, "")
.replace(CANVAS_REGEX, "");
return `${actualFolderPath}${formattedFileName}${extension}`;
Expand All @@ -88,6 +93,11 @@ describe('Canvas Template Integration', () => {
expect(normalizeTemplateFilePath('', 'MyFile.md', 'template.canvas'))
.toBe('MyFile.canvas');
});

it('should strip leading slashes from folder and file names', () => {
expect(normalizeTemplateFilePath('/Templates', '/MyFile', 'template.md'))
.toBe('Templates/MyFile.md');
});
});

describe('Template path processing logic', () => {
Expand Down
13 changes: 7 additions & 6 deletions src/preflight/runOnePagePreflight.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ async function collectForCaptureChoice(

// If captureTo indicates a folder or tag, offer a file picker requirement
const formattedTarget = choice.captureTo?.trim() ?? "";
const isTagTarget = formattedTarget.startsWith("#");
const trimmedPath = formattedTarget.replace(/\/$|\.md$/g, "");
const normalizedTarget = formattedTarget.replace(/^\/+/, "");
const isTagTarget = normalizedTarget.startsWith("#");
const trimmedPath = normalizedTarget.replace(/\/$|\.md$/g, "");
const isFolderTarget =
!isTagTarget && (formattedTarget === "" || isFolder(app, trimmedPath));
!isTagTarget && (normalizedTarget === "" || isFolder(app, trimmedPath));
// Heuristics: if target ends with '/' or contains unresolved tokens, we likely need a picker
const looksLikeFolderBySuffix = formattedTarget.endsWith("/");
const looksLikeFolderBySuffix = normalizedTarget.endsWith("/");
const containsFormatTokens = /{{[^}]+}}/.test(choice.captureTo ?? "");

if (
Expand All @@ -111,9 +112,9 @@ async function collectForCaptureChoice(
) {
let files: TFile[] = [];
if (isTagTarget) {
files = getMarkdownFilesWithTag(app, formattedTarget);
files = getMarkdownFilesWithTag(app, normalizedTarget);
} else {
const folder = formattedTarget.replace(/^\/$|\/\.md$|^\.md$/, "");
const folder = normalizedTarget.replace(/^\/$|\/\.md$|^\.md$/, "");
const base =
folder === "" ? "" : folder.endsWith("/") ? folder : `${folder}/`;
files = getMarkdownFilesInFolder(app, base);
Expand Down