Merge branch 'worktree-agent-a5fb028c'

# Conflicts:
#	src/modals/FolderConvertModal.ts
#	src/types/settings.ts
This commit is contained in:
Ethan Troy 2026-03-17 20:40:48 -04:00
commit 678b6195be
6 changed files with 72 additions and 8 deletions

View file

@ -14,6 +14,7 @@ import { checkDependencies, installPackage } from './src/utils/python';
import {
getVaultBasePath,
resolveOutputFolder,
resolveFilenameTemplate,
resolveImageDir,
toVaultRelative,
} from './src/utils/paths';
@ -147,8 +148,11 @@ export default class MarkitdownPlugin extends Plugin {
const inputPath = path.join(vaultPath, file.path);
const outputFolder = resolveOutputFolder(vaultPath, this.settings.outputPath);
const baseName = file.basename;
const outputPath = path.join(outputFolder, `${baseName}.md`);
const resolvedName = resolveFilenameTemplate(
this.settings.outputFilenameTemplate || '{filename}',
inputPath
);
const outputPath = path.join(outputFolder, `${resolvedName}.md`);
const options = this.buildConversionOptions(outputPath);
new Notice('Converting file...');

View file

@ -3,7 +3,7 @@ import * as path from 'path';
import * as fs from 'fs';
import type MarkitdownPlugin from '../../main';
import { FILE_INPUT_ACCEPT } from '../utils/fileTypes';
import { getVaultBasePath, resolveOutputFolder, toVaultRelative } from '../utils/paths';
import { getVaultBasePath, resolveOutputFolder, resolveFilenameTemplate, toVaultRelative } from '../utils/paths';
export class FileConvertModal extends Modal {
private plugin: MarkitdownPlugin;
@ -51,8 +51,11 @@ export class FileConvertModal extends Modal {
}
const outputFolder = resolveOutputFolder(vaultPath, this.plugin.settings.outputPath);
const baseName = path.basename(file.name, path.extname(file.name));
const outputPath = path.join(outputFolder, `${baseName}.md`);
const resolvedName = resolveFilenameTemplate(
this.plugin.settings.outputFilenameTemplate || '{filename}',
file.name
);
const outputPath = path.join(outputFolder, `${resolvedName}.md`);
// Write the DOM File to a temp file on disk
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');

View file

@ -3,7 +3,7 @@ import * as path from 'path';
import * as fs from 'fs';
import type MarkitdownPlugin from '../../main';
import { EXTENSION_GROUPS } from '../utils/fileTypes';
import { getVaultBasePath, resolveOutputFolder } from '../utils/paths';
import { getVaultBasePath, resolveOutputFolder, resolveFilenameTemplate } from '../utils/paths';
import { BatchProgressModal } from './BatchProgressModal';
/**
@ -176,7 +176,10 @@ export class FolderConvertModal extends Modal {
progressModal?.updateProgress(i, entry.name, success, failed);
// Determine the output path, preserving subfolder structure
const baseName = path.basename(entry.name, path.extname(entry.name));
const resolvedName = resolveFilenameTemplate(
this.plugin.settings.outputFilenameTemplate || '{filename}',
entry.name
);
const relativeDir = path.dirname(entry.relativePath);
const entryOutputFolder = relativeDir && relativeDir !== '.'
? path.join(outputFolder, relativeDir)
@ -185,7 +188,7 @@ export class FolderConvertModal extends Modal {
// Ensure the output subdirectory exists
await fs.promises.mkdir(entryOutputFolder, { recursive: true });
const outputPath = path.join(entryOutputFolder, `${baseName}.md`);
const outputPath = path.join(entryOutputFolder, `${resolvedName}.md`);
const safeName = entry.name.replace(/[^a-zA-Z0-9._-]/g, '_');
const tempFilePath = path.join(entryOutputFolder, `tmp_${Date.now()}_${i}_${safeName}`);

View file

@ -84,6 +84,21 @@ export class SettingsTab extends PluginSettingTab {
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Output filename template')
.setDesc(
'Template for converted filenames. Variables: {filename} (original name), ' +
'{ext} (original extension), {date} (YYYY-MM-DD), {datetime} (YYYY-MM-DD_HHmmss). ' +
'The .md extension is always appended automatically.'
)
.addText(text => text
.setPlaceholder('{filename}')
.setValue(this.plugin.settings.outputFilenameTemplate)
.onChange(async (value) => {
this.plugin.settings.outputFilenameTemplate = value || '{filename}';
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Extract images')
.setDesc('Extract embedded images from PDFs and save as separate files')

View file

@ -14,6 +14,7 @@ export interface MarkitdownSettings {
enableBatchProgress: boolean;
enableContextMenu: boolean;
enableRecursiveConversion: boolean;
outputFilenameTemplate: string;
}
export const DEFAULT_SETTINGS: MarkitdownSettings = {
@ -27,6 +28,7 @@ export const DEFAULT_SETTINGS: MarkitdownSettings = {
enableBatchProgress: true,
enableContextMenu: true,
enableRecursiveConversion: false,
outputFilenameTemplate: '{filename}',
};
export interface ConversionOptions {

View file

@ -37,6 +37,43 @@ export function resolveOutputFolder(vaultPath: string, outputSetting: string): s
return outputFolder;
}
/**
* Resolve an output filename template using variables from the input path.
* Supported variables:
* {filename} original file name without extension
* {ext} original file extension (without dot)
* {date} conversion date as YYYY-MM-DD
* {datetime} conversion datetime as YYYY-MM-DD_HHmmss
*
* Returns the resolved name without .md extension (caller appends it).
* Characters invalid in filenames are stripped from the result.
*/
export function resolveFilenameTemplate(template: string, inputPath: string): string {
const baseName = path.basename(inputPath, path.extname(inputPath));
const ext = path.extname(inputPath).replace(/^\./, '');
const now = new Date();
const pad = (n: number) => String(n).padStart(2, '0');
const date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
const datetime = `${date}_${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
let resolved = template
.replace(/\{filename\}/g, baseName)
.replace(/\{ext\}/g, ext)
.replace(/\{date\}/g, date)
.replace(/\{datetime\}/g, datetime);
// Remove characters invalid in filenames
resolved = resolved.replace(/[/\\:*?"<>|]/g, '');
// Fall back to the original filename if the result is empty after sanitisation
if (!resolved.trim()) {
resolved = baseName;
}
return resolved;
}
/**
* Compute the image extraction directory for a given output markdown file.
* Applies the template (e.g., "{filename}-images") to generate the folder name.