From 97a42fb7edd298c51db5c1bd923e4a749d44a40b Mon Sep 17 00:00:00 2001 From: KemoNine Date: Fri, 22 Nov 2024 11:41:05 -0500 Subject: [PATCH] add attachment management plugin --- .../plugins/import-attachments-plus/main.js | 14 +++ .../import-attachments-plus/manifest.json | 11 ++ .../import-attachments-plus/styles.css | 104 ++++++++++++++++++ 3 files changed, 129 insertions(+) create mode 100644 .obsidian/plugins/import-attachments-plus/main.js create mode 100644 .obsidian/plugins/import-attachments-plus/manifest.json create mode 100644 .obsidian/plugins/import-attachments-plus/styles.css diff --git a/.obsidian/plugins/import-attachments-plus/main.js b/.obsidian/plugins/import-attachments-plus/main.js new file mode 100644 index 0000000..2f67a6f --- /dev/null +++ b/.obsidian/plugins/import-attachments-plus/main.js @@ -0,0 +1,14 @@ + +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +"use strict";var bt=Object.create;var se=Object.defineProperty;var Ft=Object.getOwnPropertyDescriptor;var yt=Object.getOwnPropertyNames;var Et=Object.getPrototypeOf,vt=Object.prototype.hasOwnProperty;var xt=(i,e)=>{for(var n in e)se(i,n,{get:e[n],enumerable:!0})},Le=(i,e,n,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of yt(e))!vt.call(i,o)&&o!==n&&se(i,o,{get:()=>e[o],enumerable:!(t=Ft(e,o))||t.enumerable});return i};var Ce=(i,e,n)=>(n=i!=null?bt(Et(i)):{},Le(e||!i||!i.__esModule?se(n,"default",{value:i,enumerable:!0}):n,i)),At=i=>Le(se({},"__esModule",{value:!0}),i);var Bt={};xt(Bt,{default:()=>Fe});module.exports=At(Bt);var u=require("obsidian");var _=require("obsidian");var G={actionDroppedFilesOnImport:"ASK_USER",actionPastedFilesOnImport:"ASK_USER",embedFilesOnImport:"ASK_USER",lastActionPastedFilesOnImport:"COPY",lastActionDroppedFilesOnImport:"COPY",lastEmbedFilesOnImport:"NO",multipleFilesImportType:"BULLETED",attachmentFolderLocation:"SUBFOLDER",attachmentFolderPath:"${notename} (attachments)",attachmentName:"${original}",dateFormat:"YYYY_MM_DDTHH_mm_ss",customDisplayText:!0,hideExtForDisplayText:!0,useSelectionForDisplayText:!0,autoRenameAttachmentFolder:!0,autoDeleteAttachmentFolder:!0,deleteAttachmentFolderWhenEmpty:!0,showDeleteMenu:!0,showDeleteMenuForEmbedded:!0,removeWikilinkOnFileDeletion:!0,confirmDeleteAttachmentFolder:!0,hideAttachmentFolders:!0,revealAttachment:!0,revealAttachmentExtExcluded:".md",openAttachmentExternal:!0,openAttachmentExternalExtExcluded:".md",logs:{},compatibility:"1.4.0"},{compatibility:Yt,attachmentFolderPath:jt,attachmentFolderLocation:Gt,...kt}=G,Me={...kt,relativeLocation:"SAME",folderPath:"${notename} (attachments)",linkFormat:"RELATIVE"};var K=(o=>(o.MOVE="MOVE",o.COPY="COPY",o.LINK="LINK",o.ASK_USER="ASK_USER",o))(K||{}),q=(t=>(t.YES="YES",t.NO="NO",t.ASK_USER="ASK_USER",t))(q||{}),Z=(t=>(t.BULLETED="BULLETED",t.NUMBERED="NUMBERED",t.INLINE="INLINE",t))(Z||{});var Ee={IMG:"image file",AUDIO:"audio file",VIDEO:"video file"};function Re(i){return i in Ee}function Ue(i){return typeof i!="object"||i===null?!1:"compatibility"in i&&i.compatibility===G.compatibility}function Ve(i){return typeof i=="boolean"}function ve(i){return i==="absolute"||i==="relative"||i==="shortest"}function Be(i){return i==="ROOT"||i==="CURRENT"||i==="FOLDER"||i==="SUBFOLDER"}function xe(i){if(typeof i!="object"||i===null||!("fileItems"in i))return!1;let e=i;return!(typeof e.fileItems!="object"||e.fileItems===null||!("fileBeingRenamed"in i))}function We(i){return typeof i!="object"||i===null?!1:!("compatibility"in i)}function Ke(i){return typeof i=="object"&&i!==null&&"setQuery"in i&&typeof i.setQuery=="function"}var ee=require("fs"),He=Ce(require("crypto"));var v=[];for(ae=0;ae<256;++ae)v.push((ae+256).toString(16).slice(1));var ae;function $e(i,e=0){return(v[i[e+0]]+v[i[e+1]]+v[i[e+2]]+v[i[e+3]]+"-"+v[i[e+4]]+v[i[e+5]]+"-"+v[i[e+6]]+v[i[e+7]]+"-"+v[i[e+8]]+v[i[e+9]]+"-"+v[i[e+10]]+v[i[e+11]]+v[i[e+12]]+v[i[e+13]]+v[i[e+14]]+v[i[e+15]]).toLowerCase()}var re,Dt=new Uint8Array(16);function Ae(){if(!re&&(re=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!re))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return re(Dt)}var _t=typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto),ke={randomUUID:_t};function Pt(i,e,n){if(ke.randomUUID&&!e&&!i)return ke.randomUUID();i=i||{};var t=i.random||(i.rng||Ae)();if(t[6]=t[6]&15|64,t[8]=t[8]&63|128,e){n=n||0;for(var o=0;o<16;++o)e[n+o]=t[o];return e}return $e(t)}var le=Pt;var I=require("obsidian"),D=Ce(require("path"));function k(...i){return i.join("/")}function x(i){i=(0,I.normalizePath)(i);let e=i.lastIndexOf("/"),n=e!==-1?i.substring(0,e):"",t=e!==-1?i.substring(e+1):i,o=t.lastIndexOf("."),s=o!==-1?t.substring(0,o):t,a=o!==-1?t.substring(o):"";return{dir:n,base:t,filename:s,ext:a,path:i}}function Ye(i){i=(0,I.normalizePath)(i);let e=i.lastIndexOf("/"),n=e!==-1?i.substring(0,e):"",t=e!==-1?i.substring(e+1):i;return{dir:n,foldername:t,path:i}}function It(i){return i instanceof I.TFolder}function Ot(i){return i instanceof I.TFile}function je(i,e,n){let t=i.getAbstractFileByPath(e),o=i.getAbstractFileByPath(n);return t instanceof I.TFile&&o instanceof I.TFile?t.path===o.path:!1}function O(i){return i.split(D.posix.sep).join(D.sep)}async function Ge(i){let e=He.createHash("md5"),n=null;try{n=await ee.promises.open(i,"r");let t=n.createReadStream();for await(let o of t)e.update(o);return e.digest("hex")}finally{n&&await n.close()}}function qe(i){try{return window.moment().format(i)}catch(e){return e instanceof Error?console.error("Error formatting date:",e.message):console.error("Error formatting date:",e),"DATE_ERROR"}}function ze(i,e){let n=x(e),t=1,o,s=null;do s=k(n.dir,`${n.filename} (${t})${n.ext}`),o=te(i,s),t+=1;while(o);return s}async function Qe(i,e){try{let n=await ee.promises.realpath(e),t=await ee.promises.realpath(i),o=D.normalize(n),s=D.normalize(t),a=D.relative(s,o);return!a.startsWith("..")&&!D.isAbsolute(a)?a:null}catch(n){return console.error("Error resolving paths:",n),null}}async function St(i){try{return(await ee.promises.stat(i)).isDirectory()}catch(e){if(e&&typeof e=="object"&&"code"in e&&e.code==="ENOENT")return!1;throw e}}function ce(i,e){let n=i.getAbstractFileByPath(e);return!!n&&It(n)}function te(i,e){let n=i.getAbstractFileByPath(e);return!!n&&Ot(n)}function Xe(i,e){let{filename:n,path:t,ext:o}=x(e),s=Object.create(I.TFile.prototype);return s.path=t,s.name=n,s.vault=i,s.parent=null,s.extension=o,s}async function Je(i){let e=[],n=[];return await Promise.all(i.map(async t=>{await St(t.path)?n.push(t):e.push(t)})),{nonFolderFilesArray:e,foldersArray:n}}async function Te(i,e){if(!ce(i,e))try{await i.createFolder(e)}catch(n){throw new Error(`Failed to create folder at ${e}: ${n}`)}}var z="h4",de=class extends _.Modal{constructor(n,t,o){super(n.app);this.plugin=n;this.lastActionFilesOnImport=t;this.lastEmbedOnImport=o;this.promise=new Promise(s=>{this.resolveChoice=s}),this.selectedAction=t,this.selectedEmbedOption=o}promise;resolveChoice=()=>{};selectedAction;selectedEmbedOption;rememberChoice=!1;createToggle(n,t,o,s,a,c,r=!1){let h=n.createEl("tr");r&&h.addClass("sep"),h.createEl("td",{text:t,cls:"import-question"}),h.createEl("td",{text:o,cls:"import-option-A"});let g=h.createEl("td").createEl("label",{cls:"import-switch"}),p=g.createEl("input",{type:"checkbox"});a==0?p.checked=!1:p.checked=!0,g.createEl("span",{cls:"import-slider"}),h.createEl("td",{text:s,cls:"import-option-B"}),p.addEventListener("change",()=>{c&&c(p.checked?1:0)})}onOpen(){let n,{contentEl:t}=this,o=t.createDiv({cls:"import-plugin"});o.createEl(z,{text:"Import files"}),o.createEl("p",{text:"Configure the import options and then press either enter or the import button."});let s=o.createEl("table");switch(this.lastActionFilesOnImport){case"MOVE":n=0;break;case"COPY":default:n=1;break}switch(this.createToggle(s,"Do you want to move or copy the files to the vault?","Move","Copy",n,r=>{r==0?this.selectedAction="MOVE":this.selectedAction="COPY"},!0),this.lastEmbedOnImport){case"YES":n=0;break;case"NO":default:n=1;break}this.createToggle(s,"Do you want to embed or link the files to the vault?","Embed","Link",n,r=>{r==0?this.selectedEmbedOption="YES":this.selectedEmbedOption="NO"},!0),this.createToggle(s,"Save this answer in the settings for the future?","Yes","No",1,r=>{r==0?this.rememberChoice=!0:this.rememberChoice=!1},!0);let c=o.createDiv({cls:"import-buttons"}).createEl("button",{text:"Import",cls:"mod-cta"});c.addEventListener("click",()=>{this.import()}),setTimeout(()=>{c.focus()},0)}async import(){this.resolveChoice({action:this.selectedAction,embed:this.selectedEmbedOption,rememberChoice:this.rememberChoice}),this.close()}onClose(){this.contentEl.empty(),this.resolveChoice(null)}},he=class extends _.Modal{constructor(n,t,o){super(n.app);this.plugin=n;this.originalFilePath=t;this.destFilePath=o;this.promise=new Promise(a=>{this.resolveChoice=a});let s=x(o);this.filename=s.filename}promise;resolveChoice=()=>{};filename;onOpen(){let{contentEl:n}=this,t=n.createDiv({cls:"import-plugin"});t.createEl(z,{text:"Import files"});let o=t.createEl("p");o.append("You are trying to copy the file ");let{base:s}=x(this.originalFilePath);o.createEl("a",{text:s,href:"#"}).addEventListener("click",p=>{p.preventDefault(),window.require("electron").remote.shell.showItemInFolder(O(this.originalFilePath))}),o.append(" into the vault. However, a "),o.createEl("a",{text:"file",href:"#"}).addEventListener("click",p=>{p.preventDefault(),window.require("electron").remote.shell.showItemInFolder(O(k(this.plugin.vaultPath,this.destFilePath)))}),o.append(" with the same name already exists at the destination location."),t.createEl("p",{text:"How do you want to proceed?"});let r=t.createDiv({cls:"import-buttons"}),h=r.createEl("button",{text:"Keep both",cls:"mod-cta"});h.addEventListener("click",()=>{this.resolveChoice(1),this.close()}),r.createEl("button",{text:"Overwrite",cls:"mod-warning"}).addEventListener("click",()=>{this.resolveChoice(0),this.close()}),r.createEl("button",{text:"Skip",cls:"mod-cancel"}).addEventListener("click",()=>{this.resolveChoice(2),this.close()}),setTimeout(()=>{h.focus()},0)}onClose(){this.contentEl.empty(),this.resolveChoice(null)}},pe=class extends _.Modal{constructor(n,t,o,s){super(n.app);this.plugin=n;this.attachmentFolder=t;this.preDescription=o;this.postDescription=s;this.promise=new Promise(a=>{this.resolveChoice=a})}promise;resolveChoice=()=>{};onOpen(){let{contentEl:n}=this,t=n.createDiv({cls:"import-plugin"});t.createEl(z,{text:"Delete the attachment folder?"}),this.preDescription&&t.appendChild(this.preDescription);let o=t.createEl("p");o.append("Do you want to "+(_.Platform.isDesktop?"move":"delete")+" the attachment folder "),_.Platform.isDesktopApp?o.createEl("a",{text:this.attachmentFolder.name,href:"#"}).addEventListener("click",h=>{h.preventDefault(),window.require("electron").remote.shell.openPath(O(k(this.plugin.vaultPath,this.attachmentFolder.path)))}):o.createEl("strong",{text:this.attachmentFolder.name}),_.Platform.isDesktopApp?o.append(" to the system trash?"):o.append("?"),this.postDescription&&t.appendChild(this.postDescription);let s=t.createDiv({cls:"import-buttons"});s.createEl("button",{text:"Delete",cls:"mod-warning"}).addEventListener("click",()=>{this.resolveChoice(!0),this.close()});let c=s.createEl("button",{text:"Skip",cls:"mod-cancel"});c.addEventListener("click",()=>{this.resolveChoice(!1),this.close()}),setTimeout(()=>{c.focus()},0)}onClose(){this.contentEl.empty(),this.resolveChoice(!1)}},me=class extends _.Modal{constructor(n,t,o,s){super(n.app);this.plugin=n;this.vaultPath=t;this.relativeFilePath=o;this.importAction=s;this.promise=new Promise(a=>{this.resolveChoice=a})}promise;resolveChoice=()=>{};onOpen(){let{contentEl:n}=this,t=n.createDiv({cls:"import-plugin"});t.createEl(z,{text:"Import files"});let o=t.createEl("p");o.append("The file you are trying to import "),o.createEl("a",{text:this.relativeFilePath,href:"#"}).addEventListener("click",f=>{f.preventDefault(),window.require("electron").remote.shell.showItemInFolder(O(k(this.plugin.vaultPath,this.relativeFilePath)))}),o.append(" is already stored in the vault."),this.importAction=="MOVE"&&t.createEl("p",{text:"You intended to move the file. However, moving a file that is already in the vault to a new destination in the same vault is not supported; only copying and linking operations are allowed."}),t.createEl("p",{text:"Do you want to make a copy or refer to the original file in the vault through a relative path?"});let a=t.createDiv({cls:"import-buttons"}),c=a.createEl("button",{text:"Relative path",cls:"mod-cta"});c.addEventListener("click",()=>{this.resolveChoice(1),this.close()}),a.createEl("button",{text:"Copy",cls:"mod-warning"}).addEventListener("click",()=>{this.resolveChoice(0),this.close()}),a.createEl("button",{text:"Skip",cls:"mod-cancel"}).addEventListener("click",()=>{this.resolveChoice(2),this.close()}),setTimeout(()=>{c.focus()},0)}onClose(){this.contentEl.empty(),this.resolveChoice(null)}},ue=class extends _.Modal{constructor(n,t){super(n.app);this.plugin=n;this.nonFolderFilesArray=t;this.promise=new Promise(o=>{this.resolveChoice=o})}promise;resolveChoice=()=>{};onOpen(){let{contentEl:n}=this,t=n.createDiv({cls:"import-plugin"});t.createEl(z,{text:"Import files"}),t.createEl("p").append("Importing folders is not supported in Obsidian. The following folders will not be imported:");let s=t.createEl("ul");this.nonFolderFilesArray.forEach(r=>{s.createEl("li").createEl("a",{text:r.name,href:"#"}).addEventListener("click",g=>{g.preventDefault(),window.require("electron").remote.shell.openPath(O(r.path))})});let c=t.createDiv({cls:"import-buttons"}).createEl("button",{text:"Ok",cls:"mod-warning"});c.addEventListener("click",()=>{this.resolveChoice(!0),this.close()}),setTimeout(()=>{c.focus()},0)}onClose(){this.contentEl.empty(),this.resolveChoice(!1)}},fe=class extends _.Modal{constructor(n,t){super(n.app);this.plugin=n;this.attachmentFolderPath=t;this.promise=new Promise(o=>{this.resolveChoice=o})}promise;resolveChoice=()=>{};onOpen(){let{contentEl:n}=this,t=n.createDiv({cls:"import-plugin"}),o=x(this.attachmentFolderPath);t.createEl(z,{text:"Create an empty attachment folder?"});let s=t.createEl("p");s.append("The attachment folder "),s.createEl("strong",{text:o.base}),s.append(" does not exist yet. Do you want to create it?");let a=t.createDiv({cls:"import-buttons"}),c=a.createEl("button",{text:"Yes",cls:"mod-cta"});c.addEventListener("click",()=>{this.resolveChoice(!0),this.close()}),a.createEl("button",{text:"No",cls:"mod-cancel"}).addEventListener("click",()=>{this.resolveChoice(!1),this.close()}),setTimeout(()=>{c.focus()},0)}onClose(){this.contentEl.empty(),this.resolveChoice(!1)}};var ye=require("path"),Se=require("fs");var ge=require("obsidian");var C=!1,M=!1,_e=!1,L=null;function Ze(i){i.metaKey&&(C=!0),i.altKey&&(M=!0)}function et(i){i.key==="Meta"&&(C=!1),i.key==="Alt"&&(M=!1)}function tt(i){i.metaKey?C=!0:C=!1,i.altKey?M=!0:M=!1}function nt(i){i.metaKey?C=!0:C=!1,i.altKey?M=!0:M=!1}function it(){document.addEventListener("keydown",Ze),document.addEventListener("keyup",et),document.addEventListener("mousedown",tt,{capture:!0}),document.addEventListener("mouseup",nt,{capture:!0}),_e=!0}function ot(){_e&&(document.removeEventListener("keydown",Ze),document.removeEventListener("keyup",et),document.removeEventListener("mousedown",tt,{capture:!0}),document.addEventListener("mouseup",nt,{capture:!0}),_e=!1)}function st(){L&&(ge.WorkspaceLeaf.prototype.openFile=L,L=null)}function at(i){L=ge.WorkspaceLeaf.prototype.openFile,ge.WorkspaceLeaf.prototype.openFile=async function(n,t){let o="."+n.extension;if(L&&C){if(M){if(i.settings.revealAttachmentExtExcluded.split(",").some(a=>a===o))return L.call(this,n,t)}else if(i.settings.openAttachmentExternalExtExcluded.split(",").some(a=>a===o))return L.call(this,n,t)}let s=this.getViewState()?.type=="empty";if(i.settings.revealAttachment&&C&&M)window.require("electron").remote.shell.showItemInFolder(O(k(i.vaultPath,n.path)));else if(i.settings.openAttachmentExternal&&C&&!M)i.app.openWithDefaultApp(n.path);else if(L)return L.call(this,n,t);return s&&this.detach(),Promise.resolve()}}var U=require("obsidian");var Q=null,P,Pe,T;function lt(){Q&&(U.FileManager.prototype.promptForDeletion=Q,Q=null)}function ct(i){P=i,Q=U.FileManager.prototype.promptForDeletion,Pe=P.app.fileManager,U.FileManager.prototype.promptForDeletion=Mt.bind(Pe)}async function Mt(i){await dt.call(this,i)}async function dt(i){let e=i.parent,n=await Nt.call(this,i);if(n){if(P.settings.autoDeleteAttachmentFolder&&P.settings.attachmentFolderPath.includes("${notename}")){let t=x(i.path);if(t.ext===".md"||t.ext===".canvas"){let o=P.app.vault.getAbstractFileByPath(P.getAttachmentFolderOfMdNote(t));if(o instanceof U.TFolder){let s=o.children.length>0?`Note that the folder associated with the MarkDown note you have just deleted is not empty and still contains ${o.children.length} files.`:"The attachment folder is empty, and it should be safe to delete it.",a=createEl("p",{text:s});await rt(P,o,void 0,a)}}}if(P.settings.deleteAttachmentFolderWhenEmpty&&e&&P.matchAttachmentFolder(e.path)&&e.children.length===0){let t=createEl("p",{text:"The attachment folder is now empty, and it should be safe to delete it."});await rt(P,e,void 0,t)}}return n}async function rt(i,e,n,t){if(i.settings.confirmDeleteAttachmentFolder){let s=new pe(i,e,n,t);if(s.open(),!await s.promise)return}let o=e;try{await i.trashFile(o)}catch(s){let a="Failed to remove the attachment folder";console.error(a+":",o),console.error("Error msg:",s),new U.Notice(a+".")}}async function Nt(i){if(!Q)return!1;let e=new Promise((t,o)=>{T=t});if(P.app.vault.getConfig("promptDelete")){let t={childList:!0,subtree:!1};new MutationObserver((o,s)=>{for(let a of o)if(a.addedNodes.length>0){let c=Array.from(a.addedNodes).find(r=>r instanceof HTMLElement&&r.classList.contains("modal-container"));if(c){let r=c.querySelector(".modal-button-container .mod-warning"),h=c.querySelector(".modal-button-container .mod-cancel");if(!r)throw new Error('Failed to correctly identify the "Delete" button.');if(!h)throw new Error('Failed to correctly identify the "Cancel" button.');r.addEventListener("click",()=>{T&&(T(!0),T=null)}),h.addEventListener("click",()=>{T&&(T(!1),T=null)});let f=new MutationObserver((g,p)=>{for(let w of g)if(Array.from(w.removedNodes).includes(c)){p.disconnect(),T&&(T(!1),T=null);break}});c.parentNode&&f.observe(c.parentNode,t),s.disconnect();break}}}).observe(document.body,t)}else T&&(T(!0),T=null);return await Q.call(this,i),await e}async function Ie(i){return await dt.call(Pe,i)}var V=require("obsidian");var X=null,$=null,we=null;function ht(){X&&(V.Vault.prototype.getAvailablePathForAttachments=X,X=null),$&&(V.App.prototype.saveAttachment=$,$=null)}function pt(i){X||(X=V.Vault.prototype.getAvailablePathForAttachments),V.Vault.prototype.getAvailablePathForAttachments=async function(n,t,o){if(!X)throw new Error("Could not execute the original getAvailablePathForAttachments function.");if(!we)throw new Error("The variable data is unexpectedly null.");let s=o?x(o.path):void 0;return await i.createAttachmentName(n+"."+t,we,s)},$||($=V.App.prototype.saveAttachment),V.App.prototype.saveAttachment=async function(n,t,o){if(!$)throw new Error("Could not execute the original saveAttachment function.");we=o;let s=await $.apply(this,[n,t,o]);return we=null,s}}var H=require("obsidian");var be=null,S=null,W=null,N=null,B=null,Oe=null;function ut(){N&&be&&(be["file-explorer"]=N,N=null),W&&S&&(S.prototype.createFolderDom=W,W=null),Oe&&S&&(S.prototype.onCreate=Oe,Oe=null),B&&S&&(S.prototype.acceptRename=B,B=null)}function Rt(i,e){B||(B=e.prototype.acceptRename,e.prototype.acceptRename=async function(){if(!B)throw new Error("Something went wrong in patching file-explorer plugin.");let n=this.fileBeingRenamed;if(n instanceof H.TFolder){let t=this.fileItems[n.path];await B.apply(this),i.settings.hideAttachmentFolders&&i.matchAttachmentFolder(t.file.path)&&t.el.toggleClass("import-plugin-hidden",!0)}else await B.apply(this)})}function Ut(i,e){W||(W=e.prototype.createFolderDom,e.prototype.createFolderDom=function(n){if(!W)throw new Error("Something went wrong in patching file-explorer plugin.");let t=W.apply(this,[n]);return i.settings.hideAttachmentFolders&&i.matchAttachmentFolder(t.file.path)&&t.el.toggleClass("import-plugin-hidden",!0),t})}function mt(i,e){Rt(i,e),Ut(i,e)}async function ne(i){let e=i.app.workspace.getLeavesOfType("file-explorer"),n=i.settings.hideAttachmentFolders;for(let t of e){(0,H.requireApiVersion)("1.7.2")&&t.isDeferred&&await t.loadIfDeferred();let o=t.view;xe(o)&&(n?Object.entries(o.fileItems).forEach(([s,a])=>{a.file instanceof H.TFolder&&a.el.toggleClass("import-plugin-hidden",i.matchAttachmentFolder(s))}):Object.entries(o.fileItems).forEach(([s,a])=>{a.file instanceof H.TFolder&&a.el.toggleClass("import-plugin-hidden",!1)}))}}async function ft(i){if(N||W)return;let e=i.app.workspace.getLeavesOfType("file-explorer"),n=!1;for(let a of e){(0,H.requireApiVersion)("1.7.2")&&a.isDeferred&&await a.loadIfDeferred();let c=a.view;if(xe(c)){S=c.constructor,mt(i,S),n=!0;break}}if(n){ne(i);return}let t=i.app.internalPlugins.getPluginById("file-explorer");if(!t){console.warn("File Explorer internal plugin is not loaded, therefore it cannot be patched");return}N=t.views["file-explorer"];let s=function(a){if(!N)throw new Error("Something went wrong when patching ViewFactory.");let c=N.apply(this,[a]);return S=c.constructor,mt(i,S),t.views["file-explorer"]=N,N=null,c};be=t.views,be["file-explorer"]=s}var Vt=require("obsidian"),A={debug:null,error:null,info:null,log:null,warn:null};function gt(){A.debug&&(console.debug=A.debug,A.debug=null),A.error&&(console.error=A.error,A.error=null),A.info&&(console.info=A.info,A.info=null),A.log&&(console.log=A.log,A.log=null),A.warn&&(console.warn=A.warn,A.warn=null)}var wt=require("@codemirror/state");var m=require("obsidian");var ie=class extends m.PluginSettingTab{plugin;saveTimeout=null;constructor(e,n){super(e,n),this.plugin=n}debouncedSaveSettings(e){this.saveTimeout&&clearTimeout(this.saveTimeout),this.saveTimeout=window.setTimeout(()=>{e===void 0?this.plugin.saveSettings():e.call(this),this.saveTimeout=null},50)}display(){let{containerEl:e}=this;if(e.empty(),m.Platform.isDesktopApp){new m.Setting(e).setName("Importing").setHeading(),new m.Setting(e).setName("Whether to move or copy files that are drag-and-dropped?").setDesc("Choose whether files that are dragged and dropped into the editor should be moved or copied. Alternatively, the user is asked each time. By holding the shift key \u21E7 pressed, you will be shown the import panel, however you configured this option.").addDropdown(l=>{l.addOption("ASK_USER","Ask each time"),l.addOption("MOVE","Move"),l.addOption("COPY","Copy"),l.setValue(this.plugin.settings.actionDroppedFilesOnImport).onChange(async d=>{d in K?(this.plugin.settings.actionDroppedFilesOnImport=d,d!="ASK_USER"&&(this.plugin.settings.lastActionDroppedFilesOnImport=d),this.debouncedSaveSettings()):console.error("Invalid import action type:",d)})}),new m.Setting(e).setName("Whether to move or copy files that are copy-and-pasted?").setDesc("Choose whether files that are copy and pasted into the editor should be moved or copied. Alternatively, the user is asked each time. By holding the shift key \u21E7 pressed, you will be shown the import panel, however you configured this option.").addDropdown(l=>{l.addOption("ASK_USER","Ask each time"),l.addOption("MOVE","Move"),l.addOption("COPY","Copy"),l.setValue(this.plugin.settings.actionPastedFilesOnImport).onChange(async d=>{d in K?(this.plugin.settings.actionPastedFilesOnImport=d,d!="ASK_USER"&&(this.plugin.settings.lastActionPastedFilesOnImport=d),this.debouncedSaveSettings()):console.error("Invalid import action type:",d)})}),new m.Setting(e).setName("Embed imported documents:").setDesc("With this option enabled, the files are imported as an embedded document; if it is deactivated, they are imported as a linked document. By holding the shift key \u21E7 pressed, you will be shown the import panel, however you configured this option.").addDropdown(l=>{l.addOption("ASK_USER","Ask each time"),l.addOption("YES","Yes"),l.addOption("NO","No"),l.setValue(this.plugin.settings.embedFilesOnImport).onChange(async d=>{Object.values(q).includes(d)?(this.plugin.settings.embedFilesOnImport=d,d!="ASK_USER"&&(this.plugin.settings.lastEmbedFilesOnImport=d),this.debouncedSaveSettings()):console.error("Invalid option selection:",d)})}),new m.Setting(e).setName("Import multiple files as:").setDesc("Choose how to import multiple files: as a bulleted list, as a numbered list, or inline without using lists.").addDropdown(l=>{l.addOption("BULLETED","Bulleted list"),l.addOption("NUMBERED","Numbered list"),l.addOption("INLINE","Inline"),l.setValue(this.plugin.settings.multipleFilesImportType).onChange(async d=>{Object.values(Z).includes(d)?(this.plugin.settings.multipleFilesImportType=d,this.debouncedSaveSettings()):console.error("Invalid option selection:",d)})}),new m.Setting(e).setName("Use the selected text for the displayed text:").setDesc("With this option enabled, the selected text is replaced with the link to the imported document and the same selected text is automatically used as the display text for the link itself. Note that you need to drag the attachment onto the selected text. This option is ignored when multiple attachments are imported.").addToggle(l=>l.setValue(this.plugin.settings.useSelectionForDisplayText).onChange(async d=>{this.plugin.settings.useSelectionForDisplayText=d,this.debouncedSaveSettings()}));let s=new m.Setting(e).setName("Use the filename for the displayed text:").setDesc("With this option enabled, the filename of the imported document is used as the display text. This option is ignored when the previous option applies."),a=new m.Setting(e).setName("Hide the extension in the filename for the displayed text:").setDesc("With this option enabled, the extension of the imported file is not included in the displayed text.").addToggle(l=>l.setValue(this.plugin.settings.hideExtForDisplayText).onChange(async d=>{this.plugin.settings.hideExtForDisplayText=d,this.debouncedSaveSettings()}));a.settingEl.style.display=this.plugin.settings.customDisplayText?"":"none",s.addToggle(l=>l.setValue(this.plugin.settings.customDisplayText).onChange(async d=>{this.plugin.settings.customDisplayText=d,a.settingEl.style.display=d?"":"none",this.debouncedSaveSettings()}));let c=new m.Setting(e).setName("Use [[Wikilinks]]:").setDesc(createFragment(l=>{l.appendText("Auto-generate Wikilinks for [[links]] and [[images]] instead of Markdown links and images. Disable this option to generate Markdown links instead. "),this.addWarningGeneralSettings(l)}));c.addToggle(l=>{let d=this.app.vault.getConfig("useMarkdownLinks");if(!Ve(d)){c.settingEl.remove();return}l.setValue(!d).onChange(async F=>{this.app.vault.setConfig("useMarkdownLinks",!F)})});let r=new m.Setting(e).setName("New link format:").setDesc(createFragment(l=>{l.appendText("What links to insert when auto-generating internal links. "),this.addWarningGeneralSettings(l)}));r.addDropdown(l=>{let d=this.app.vault.getConfig("newLinkFormat");if(!ve(d)){r.settingEl.remove();return}l.addOption("shortest","Shortest path when possible"),l.addOption("relative","Relative path to note"),l.addOption("absolute","Absolute path in vault"),l.setValue(d).onChange(async F=>{ve(F)?this.app.vault.setConfig("newLinkFormat",F):console.error("Invalid option selection:",F)})}),new m.Setting(e).setName("Opening").setHeading();let h;m.Platform.isMacOS?h="\u2318":h="Ctrl";let f=(l,d)=>d.split(",").map(y=>y.trim()).filter(y=>y!=="").map(y=>(y.startsWith(".")||(y="."+y),y)).filter((y,R,J)=>J.indexOf(y)===R).join(", "),g=new m.Setting(e).setName("Open attachments with default external application:").setDesc(`With this option enabled, when you open an attachment by holding ${h}, the attachment opens in default external application.`),p=new m.Setting(e).setName("Exclude the following extensions:").setDesc("Enter a list of extensions separated by comma (e.g.: .md, .pdf) for which the default Obsidian behavior applies instead of opening the file in the default external application.").addText(l=>{l.setPlaceholder("Enter a list of extensions"),l.setValue(this.plugin.settings.openAttachmentExternalExtExcluded),l.onChange(async d=>{this.plugin.settings.openAttachmentExternalExtExcluded=f(l,d),this.debouncedSaveSettings()}),l.inputEl.onblur=async()=>{l.setValue(this.plugin.settings.openAttachmentExternalExtExcluded)}});p.settingEl.style.display=this.plugin.settings.openAttachmentExternal?"":"none",g.addToggle(l=>l.setValue(this.plugin.settings.openAttachmentExternal).onChange(async d=>{this.plugin.settings.openAttachmentExternal=d,this.debouncedSaveSettings(),p.settingEl.style.display=d?"":"none"})),m.Platform.isMacOS?h="\u2318+\u2325":h="Ctrl+Alt";let w=new m.Setting(e).setName("Reveal attachments in system's file manager:").setDesc(`With this option enabled, when you open an attachment by holding ${h}, the attachment is shown in the system's file manager.`),b=new m.Setting(e).setName("Exclude the following extensions:").setDesc("Enter a list of extensions separated by comma (e.g.: .md, .pdf) for which the default Obsidian behavior applies instead of revealing the file in the system's file manager").addText(l=>{l.setPlaceholder("Enter a list of extensions"),l.setValue(this.plugin.settings.revealAttachmentExtExcluded),l.onChange(async d=>{this.plugin.settings.revealAttachmentExtExcluded=f(l,d),this.debouncedSaveSettings()}),l.inputEl.onblur=async()=>{l.setValue(this.plugin.settings.revealAttachmentExtExcluded)}});b.settingEl.style.display=this.plugin.settings.revealAttachment?"":"none",w.addToggle(l=>l.setValue(this.plugin.settings.revealAttachment).onChange(async d=>{this.plugin.settings.revealAttachment=d,this.debouncedSaveSettings(),b.settingEl.style.display=d?"":"none"}))}new m.Setting(e).setName("Managing").setHeading();let n=new m.Setting(e).setName("Show option in context menu of embedded images to delete them:").setDesc("With this option enabled, when you right click on an embedded image in your note, an option 'Delete image' will be shown in the context menu.").addToggle(s=>{s.setValue(this.plugin.settings.showDeleteMenuForEmbedded).onChange(async a=>{this.plugin.settings.showDeleteMenuForEmbedded=a,a?this.plugin.addDeleteMenuForEmbeddedImages("all"):this.plugin.removeDeleteMenuForEmbeddedImages("all"),this.debouncedSaveSettings()})}),t=new m.Setting(e).setName("Show option in context menu to delete attachment files:").setDesc("With this option enabled, when you right click on a Wikilink in your note, an 'Delete file' will be shown in the context menu.").addToggle(s=>{s.setValue(this.plugin.settings.showDeleteMenu).onChange(async a=>{this.plugin.settings.showDeleteMenu=a,this.plugin.addDeleteMenuForLinks(a),this.debouncedSaveSettings()})}),o=new m.Setting(e).setName("Remove Wikilink when deleting an attachment file:").setDesc("With this option enabled, when you right click on a Wikilink or MarkDown link in your note to delete the attachment, not only the attachment will be deleted, but also the Wikilink or MarkDown link, respectively, will be removed from your note.").addToggle(s=>s.setValue(this.plugin.settings.removeWikilinkOnFileDeletion).onChange(async a=>{this.plugin.settings.removeWikilinkOnFileDeletion=a,this.debouncedSaveSettings()}));new m.Setting(e).setName("Automatically remove attachment folders when empty:").setDesc("With this option enabled, after deleting an attachment, the plugin will check if the attachments folder is now empty, and if it is, it will delete the attachments folder as well.").addToggle(s=>s.setValue(this.plugin.settings.deleteAttachmentFolderWhenEmpty).onChange(async a=>{this.plugin.settings.deleteAttachmentFolderWhenEmpty=a,this.debouncedSaveSettings()})),new m.Setting(e).setName("Rename the attachment folder automatically and update all links correspondingly:").setDesc("With this option enabled, when you rename/move an note, if the renamed note has an attachment folder connected to it, its attachment folder is renamed/moved to a new name/location corresponding to the new name of the note.").addToggle(s=>s.setValue(this.plugin.settings.autoRenameAttachmentFolder).onChange(async a=>{this.plugin.settings.autoRenameAttachmentFolder=a,this.debouncedSaveSettings()})),new m.Setting(e).setName("Delete the attachment folder automatically when the corresponding note is deleted:").setDesc("With this option enabled, when you delete a note, if the deleted note has an attachment folder connected to it, its attachment folder will be deleted as well. Note: automatic deletion only works when the name of the attachment folder contains ${notename}.").addToggle(s=>s.setValue(this.plugin.settings.autoDeleteAttachmentFolder).onChange(async a=>{this.plugin.settings.autoDeleteAttachmentFolder=a,await this.debouncedSaveSettings()})),new m.Setting(e).setName("Ask confirmation before deleting the attachment folder:").setDesc("If enabled, the user is asked each time whether to delete the attachment folder.").addToggle(s=>s.setValue(this.plugin.settings.confirmDeleteAttachmentFolder).onChange(async a=>{this.plugin.settings.confirmDeleteAttachmentFolder=a,await this.debouncedSaveSettings()})),new m.Setting(e).setName("Attachment folder").setHeading(),m.Platform.isDesktopApp&&this.addAttachmentFolderSettings(e),new m.Setting(e).setName("Hide attachment folders:").setDesc("With this option enabled, the attachment folders will not be shown.").addToggle(s=>s.setValue(this.plugin.settings.hideAttachmentFolders).onChange(async a=>{this.plugin.settings.hideAttachmentFolders=a,await this.debouncedSaveSettings(),ne(this.plugin)})),m.Platform.isDesktopApp&&(new m.Setting(e).setName("Attachments").setHeading(),new m.Setting(e).setName("Name of the imported attachments:").setDesc(createFragment(s=>{s.appendText("Choose how to name the imported attachments, using the following variables as a placeholder:");let a=s.createEl("ul");a.createEl("li",{text:"${original} for the original name (omitting file extension) of the imported attachment files"}),a.createEl("li",{text:"${notename} for the name of the MarkDown note into which the attachment files will be imported"}),a.createEl("li",{text:"${date} for the current date"}),a.createEl("li",{text:"${uuid} for a 128-bit Universally Unique Identifier"}),a.createEl("li",{text:"${md5} for a MD5 hash of the imported file"}),s.appendText("Note that the file extension of the imported attachments is preserved.")})).addText(s=>{s.setPlaceholder("Enter attachment name"),s.setValue(this.plugin.settings.attachmentName),s.onChange(async a=>{a.trim()==""&&(a="${original}"),this.plugin.settings.attachmentName=a,await this.debouncedSaveSettings()})}),new m.Setting(e).setName("Date format for files:").setDesc(createFragment(s=>{s.appendText("Choose the date format for the placeholder ${date} in the attachment name, based on "),s.createEl("a",{href:"https://momentjscom.readthedocs.io/en/latest/moment/04-displaying/01-format",text:"momentjs"}),s.appendText(" syntax.")})).addText(s=>{s.setPlaceholder("Enter date format"),s.setValue(this.plugin.settings.dateFormat),s.onChange(async a=>{this.plugin.settings.dateFormat=a,await this.debouncedSaveSettings()})})),m.Platform.isDesktopApp&&(new m.Setting(e).setName("Commands and hotkeys").setHeading(),new m.Setting(e).setName(createFragment(s=>{s.appendText("The plugin offers a range of commands to import attachments as well. You can review the commands and customize them with hotkeys by visiting the ");let a=createEl("em"),c=s.createEl("a",{href:"#",text:"Hotkeys"});c.onclick=()=>{let r=this.app.setting.openTabById("hotkeys");Ke(r)&&r.setQuery(this.plugin.manifest.id)},a.appendChild(c),s.appendChild(a),s.appendText(" configuration pane.")})))}cleanUpAttachmentFolderSettings(){let e=(0,m.normalizePath)(this.plugin.settings.attachmentFolderPath).replace(/^(\.\/)*\.?/,"");this.plugin.settings.attachmentFolderLocation==="FOLDER"&&e=="/"&&(this.plugin.settings.attachmentFolderLocation="ROOT"),this.plugin.settings.attachmentFolderLocation==="SUBFOLDER"&&e=="/"&&(this.plugin.settings.attachmentFolderLocation="CURRENT")}hide(){this.cleanUpAttachmentFolderSettings()}addAttachmentFolderSettings(e){this.cleanUpAttachmentFolderSettings();let n=new m.Setting(e).setName("Default location for new attachments:").setDesc(createFragment(o=>{o.appendText("Where newly added attachments are placed.")})),t=new m.Setting(e).setName("Attachment folder path:").setDesc(createFragment(o=>{o.appendText("Place newly created attachment files, such as images created via drag-and-drop or audio recordings, in this folder. Use the following variables as a placeholder:"),o.createEl("ul").createEl("li",{text:"${notename} for the name of the MarkDown note into which the attachment files will be imported"})})).addText(o=>{o.setPlaceholder("Example: folder 1/folder"),o.setValue(this.plugin.settings.attachmentFolderPath),o.onChange(async s=>{this.plugin.settings.attachmentFolderPath=s,this.debouncedSaveSettings(()=>{this.plugin.saveSettings(),this.plugin.parseAttachmentFolderPath(),ne(this.plugin)})})});n.addDropdown(o=>{let s=a=>{switch(a){case"ROOT":case"CURRENT":t.settingEl.style.display="none";break;case"FOLDER":case"SUBFOLDER":t.settingEl.style.display="";break}};o.addOption("ROOT","Vault folder"),o.addOption("FOLDER","In the folder specified below"),o.addOption("CURRENT","Same folder as current file"),o.addOption("SUBFOLDER","In subfolder under current folder"),o.setValue(this.plugin.settings.attachmentFolderLocation),s(this.plugin.settings.attachmentFolderLocation),o.onChange(async a=>{if(!Be(a)){console.error("Invalid option selection:",a);return}this.plugin.settings.attachmentFolderLocation=a,s(a),this.debouncedSaveSettings(()=>{this.plugin.saveSettings(),this.plugin.parseAttachmentFolderPath(),ne(this.plugin)})})})}addWarningGeneralSettings(e){let n=e.createSpan({text:"Be aware that this setting is a mirror of the corresponding setting in the vault preference pane ",cls:"mod-warning"}),t=n.createEl("a",{text:"Files and links",href:"#"});return t.id="file-link-settings",t.addEventListener("click",o=>{o.preventDefault(),this.app.setting.openTabById("file")}),n.appendText(". Any change made here is carried over to the general setting and viceversa."),n}};var Y=class extends Error{},Fe=class extends u.Plugin{settings={...G};vaultPath;settingsTab;matchAttachmentFolder=e=>!0;file_menu_cb_registered=!1;file_menu_embedded_cb_registered_docs=new Map;constructor(e,n){if(super(e,n),this.settingsTab=new ie(this.app,this),this.file_menu_cb=this.file_menu_cb.bind(this),this.editor_drop_cb=this.editor_drop_cb.bind(this),this.editor_paste_cb=this.editor_paste_cb.bind(this),this.editor_rename_cb=this.editor_rename_cb.bind(this),this.context_menu_cb=this.context_menu_cb.bind(this),u.Platform.isDesktopApp){let t=this.app.vault.adapter;if(!(t instanceof u.FileSystemAdapter))throw new Error("The vault folder could not be determined.");this.vaultPath=t.getBasePath().split(ye.sep).join(ye.posix.sep)}else this.vaultPath=""}parseAttachmentFolderPath(){switch(this.settings.attachmentFolderLocation){case"CURRENT":case"ROOT":this.matchAttachmentFolder=o=>!1;return;case"FOLDER":case"SUBFOLDER":}let e=this.settings.attachmentFolderPath,n="${notename}";if(e.includes(n)){let h=function(p){return p.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")};var t=h;let o=e.indexOf(n),a=e.lastIndexOf(n)+n.length,c=e.substring(0,o),r=e.substring(a),f=(p=>{let[w,b]=p.split("${notename}"),l=h(w),d=h(b),F=`^${l}(.*?)${d}$`;return new RegExp(F)})(e),g=this.settings.attachmentFolderLocation==="SUBFOLDER";this.matchAttachmentFolder=p=>{let w=p.startsWith(c)||p.includes(`/${c}`),b=p.endsWith(r),l=w&&b;if(l&&g){let{foldername:d,dir:F}=Ye(p),y=d.match(f);if(y&&y[1]){let R=(0,u.normalizePath)(k(F,y[1]));return te(this.app.vault,R+".md")||te(this.app.vault,R+".canvas")}else return!1}return l};return}else switch(this.settings.attachmentFolderLocation){case"FOLDER":this.matchAttachmentFolder=o=>o===e;return;case"SUBFOLDER":this.matchAttachmentFolder=o=>o.endsWith(`/${e}`)||o===e;return}}splitAroundOriginal(e,n){let t=e.indexOf(n);if(t===-1)return[e,""];let s=e.lastIndexOf(n)+n.length,a=e.substring(0,t),c=e.substring(s);return[a,c]}async onload(){await this.loadSettings(),this.addSettingTab(this.settingsTab),u.Platform.isDesktopApp&&(at(this),it()),u.Platform.isDesktopApp&&pt(this),ct(this),this.app.workspace.onLayoutReady(()=>{ft(this)}),u.Platform.isDesktopApp&&this.addCommands(),u.Platform.isDesktopApp&&this.registerEvent(this.app.workspace.on("editor-drop",this.editor_drop_cb)),u.Platform.isDesktopApp&&this.registerEvent(this.app.workspace.on("editor-paste",this.editor_paste_cb)),this.registerEvent(this.app.vault.on("rename",this.editor_rename_cb)),this.addDeleteMenuForLinks(this.settings.showDeleteMenu),this.registerDocuments(),console.log("Loaded plugin Import Attachments+")}iterateOverAllDocuments(e){this.app.workspace.iterateAllLeaves(n=>{let t=n.view.containerEl.ownerDocument;e(t)})}registerDocuments(){this.addDeleteMenuForEmbeddedImages(document),this.iterateOverAllDocuments(e=>{this.file_menu_embedded_cb_registered_docs.has(e)||(this.file_menu_embedded_cb_registered_docs.set(e,!1),this.settings.showDeleteMenuForEmbedded&&this.addDeleteMenuForEmbeddedImages(e))}),this.app.workspace.on("window-open",(e,n)=>{let t=n.document;this.file_menu_embedded_cb_registered_docs.has(t)||this.file_menu_embedded_cb_registered_docs.set(t,!1),this.settings.showDeleteMenuForEmbedded&&this.addDeleteMenuForEmbeddedImages(t)}),this.app.workspace.on("window-close",(e,n)=>{let t=n.document;this.file_menu_embedded_cb_registered_docs.has(t)&&(this.removeDeleteMenuForEmbeddedImages(t),this.file_menu_embedded_cb_registered_docs.delete(t))})}addCommands(){this.addCommand({id:"move-file-to-vault-link",name:"Move file to vault as linked attachment",callback:()=>this.choose_file_to_import_cb({embed:!1,action:"MOVE"})}),this.addCommand({id:"move-file-to-vault-embed",name:"Move file to vault as embedded attachment",callback:()=>this.choose_file_to_import_cb({embed:!0,action:"MOVE"})}),this.addCommand({id:"copy-file-to-vault-link",name:"Copy file to vault as linked attachment",callback:()=>this.choose_file_to_import_cb({embed:!1,action:"COPY"})}),this.addCommand({id:"copy-file-to-vault-embed",name:"Copy file to vault as embedded attachment",callback:()=>this.choose_file_to_import_cb({embed:!0,action:"COPY"})}),this.addCommand({id:"open-attachments-folder",name:"Open attachments folder",callback:()=>this.open_attachments_folder_cb()})}onunload(){st(),ot(),lt(),ht(),ut(),gt(),this.addDeleteMenuForLinks(!1),this.removeDeleteMenuForEmbeddedImages("all")}addDeleteMenuForLinks(e){e&&!this.file_menu_cb_registered?(this.app.workspace.on("file-menu",this.file_menu_cb),this.file_menu_cb_registered=!0):this.file_menu_cb_registered&&(this.app.workspace.off("file-menu",this.file_menu_cb),this.file_menu_cb_registered=!1)}addDeleteMenuForEmbeddedImages(e){let n=t=>{t.addEventListener("contextmenu",this.context_menu_cb),this.file_menu_embedded_cb_registered_docs.set(t,!0)};if(e==="all")this.file_menu_embedded_cb_registered_docs.forEach((t,o)=>{t===!1&&n(o)});else{let t=this.file_menu_embedded_cb_registered_docs.get(e);(t===void 0||t===!1)&&n(e)}}removeDeleteMenuForEmbeddedImages(e){let n=t=>{t.removeEventListener("contextmenu",this.context_menu_cb),this.file_menu_embedded_cb_registered_docs.set(t,!1)};e==="all"?this.file_menu_embedded_cb_registered_docs.forEach((t,o)=>{t===!0&&n(o)}):this.file_menu_embedded_cb_registered_docs.get(e)===!0&&n(e)}file_menu_cb(e,n){if(!e.sections.contains("canvas")&&n instanceof u.TFile){let t=null,o=null,s=null;for(let r of e.items){let h=`${r.callback}`;t===null&&r.section==="action"&&(t=r),o===null&&h.contains("promptForFileRename")&&(o=r),s===null&&h.contains("promptForFileDeletion")&&(s=r),s===null&&h.contains("promptForDeletion")&&(s=r)}if(o===null&&(o=t),s)return;let a=null;if(e.addItem(r=>{a=r,r.setTitle("Delete link and file").setIcon("lucide-trash-2").setSection("danger").onClick(h=>{this.delete_file_cb(n)}),r.dom.classList.add("is-warning")}),!1&&o&&a){let r=e.items,h=r.indexOf(o),f=r.indexOf(a);if(h!==-1&&f!==-1){let[g]=r.splice(f,1);r.splice(h+1,0,g)}}}}async delete_file_cb(e,n){try{let t=this.app.workspace.getActiveViewOfType(u.MarkdownView);if(t===null)throw new Y("not active view of type 'MarkdownView' was found");let o=t.editor,s=o.cm,a=s.state.doc,c=(()=>{let w;return n?w=s.posAtDOM(n):w=s.state.selection.main.head,w!==null&&(w=Math.clamp(w,0,a.length)),w})();if(c===null)throw new Y("could not determine the link position in the MarkDown note");let r=a.lineAt(c),h=r.text,f={line:r.number-1,ch:c-r.from},g=/\!?\[\[\s*(.*?)\s*(?:\|.*?)?\]\]|\!?\[.*?\]\(([^\s]+)\)/g,p;for(;(p=g.exec(h))!==null;){let w=p.index,b=w+p[0].length;if(f.ch>=w&&f.ch<=b){let l=(()=>{let F;return p[1]?F=p[1]:F=decodeURIComponent(p[2]),this.app.vault.getFileByPath(F)})();if(l&&l!==e)throw new Y(`after parsing the link, file '${e.path}' was found in the vault, but does not match with clicked file '${e.path}'`);return await Ie(e)&&this.settings.removeWikilinkOnFileDeletion&&o.replaceRange("",{line:r.number-1,ch:w},{line:r.number-1,ch:b}),!0}}throw new Y(`no link was found at the line number ${r.number} containing: ${h}`)}catch(t){if(t instanceof Y)console.error(`No matching link found at the click position: ${t.message}`),await Ie(e);else throw t}return!1}async delete_img_cb(e,n){let t=(()=>{let o=n.parentElement;if(!o)return null;let s=o.getAttribute("src");return s?this.app.vault.getFileByPath(s):null})();t&&this.delete_file_cb(t,n)}async onExternalSettingsChange(){await this.loadSettings();let e=this.app.setting.activeTab;e&&e instanceof ie&&e.display()}async loadSettings(){let e=n=>{if(Ue(n))return n;if(We(n)){let t=Object.assign({},Me,n),o=t.folderPath,s=t.relativeLocation,a;switch(s){case"SAME":a="SUBFOLDER";break;case"VAULT":a="ROOT";break}let c=o,{folderPath:r,relativeLocation:h,linkFormat:f,...g}=t,p={...g,attachmentFolderPath:c,attachmentFolderLocation:a,compatibility:"1.4.0"};return e(p)}};this.settings=Object.assign({},G,e(await this.loadData())),delete this.settings.logs,this.parseAttachmentFolderPath()}async saveSettings(){await this.saveData(this.settings)}async renameFile(e,n){try{let t=this.app.vault.getAbstractFileByPath(e);t instanceof u.TAbstractFile?(await this.app.fileManager.renameFile(t,n),new u.Notice("Attachment folder renamed successfully.")):new u.Notice("Attachment folder could not be found at the given location.")}catch(t){let o="Failed to rename file";console.error(o+":",t),new u.Notice(o+".")}}async trashFile(e){try{e instanceof u.TAbstractFile?(await this.app.vault.adapter.trashSystem(e.path),u.Platform.isDesktop?new u.Notice("Attachment folder moved to the system trash successfully."):new u.Notice("Attachment folder deleted successfully.")):new u.Notice("Attachment folder could not be found at the given location.")}catch(n){let t="Failed to rename file";console.error(t+":",n),new u.Notice(t+".")}}getAttachmentFolderOfMdNote(e){if(e===void 0){let a=this.app.workspace.getActiveFile();if(a===null)throw new Error("The active note could not be determined.");e=x(a.path)}if(e.ext!==".md"&&e.ext!==".canvas")throw new Error("No Markdown file was provided.");let n=e.dir,t=e.filename,o=this.settings.attachmentFolderPath.replace(/\$\{notename\}/g,t),s;switch(this.settings.attachmentFolderLocation){case"CURRENT":s=n;break;case"SUBFOLDER":s=k(n,o);break;case"ROOT":s="/";break;case"FOLDER":s=o;break}return s=(0,u.normalizePath)(s),s}async createAttachmentName(e,n,t){let o=x(e),s=this.settings.attachmentName,a=this.settings.dateFormat,c=o.filename,r=s.replace(/\$\{original\}/g,c).replace(/\$\{uuid\}/g,le()).replace(/\$\{date\}/g,qe(a));if(t&&(r=r.replace(/\$\{notename\}/g,t.filename)),s.includes("${md5}")){let f="";try{f=await Ge(e)}catch(g){console.error("Error hashing the file:",g)}r=r.replace(/\$\{md5\}/g,f)}r+=o.ext;let h=this.getAttachmentFolderOfMdNote(t);return await Te(this.app.vault,h),k(h,r)}context_menu_cb(e){let n=this.app.workspace.activeLeaf;if(n&&n.view.getViewType()!="markdown"||!(e.target instanceof HTMLElement))return;let t=e.target,o=t.tagName;if(Re(o)){if(!t.parentElement)return;e.preventDefault();let a=new u.Menu;a.addItem(c=>{c.setTitle(`Delete ${Ee[o]}`).setIcon("trash-2").setSection("danger").onClick(()=>{this.delete_img_cb(e,t)}),c.dom.classList.add("is-warning")}),a.showAtMouseEvent(e)}}async editor_rename_cb(e,n){if(!this.settings.autoRenameAttachmentFolder)return;let t=x(n);if(t.ext!==".md"&&t.ext!==".canvas")return;let o=this.getAttachmentFolderOfMdNote(t);if(o&&ce(this.app.vault,o)){let s=this.getAttachmentFolderOfMdNote(x(e.path)),a=o,c=s;try{await this.renameFile(a,c)}catch(r){let h="Failed to rename the attachment folder";console.error(h),console.error("Original attachment folder:",a),console.error("New attachment folder:",c),console.error("Error msg:",r),new u.Notice(h+".")}}}async editor_paste_cb(e,n,t){if(e.defaultPrevented)return;if(!(t instanceof u.MarkdownView)){console.error("No view provided");return}let o=e.clipboardData;if(o){let s=o.files;if(s&&s.length>0){let a=Array.from(s);if(a.every(r=>r.path&&r.path!=="")){e.preventDefault();let r=!1;this.handleFiles(a,n,t,r,0)}}}}async choose_file_to_import_cb(e){let n=this.app.workspace.getActiveViewOfType(u.MarkdownView),t=n?.editor;if(!t){let s="No active markdown editor found.";console.error(s),new u.Notice(s);return}let o=document.createElement("input");o.type="file",o.multiple=!0,o.onchange=async s=>{let c=s.target.files;if(c&&c.length>0)await this.moveFileToAttachmentsFolder(Array.from(c),t,n,e);else{let r="No files selected or file access error.";console.error(r),new u.Notice(r)}},o.click()}async editor_drop_cb(e,n,t){if(e.defaultPrevented)return;if(!(t instanceof u.MarkdownView)){console.error("No view provided");return}if(e.altKey)return;e.preventDefault();let s=e.shiftKey,a=e?.dataTransfer?.files;if(a)if(a.length>0){let c=n.cm.posAtCoords({x:e.clientX,y:e.clientY});if(c===null){console.error("Unable to determine drop position");return}let h=n.cm.state.selection.main,f=!h.empty,g=h.from,p=h.to;f&&c>=g&&c<=p||n.cm.dispatch({selection:wt.EditorSelection.single(c)});let b=require("electron").webUtils,l;if(b)l=Array.from(a).map(d=>{let F=b.getPathForFile(d);return d.path=F,d});else{let d=require("electron").remote.app.getVersion();console.warn(`webUtils not available with the current Obsidian installer version ${d}. Please replace your installation of Obsidian with a fresh new installation.`),l=Array.from(a)}this.handleFiles(l,n,t,s,1)}else console.error("No files dropped")}async handleFiles(e,n,t,o,s){let{nonFolderFilesArray:a,foldersArray:c}=await Je(e);if(c.length>0){let b=new ue(this,c);b.open(),await b.promise}let r="COPY",h="COPY";switch(s){case 1:r=this.settings.actionDroppedFilesOnImport,h=this.settings.lastActionDroppedFilesOnImport;break;case 0:r=this.settings.actionPastedFilesOnImport,h=this.settings.lastActionPastedFilesOnImport;break}let f=this.settings.embedFilesOnImport,g=this.settings.lastEmbedFilesOnImport;if(o||r=="ASK_USER"||f=="ASK_USER"){let b=new de(this,h,g);b.open();let l=await b.promise;if(l==null)return;switch(r=l.action,s){case 1:l.rememberChoice&&(this.settings.actionDroppedFilesOnImport=r),this.settings.lastActionDroppedFilesOnImport=r;break;case 0:l.rememberChoice&&(this.settings.actionPastedFilesOnImport=r),this.settings.lastActionPastedFilesOnImport=r;break}f=l.embed,l.rememberChoice&&(this.settings.embedFilesOnImport=f),this.settings.lastEmbedFilesOnImport=f,this.settingsTab.debouncedSaveSettings()}let w={embed:f=="YES",action:r};this.moveFileToAttachmentsFolder(a,n,t,w)}async moveFileToAttachmentsFolder(e,n,t,o){let s=t.file;if(s===null)throw new Error("The active note could not be determined.");let a=x(s.path),c=n.getCursor();e.length>1&&this.settings.multipleFilesImportType!="INLINE"&&c.ch!==0&&(n.replaceRange(` +`,c),n.setCursor({line:c.line+1,ch:0}));let r=e.length>1,h=e.map(async p=>{let w=p.path,b=await this.createAttachmentName(w,p,a),l=await te(this.app.vault,b),d=await Qe(this.vaultPath,w);if(d){if(l&&je(this.app.vault,d,b))return b;let F=new me(this,w,d,o.action);F.open();let y=await F.promise;if(y==null)return null;switch(y){case 2:return null;case 1:o.action="LINK";break;case 0:o.action="COPY";break}}if(l&&o.action!="LINK"){let F=new he(this,w,b);F.open();let y=await F.promise;if(y==null)return null;switch(y){case 0:break;case 1:b=ze(this.app.vault,b);break;case 2:return null}}try{switch(o.action){case"MOVE":return await Se.promises.rename(w,k(this.vaultPath,b)),b;case"COPY":return await Se.promises.copyFile(w,k(this.vaultPath,b)),b;case"LINK":default:return d}}catch(F){let y="Failed to process the file";return new u.Notice(y+"."),console.error(y+":",w,F),null}}),f=await Promise.all(h),g=0;if(f.forEach(p=>{p&&this.insertLinkToEditor(p,n,s.path,o,r?++g:void 0)}),g>0){let p="";switch(o.action){case"MOVE":p="Moved";break;case"COPY":p="Copied";break}new u.Notice(`${p} successfully ${g} files to the attachments folder.`)}}insertLinkToEditor(e,n,t,o,s){let a="",c="";if(s)switch(this.settings.multipleFilesImportType){case"BULLETED":a="- ",c=` +`;break;case"NUMBERED":a=`${s}. `,c=` +`;break;case"INLINE":s>1&&(a=` + +`);break}let r=n.cm.state.selection.main,h=Xe(this.app.vault,e),f=h.name,g=(()=>{let E="";if(this.settings.customDisplayText&&(E=f+(this.settings.hideExtForDisplayText?"":h.extension)),!s&&this.settings.useSelectionForDisplayText){let oe=n.cm.state.doc.sliceString(r.from,r.to);oe.length>0&&(E=oe)}return E})(),p=this.app.fileManager.generateMarkdownLink(h,t,void 0,this.settings.customDisplayText?g:void 0),w=new RegExp("^(!)?(\\[[^\\]]*\\])(.*)$"),b=new RegExp("^(!)?(.*?)(|[^|]*)?$"),l=this.app.vault.getConfig("useMarkdownLinks"),d,F,y=!1;if(l){let E=p.match(w);d=p.length,F=p,E&&(d=1,F="["+g+"]"+E[3],y=!0)}else{let E=p.match(b);d=p.length,F=p,E&&(d=E[2].length,F=E[2]+(E[3]?E[3]:""),y=!0)}o.embed&&(a=a+"!");let R=a+F+c,J=n.getCursor("from"),j=n.getCursor("to");if(n.replaceRange(R,J,j),s==0)if(y){let E={line:j.line,ch:j.ch+d+a.length},oe={line:j.line,ch:E.ch+g.length};n.setSelection(E,oe)}else{let E={line:j.line,ch:j.ch+R.length};n.setCursor(E)}else{let E={line:J.line,ch:J.ch+R.length};n.setCursor(E)}}async open_attachments_folder_cb(){let e=this.app.workspace.getActiveFile();if(!e){console.error("Cannot open the attachment folder. The user must first select a markdown note.");return}let n=this.getAttachmentFolderOfMdNote(x(e.path));if(!ce(this.app.vault,n)){let o=new fe(this,n);if(o.open(),await o.promise==!1)return;await Te(this.app.vault,n)}let t=k(this.vaultPath,n);require("electron").remote.shell.openPath(O(t))}}; + +/* nosourcemap */ \ No newline at end of file diff --git a/.obsidian/plugins/import-attachments-plus/manifest.json b/.obsidian/plugins/import-attachments-plus/manifest.json new file mode 100644 index 0000000..fe92b80 --- /dev/null +++ b/.obsidian/plugins/import-attachments-plus/manifest.json @@ -0,0 +1,11 @@ +{ + "id": "import-attachments-plus", + "name": "Import Attachments+", + "version": "1.5.12", + "minAppVersion": "1.5.0", + "description": "Move and link the attachments into the vault.", + "author": "Andrea Alberti", + "authorUrl": "https://www.linkedin.com/in/dr-andrea-alberti/", + "fundingUrl": "https://buymeacoffee.com/alberti", + "isDesktopOnly": false +} diff --git a/.obsidian/plugins/import-attachments-plus/styles.css b/.obsidian/plugins/import-attachments-plus/styles.css new file mode 100644 index 0000000..a440c32 --- /dev/null +++ b/.obsidian/plugins/import-attachments-plus/styles.css @@ -0,0 +1,104 @@ +.nav-files-container:not(.import-plugin-hidden) .import-plugin-hidden { + display: none; +} + +.import-plugin table { + width: 100%; + border-collapse: collapse; +} + +.import-plugin tr.sep { + padding-top: 10px; + border-top: 1px solid var(--background-modifier-border); + border-bottom: 1px solid var(--background-modifier-border); +} + +.import-plugin td { + padding: 18px 0 18px 0; +} + +.import-plugin .import-question { + width: 75%; + text-align: left; +} + +.import-plugin .import-option-A { + width: auto; + text-align: right; + padding-right: 5px; +} + +.import-plugin .import-option-B { + width: auto; + text-align: left; + padding-left: 5px; + padding-right: 5px; +} + +.import-buttons { + display: flex; /* Establishes this container as a flex container */ + justify-content: flex-end; /* Aligns children (the button) to the right */ +} + +/* Style for the container that holds the buttons */ +.import-buttons button { + padding: 16px 16px; + margin: 15px 0 0 15px; /* Optional: Adds margin on the right for spacing from container edge */ +} + +/* Focus styles for accessibility */ +/* +.import-buttons button:focus { + outline: 3px solid var(--interactive-accent-hover); + outline-offset: 2px; +} +*/ + +.import-switch { + position: relative; + display: inline-block; + width: 50px; + /* Width of the slider */ + height: 24px; + /* Height of the slider */ +} + +.import-switch input { + opacity: 0; + width: 0; + height: 0; +} + +.import-slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: var(--color-base-60); + transition: .4s; + border-radius: 24px; +} + +.import-slider:before { + position: absolute; + content: ""; + height: 20px; + width: 20px; + left: 2px; + bottom: 2px; + background-color: white; + transition: .2s; + border-radius: 50%; +} + +input:checked+.import-slider:before { + transform: translateX(26px); + /* Adjust translation distance */ +} + +.import-slider:hover { + /* Keep the original accent color on hover */ + background-color: var(--color-base-70); +}