mirror of
https://github.com/ethanolivertroy/obsidian-markitdown.git
synced 2026-07-22 05:41:54 +00:00
Add drag-and-drop file conversion (ENG-641)
This commit is contained in:
parent
a93ed8341d
commit
28780173d9
3 changed files with 99 additions and 1 deletions
88
main.ts
88
main.ts
|
|
@ -1,5 +1,6 @@
|
|||
import { Notice, Plugin, TFile } from 'obsidian';
|
||||
import { Notice, Plugin, TFile, MarkdownView, MarkdownFileInfo, Editor } from 'obsidian';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import {
|
||||
MarkitdownSettings,
|
||||
DEFAULT_SETTINGS,
|
||||
|
|
@ -66,6 +67,11 @@ export default class MarkitdownPlugin extends Plugin {
|
|||
this.registerFileMenu();
|
||||
}
|
||||
|
||||
// Drag-and-drop conversion
|
||||
if (this.settings.enableDragDrop) {
|
||||
this.registerDropHandler();
|
||||
}
|
||||
|
||||
// Settings tab
|
||||
this.addSettingTab(new SettingsTab(this.app, this));
|
||||
}
|
||||
|
|
@ -108,6 +114,86 @@ export default class MarkitdownPlugin extends Plugin {
|
|||
);
|
||||
}
|
||||
|
||||
/** Register drag-and-drop handler for converting dropped files. */
|
||||
private registerDropHandler() {
|
||||
this.registerEvent(
|
||||
this.app.workspace.on('editor-drop', (evt: DragEvent, editor: Editor, info: MarkdownView | MarkdownFileInfo) => {
|
||||
const files = evt.dataTransfer?.files;
|
||||
if (!files || files.length === 0) return;
|
||||
|
||||
// Check if any dropped file is convertible
|
||||
const convertibleFiles: File[] = [];
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const ext = path.extname(file.name).toLowerCase().replace(/^\./, '');
|
||||
if (isConvertible(ext)) {
|
||||
convertibleFiles.push(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (convertibleFiles.length === 0) return;
|
||||
|
||||
// Intercept the event for convertible files
|
||||
evt.preventDefault();
|
||||
|
||||
// Process each convertible file asynchronously
|
||||
for (const file of convertibleFiles) {
|
||||
this.handleDroppedFile(file, editor).catch((error) => {
|
||||
const msg = error instanceof Error ? error.message : String(error);
|
||||
new Notice(`Drop conversion error: ${msg}`);
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/** Handle a single dropped file: write to temp, convert, insert link, clean up. */
|
||||
private async handleDroppedFile(file: File, editor: Editor): Promise<void> {
|
||||
const vaultPath = getVaultBasePath(this.app);
|
||||
if (!vaultPath) {
|
||||
new Notice('Could not determine vault path. This plugin requires a local vault.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.dependencyStatus.markitdownInstalled) {
|
||||
new SetupModal(this.app, this).open();
|
||||
return;
|
||||
}
|
||||
|
||||
new Notice(`Converting dropped file: ${file.name}...`);
|
||||
|
||||
const outputFolder = resolveOutputFolder(vaultPath, this.settings.outputPath);
|
||||
const baseName = path.basename(file.name, path.extname(file.name));
|
||||
const outputPath = path.join(outputFolder, `${baseName}.md`);
|
||||
|
||||
// Write the DOM File to a temp file on disk
|
||||
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, '_');
|
||||
const tempFilePath = path.join(outputFolder, `tmp_${Date.now()}_${safeName}`);
|
||||
const buffer = await file.arrayBuffer();
|
||||
await fs.promises.writeFile(tempFilePath, Buffer.from(buffer));
|
||||
|
||||
try {
|
||||
const result = await this.convertExternalFile(tempFilePath, outputPath);
|
||||
|
||||
if (result.success) {
|
||||
const relativePath = toVaultRelative(outputPath, vaultPath);
|
||||
const linkText = `[[${relativePath.replace(/\.md$/, '')}]]`;
|
||||
const cursor = editor.getCursor();
|
||||
editor.replaceRange(linkText, cursor);
|
||||
|
||||
const msg = result.imagesExtracted
|
||||
? `Converted ${file.name} (${result.imagesExtracted} images extracted)`
|
||||
: `Converted ${file.name} successfully`;
|
||||
new Notice(msg);
|
||||
} else {
|
||||
new Notice(`Conversion failed for ${file.name}: ${result.error}`);
|
||||
}
|
||||
} finally {
|
||||
// Clean up temp file
|
||||
await fs.promises.unlink(tempFilePath).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert a file that already exists in the vault (from context menu). */
|
||||
async convertVaultFile(file: TFile): Promise<void> {
|
||||
const vaultPath = getVaultBasePath(this.app);
|
||||
|
|
|
|||
|
|
@ -120,6 +120,16 @@ export class SettingsTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Drag and drop')
|
||||
.setDesc('Convert supported files automatically when dropped into the editor')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.enableDragDrop)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.enableDragDrop = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
// ── Advanced ─────────────────────────────
|
||||
new Setting(containerEl)
|
||||
.setName('Advanced')
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export interface MarkitdownSettings {
|
|||
imageSubfolderTemplate: string;
|
||||
enableBatchProgress: boolean;
|
||||
enableContextMenu: boolean;
|
||||
enableDragDrop: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: MarkitdownSettings = {
|
||||
|
|
@ -25,6 +26,7 @@ export const DEFAULT_SETTINGS: MarkitdownSettings = {
|
|||
imageSubfolderTemplate: '{filename}-images',
|
||||
enableBatchProgress: true,
|
||||
enableContextMenu: true,
|
||||
enableDragDrop: true,
|
||||
};
|
||||
|
||||
export interface ConversionOptions {
|
||||
|
|
|
|||
Loading…
Reference in a new issue