var G=Object.defineProperty;var ot=Object.getOwnPropertyDescriptor;var lt=Object.getOwnPropertyNames;var ct=Object.prototype.hasOwnProperty;var dt=(i,t)=>{for(var e in t)G(i,e,{get:t[e],enumerable:!0})},mt=(i,t,e,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of lt(t))!ct.call(i,a)&&a!==e&&G(i,a,{get:()=>t[a],enumerable:!(n=ot(t,a))||n.enumerable});return i};var gt=i=>mt(G({},"__esModule",{value:!0}),i);var ft={};dt(ft,{default:()=>wt});module.exports=gt(ft);var w=require("obsidian");var g=require("obsidian"),J={remarkableHost:"https://10.11.99.1",rmapiBinaryPath:"rmapi",downloadPath:"remarkable",convertToPdf:!0,enableHandwritingMd:!0,glmocrServerUrl:"http://localhost:5002",glmocrApiKey:"",ollamaHost:"http://localhost:11435",styleModel:"qwen3:32b",pageRenderer:"drawj2d",javaPath:"java",drawj2dPath:"drawj2d.jar",maxPagesPerBatch:20,syncInterval:0},T=class extends g.PluginSettingTab{plugin;constructor(t,e){super(t,e),this.plugin=e}display(){let{containerEl:t}=this;t.empty(),t.createEl("h2",{text:"reMarkable Sync Settings"}),new g.Setting(t).setName("reMarkable Host").setDesc("URL of your reMarkable tablet (e.g., https://10.11.99.1)").addText(e=>e.setValue(this.plugin.settings.remarkableHost).onChange(async n=>{this.plugin.settings.remarkableHost=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("rmapi Binary Path").setDesc("Path to rmapi binary (e.g., rmapi or /usr/local/bin/rmapi)").addText(e=>e.setValue(this.plugin.settings.rmapiBinaryPath).onChange(async n=>{this.plugin.settings.rmapiBinaryPath=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Download Path").setDesc("Folder in vault for downloaded documents").addText(e=>e.setValue(this.plugin.settings.downloadPath).onChange(async n=>{this.plugin.settings.downloadPath=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Convert to PDF").setDesc("Automatically convert documents to annotated PDF").addToggle(e=>e.setValue(this.plugin.settings.convertToPdf).onChange(async n=>{this.plugin.settings.convertToPdf=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Enable Handwriting to Markdown").setDesc("Convert handwritten notes to styled Markdown").addToggle(e=>e.setValue(this.plugin.settings.enableHandwritingMd).onChange(async n=>{this.plugin.settings.enableHandwritingMd=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("GLM-OCR Server URL").setDesc("URL of GLM-OCR SDK Server (e.g., http://localhost:5002)").addText(e=>e.setValue(this.plugin.settings.glmocrServerUrl).onChange(async n=>{this.plugin.settings.glmocrServerUrl=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("GLM-OCR API Key").setDesc("API key for GLM-OCR Server (can be any string for self-hosted)").addText(e=>{e.inputEl.type="password",e.setValue(this.plugin.settings.glmocrApiKey).onChange(async n=>{this.plugin.settings.glmocrApiKey=n,await this.plugin.saveSettings()})}),new g.Setting(t).setName("Ollama Host").setDesc("URL of Ollama server for style refinement (e.g., http://localhost:11435)").addText(e=>e.setValue(this.plugin.settings.ollamaHost).onChange(async n=>{this.plugin.settings.ollamaHost=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Style Model").setDesc("Ollama model for Markdown style refinement").addText(e=>e.setValue(this.plugin.settings.styleModel).onChange(async n=>{this.plugin.settings.styleModel=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Page Renderer").setDesc("Tool to render .rm pages as PNG").addDropdown(e=>e.addOption("drawj2d","drawj2d (recommended)").addOption("rM2svg","rM2svg (lightweight)").setValue(this.plugin.settings.pageRenderer).onChange(async n=>{this.plugin.settings.pageRenderer=n,await this.plugin.saveSettings()})),new g.Setting(t).setName("Java Path").setDesc("Path to Java runtime (for drawj2d)").addText(e=>{e.setValue(this.plugin.settings.javaPath).onChange(async n=>{this.plugin.settings.javaPath=n,await this.plugin.saveSettings()})}),new g.Setting(t).setName("drawj2d Path").setDesc("Path to drawj2d.jar (e.g., /opt/drawj2d.jar)").addText(e=>{e.setValue(this.plugin.settings.drawj2dPath).onChange(async n=>{this.plugin.settings.drawj2dPath=n,await this.plugin.saveSettings()})}),new g.Setting(t).setName("Max Pages per Batch").setDesc("Max pages per GLM-OCR request (to avoid timeouts)").addText(e=>{e.setValue(String(this.plugin.settings.maxPagesPerBatch)).onChange(async n=>{let a=parseInt(n);this.plugin.settings.maxPagesPerBatch=isNaN(a)||a<1?20:a,await this.plugin.saveSettings()}),e.inputEl.type="number",e.inputEl.min="1"}),new g.Setting(t).setName("Sync Interval (minutes)").setDesc("0 = manual only, >0 = auto-sync every N minutes").addText(e=>{e.setValue(String(this.plugin.settings.syncInterval)).onChange(async n=>{let a=parseInt(n);this.plugin.settings.syncInterval=isNaN(a)||a<0?0:a,await this.plugin.saveSettings()}),e.inputEl.type="number",e.inputEl.min="0"})}};var $=require("obsidian");var z=require("obsidian"),K=require("path");function D(i){return i.app.vault.adapter.getBasePath()}function v(...i){let t=i.join("/").split("/").filter(e=>e&&e!=="."&&e!=="..").join("/");return(0,z.normalizePath)(t)}function x(i,t){return(0,K.join)(D(i),...(0,z.normalizePath)(t).split("/"))}function pt(i){return i.replace(/[\\/:*?"<>|]/g,"_")}function ut(i){return i.split("/").filter(Boolean).map(pt)}var O=class{plugin;constructor(t){this.plugin=t}async syncAll(){try{new $.Notice("Syncing from reMarkable...");let t=await this.listAllDocuments("/");for(let e of t)await this.downloadDocument(e);new $.Notice(`Sync completed! ${t.length} document(s) synced.`)}catch(t){throw new $.Notice(`Sync failed: ${t}`),console.error("Sync error:",t),t}}async listAllDocuments(t){let e=await this.plugin.rmapi.list(t),n=e.filter(a=>a.type==="DocumentType").map(a=>({node:a,remotePath:t==="/"?`/${a.name}`:`${t}/${a.name}`}));for(let a of e.filter(r=>r.type==="CollectionType")){let r=t==="/"?`/${a.name}`:`${t}/${a.name}`,s=await this.listAllDocuments(r);n=n.concat(s)}return n}buildLocalPaths(t){let e=ut(t),n=e.pop()||"Untitled",a=v(this.plugin.settings.downloadPath,...e);return{localDir:a,localPath:v(a,`${n}.rm`),mdPath:v(a,`${n}.md`),pdfPath:v(a,`${n}.pdf`)}}async downloadDocument(t,e={}){let{node:n,remotePath:a}=t,r=e.convertToPdf??this.plugin.settings.convertToPdf,s=e.convertToMd??this.plugin.settings.enableHandwritingMd,o=await this.plugin.tracker.getSyncedDocument(n.id),d=this.buildLocalPaths(a),p=o?.remoteVersion===n.version&&o?.remoteModified===n.modifiedClient,m=await this.plugin.app.vault.adapter.exists(d.localPath),h=e.forceDownload||!p||!m,f=s&&(!p||!o?.hasMd||!await this.plugin.app.vault.adapter.exists(d.mdPath));if(!h&&!f)return;let{localDir:M,localPath:P,mdPath:V,pdfPath:l}=d,k=x(this.plugin,P);await this.ensureVaultFolder(M),h&&await this.plugin.rmapi.downloadFile(a,k);let y=o?.hasPdf??!1;r&&h&&(await this.plugin.rmapi.downloadAnnotatedPdf(a,x(this.plugin,l)),y=!0);let S=o?.hasMd??!1;await this.plugin.tracker.trackDocument(n,P,y,S),f&&await this.plugin.ocrPipeline.processDocument(P,V)&&(S=!0,await this.plugin.tracker.trackDocument(n,P,y,S))}async ensureVaultFolder(t){let e=t.split("/").filter(Boolean),n="";for(let a of e){n=n?v(n,a):a;try{await this.plugin.app.vault.adapter.mkdir(n)}catch(r){if(!await this.plugin.app.vault.adapter.exists(n))throw r}}}};var q=require("child_process");async function c(i,t=[],e={},n){return new Promise(a=>{let r=(0,q.spawn)(i,t,{env:{...process.env,...e},cwd:n}),s="",o="",d=!1,p=(m,h)=>{d||(d=!0,a({stdout:s,stderr:h?`${o} ${h.message}`:o,code:m}))};r.stdout.on("data",m=>{s+=m.toString()}),r.stderr.on("data",m=>{o+=m.toString()}),r.on("error",m=>{p(1,m)}),r.on("close",m=>{p(m??0)})})}var _=require("path");var N=class{plugin;constructor(t){this.plugin=t}getEnv(){return{RMAPI_HOST:this.plugin.settings.remarkableHost,RMAPI_CONFIG:(0,_.join)(D(this.plugin),".obsidian","rmapi")}}async runRmapi(t){return c(this.plugin.settings.rmapiBinaryPath,t,this.getEnv())}async list(t="/"){let{stdout:e,stderr:n,code:a}=await this.runRmapi(["ls","--json",t]);if(a!==0)throw new Error(`rmapi ls failed: ${n||e}`);try{return JSON.parse(e)}catch(r){throw new Error(`Failed to parse rmapi output: ${r}`)}}async downloadFile(t,e){let{stdout:n,stderr:a,code:r}=await this.runRmapi(["get",t,"-o",e]);if(r!==0)throw new Error(`rmapi get failed: ${a||n}`)}async downloadAnnotatedPdf(t,e){let{stdout:n,stderr:a,code:r}=await this.runRmapi(["geta",t,"-o",e]);if(r!==0)throw new Error(`rmapi geta failed: ${a||n}`)}async isAuthenticated(){let{code:t,stderr:e}=await this.runRmapi(["ls","--json","/"]);return t===0&&!e.includes("auth")&&!e.includes("register")}};var E=class{plugin;constructor(t){this.plugin=t}async renderPageToPng(t,e){this.plugin.settings.pageRenderer==="drawj2d"?await this.renderWithDrawj2d(t,e):await this.renderWithRm2svg(t,e)}async renderWithDrawj2d(t,e){let n=["-jar",this.plugin.settings.drawj2dPath,"-Trm",t,e],{code:a,stderr:r,stdout:s}=await c(this.plugin.settings.javaPath,n);if(a!==0)throw new Error(`drawj2d failed: ${r||s}`)}async renderWithRm2svg(t,e){let n=e.replace(".png",".svg"),{code:a,stderr:r,stdout:s}=await c("rM2svg",[t,n]);if(a!==0)throw new Error(`rM2svg failed: ${r||s}`);let{code:o}=await c("rsvg-convert",["-o",e,n]);if(o!==0){let{code:d,stderr:p,stdout:m}=await c("convert",[n,e]);if(d!==0)throw new Error(`SVG to PNG conversion failed: ${p||m}`)}}};var W=require("fs/promises");async function Y(i){let t=`${i}/content.json`;try{let n=await(0,W.readFile)(t,"utf-8");try{let a=JSON.parse(n),r=ht(a);if(r)return r}catch{}}catch{}let e=`${i}/metadata.json`;try{let n=await(0,W.readFile)(e,"utf-8");try{let a=JSON.parse(n);if(a?.text)return a.text}catch{}}catch{}return""}function ht(i){let t=[];if(i.cPages&&Array.isArray(i.cPages.pages)){for(let e of i.cPages.pages)if(e.text&&t.push(e.text),e.layers)for(let n of e.layers)n.text&&t.push(n.text)}if(i.pages&&Array.isArray(i.pages))for(let e of i.pages)e.text&&t.push(e.text);return t.join(` `)}var b=require("fs/promises"),X=require("path"),Q=require("os");var j=class{plugin;constructor(t){this.plugin=t}async parseImages(t){let e=`${this.plugin.settings.glmocrServerUrl}/glmocr/parse`,n=this.plugin.settings.glmocrApiKey,a=[];for(let s of t){let o=await(0,b.readFile)(s);a.push(`data:image/png;base64,${o.toString("base64")}`)}let r=(0,X.join)((0,Q.tmpdir)(),`glmocr-body-${Date.now()}.json`);await(0,b.writeFile)(r,JSON.stringify({images:a}));try{let s=["-s","-X","POST","-H","Content-Type: application/json","-H",`Authorization: Bearer ${n}`,"--max-time","60","--data-binary",`@${r}`,e],{stdout:o,stderr:d,code:p}=await c("curl",s);if(p!==0)throw new Error(`GLM-OCR request failed: ${d||o}`);try{return JSON.parse(o)}catch(m){throw new Error(`Failed to parse GLM-OCR response: ${m} Raw: ${o}`)}}finally{await(0,b.unlink)(r).catch(()=>{})}}};var A=require("fs/promises"),Z=require("path"),tt=require("os");var B=class{plugin;constructor(t){this.plugin=t}async refineMarkdown(t){let e=`You are formatting the output of an OCR engine that processed a handwritten document. The input is markdown that may have issues with heading levels, list formatting, and paragraph breaks. Analyze the document's structure and: 1. Fix heading hierarchy (h1 \u2192 h2 \u2192 h3 logically) 2. Consolidate fragmented paragraphs 3. Detect and format lists, checkboxes, tables 4. Identify emphasized/underlined text and apply bold/italic 5. Preserve all content \u2014 do not summarize or remove anything INPUT MARKDOWN: ${t} OUTPUT ONLY THE REFINED MARKDOWN. No explanations, no markdown code fences around the output.`,n=(await this.queryOllama(e)).trim();return this.validateOllamaOutput(t,n),n}async mergeAndRefine(t,e){if(!t.trim())return this.refineMarkdown(e);let n=`You have two sources of text from the same handwritten document: SOURCE 1 \u2014 ReMarkable built-in handwriting recognition (raw text, no formatting): ${t} SOURCE 2 \u2014 GLM-OCR engine output (markdown with structure but possible errors): ${e} Your task: 1. Merge both sources, preferring SOURCE 2 for structure and SOURCE 1 for text accuracy 2. Fix heading hierarchy (h1 \u2192 h2 \u2192 h3 logically) 3. Consolidate fragmented paragraphs 4. Detect and format lists, checkboxes, tables 5. Identify emphasized/underlined text and apply bold/italic 6. Preserve all content \u2014 do not summarize or remove anything OUTPUT ONLY THE FINAL REFINED MARKDOWN. No explanations, no markdown code fences around the output.`,a=(await this.queryOllama(n)).trim();return this.validateOllamaOutput(`${t} ${e}`,a),a}async queryOllama(t){let e=`${this.plugin.settings.ollamaHost}/api/generate`,n=this.plugin.settings.styleModel,a=(0,Z.join)((0,tt.tmpdir)(),`ollama-body-${Date.now()}.json`);await(0,A.writeFile)(a,JSON.stringify({model:n,prompt:t,stream:!1}));try{let r=["-s","-X","POST","-H","Content-Type: application/json","--max-time","300","--data-binary",`@${a}`,e],{stdout:s,stderr:o,code:d}=await c("curl",r);if(d!==0)throw new Error(`Ollama request failed: ${o||s}`);try{return JSON.parse(s).response||""}catch(p){throw new Error(`Failed to parse Ollama response: ${p} Raw: ${s}`)}}finally{await(0,A.unlink)(a).catch(()=>{})}}validateOllamaOutput(t,e){let n=t.trim().length,a=e.trim().length;if(a===0)throw new Error("Ollama returned empty Markdown");if(n>500&&aa.endsWith(".rm")&&!a.endsWith(".zip")),n=t.some(a=>a.endsWith("content.pdf")||a.endsWith(".pdf"));return e&&!n}async function st(i){return(await rt(i)).filter(e=>e.endsWith(".rm")&&!e.endsWith(".zip")).sort()}var R=require("obsidian"),H=require("fs/promises"),I=require("path");var F=class{plugin;renderer;glmOcr;refiner;constructor(t){this.plugin=t,this.renderer=new E(t),this.glmOcr=new j(t),this.refiner=new B(t)}async processDocument(t,e){let n=(0,I.join)(D(this.plugin),".obsidian","rmapi-tmp"),a=x(this.plugin,t);try{new R.Notice("Converting handwriting to Markdown..."),await this.ensureDir(n);let r=(0,I.join)(n,"extracted");if(await this.ensureDir(r),await at(a,r),!await it(r))return new R.Notice("Document is not a handwritten notebook \u2014 skipping OCR."),"";let o=await Y(r),d=await st(r),p=[],m=d.length;for(let l=0;ld.id===t.id),o={id:t.id,name:t.name,lastSynced:new Date().toISOString(),remoteModified:t.modifiedClient,remoteVersion:t.version,localPath:e,hasPdf:n,hasMd:a};s>=0?r[s]=o:r.push(o),await this.saveSyncedDocuments(r)}async isDocumentSynced(t){return(await this.getCache()).some(n=>n.id===t)}async getSyncedDocument(t){return(await this.getCache()).find(n=>n.id===t)||null}};var u=require("obsidian"),U=class extends u.Modal{plugin;currentPath="/";constructor(t){super(t.app),this.plugin=t}onOpen(){this.render()}onClose(){this.contentEl.empty()}async render(){let{contentEl:t}=this;t.empty(),t.createEl("h2",{text:"Browse reMarkable"});let e=t.createDiv({cls:"remarkable-browser-toolbar"});e.createSpan({text:this.currentPath}),this.currentPath!=="/"&&new u.ButtonComponent(e).setButtonText("Up").onClick(()=>{this.currentPath=this.parentPath(this.currentPath),this.render()}),new u.ButtonComponent(e).setButtonText("Refresh").onClick(()=>this.render());let n=t.createDiv({cls:"remarkable-browser-list"});n.createEl("p",{text:"Loading..."});try{let a=await this.plugin.rmapi.list(this.currentPath);this.renderNodes(n,a)}catch(a){n.empty(),n.createEl("p",{text:`Failed to load reMarkable files: ${a}`}),new u.Notice(`Failed to load reMarkable files: ${a}`)}}renderNodes(t,e){if(t.empty(),e.length===0){t.createEl("p",{text:"No documents found."});return}for(let n of e){let a=t.createDiv({cls:"remarkable-browser-row"}),r=n.type==="CollectionType",s=r?`Folder: ${n.name}`:n.name;if(a.createSpan({text:s}),r){new u.ButtonComponent(a).setButtonText("Open").onClick(()=>{this.currentPath=this.childPath(this.currentPath,n.name),this.render()});continue}if(n.type!=="DocumentType")continue;let o=this.childPath(this.currentPath,n.name);new u.ButtonComponent(a).setButtonText("Import").onClick(()=>this.importDocument(n,o,!1)),new u.ButtonComponent(a).setButtonText("Handwriting to Markdown").onClick(()=>this.importDocument(n,o,!0))}}async importDocument(t,e,n){try{new u.Notice(n?`Importing and converting ${t.name}...`:`Importing ${t.name}...`),await this.plugin.downloader.downloadDocument({node:t,remotePath:e},{convertToMd:n,forceDownload:!0}),new u.Notice(n?`Markdown created for ${t.name}`:`Imported ${t.name}`)}catch(a){new u.Notice(`Import failed: ${a}`),console.error("reMarkable import failed:",a)}}childPath(t,e){return t==="/"?`/${e}`:`${t}/${e}`}parentPath(t){let e=t.split("/").filter(Boolean);return e.pop(),e.length===0?"/":`/${e.join("/")}`}};var C=class extends w.Plugin{settings;downloader;rmapi;ocrPipeline;tracker;statusBarItem;isSyncing=!1;async onload(){if(await this.loadSettings(),this.rmapi=new N(this),this.tracker=new L(this),this.downloader=new O(this),this.ocrPipeline=new F(this),this.settings.enableHandwritingMd){let t=await this.ocrPipeline.validateDependencies();t.length>0&&(new w.Notice(`OCR dependencies missing: ${t.join(", ")}. Disable "Enable Handwriting to Markdown" in settings or install required tools.`,15e3),this.settings.enableHandwritingMd=!1,await this.saveSettings())}this.addRibbonIcon("pencil","Sync from reMarkable",()=>{this.performSync()}),this.addRibbonIcon("folder-open","Browse reMarkable",()=>{this.openRemarkableBrowser()}),this.addCommand({id:"sync-all",name:"Sync from reMarkable",callback:()=>this.performSync()}),this.addCommand({id:"browse-remarkable",name:"Browse reMarkable",callback:()=>this.openRemarkableBrowser()}),this.addCommand({id:"convert-handwriting-md",name:"Convert handwriting to Markdown",callback:()=>this.convertActiveFileToMd()}),this.addSettingTab(new T(this.app,this)),this.statusBarItem=this.addStatusBarItem(),this.updateStatusBar("Idle"),this.settings.syncInterval>0&&this.registerInterval(window.setInterval(()=>{this.performSync()},this.settings.syncInterval*60*1e3))}onunload(){}async performSync(){if(this.isSyncing){new w.Notice("Sync already in progress.");return}this.isSyncing=!0;try{if(!await this.rmapi.isAuthenticated()){new w.Notice("reMarkable not authenticated. Run 'rmapi' in a terminal to pair your device.",1e4);return}this.updateStatusBar("Syncing..."),await this.downloader.syncAll(),this.updateStatusBar(`Last sync: ${new Date().toLocaleTimeString()}`)}catch(t){this.updateStatusBar("Sync failed"),console.error("Sync error:",t)}finally{this.isSyncing=!1}}async convertActiveFileToMd(){let t=this.app.workspace.getActiveFile();if(!t){new w.Notice("No active file selected");return}let e=t.path;if(!e.endsWith(".rm")){new w.Notice("Active file is not a .rm reMarkable document");return}let n=e.replace(/\.rm$/,".md");try{this.updateStatusBar("Converting to Markdown..."),await this.ocrPipeline.processDocument(e,n),this.updateStatusBar(`Converted: ${t.name}`)}catch(a){this.updateStatusBar("Conversion failed"),new w.Notice(`Conversion failed: ${a}`),console.error("OCR error:",a)}}openRemarkableBrowser(){new U(this).open()}updateStatusBar(t){this.statusBarItem.setText(`reMarkable: ${t}`)}async loadSettings(){this.settings=Object.assign({},J,await this.loadData())}async saveSettings(){let t=await this.loadData()||{};await this.saveData({...t,...this.settings})}};var wt=C;