From b2749e81c3f42c1021def38cb6c4ddd6a8c95ca7 Mon Sep 17 00:00:00 2001 From: Rhys Date: Sun, 3 May 2026 20:17:02 +0200 Subject: [PATCH] refactor: rename DocDrop plugin to MarkItDown to reflect its core functionality --- .gitignore | 2 ++ main.js | 4 ++-- src/main.ts | 52 ++++++++++++++++++++-------------------------------- styles.css | 32 +++++++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index 6c4042f..c864e39 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ node_modules/ *.js.map +.DS_Store +main.js \ No newline at end of file diff --git a/main.js b/main.js index c638799..ebb9b8d 100644 --- a/main.js +++ b/main.js @@ -1,2 +1,2 @@ -var g=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var D=Object.prototype.hasOwnProperty;var I=(a,e)=>{for(var t in e)g(a,t,{get:e[t],enumerable:!0})},b=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of k(e))!D.call(a,n)&&n!==t&&g(a,n,{get:()=>e[n],enumerable:!(s=P(e,n))||s.enumerable});return a};var x=a=>b(g({},"__esModule",{value:!0}),a);var T={};I(T,{default:()=>u});module.exports=x(T);var i=require("obsidian"),f=require("child_process"),v=new Set(["pdf","docx","doc","pptx","ppt","xlsx","xls","jpg","jpeg","png","gif","webp","bmp","tiff","html","htm","csv","json","xml","epub","zip","mp3","wav"]),E={executablePath:"markitdown",outputMode:"same",customOutputFolder:"",keepDataUris:!1,usePlugins:!1,openAiApiKey:"",openAiBaseUrl:"",openAiModel:"gpt-4o",useDocIntel:!1,docIntelEndpoint:"",docIntelApiKey:"",mimeType:"",charset:""},h=class extends i.Modal{constructor(e,t,s,n){super(e),this.fileName=t,this.onConfirm=s,this.onCancel=n}onOpen(){let{contentEl:e}=this;e.empty(),e.createEl("h2",{text:"File already exists"}),e.createEl("p",{text:`"${this.fileName}" already exists. Overwrite it?`});let t=e.createDiv({cls:"modal-button-container"});t.createEl("button",{text:"Overwrite",cls:"mod-warning"}).addEventListener("click",()=>{this.close(),this.onConfirm()}),t.createEl("button",{text:"Cancel"}).addEventListener("click",()=>{this.close(),this.onCancel()})}onClose(){this.contentEl.empty()}},m=class extends i.PluginSettingTab{constructor(e,t){super(e,t),this.plugin=t}display(){let{containerEl:e}=this;e.empty(),e.createEl("h2",{text:"DocDrop"}),new i.Setting(e).setName("Executable path").setDesc('Full path to the markitdown binary, or just "markitdown" if it is on your PATH.').addText(t=>t.setPlaceholder("markitdown").setValue(this.plugin.settings.executablePath).onChange(async s=>{this.plugin.settings.executablePath=s.trim()||"markitdown",await this.plugin.saveSettings()})),new i.Setting(e).setName("Output location").setDesc("Where to save converted Markdown files.").addDropdown(t=>t.addOption("same","Same folder as PDF").addOption("custom","Custom folder").setValue(this.plugin.settings.outputMode).onChange(async s=>{this.plugin.settings.outputMode=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.outputMode==="custom"&&new i.Setting(e).setName("Custom output folder").setDesc('Vault-relative path, e.g. "Converted". The folder must already exist.').addText(t=>t.setPlaceholder("Converted").setValue(this.plugin.settings.customOutputFolder).onChange(async s=>{this.plugin.settings.customOutputFolder=s.trim(),await this.plugin.saveSettings()})),e.createEl("h3",{text:"Conversion options"}),new i.Setting(e).setName("Keep images").setDesc("Preserve images embedded in the PDF as base64 data inside the Markdown file. Turning this on makes the output file much larger but keeps all visuals inline. Leave off if you only need the text.").addToggle(t=>t.setValue(this.plugin.settings.keepDataUris).onChange(async s=>{this.plugin.settings.keepDataUris=s,await this.plugin.saveSettings()})),new i.Setting(e).setName("MIME type hint").setDesc('Tells markitdown what kind of file it is receiving, e.g. "application/pdf". Normally not needed \u2014 markitdown detects the type automatically. Only set this if conversion produces wrong results and you suspect a detection failure.').addText(t=>t.setPlaceholder("application/pdf").setValue(this.plugin.settings.mimeType).onChange(async s=>{this.plugin.settings.mimeType=s.trim(),await this.plugin.saveSettings()})),new i.Setting(e).setName("Character encoding hint").setDesc('Tells markitdown what text encoding the file uses, e.g. "UTF-8" or "ISO-8859-1". Leave blank unless converted text contains garbled characters \u2014 markitdown detects encoding automatically.').addText(t=>t.setPlaceholder("UTF-8").setValue(this.plugin.settings.charset).onChange(async s=>{this.plugin.settings.charset=s.trim(),await this.plugin.saveSettings()})),e.createEl("h3",{text:"markitdown-ocr plugin (optional)"}),e.createEl("p",{text:"markitdown-ocr is a free, separately-installed plugin that uses an AI vision model (like OpenAI's GPT-4o) to read text from images inside PDFs \u2014 useful for scanned documents or PDFs that are just pictures of pages. Install it first with: pip install markitdown-ocr. You will need an OpenAI account (or a compatible service) to provide the AI.",cls:"setting-item-description"}),new i.Setting(e).setName("Enable markitdown-ocr").setDesc("Activate any installed markitdown plugins, including markitdown-ocr. Has no effect if markitdown-ocr is not installed. Install it with: pip install markitdown-ocr").addToggle(t=>t.setValue(this.plugin.settings.usePlugins).onChange(async s=>{this.plugin.settings.usePlugins=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.usePlugins&&(new i.Setting(e).setName("OpenAI API key").setDesc("Your secret API key from OpenAI (or a compatible service). Required for markitdown-ocr to call the AI vision model. Get one at platform.openai.com \u2192 API keys. Looks like: sk-proj-...").addText(t=>{t.setPlaceholder("sk-proj-...").setValue(this.plugin.settings.openAiApiKey).onChange(async s=>{this.plugin.settings.openAiApiKey=s.trim(),await this.plugin.saveSettings()}),t.inputEl.type="password"}),new i.Setting(e).setName("AI model").setDesc('The vision-capable AI model markitdown-ocr will use to read images. Must support image/vision input. "gpt-4o" is the default and works well. Other options: gpt-4o-mini (cheaper, slightly less accurate).').addText(t=>t.setPlaceholder("gpt-4o").setValue(this.plugin.settings.openAiModel).onChange(async s=>{this.plugin.settings.openAiModel=s.trim()||"gpt-4o",await this.plugin.saveSettings()})),new i.Setting(e).setName("OpenAI API base URL (optional)").setDesc("Override the API server markitdown-ocr connects to. Leave blank to use the default OpenAI servers. Set this if you are using Azure OpenAI or a self-hosted compatible service (e.g. https://your-resource.openai.azure.com/). ").addText(t=>t.setPlaceholder("https://api.openai.com/v1").setValue(this.plugin.settings.openAiBaseUrl).onChange(async s=>{this.plugin.settings.openAiBaseUrl=s.trim(),await this.plugin.saveSettings()}))),e.createEl("h3",{text:"Azure Document Intelligence (optional)"}),e.createEl("p",{text:"Document Intelligence is a paid Microsoft Azure cloud service that uses AI to read PDFs with much higher accuracy than offline conversion \u2014 especially for scanned documents, handwriting, tables, and complex layouts. Requires an Azure account. Leave these blank to use free offline conversion instead.",cls:"setting-item-description"}),new i.Setting(e).setName("Use Document Intelligence").setDesc("Send the PDF to Microsoft Azure for conversion instead of processing it locally. Produces better results for scanned or image-based PDFs, but requires internet access and a paid Azure subscription.").addToggle(t=>t.setValue(this.plugin.settings.useDocIntel).onChange(async s=>{this.plugin.settings.useDocIntel=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.useDocIntel&&(new i.Setting(e).setName("Endpoint URL").setDesc("The URL of your Azure Document Intelligence resource. Find it in the Azure Portal: open your Document Intelligence resource \u2192 Keys and Endpoint. Looks like: https://your-resource-name.cognitiveservices.azure.com/").addText(t=>t.setPlaceholder("https://your-resource.cognitiveservices.azure.com/").setValue(this.plugin.settings.docIntelEndpoint).onChange(async s=>{this.plugin.settings.docIntelEndpoint=s.trim(),await this.plugin.saveSettings()})),new i.Setting(e).setName("API key").setDesc("The secret key that authenticates you with Azure Document Intelligence. Find it in the Azure Portal: open your Document Intelligence resource \u2192 Keys and Endpoint \u2192 KEY 1 or KEY 2. Either key works.").addText(t=>{t.setPlaceholder("Your Azure API key").setValue(this.plugin.settings.docIntelApiKey).onChange(async s=>{this.plugin.settings.docIntelApiKey=s.trim(),await this.plugin.saveSettings()}),t.inputEl.type="password"}))}},u=class extends i.Plugin{async onload(){await this.loadSettings(),this.addSettingTab(new m(this.app,this)),this.addCommand({id:"convert-active-file",name:"Convert active file to Markdown",callback:()=>{let e=this.app.workspace.getActiveFile();if(!e){new i.Notice("No file is currently open.");return}if(!v.has(e.extension.toLowerCase())){new i.Notice(`DocDrop does not support .${e.extension} files.`);return}this.convertFile(e)}}),this.registerEvent(this.app.workspace.on("file-menu",(e,t)=>{t instanceof i.TFile&&v.has(t.extension.toLowerCase())&&e.addItem(s=>{s.setTitle("Convert to Markdown with DocDrop").setIcon("file-text").onClick(()=>this.convertFile(t))})}))}onunload(){}async loadSettings(){this.settings=Object.assign({},E,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}async convertFile(e){let n=`${this.app.vault.adapter.getBasePath()}/${e.path}`,o=this.resolveOutputPath(e);if(o===null){new i.Notice(`Custom output folder "${this.settings.customOutputFolder}" does not exist in the vault.`);return}let r=this.app.vault.getAbstractFileByPath(o);if(r instanceof i.TFile){new h(this.app,o,()=>this.runConversion(n,o,r),()=>new i.Notice("Conversion cancelled.")).open();return}await this.runConversion(n,o,null)}resolveOutputPath(e){var o,r;let t=e.basename;if(this.settings.outputMode==="same"){let c=(r=(o=e.parent)==null?void 0:o.path)!=null?r:"",p=c==="/"?"":c;return p?`${p}/${t}.md`:`${t}.md`}let s=this.settings.customOutputFolder.replace(/\/$/,"");return s===""?`${t}.md`:this.app.vault.getAbstractFileByPath(s)instanceof i.TFolder?`${s}/${t}.md`:null}async runConversion(e,t,s){let n=new i.Notice(`Converting "${t}"\u2026`,0);try{let o=await this.invokeMarkItDown(e);n.hide(),s?await this.app.vault.modify(s,o):await this.app.vault.create(t,o),new i.Notice(`Converted: ${t}`,5e3)}catch(o){n.hide();let r=o instanceof Error?o.message:String(o);new i.Notice(`DocDrop failed: -${r}`,8e3),console.error("[DocDrop] conversion error",o)}}invokeMarkItDown(e){var r,c;let t=["/usr/local/bin","/opt/homebrew/bin","/opt/homebrew/sbin","/Library/Frameworks/Python.framework/Versions/3.12/bin","/Library/Frameworks/Python.framework/Versions/3.11/bin","/Library/Frameworks/Python.framework/Versions/3.10/bin","/usr/bin","/bin"],s=[...(c=(r=process.env.PATH)==null?void 0:r.split(":"))!=null?c:[],...t].join(":"),n=[e];this.settings.keepDataUris&&n.push("--keep-data-uris"),this.settings.usePlugins&&n.push("--use-plugins"),this.settings.mimeType&&n.push("--mime-type",this.settings.mimeType),this.settings.charset&&n.push("--charset",this.settings.charset),this.settings.useDocIntel&&(n.push("--use-docintel"),this.settings.docIntelEndpoint&&n.push("--endpoint",this.settings.docIntelEndpoint));let o={PATH:s};return this.settings.useDocIntel&&this.settings.docIntelApiKey&&(o.AZURE_API_KEY=this.settings.docIntelApiKey),this.settings.usePlugins&&this.settings.openAiApiKey&&(o.OPENAI_API_KEY=this.settings.openAiApiKey),this.settings.usePlugins&&this.settings.openAiModel&&(o.OPENAI_MODEL=this.settings.openAiModel),this.settings.usePlugins&&this.settings.openAiBaseUrl&&(o.OPENAI_BASE_URL=this.settings.openAiBaseUrl),new Promise((p,w)=>{(0,f.execFile)(this.settings.executablePath,n,{encoding:"utf8",maxBuffer:50*1024*1024,env:{...process.env,...o}},(d,y,l)=>{if(d){let A=(l==null?void 0:l.trim())||d.message;w(new Error(`Exit code ${d.code}: ${A}`));return}l!=null&&l.trim()&&console.warn("[DocDrop] stderr:",l.trim()),p(y)})})}}; +var g=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var b=(a,t)=>{for(var i in t)g(a,i,{get:t[i],enumerable:!0})},I=(a,t,i,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let e of D(t))!k.call(a,e)&&e!==i&&g(a,e,{get:()=>t[e],enumerable:!(o=P(t,e))||o.enumerable});return a};var E=a=>I(g({},"__esModule",{value:!0}),a);var C={};b(C,{default:()=>p});module.exports=E(C);var n=require("obsidian"),w=require("child_process"),v=new Set(["pdf","docx","doc","pptx","ppt","xlsx","xls","jpg","jpeg","png","gif","webp","bmp","tiff","html","htm","csv","json","xml","epub","zip","mp3","wav"]),x={executablePath:"markitdown",outputMode:"same",customOutputFolder:"",keepDataUris:!1,usePlugins:!1,openAiApiKey:"",openAiBaseUrl:"",openAiModel:"gpt-4o",useDocIntel:!1,docIntelEndpoint:"",docIntelApiKey:"",charset:""},h=class extends n.Modal{constructor(t,i,o,e){super(t),this.fileName=i,this.onConfirm=o,this.onCancel=e}onOpen(){let{contentEl:t}=this;t.empty(),t.createEl("h2",{text:"File already exists"}),t.createEl("p",{text:`"${this.fileName}" already exists. Overwrite it?`});let i=t.createDiv({cls:"modal-button-container"});i.createEl("button",{text:"Overwrite",cls:"mod-warning"}).addEventListener("click",()=>{this.close(),this.onConfirm()}),i.createEl("button",{text:"Cancel"}).addEventListener("click",()=>{this.close(),this.onCancel()})}onClose(){this.contentEl.empty()}},m=class extends n.PluginSettingTab{constructor(t,i){super(t,i),this.plugin=i}display(){let{containerEl:t}=this;t.empty(),t.createEl("h2",{text:"DocDrop"}),new n.Setting(t).setName("Executable path").setDesc('Full path to the markitdown binary, or just "markitdown" if it is on your PATH.').addText(e=>e.setPlaceholder("markitdown").setValue(this.plugin.settings.executablePath).onChange(async s=>{this.plugin.settings.executablePath=s.trim()||"markitdown",await this.plugin.saveSettings()})),new n.Setting(t).setName("Output location").setDesc("Where to save converted Markdown files.").addDropdown(e=>e.addOption("same","Same folder as PDF").addOption("custom","Custom folder").setValue(this.plugin.settings.outputMode).onChange(async s=>{this.plugin.settings.outputMode=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.outputMode==="custom"&&new n.Setting(t).setName("Custom output folder").setDesc('Vault-relative path, e.g. "Converted". The folder must already exist.').addText(e=>e.setPlaceholder("Converted").setValue(this.plugin.settings.customOutputFolder).onChange(async s=>{this.plugin.settings.customOutputFolder=s.trim(),await this.plugin.saveSettings()})),t.createEl("h3",{text:"Conversion options"}),new n.Setting(t).setName("Keep images").setDesc("Preserve images embedded in the PDF as base64 data inside the Markdown file. Turning this on makes the output file much larger but keeps all visuals inline. Leave off if you only need the text.").addToggle(e=>e.setValue(this.plugin.settings.keepDataUris).onChange(async s=>{this.plugin.settings.keepDataUris=s,await this.plugin.saveSettings()})),new n.Setting(t).setName("Character encoding hint").setDesc('Tells markitdown what text encoding the file uses, e.g. "UTF-8" or "ISO-8859-1". Leave blank unless converted text contains garbled characters \u2014 markitdown detects encoding automatically.').addText(e=>e.setPlaceholder("UTF-8").setValue(this.plugin.settings.charset).onChange(async s=>{this.plugin.settings.charset=s.trim(),await this.plugin.saveSettings()}));let i=t.createEl("details",{cls:"docdrop-collapsible"});i.open=this.plugin.settings.usePlugins,i.createEl("summary",{text:"markitdown-ocr plugin (optional)"}),i.createEl("p",{text:"markitdown-ocr is a free, separately-installed plugin that uses an AI vision model (like OpenAI's GPT-4o) to read text from images inside PDFs \u2014 useful for scanned documents or PDFs that are just pictures of pages. Install it first with: pip install markitdown-ocr. You will need an OpenAI account (or a compatible service) to provide the AI.",cls:"setting-item-description"}),new n.Setting(i).setName("Enable markitdown-ocr").setDesc("Activate any installed markitdown plugins, including markitdown-ocr. Has no effect if markitdown-ocr is not installed. Install it with: pip install markitdown-ocr").addToggle(e=>e.setValue(this.plugin.settings.usePlugins).onChange(async s=>{this.plugin.settings.usePlugins=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.usePlugins&&(new n.Setting(i).setName("OpenAI API key").setDesc("Your secret API key from OpenAI (or a compatible service). Required for markitdown-ocr to call the AI vision model. Get one at platform.openai.com \u2192 API keys. Looks like: sk-proj-...").addText(e=>{e.setPlaceholder("sk-proj-...").setValue(this.plugin.settings.openAiApiKey).onChange(async s=>{this.plugin.settings.openAiApiKey=s.trim(),await this.plugin.saveSettings()}),e.inputEl.type="password"}),new n.Setting(i).setName("AI model").setDesc('The vision-capable AI model markitdown-ocr will use to read images. Must support image/vision input. "gpt-4o" is the default and works well. Other options: gpt-4o-mini (cheaper, slightly less accurate).').addText(e=>e.setPlaceholder("gpt-4o").setValue(this.plugin.settings.openAiModel).onChange(async s=>{this.plugin.settings.openAiModel=s.trim()||"gpt-4o",await this.plugin.saveSettings()})),new n.Setting(i).setName("OpenAI API base URL (optional)").setDesc("Override the API server markitdown-ocr connects to. Leave blank to use the default OpenAI servers. Set this if you are using Azure OpenAI or a self-hosted compatible service (e.g. https://your-resource.openai.azure.com/).").addText(e=>e.setPlaceholder("https://api.openai.com/v1").setValue(this.plugin.settings.openAiBaseUrl).onChange(async s=>{this.plugin.settings.openAiBaseUrl=s.trim(),await this.plugin.saveSettings()})));let o=t.createEl("details",{cls:"docdrop-collapsible"});o.open=this.plugin.settings.useDocIntel,o.createEl("summary",{text:"Azure Document Intelligence (optional)"}),o.createEl("p",{text:"Document Intelligence is a paid Microsoft Azure cloud service that uses AI to read PDFs with much higher accuracy than offline conversion \u2014 especially for scanned documents, handwriting, tables, and complex layouts. Requires an Azure account. Leave these blank to use free offline conversion instead.",cls:"setting-item-description"}),new n.Setting(o).setName("Use Document Intelligence").setDesc("Send the PDF to Microsoft Azure for conversion instead of processing it locally. Produces better results for scanned or image-based PDFs, but requires internet access and a paid Azure subscription.").addToggle(e=>e.setValue(this.plugin.settings.useDocIntel).onChange(async s=>{this.plugin.settings.useDocIntel=s,await this.plugin.saveSettings(),this.display()})),this.plugin.settings.useDocIntel&&(new n.Setting(o).setName("Endpoint URL").setDesc("The URL of your Azure Document Intelligence resource. Find it in the Azure Portal: open your Document Intelligence resource \u2192 Keys and Endpoint. Looks like: https://your-resource-name.cognitiveservices.azure.com/").addText(e=>e.setPlaceholder("https://your-resource.cognitiveservices.azure.com/").setValue(this.plugin.settings.docIntelEndpoint).onChange(async s=>{this.plugin.settings.docIntelEndpoint=s.trim(),await this.plugin.saveSettings()})),new n.Setting(o).setName("API key").setDesc("The secret key that authenticates you with Azure Document Intelligence. Find it in the Azure Portal: open your Document Intelligence resource \u2192 Keys and Endpoint \u2192 KEY 1 or KEY 2. Either key works.").addText(e=>{e.setPlaceholder("Your Azure API key").setValue(this.plugin.settings.docIntelApiKey).onChange(async s=>{this.plugin.settings.docIntelApiKey=s.trim(),await this.plugin.saveSettings()}),e.inputEl.type="password"}))}},p=class extends n.Plugin{async onload(){await this.loadSettings(),this.addSettingTab(new m(this.app,this)),this.addCommand({id:"convert-active-file",name:"Convert active file to Markdown",callback:()=>{let t=this.app.workspace.getActiveFile();if(!t){new n.Notice("No file is currently open.");return}if(!v.has(t.extension.toLowerCase())){new n.Notice(`DocDrop does not support .${t.extension} files.`);return}this.convertFile(t)}}),this.registerEvent(this.app.workspace.on("file-menu",(t,i)=>{i instanceof n.TFile&&v.has(i.extension.toLowerCase())&&t.addItem(o=>{o.setTitle("Convert to Markdown with DocDrop").setIcon("file-text").onClick(()=>this.convertFile(i))})}))}onunload(){}async loadSettings(){this.settings=Object.assign({},x,await this.loadData())}async saveSettings(){await this.saveData(this.settings)}async convertFile(t){let e=`${this.app.vault.adapter.getBasePath()}/${t.path}`,s=this.resolveOutputPath(t);if(s===null){new n.Notice(`Custom output folder "${this.settings.customOutputFolder}" does not exist in the vault.`);return}let r=this.app.vault.getAbstractFileByPath(s);if(r instanceof n.TFile){new h(this.app,s,()=>this.runConversion(e,s,r),()=>new n.Notice("Conversion cancelled.")).open();return}await this.runConversion(e,s,null)}resolveOutputPath(t){var s,r;let i=t.basename;if(this.settings.outputMode==="same"){let c=(r=(s=t.parent)==null?void 0:s.path)!=null?r:"",u=c==="/"?"":c;return u?`${u}/${i}.md`:`${i}.md`}let o=this.settings.customOutputFolder.replace(/\/$/,"");return o===""?`${i}.md`:this.app.vault.getAbstractFileByPath(o)instanceof n.TFolder?`${o}/${i}.md`:null}async runConversion(t,i,o){let e=new n.Notice(`Converting "${i}"\u2026`,0);try{let s=await this.invokeMarkItDown(t);e.hide(),o?await this.app.vault.modify(o,s):await this.app.vault.create(i,s),new n.Notice(`Converted: ${i}`,5e3)}catch(s){e.hide();let r=s instanceof Error?s.message:String(s);new n.Notice(`DocDrop failed: +${r}`,8e3),console.error("[DocDrop] conversion error",s)}}invokeMarkItDown(t){var r,c;let i=["/usr/local/bin","/opt/homebrew/bin","/opt/homebrew/sbin","/Library/Frameworks/Python.framework/Versions/3.12/bin","/Library/Frameworks/Python.framework/Versions/3.11/bin","/Library/Frameworks/Python.framework/Versions/3.10/bin","/usr/bin","/bin"],o=[...(c=(r=process.env.PATH)==null?void 0:r.split(":"))!=null?c:[],...i].join(":"),e=[t];this.settings.keepDataUris&&e.push("--keep-data-uris"),this.settings.usePlugins&&e.push("--use-plugins"),this.settings.charset&&e.push("--charset",this.settings.charset),this.settings.useDocIntel&&(e.push("--use-docintel"),this.settings.docIntelEndpoint&&e.push("--endpoint",this.settings.docIntelEndpoint));let s={PATH:o};return this.settings.useDocIntel&&this.settings.docIntelApiKey&&(s.AZURE_API_KEY=this.settings.docIntelApiKey),this.settings.usePlugins&&this.settings.openAiApiKey&&(s.OPENAI_API_KEY=this.settings.openAiApiKey),this.settings.usePlugins&&this.settings.openAiModel&&(s.OPENAI_MODEL=this.settings.openAiModel),this.settings.usePlugins&&this.settings.openAiBaseUrl&&(s.OPENAI_BASE_URL=this.settings.openAiBaseUrl),new Promise((u,f)=>{(0,w.execFile)(this.settings.executablePath,e,{encoding:"utf8",maxBuffer:50*1024*1024,env:{...process.env,...s}},(d,y,l)=>{if(d){let A=(l==null?void 0:l.trim())||d.message;f(new Error(`Exit code ${d.code}: ${A}`));return}l!=null&&l.trim()&&console.warn("[DocDrop] stderr:",l.trim()),u(y)})})}}; diff --git a/src/main.ts b/src/main.ts index e63fa3f..44152d4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -42,7 +42,6 @@ interface DocDropSettings { useDocIntel: boolean; docIntelEndpoint: string; docIntelApiKey: string; - mimeType: string; charset: string; } @@ -58,7 +57,6 @@ const DEFAULT_SETTINGS: DocDropSettings = { useDocIntel: false, docIntelEndpoint: "", docIntelApiKey: "", - mimeType: "", charset: "", }; @@ -193,23 +191,6 @@ class DocDropSettingTab extends PluginSettingTab { }) ); - new Setting(containerEl) - .setName("MIME type hint") - .setDesc( - "Tells markitdown what kind of file it is receiving, e.g. \"application/pdf\". " + - "Normally not needed — markitdown detects the type automatically. " + - "Only set this if conversion produces wrong results and you suspect a detection failure." - ) - .addText((text) => - text - .setPlaceholder("application/pdf") - .setValue(this.plugin.settings.mimeType) - .onChange(async (value) => { - this.plugin.settings.mimeType = value.trim(); - await this.plugin.saveSettings(); - }) - ); - new Setting(containerEl) .setName("Character encoding hint") .setDesc( @@ -228,9 +209,13 @@ class DocDropSettingTab extends PluginSettingTab { // ── markitdown-ocr ────────────────────────────────────────────────────── - containerEl.createEl("h3", { text: "markitdown-ocr plugin (optional)" }); + const ocrDetails = containerEl.createEl("details", { + cls: "docdrop-collapsible", + }); + ocrDetails.open = this.plugin.settings.usePlugins; + ocrDetails.createEl("summary", { text: "markitdown-ocr plugin (optional)" }); - containerEl.createEl("p", { + ocrDetails.createEl("p", { text: "markitdown-ocr is a free, separately-installed plugin that uses an AI vision model (like " + "OpenAI's GPT-4o) to read text from images inside PDFs — useful for scanned documents or " + @@ -239,7 +224,7 @@ class DocDropSettingTab extends PluginSettingTab { cls: "setting-item-description", }); - new Setting(containerEl) + new Setting(ocrDetails) .setName("Enable markitdown-ocr") .setDesc( "Activate any installed markitdown plugins, including markitdown-ocr. " + @@ -257,7 +242,7 @@ class DocDropSettingTab extends PluginSettingTab { ); if (this.plugin.settings.usePlugins) { - new Setting(containerEl) + new Setting(ocrDetails) .setName("OpenAI API key") .setDesc( "Your secret API key from OpenAI (or a compatible service). " + @@ -275,7 +260,7 @@ class DocDropSettingTab extends PluginSettingTab { text.inputEl.type = "password"; }); - new Setting(containerEl) + new Setting(ocrDetails) .setName("AI model") .setDesc( "The vision-capable AI model markitdown-ocr will use to read images. " + @@ -292,13 +277,13 @@ class DocDropSettingTab extends PluginSettingTab { }) ); - new Setting(containerEl) + new Setting(ocrDetails) .setName("OpenAI API base URL (optional)") .setDesc( "Override the API server markitdown-ocr connects to. " + "Leave blank to use the default OpenAI servers. " + "Set this if you are using Azure OpenAI or a self-hosted compatible service " + - "(e.g. https://your-resource.openai.azure.com/). " + "(e.g. https://your-resource.openai.azure.com/)." ) .addText((text) => text @@ -313,9 +298,13 @@ class DocDropSettingTab extends PluginSettingTab { // ── Azure Document Intelligence ───────────────────────────────────────── - containerEl.createEl("h3", { text: "Azure Document Intelligence (optional)" }); + const azureDetails = containerEl.createEl("details", { + cls: "docdrop-collapsible", + }); + azureDetails.open = this.plugin.settings.useDocIntel; + azureDetails.createEl("summary", { text: "Azure Document Intelligence (optional)" }); - containerEl.createEl("p", { + azureDetails.createEl("p", { text: "Document Intelligence is a paid Microsoft Azure cloud service that uses AI to read PDFs with " + "much higher accuracy than offline conversion — especially for scanned documents, handwriting, " + @@ -324,7 +313,7 @@ class DocDropSettingTab extends PluginSettingTab { cls: "setting-item-description", }); - new Setting(containerEl) + new Setting(azureDetails) .setName("Use Document Intelligence") .setDesc( "Send the PDF to Microsoft Azure for conversion instead of processing it locally. " + @@ -341,7 +330,7 @@ class DocDropSettingTab extends PluginSettingTab { ); if (this.plugin.settings.useDocIntel) { - new Setting(containerEl) + new Setting(azureDetails) .setName("Endpoint URL") .setDesc( "The URL of your Azure Document Intelligence resource. " + @@ -358,7 +347,7 @@ class DocDropSettingTab extends PluginSettingTab { }) ); - new Setting(containerEl) + new Setting(azureDetails) .setName("API key") .setDesc( "The secret key that authenticates you with Azure Document Intelligence. " + @@ -524,7 +513,6 @@ export default class DocDropPlugin extends Plugin { const args: string[] = [absolutePdfPath]; if (this.settings.keepDataUris) args.push("--keep-data-uris"); if (this.settings.usePlugins) args.push("--use-plugins"); - if (this.settings.mimeType) args.push("--mime-type", this.settings.mimeType); if (this.settings.charset) args.push("--charset", this.settings.charset); if (this.settings.useDocIntel) { args.push("--use-docintel"); diff --git a/styles.css b/styles.css index da50182..85b2632 100644 --- a/styles.css +++ b/styles.css @@ -1 +1,31 @@ -/* markitdown-pdf-converter styles */ +/* DocDrop collapsible settings sections */ +.docdrop-collapsible { + border: 1px solid var(--background-modifier-border); + border-radius: 6px; + margin: 12px 0; + padding: 0 12px; +} + +.docdrop-collapsible > summary { + cursor: pointer; + font-size: 1em; + font-weight: 600; + padding: 10px 0; + user-select: none; + color: var(--text-normal); + list-style: none; +} + +.docdrop-collapsible > summary::before { + content: "▶ "; + font-size: 0.75em; + color: var(--text-muted); +} + +.docdrop-collapsible[open] > summary::before { + content: "▼ "; +} + +.docdrop-collapsible .setting-item:first-of-type { + border-top: 1px solid var(--background-modifier-border); +}