refactor: improve plugin stability with filesystem checks, add ESLint configuration, and update settings UI labels

This commit is contained in:
Rhys 2026-05-04 11:43:14 +02:00
parent 20972accc4
commit de7db324c3
5 changed files with 4563 additions and 13 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

20
eslint.config.js Normal file
View file

@ -0,0 +1,20 @@
// eslint.config.mjs
import tsparser from "@typescript-eslint/parser";
import { defineConfig } from "eslint/config";
import obsidianmd from "eslint-plugin-obsidianmd";
export default defineConfig([
...obsidianmd.configs.recommended,
{
files: ["**/*.ts"],
languageOptions: {
parser: tsparser,
parserOptions: { project: "./tsconfig.json" },
},
rules: {
"obsidianmd/sample-names": "off",
"obsidianmd/prefer-file-manager-trash-file": "error",
},
},
]);

4513
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,8 @@
"type": "module",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc --noEmit --skipLibCheck && node esbuild.config.mjs production"
"build": "tsc --noEmit --skipLibCheck && node esbuild.config.mjs production",
"lint": "eslint \"**/*.ts\""
},
"license": "MIT",
"dependencies": {
@ -14,8 +15,11 @@
},
"devDependencies": {
"@types/node": "^22.0.0",
"@typescript-eslint/parser": "^8.59.1",
"builtin-modules": "^4.0.0",
"esbuild": "^0.25.5",
"eslint": "^9.39.4",
"eslint-plugin-obsidianmd": "^0.2.9",
"typescript": "^5.8.3"
}
}

View file

@ -1,5 +1,6 @@
import {
App,
FileSystemAdapter,
Modal,
Notice,
Plugin,
@ -111,17 +112,16 @@ class DocDropSettingTab extends PluginSettingTab {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("DocDrop")
.setHeading();
new Setting(containerEl)
.setName("Executable path")
.setDesc(
// /skip -- PATH is an environment variable, all-caps is correct
// /skip -- "markitdown" is the tool's actual lowercase name
'Full path to the markitdown binary, or just "markitdown" if it is on your PATH.'
)
.addText((text) =>
text
// /skip -- "markitdown" is the tool's actual lowercase name
.setPlaceholder("markitdown")
.setValue(this.plugin.settings.executablePath)
.onChange((value) => {
@ -149,6 +149,7 @@ class DocDropSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName("Custom output folder")
.setDesc(
// /skip -- "Converted" is an example folder name shown verbatim, not a UI label
'Vault-relative path, e.g. "Converted". The folder must already exist.'
)
.addText((text) =>
@ -163,7 +164,7 @@ class DocDropSettingTab extends PluginSettingTab {
}
new Setting(containerEl)
.setName("Conversion options")
.setName("Conversion")
.setHeading();
new Setting(containerEl)
@ -183,6 +184,7 @@ class DocDropSettingTab extends PluginSettingTab {
);
new Setting(containerEl)
// /skip -- MIME is an acronym, all-caps is correct
.setName("MIME type hint")
.setDesc(
"Tells markitdown what kind of file it is receiving, e.g. \"application/pdf\". " +
@ -191,6 +193,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) =>
text
// /skip -- "application/pdf" is a MIME type string, lowercase per spec
.setPlaceholder("application/pdf")
.setValue(this.plugin.settings.mimeType)
.onChange((value) => {
@ -218,6 +221,7 @@ class DocDropSettingTab extends PluginSettingTab {
// ── markitdown-ocr ──────────────────────────────────────────────────────
new Setting(containerEl)
// /skip -- "markitdown-ocr" is the tool's actual lowercase name
.setName("markitdown-ocr plugin (optional)")
.setHeading();
@ -257,6 +261,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) => {
text
// /skip -- "sk-proj-..." is an API key format placeholder, lowercase is the actual format
.setPlaceholder("sk-proj-...")
.setValue(this.plugin.settings.openAiApiKey)
.onChange((value) => {
@ -275,6 +280,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) =>
text
// /skip -- "gpt-4o" is the model's API identifier, lowercase per OpenAI convention
.setPlaceholder("gpt-4o")
.setValue(this.plugin.settings.openAiModel)
.onChange((value) => {
@ -293,6 +299,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) =>
text
// /skip -- URL placeholder, case-sensitive and must match actual endpoint
.setPlaceholder("https://api.openai.com/v1")
.setValue(this.plugin.settings.openAiBaseUrl)
.onChange((value) => {
@ -305,6 +312,7 @@ class DocDropSettingTab extends PluginSettingTab {
// ── Azure Document Intelligence ─────────────────────────────────────────
new Setting(containerEl)
// /skip -- "Document Intelligence" is a proper Microsoft product name
.setName("Azure Document Intelligence (optional)")
.setHeading();
@ -318,6 +326,7 @@ class DocDropSettingTab extends PluginSettingTab {
});
new Setting(containerEl)
// /skip -- "Document Intelligence" is a proper Microsoft product name
.setName("Use Document Intelligence")
.setDesc(
"Send the PDF to Microsoft Azure for conversion instead of processing it locally. " +
@ -343,6 +352,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) =>
text
// /skip -- URL placeholder, case-sensitive and must match actual endpoint format
.setPlaceholder("https://your-resource.cognitiveservices.azure.com/")
.setValue(this.plugin.settings.docIntelEndpoint)
.onChange((value) => {
@ -360,6 +370,7 @@ class DocDropSettingTab extends PluginSettingTab {
)
.addText((text) => {
text
// /skip -- "Azure" is a proper noun
.setPlaceholder("Your Azure API key")
.setValue(this.plugin.settings.docIntelApiKey)
.onChange((value) => {
@ -395,7 +406,7 @@ export default class DocDropPlugin extends Plugin {
new Notice("The active file is not a PDF.");
return;
}
this.convertPdf(activeFile);
void this.convertPdf(activeFile);
},
});
@ -406,6 +417,7 @@ export default class DocDropPlugin extends Plugin {
menu.addItem((item) => {
item
// /skip -- "DocDrop" is the plugin's proper name
.setTitle("Convert to Markdown with DocDrop")
.setIcon("file-text")
.onClick(() => this.convertPdf(file));
@ -414,10 +426,10 @@ export default class DocDropPlugin extends Plugin {
);
}
onunload(): void {}
onunload(): void { }
async loadSettings(): Promise<void> {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<DocDropSettings>);
}
async saveSettings(): Promise<void> {
@ -426,7 +438,12 @@ export default class DocDropPlugin extends Plugin {
private async convertPdf(pdfFile: TFile): Promise<void> {
const adapter = this.app.vault.adapter;
const basePath = (adapter as { getBasePath(): string }).getBasePath();
if (!(adapter instanceof FileSystemAdapter)) {
// /skip -- "DocDrop" is the plugin's proper name
new Notice("DocDrop only works on desktop (local vault).");
return;
}
const basePath = adapter.getBasePath();
const absolutePdfPath = `${basePath}/${pdfFile.path}`;
const outputVaultPath = this.resolveOutputPath(pdfFile);
@ -442,7 +459,7 @@ export default class DocDropPlugin extends Plugin {
new OverwriteModal(
this.app,
outputVaultPath,
() => this.runConversion(absolutePdfPath, outputVaultPath, existing),
() => { void this.runConversion(absolutePdfPath, outputVaultPath, existing); },
() => new Notice("Conversion cancelled.")
).open();
return;