-
Notifications
You must be signed in to change notification settings - Fork 2
Add context menu functionality. AI-assisted, tested manually. #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -87,6 +87,92 @@ export default class MarkitdownPlugin extends Plugin { | |||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Register file menu event handler for context menu | ||||||||||||||||||||||||||||
| this.registerEvent( | ||||||||||||||||||||||||||||
| this.app.workspace.on('file-menu', (menu, file) => { | ||||||||||||||||||||||||||||
| // Only show for files, not folders | ||||||||||||||||||||||||||||
| if (!(file instanceof TFile)) return; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Get file extension | ||||||||||||||||||||||||||||
| const extension = file.extension.toLowerCase(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // List of supported extensions | ||||||||||||||||||||||||||||
| const supportedExtensions = ['pdf', 'docx', 'pptx', 'xlsx', 'xls', | ||||||||||||||||||||||||||||
| 'html', 'htm', 'txt', 'csv', 'json', 'xml', | ||||||||||||||||||||||||||||
| 'jpg', 'jpeg', 'png', 'gif', 'wav', 'mp3', 'zip']; | ||||||||||||||||||||||||||||
|
Comment on lines
+101
to
+103
|
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Only show for supported file types | ||||||||||||||||||||||||||||
| if (!supportedExtensions.includes(extension)) return; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| menu.addItem((item) => { | ||||||||||||||||||||||||||||
| item | ||||||||||||||||||||||||||||
| .setTitle('Convert to Markdown with Markitdown') | ||||||||||||||||||||||||||||
| .setIcon('file-text') | ||||||||||||||||||||||||||||
| .onClick(async () => { | ||||||||||||||||||||||||||||
| if (!this.markitdownInstalled) { | ||||||||||||||||||||||||||||
| new MarkitdownSetupModal(this.app, this).open(); | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||
| // Get vault path | ||||||||||||||||||||||||||||
| let vaultPath = ''; | ||||||||||||||||||||||||||||
| if (this.app.vault.adapter instanceof FileSystemAdapter) { | ||||||||||||||||||||||||||||
| vaultPath = this.app.vault.adapter.getBasePath(); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (!vaultPath) { | ||||||||||||||||||||||||||||
| new Notice('Could not determine vault path. This plugin requires a local vault.'); | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Get file path | ||||||||||||||||||||||||||||
| const filePath = path.join(vaultPath, file.path); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Determine output path | ||||||||||||||||||||||||||||
| let outputFolder = this.settings.outputPath || ''; | ||||||||||||||||||||||||||||
| if (!outputFolder) { | ||||||||||||||||||||||||||||
| outputFolder = path.join(vaultPath, 'markitdown-output'); | ||||||||||||||||||||||||||||
| if (!fs.existsSync(outputFolder)) { | ||||||||||||||||||||||||||||
| fs.mkdirSync(outputFolder, { recursive: true }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||
| outputFolder = path.join(vaultPath, outputFolder); | ||||||||||||||||||||||||||||
| if (!fs.existsSync(outputFolder)) { | ||||||||||||||||||||||||||||
| fs.mkdirSync(outputFolder, { recursive: true }); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Create output filename | ||||||||||||||||||||||||||||
| const baseName = path.basename(file.path, path.extname(file.path)); | ||||||||||||||||||||||||||||
| const outputPath = path.join(outputFolder, `${baseName}.md`); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
Comment on lines
+147
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid overwriting conversions for same base filename. Line 148 builds 🔧 Proposed fix (preserve relative folder structure)- const baseName = path.basename(file.path, path.extname(file.path));
- const outputPath = path.join(outputFolder, `${baseName}.md`);
+ const baseName = path.basename(file.path, path.extname(file.path));
+ const relDir = path.dirname(file.path);
+ const outputDir = path.join(outputFolder, relDir);
+ if (!fs.existsSync(outputDir)) {
+ fs.mkdirSync(outputDir, { recursive: true });
+ }
+ const outputPath = path.join(outputDir, `${baseName}.md`);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
| new Notice('Converting file...'); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Convert the file | ||||||||||||||||||||||||||||
| await this.convertFile(filePath, outputPath); | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential command injection vulnerability in file paths. The 🔒 Recommended mitigationUse // In convertFile method, replace exec with spawn
import { spawn } from 'child_process';
async convertFile(filePath: string, outputPath: string): Promise<string> {
return new Promise((resolve, reject) => {
const outputStream = fs.createWriteStream(outputPath);
const proc = spawn('markitdown', [filePath], { shell: false });
proc.stdout.pipe(outputStream);
proc.stderr.on('data', (data) => { /* handle stderr */ });
proc.on('close', (code) => {
if (code === 0) resolve('');
else reject(new Error(`markitdown exited with code ${code}`));
});
});
}Alternatively, validate/sanitize file paths before use. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| // Refresh the vault to see the new file | ||||||||||||||||||||||||||||
| await this.app.vault.adapter.exists(outputPath); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| new Notice(`File converted and saved to ${outputPath}`); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
| new Notice(`File converted and saved to ${outputPath}`); | |
| new Notice(`File converted and saved to ${relativePath}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle unknown error type safely.
In TypeScript strict mode, caught errors are typed as unknown. Accessing error.message directly may fail if the error is not an Error instance.
Suggested fix
} catch (error) {
console.error('Error during conversion:', error);
- new Notice(`Error: ${error.message}`);
+ const message = error instanceof Error ? error.message : String(error);
+ new Notice(`Error: ${message}`);
}This pattern should also be applied at lines 391 and 598.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| } catch (error) { | |
| console.error('Error during conversion:', error); | |
| new Notice(`Error: ${error.message}`); | |
| } catch (error) { | |
| console.error('Error during conversion:', error); | |
| const message = error instanceof Error ? error.message : String(error); | |
| new Notice(`Error: ${message}`); | |
| } |
🤖 Prompt for AI Agents
In `@main.ts` around lines 167 - 169, The catch blocks that log errors and create
Notices (e.g., the one with console.error('Error during conversion:', error) and
new Notice(`Error: ${error.message}`)) assume caught values are Error instances;
change them to safely extract a message by using a type guard/normalizer (e.g.,
const msg = error instanceof Error ? error.message : String(error)) and use msg
for both console.error and new Notice; apply the same change to the other
similar catch blocks mentioned (the ones that currently access error.message at
the other two locations).
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conversion logic (lines 118-170) largely duplicates the logic in MarkitdownFileModal (lines 329-388). Consider extracting the shared conversion logic into a reusable method on the plugin class to reduce duplication and improve maintainability. For example, create a method like async performConversion(file: TFile, vaultPath: string) that handles the output folder creation, path resolution, conversion, and file opening logic.
Copilot
AI
Nov 12, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent indentation: this section uses spaces for indentation while the rest of the file appears to use tabs (based on the surrounding code at lines 65-88). This creates a mixed indentation style. Consider using tabs to match the existing code style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: inconsistent indentation - file uses tabs but these lines use spaces
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI