From f119a01042e0996b16eeb7e85cbb3db7ce363501 Mon Sep 17 00:00:00 2001 From: KemoNine Date: Sun, 13 Oct 2024 16:46:19 -0400 Subject: [PATCH] add attachment management plugin --- .../plugins/attachment-management/main.js | 2076 +++++++++++++++++ .../attachment-management/manifest.json | 10 + .../plugins/attachment-management/styles.css | 15 + 3 files changed, 2101 insertions(+) create mode 100644 .obsidian/plugins/attachment-management/main.js create mode 100644 .obsidian/plugins/attachment-management/manifest.json create mode 100644 .obsidian/plugins/attachment-management/styles.css diff --git a/.obsidian/plugins/attachment-management/main.js b/.obsidian/plugins/attachment-management/main.js new file mode 100644 index 0000000..f071ea4 --- /dev/null +++ b/.obsidian/plugins/attachment-management/main.js @@ -0,0 +1,2076 @@ +/* +THIS IS A GENERATED/BUNDLED FILE BY ESBUILD +if you want to view the source, please visit the github repository of this plugin +*/ + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/main.ts +var main_exports = {}; +__export(main_exports, { + default: () => AttachmentManagementPlugin +}); +module.exports = __toCommonJS(main_exports); +var import_obsidian11 = require("obsidian"); + +// src/settings/settings.ts +var import_obsidian3 = require("obsidian"); + +// src/lib/constant.ts +var SETTINGS_VARIABLES_DATES = "${date}"; +var SETTINGS_VARIABLES_NOTEPATH = "${notepath}"; +var SETTINGS_VARIABLES_NOTENAME = "${notename}"; +var SETTINGS_VARIABLES_NOTEPARENT = "${parent}"; +var SETTINGS_VARIABLES_ORIGINALNAME = "${originalname}"; +var SETTINGS_VARIABLES_MD5 = "${md5}"; +var SETTINGS_ROOT_OBSFOLDER = "obsFolder"; +var SETTINGS_ROOT_INFOLDER = "inFolderBelow"; +var SETTINGS_ROOT_NEXTTONOTE = "nextToNote"; + +// src/model/extensionOverride.ts +var import_obsidian2 = require("obsidian"); + +// src/utils.ts +var import_obsidian = require("obsidian"); + +// node_modules/ts-md5/dist/esm/md5.js +var Md5 = class { + constructor() { + this._dataLength = 0; + this._bufferLength = 0; + this._state = new Int32Array(4); + this._buffer = new ArrayBuffer(68); + this._buffer8 = new Uint8Array(this._buffer, 0, 68); + this._buffer32 = new Uint32Array(this._buffer, 0, 17); + this.start(); + } + static hashStr(str, raw = false) { + return this.onePassHasher.start().appendStr(str).end(raw); + } + static hashAsciiStr(str, raw = false) { + return this.onePassHasher.start().appendAsciiStr(str).end(raw); + } + static _hex(x) { + const hc = Md5.hexChars; + const ho = Md5.hexOut; + let n; + let offset; + let j; + let i; + for (i = 0; i < 4; i += 1) { + offset = i * 8; + n = x[i]; + for (j = 0; j < 8; j += 2) { + ho[offset + 1 + j] = hc.charAt(n & 15); + n >>>= 4; + ho[offset + 0 + j] = hc.charAt(n & 15); + n >>>= 4; + } + } + return ho.join(""); + } + static _md5cycle(x, k) { + let a = x[0]; + let b = x[1]; + let c = x[2]; + let d = x[3]; + a += (b & c | ~b & d) + k[0] - 680876936 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[1] - 389564586 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[2] + 606105819 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[3] - 1044525330 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[4] - 176418897 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[5] + 1200080426 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[6] - 1473231341 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[7] - 45705983 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[8] + 1770035416 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[9] - 1958414417 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[10] - 42063 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[11] - 1990404162 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & c | ~b & d) + k[12] + 1804603682 | 0; + a = (a << 7 | a >>> 25) + b | 0; + d += (a & b | ~a & c) + k[13] - 40341101 | 0; + d = (d << 12 | d >>> 20) + a | 0; + c += (d & a | ~d & b) + k[14] - 1502002290 | 0; + c = (c << 17 | c >>> 15) + d | 0; + b += (c & d | ~c & a) + k[15] + 1236535329 | 0; + b = (b << 22 | b >>> 10) + c | 0; + a += (b & d | c & ~d) + k[1] - 165796510 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[6] - 1069501632 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[11] + 643717713 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[0] - 373897302 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[5] - 701558691 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[10] + 38016083 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[15] - 660478335 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[4] - 405537848 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[9] + 568446438 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[14] - 1019803690 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[3] - 187363961 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[8] + 1163531501 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b & d | c & ~d) + k[13] - 1444681467 | 0; + a = (a << 5 | a >>> 27) + b | 0; + d += (a & c | b & ~c) + k[2] - 51403784 | 0; + d = (d << 9 | d >>> 23) + a | 0; + c += (d & b | a & ~b) + k[7] + 1735328473 | 0; + c = (c << 14 | c >>> 18) + d | 0; + b += (c & a | d & ~a) + k[12] - 1926607734 | 0; + b = (b << 20 | b >>> 12) + c | 0; + a += (b ^ c ^ d) + k[5] - 378558 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[8] - 2022574463 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[11] + 1839030562 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[14] - 35309556 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[1] - 1530992060 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[4] + 1272893353 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[7] - 155497632 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[10] - 1094730640 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[13] + 681279174 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[0] - 358537222 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[3] - 722521979 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[6] + 76029189 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (b ^ c ^ d) + k[9] - 640364487 | 0; + a = (a << 4 | a >>> 28) + b | 0; + d += (a ^ b ^ c) + k[12] - 421815835 | 0; + d = (d << 11 | d >>> 21) + a | 0; + c += (d ^ a ^ b) + k[15] + 530742520 | 0; + c = (c << 16 | c >>> 16) + d | 0; + b += (c ^ d ^ a) + k[2] - 995338651 | 0; + b = (b << 23 | b >>> 9) + c | 0; + a += (c ^ (b | ~d)) + k[0] - 198630844 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[7] + 1126891415 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[14] - 1416354905 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[5] - 57434055 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[12] + 1700485571 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[3] - 1894986606 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[10] - 1051523 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[1] - 2054922799 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[8] + 1873313359 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[15] - 30611744 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[6] - 1560198380 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[13] + 1309151649 | 0; + b = (b << 21 | b >>> 11) + c | 0; + a += (c ^ (b | ~d)) + k[4] - 145523070 | 0; + a = (a << 6 | a >>> 26) + b | 0; + d += (b ^ (a | ~c)) + k[11] - 1120210379 | 0; + d = (d << 10 | d >>> 22) + a | 0; + c += (a ^ (d | ~b)) + k[2] + 718787259 | 0; + c = (c << 15 | c >>> 17) + d | 0; + b += (d ^ (c | ~a)) + k[9] - 343485551 | 0; + b = (b << 21 | b >>> 11) + c | 0; + x[0] = a + x[0] | 0; + x[1] = b + x[1] | 0; + x[2] = c + x[2] | 0; + x[3] = d + x[3] | 0; + } + /** + * Initialise buffer to be hashed + */ + start() { + this._dataLength = 0; + this._bufferLength = 0; + this._state.set(Md5.stateIdentity); + return this; + } + // Char to code point to to array conversion: + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt + // #Example.3A_Fixing_charCodeAt_to_handle_non-Basic-Multilingual-Plane_characters_if_their_presence_earlier_in_the_string_is_unknown + /** + * Append a UTF-8 string to the hash buffer + * @param str String to append + */ + appendStr(str) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let code; + let i; + for (i = 0; i < str.length; i += 1) { + code = str.charCodeAt(i); + if (code < 128) { + buf8[bufLen++] = code; + } else if (code < 2048) { + buf8[bufLen++] = (code >>> 6) + 192; + buf8[bufLen++] = code & 63 | 128; + } else if (code < 55296 || code > 56319) { + buf8[bufLen++] = (code >>> 12) + 224; + buf8[bufLen++] = code >>> 6 & 63 | 128; + buf8[bufLen++] = code & 63 | 128; + } else { + code = (code - 55296) * 1024 + (str.charCodeAt(++i) - 56320) + 65536; + if (code > 1114111) { + throw new Error("Unicode standard supports code points up to U+10FFFF"); + } + buf8[bufLen++] = (code >>> 18) + 240; + buf8[bufLen++] = code >>> 12 & 63 | 128; + buf8[bufLen++] = code >>> 6 & 63 | 128; + buf8[bufLen++] = code & 63 | 128; + } + if (bufLen >= 64) { + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen -= 64; + buf32[0] = buf32[16]; + } + } + this._bufferLength = bufLen; + return this; + } + /** + * Append an ASCII string to the hash buffer + * @param str String to append + */ + appendAsciiStr(str) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let i; + let j = 0; + for (; ; ) { + i = Math.min(str.length - j, 64 - bufLen); + while (i--) { + buf8[bufLen++] = str.charCodeAt(j++); + } + if (bufLen < 64) { + break; + } + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen = 0; + } + this._bufferLength = bufLen; + return this; + } + /** + * Append a byte array to the hash buffer + * @param input array to append + */ + appendByteArray(input) { + const buf8 = this._buffer8; + const buf32 = this._buffer32; + let bufLen = this._bufferLength; + let i; + let j = 0; + for (; ; ) { + i = Math.min(input.length - j, 64 - bufLen); + while (i--) { + buf8[bufLen++] = input[j++]; + } + if (bufLen < 64) { + break; + } + this._dataLength += 64; + Md5._md5cycle(this._state, buf32); + bufLen = 0; + } + this._bufferLength = bufLen; + return this; + } + /** + * Get the state of the hash buffer + */ + getState() { + const s = this._state; + return { + buffer: String.fromCharCode.apply(null, Array.from(this._buffer8)), + buflen: this._bufferLength, + length: this._dataLength, + state: [s[0], s[1], s[2], s[3]] + }; + } + /** + * Override the current state of the hash buffer + * @param state New hash buffer state + */ + setState(state) { + const buf = state.buffer; + const x = state.state; + const s = this._state; + let i; + this._dataLength = state.length; + this._bufferLength = state.buflen; + s[0] = x[0]; + s[1] = x[1]; + s[2] = x[2]; + s[3] = x[3]; + for (i = 0; i < buf.length; i += 1) { + this._buffer8[i] = buf.charCodeAt(i); + } + } + /** + * Hash the current state of the hash buffer and return the result + * @param raw Whether to return the value as an `Int32Array` + */ + end(raw = false) { + const bufLen = this._bufferLength; + const buf8 = this._buffer8; + const buf32 = this._buffer32; + const i = (bufLen >> 2) + 1; + this._dataLength += bufLen; + const dataBitsLen = this._dataLength * 8; + buf8[bufLen] = 128; + buf8[bufLen + 1] = buf8[bufLen + 2] = buf8[bufLen + 3] = 0; + buf32.set(Md5.buffer32Identity.subarray(i), i); + if (bufLen > 55) { + Md5._md5cycle(this._state, buf32); + buf32.set(Md5.buffer32Identity); + } + if (dataBitsLen <= 4294967295) { + buf32[14] = dataBitsLen; + } else { + const matches = dataBitsLen.toString(16).match(/(.*?)(.{0,8})$/); + if (matches === null) { + return; + } + const lo = parseInt(matches[2], 16); + const hi = parseInt(matches[1], 16) || 0; + buf32[14] = lo; + buf32[15] = hi; + } + Md5._md5cycle(this._state, buf32); + return raw ? this._state : Md5._hex(this._state); + } +}; +Md5.stateIdentity = new Int32Array([1732584193, -271733879, -1732584194, 271733878]); +Md5.buffer32Identity = new Int32Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); +Md5.hexChars = "0123456789abcdef"; +Md5.hexOut = []; +Md5.onePassHasher = new Md5(); +if (Md5.hashStr("hello") !== "5d41402abc4b2a76b9719d911017c592") { + throw new Error("Md5 self test failed."); +} + +// src/utils.ts +var PASTED_IMAGE_PREFIX = "Pasted image "; +var ImageExtensionRegex = /^(jpe?g|png|gif|svg|bmp|eps|webp)$/i; +function isMarkdownFile(extension) { + return extension === "md"; +} +function isCanvasFile(extension) { + return extension === "canvas"; +} +function isPastedImage(file) { + if (file instanceof import_obsidian.TFile) { + if (file.name.startsWith(PASTED_IMAGE_PREFIX)) { + return true; + } + } + return false; +} +function isImage(extension) { + const match = extension.match(ImageExtensionRegex); + if (match !== null) { + return true; + } + return false; +} +function stripPaths(src, dst) { + if (src === dst) { + return { stripedSrc: src, stripedDst: dst }; + } + const srcParts = src.split("/"); + const dstParts = dst.split("/"); + if (srcParts.length !== dstParts.length) { + return { stripedSrc: src, stripedDst: dst }; + } + for (let i = 0; i < srcParts.length; i++) { + const srcPart = srcParts[i]; + const dstPart = dstParts[i]; + if (srcPart !== dstPart) { + return { + stripedSrc: srcParts.slice(0, i + 1).join("/"), + stripedDst: dstParts.slice(0, i + 1).join("/") + }; + } + } + return { stripedSrc: "", stripedDst: "" }; +} +function matchExtension(extension, pattern) { + if (!pattern || pattern === "") + return false; + return new RegExp(pattern).test(extension); +} +function isAttachment(settings, filePath) { + let file = null; + if (filePath instanceof import_obsidian.TAbstractFile) { + file = filePath; + } else { + file = this.app.vault.getAbstractFileByPath(filePath); + } + if (file === null || !(file instanceof import_obsidian.TFile)) { + return false; + } + if (isMarkdownFile(file.extension) || isCanvasFile(file.extension)) { + return false; + } + return !matchExtension(file.extension, settings.excludeExtensionPattern); +} +async function md5sum(adapter, file) { + const md5 = new Md5(); + if (!adapter.exists(file.path, true)) { + return ""; + } + const content = await adapter.readBinary(file.path); + md5.appendByteArray(new Uint8Array(content)); + const ret = md5.end(); + return ret.toUpperCase(); +} +function validateExtensionEntry(setting, plugin) { + const wrongIndex = []; + if (setting.extensionOverride !== void 0) { + const extOverride = setting.extensionOverride; + if (extOverride.some((ext) => ext.extension === "")) { + wrongIndex.push({ type: "empty", index: extOverride.findIndex((ext) => ext.extension === "") }); + } + const duplicate = extOverride.map((ext) => ext.extension).filter((value, index, self) => self.indexOf(value) !== index); + if (duplicate.length > 0) { + duplicate.forEach((dupli) => { + wrongIndex.push({ type: "duplicate", index: extOverride.findIndex((ext) => dupli === ext.extension) }); + }); + } + const markdown = extOverride.filter((ext) => ext.extension === "md"); + if (markdown.length > 0) { + wrongIndex.push({ type: "md", index: extOverride.findIndex((ext) => ext.extension === "md") }); + } + const canvas = extOverride.filter((ext) => ext.extension === "canvas"); + if (canvas.length > 0) { + wrongIndex.push({ type: "canvas", index: extOverride.findIndex((ext) => ext.extension === "canvas") }); + } + const excludedFromSettings = plugin.excludeExtensionPattern.split("|"); + const excluded = extOverride.filter((ext) => excludedFromSettings.includes(ext.extension)); + if (excluded.length > 0) { + wrongIndex.push({ + type: "excluded", + index: extOverride.findIndex((ext) => excludedFromSettings.includes(ext.extension)) + }); + } + } + return wrongIndex; +} +function generateErrorExtensionMessage(type) { + if (type === "canvas") { + new import_obsidian.Notice("Canvas is not supported as an extension override."); + } else if (type === "md") { + new import_obsidian.Notice("Markdown is not supported as an extension override."); + } else if (type === "empty") { + new import_obsidian.Notice("Extension override cannot be empty."); + } else if (type === "duplicate") { + new import_obsidian.Notice("Duplicate extension override."); + } else if (type === "excluded") { + new import_obsidian.Notice("Extension override cannot be an excluded extension."); + } +} + +// src/lib/log.ts +var DEBUG = false; +if (DEBUG) + console.log("DEBUG is enabled"); +function debugLog(...args) { + if (DEBUG) { + console.log(new Date().toISOString().slice(11, 23), ...args); + } +} + +// src/model/extensionOverride.ts +function getExtensionOverrideSetting(extension, settings) { + if (settings.extensionOverride === void 0 || settings.extensionOverride.length === 0) { + return { extSetting: void 0 }; + } + for (let i = 0; i < settings.extensionOverride.length; i++) { + if (matchExtension(extension, settings.extensionOverride[i].extension)) { + debugLog( + "getExtensionOverrideSetting - ", + settings.extensionOverride[i].extension, + settings.extensionOverride[i] + ); + return { extSetting: settings.extensionOverride[i] }; + } + } + return { extSetting: void 0 }; +} +var OverrideExtensionModal = class extends import_obsidian2.Modal { + constructor(plugin, settings, onSubmit) { + super(plugin.app); + this.plugin = plugin; + this.settings = settings; + this.onSubmit = onSubmit; + } + displaySw(cont) { + cont.findAll(".setting-item").forEach((el) => { + var _a; + if ((_a = el.getAttr("class")) == null ? void 0 : _a.includes("override_root_folder_set")) { + if (this.settings.saveAttE === "obsFolder") { + el.hide(); + } else { + el.show(); + } + } + }); + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h3", { + text: `Extension settings for ${this.settings.extension}` + }); + new import_obsidian2.Setting(contentEl).setName("Root path to save attachment").setDesc("Select root path of attachment").addDropdown( + (text) => text.addOption(`${SETTINGS_ROOT_OBSFOLDER}`, "Copy Obsidian settings").addOption(`${SETTINGS_ROOT_INFOLDER}`, "In the folder specified below").addOption(`${SETTINGS_ROOT_NEXTTONOTE}`, "Next to note in folder specified below").setValue(this.settings.saveAttE).onChange(async (value) => { + this.settings.saveAttE = value; + this.displaySw(contentEl); + this.onOpen(); + }) + ); + if (this.settings.saveAttE !== "obsFolder") { + new import_obsidian2.Setting(contentEl).setName("Root folder").setClass("override_root_folder_set").addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentRoot).setValue(this.settings.attachmentRoot).onChange(async (value) => { + this.settings.attachmentRoot = value; + }) + ); + } + new import_obsidian2.Setting(contentEl).setName("Attachment path").setDesc( + `Path of attachment in root folder, available variables ${SETTINGS_VARIABLES_NOTEPATH}, ${SETTINGS_VARIABLES_NOTENAME} and ${SETTINGS_VARIABLES_NOTEPARENT}` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentPath).setValue(this.settings.attachmentPath).onChange(async (value) => { + this.settings.attachmentPath = value; + }) + ); + new import_obsidian2.Setting(contentEl).setName("Attachment format").setDesc( + `Define how to name the attachment file, available variables ${SETTINGS_VARIABLES_DATES}, ${SETTINGS_VARIABLES_NOTENAME}, ${SETTINGS_VARIABLES_MD5} and ${SETTINGS_VARIABLES_ORIGINALNAME}.` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachFormat).setValue(this.settings.attachFormat).onChange(async (value) => { + this.settings.attachFormat = value; + }) + ); + new import_obsidian2.Setting(contentEl).addButton( + (button) => button.setButtonText("Save").onClick(async () => { + this.onSubmit(this.settings); + this.close(); + }) + ); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/settings/settings.ts +var DEFAULT_SETTINGS = { + attachPath: { + attachmentRoot: "", + saveAttE: `${SETTINGS_ROOT_OBSFOLDER}`, + attachmentPath: `${SETTINGS_VARIABLES_NOTEPATH}/${SETTINGS_VARIABLES_NOTENAME}`, + attachFormat: `IMG-${SETTINGS_VARIABLES_DATES}`, + type: "GLOBAL" /* GLOBAL */ + }, + dateFormat: "YYYYMMDDHHmmssSSS", + excludeExtensionPattern: "", + autoRenameAttachment: true, + excludedPaths: "", + excludePathsArray: [], + excludeSubpaths: false, + originalNameStorage: [], + overridePath: {}, + disableNotification: false +}; +var SettingTab = class extends import_obsidian3.PluginSettingTab { + constructor(app2, plugin) { + super(app2, plugin); + this.plugin = plugin; + } + displaySw(cont) { + cont.findAll(".setting-item").forEach((el) => { + var _a; + if ((_a = el.getAttr("class")) == null ? void 0 : _a.includes("root_folder_set")) { + if (this.plugin.settings.attachPath.saveAttE === "obsFolder") { + el.hide(); + } else { + el.show(); + } + } + }); + } + splitPath(path2) { + const splitted = path2.split(";"); + const rets = []; + for (const s of splitted) { + rets.push(s.trim()); + } + return { splittedPaths: rets }; + } + display() { + const { containerEl } = this; + containerEl.empty(); + new import_obsidian3.Setting(containerEl).setName("Root path to save attachment").setDesc("Select root path of attachment").addDropdown( + (text) => text.addOption(`${SETTINGS_ROOT_OBSFOLDER}`, "Copy Obsidian settings").addOption(`${SETTINGS_ROOT_INFOLDER}`, "In the folder specified below").addOption(`${SETTINGS_ROOT_NEXTTONOTE}`, "Next to note in folder specified below").setValue(this.plugin.settings.attachPath.saveAttE).onChange(async (value) => { + this.plugin.settings.attachPath.saveAttE = value; + this.displaySw(containerEl); + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Root folder").setDesc("Root folder of new attachment").setClass("root_folder_set").addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentRoot).setValue(this.plugin.settings.attachPath.attachmentRoot).onChange(async (value) => { + debugLog("setting - attachment root:" + value); + this.plugin.settings.attachPath.attachmentRoot = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Attachment path").setDesc( + `Path of attachment in root folder, available variables ${SETTINGS_VARIABLES_NOTEPATH}, ${SETTINGS_VARIABLES_NOTENAME}, ${SETTINGS_VARIABLES_NOTEPARENT}` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentPath).setValue(this.plugin.settings.attachPath.attachmentPath).onChange(async (value) => { + debugLog("setting - attachment path:" + value); + this.plugin.settings.attachPath.attachmentPath = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Attachment format").setDesc( + `Define how to name the attachment file, available variables ${SETTINGS_VARIABLES_DATES}, ${SETTINGS_VARIABLES_NOTENAME}, ${SETTINGS_VARIABLES_MD5} and ${SETTINGS_VARIABLES_ORIGINALNAME}.` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachFormat).setValue(this.plugin.settings.attachPath.attachFormat).onChange(async (value) => { + debugLog("setting - attachment format:" + value); + this.plugin.settings.attachPath.attachFormat = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Date format").setDesc( + createFragment((frag) => { + frag.appendText("Moment date format to use "); + frag.createEl("a", { + href: "https://momentjscom.readthedocs.io/en/latest/moment/04-displaying/01-format", + text: "Moment format options" + }); + }) + ).addMomentFormat((component) => { + component.setPlaceholder(DEFAULT_SETTINGS.dateFormat).setValue(this.plugin.settings.dateFormat).onChange(async (value) => { + debugLog("setting - date format:" + value); + this.plugin.settings.dateFormat = value; + await this.plugin.saveSettings(); + }); + }); + new import_obsidian3.Setting(containerEl).setName("Automatically rename attachment").setDesc( + "Automatically rename the attachment folder/filename when you rename the folder/filename where the corresponding md/canvas file be placed." + ).addToggle( + (toggle) => toggle.setValue(this.plugin.settings.autoRenameAttachment).onChange(async (value) => { + debugLog("setting - automatically rename attachment folder:" + value); + this.plugin.settings.autoRenameAttachment = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Extension override").setDesc("Using the extension override if you want to autorename the attachment with a specific extension (e.g. pdf or zip).").addButton((btn) => { + btn.setButtonText("Add extension overrides").onClick(async () => { + if (this.plugin.settings.attachPath.extensionOverride === void 0) { + this.plugin.settings.attachPath.extensionOverride = []; + } + this.plugin.settings.attachPath.extensionOverride.push({ + extension: "", + attachmentRoot: this.plugin.settings.attachPath.attachmentRoot, + saveAttE: this.plugin.settings.attachPath.saveAttE, + attachmentPath: this.plugin.settings.attachPath.attachmentPath, + attachFormat: this.plugin.settings.attachPath.attachFormat + }); + await this.plugin.saveSettings(); + this.display(); + }); + }); + if (this.plugin.settings.attachPath.extensionOverride !== void 0) { + this.plugin.settings.attachPath.extensionOverride.forEach((ext) => { + new import_obsidian3.Setting(containerEl).setName("Extension").setDesc("Extension to override").setClass("override_extension_set").addText( + (text) => text.setPlaceholder("pdf|docx?").setValue(ext.extension).onChange(async (value) => { + ext.extension = value; + }) + ).addButton((btn) => { + btn.setIcon("trash").setTooltip("Remove extension override").onClick(async () => { + var _a, _b, _c; + const index = (_b = (_a = this.plugin.settings.attachPath.extensionOverride) == null ? void 0 : _a.indexOf(ext)) != null ? _b : -1; + (_c = this.plugin.settings.attachPath.extensionOverride) == null ? void 0 : _c.splice(index, 1); + await this.plugin.saveSettings(); + this.display(); + }); + }).addButton((btn) => { + btn.setIcon("pencil").setTooltip("Edit extension override").onClick(async () => { + new OverrideExtensionModal(this.plugin, ext, (result) => { + ext = result; + }).open(); + }); + }).addButton((btn) => { + btn.setIcon("check").setTooltip("Save extension override").onClick(async () => { + const wrongIndex = validateExtensionEntry(this.plugin.settings.attachPath, this.plugin.settings); + if (wrongIndex.length > 0) { + for (const i of wrongIndex) { + const resIndex = i.index < 0 ? 0 : i.index; + const wrongSetting = containerEl.getElementsByClassName("override_extension_set")[resIndex]; + wrongSetting.getElementsByTagName("input")[0].style.border = "1px solid var(--color-red)"; + generateErrorExtensionMessage(i.type); + } + return; + } + await this.plugin.saveSettings(); + this.display(); + new import_obsidian3.Notice("Saved extension override"); + }); + }); + }); + } + new import_obsidian3.Setting(containerEl).setName("Exclude extension pattern").setDesc(`Regex pattern to exclude certain extensions from being handled.`).addText( + (text) => text.setPlaceholder("pdf|docx?|xlsx?|pptx?|zip|rar").setValue(this.plugin.settings.excludeExtensionPattern).onChange(async (value) => { + this.plugin.settings.excludeExtensionPattern = value; + await this.plugin.saveSettings(); + }) + ); + new import_obsidian3.Setting(containerEl).setName("Excluded paths").setDesc( + `Provide the full path of the folder names (case sensitive and without leading slash '/') divided by semicolon (;) to be excluded from renaming.` + ).addTextArea((component) => { + component.setValue(this.plugin.settings.excludedPaths).onChange(async (value) => { + this.plugin.settings.excludedPaths = value; + const { splittedPaths } = this.splitPath(value); + this.plugin.settings.excludePathsArray = splittedPaths; + debugLog("setting - excluded paths:" + value, splittedPaths); + await this.plugin.saveSettings(); + }); + }); + new import_obsidian3.Setting(containerEl).setName("Exclude subpaths").setDesc("Turn on this option if you want to also exclude all subfolders of the folder paths provided above.").addToggle( + (toggle) => toggle.setValue(this.plugin.settings.excludeSubpaths).onChange(async (value) => { + debugLog("setting - excluded subpaths:" + value); + this.plugin.settings.excludeSubpaths = value; + await this.plugin.saveSettings(); + }) + ); + this.displaySw(containerEl); + } +}; + +// src/model/override.ts +var import_obsidian4 = require("obsidian"); +var OverrideModal = class extends import_obsidian4.Modal { + constructor(plugin, file, setting) { + super(plugin.app); + this.plugin = plugin; + this.file = file; + this.setting = setting; + } + displaySw(cont) { + cont.findAll(".setting-item").forEach((el) => { + var _a; + if ((_a = el.getAttr("class")) == null ? void 0 : _a.includes("override_root_folder_set")) { + if (this.setting.saveAttE === "obsFolder") { + el.hide(); + } else { + el.show(); + } + } + }); + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h3", { + text: "Overriding Settings" + }); + new import_obsidian4.Setting(contentEl).setName("Root path to save attachment").setDesc("Select root path of attachment").addDropdown( + (text) => text.addOption(`${SETTINGS_ROOT_OBSFOLDER}`, "Copy Obsidian settings").addOption(`${SETTINGS_ROOT_INFOLDER}`, "In the folder specified below").addOption(`${SETTINGS_ROOT_NEXTTONOTE}`, "Next to note in folder specified below").setValue(this.setting.saveAttE).onChange(async (value) => { + this.setting.saveAttE = value; + this.displaySw(contentEl); + }) + ); + new import_obsidian4.Setting(contentEl).setName("Root folder").setClass("override_root_folder_set").addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentRoot).setValue(this.setting.attachmentRoot).onChange(async (value) => { + debugLog("override - attachment root:" + value); + this.setting.attachmentRoot = value; + }) + ); + new import_obsidian4.Setting(contentEl).setName("Attachment path").setDesc( + `Path of attachment in root folder, available variables ${SETTINGS_VARIABLES_NOTEPATH}, ${SETTINGS_VARIABLES_NOTENAME} and ${SETTINGS_VARIABLES_NOTEPARENT}` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachmentPath).setValue(this.setting.attachmentPath).onChange(async (value) => { + debugLog("override - attachment path:" + value); + this.setting.attachmentPath = value; + }) + ); + new import_obsidian4.Setting(contentEl).setName("Attachment format").setDesc( + `Define how to name the attachment file, available variables ${SETTINGS_VARIABLES_DATES}, ${SETTINGS_VARIABLES_NOTENAME}, ${SETTINGS_VARIABLES_MD5} and ${SETTINGS_VARIABLES_ORIGINALNAME}.` + ).addText( + (text) => text.setPlaceholder(DEFAULT_SETTINGS.attachPath.attachFormat).setValue(this.setting.attachFormat).onChange(async (value) => { + debugLog("override - attachment format:" + value); + this.setting.attachFormat = value; + }) + ); + new import_obsidian4.Setting(contentEl).addButton((btn) => { + btn.setButtonText("Add extension overrides").onClick(async () => { + if (this.setting.extensionOverride === void 0) { + this.setting.extensionOverride = []; + } + this.setting.extensionOverride.push({ + extension: "", + saveAttE: this.setting.saveAttE, + attachmentRoot: this.setting.attachmentRoot, + attachmentPath: this.setting.attachmentPath, + attachFormat: this.setting.attachFormat + }); + this.onOpen(); + }); + }); + if (this.setting.extensionOverride !== void 0) { + this.setting.extensionOverride.forEach((ext) => { + new import_obsidian4.Setting(contentEl).setName("Extension").setDesc("Extension to override").setClass("override_extension_set").addText( + (text) => text.setPlaceholder("pdf").setValue(ext.extension).onChange(async (value) => { + ext.extension = value; + }) + ).addButton((btn) => { + btn.setIcon("trash").onClick(async () => { + var _a, _b, _c; + const index = (_b = (_a = this.setting.extensionOverride) == null ? void 0 : _a.indexOf(ext)) != null ? _b : -1; + (_c = this.setting.extensionOverride) == null ? void 0 : _c.splice(index, 1); + this.onOpen(); + }); + }).addButton((btn) => { + btn.setIcon("pencil").onClick(async () => { + new OverrideExtensionModal(this.plugin, ext, (result) => { + ext = result; + }).open(); + }); + }); + }); + } + new import_obsidian4.Setting(contentEl).addButton((btn) => { + btn.setButtonText("Reset").onClick(async () => { + this.setting = this.plugin.settings.attachPath; + delete this.plugin.settings.overridePath[this.file.path]; + await this.plugin.saveSettings(); + await this.plugin.loadSettings(); + new import_obsidian4.Notice(`Reset attachment setting of ${this.file.path}`); + this.close(); + }); + }).addButton( + (btn) => btn.setButtonText("Submit").setCta().onClick(async () => { + if (this.file instanceof import_obsidian4.TFile) { + this.setting.type = "FILE" /* FILE */; + } else if (this.file instanceof import_obsidian4.TFolder) { + this.setting.type = "FOLDER" /* FOLDER */; + } + this.plugin.settings.overridePath[this.file.path] = this.setting; + await this.plugin.saveSettings(); + debugLog("override - overriding settings:", this.file.path, this.setting); + new import_obsidian4.Notice(`Overridden attachment setting of ${this.file.path}`); + this.close(); + }) + ); + this.displaySw(contentEl); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/model/confirm.ts +var import_obsidian9 = require("obsidian"); + +// src/arrange.ts +var import_obsidian8 = require("obsidian"); + +// src/lib/path.ts +var path = { + // Credit: [@creationix/path.js](https://gist.github.com/creationix/7435851) + join(...partSegments) { + let parts = []; + for (let i = 0, l = partSegments.length; i < l; i++) { + parts = parts.concat(partSegments[i].split("/")); + } + const newParts = []; + for (let i = 0, l = parts.length; i < l; i++) { + const part = parts[i]; + if (!part || part === ".") + continue; + if (part === "..") + newParts.pop(); + else + newParts.push(part); + } + if (parts[0] === "") + newParts.unshift(""); + return newParts.join("/"); + }, + // A simple function to get the dirname of a path + // Trailing slashes are ignored. Leading slash is preserved. + dirname(filepath) { + return this.join(filepath, ".."); + }, + // returns the last part of a path, e.g. 'foo.jpg' + basename(filepath, extension = "") { + const sp = filepath.split("/"); + const filename = sp[sp.length - 1]; + if (extension !== "") { + return filename.slice(0, filename.length - extension.length - 1); + } + return sp[sp.length - 1]; + }, + // return extension without dot, e.g. 'jpg' + extname(filepath) { + const positions = [...filepath.matchAll(new RegExp("\\.", "gi"))].map((a) => a.index); + const idx = positions[positions.length - 1]; + if (idx === void 0) { + return ""; + } + return filepath.slice(idx + 1); + } +}; + +// src/override.ts +var import_obsidian5 = require("obsidian"); +function getOverrideSetting(settings, file, oldPath = "") { + if (Object.keys(settings.overridePath).length === 0) { + return { settingPath: "", setting: settings.attachPath }; + } + const candidates = {}; + let fileType; + let filePath; + fileType = file instanceof import_obsidian5.TFile; + fileType = !(file instanceof import_obsidian5.TFolder); + if (oldPath === "") { + filePath = file.path; + } else { + filePath = oldPath; + } + for (const overridePath of Object.keys(settings.overridePath)) { + const overrideSetting = settings.overridePath[overridePath]; + if (fileType) { + if (overridePath === filePath && overrideSetting.type === "FILE" /* FILE */) { + return { settingPath: overridePath, setting: overrideSetting }; + } else if (filePath.startsWith(overridePath) && filePath.charAt(overridePath.length) === "/" && overrideSetting.type === "FOLDER" /* FOLDER */) { + candidates[overridePath] = overrideSetting; + } + } else { + if (overridePath === filePath && overrideSetting.type === "FOLDER" /* FOLDER */) { + return { settingPath: overridePath, setting: overrideSetting }; + } else if (filePath.startsWith(overridePath) && filePath.charAt(overridePath.length) === "/" && overrideSetting.type === "FOLDER" /* FOLDER */) { + candidates[overridePath] = overrideSetting; + } + } + } + if (Object.keys(candidates).length === 0) { + return { settingPath: "", setting: settings.attachPath }; + } + const sortedK = Object.keys(candidates).sort( + (a, b) => a.split("/").length > b.split("/").length ? -1 : a.split("/").length < b.split("/").length ? 1 : 0 + ); + debugLog("getOverrideSetting - sortedK:", sortedK); + for (const k of sortedK) { + if (filePath.startsWith(k)) { + return { settingPath: k, setting: candidates[k] }; + } + } + return { settingPath: "", setting: settings.attachPath }; +} +function getRenameOverrideSetting(settings, file, oldPath) { + if (Object.keys(settings.overridePath).length === 0) { + return { settingPath: "", setting: settings.attachPath }; + } + const { settingPath: np, setting: ns } = getOverrideSetting(settings, file); + const { settingPath: op, setting: os } = getOverrideSetting(settings, file, oldPath); + if (ns.type === "GLOBAL" /* GLOBAL */) { + return { settingPath: op, setting: os }; + } + if (os.type === "GLOBAL" /* GLOBAL */) { + return { settingPath: np, setting: ns }; + } + if (ns.type === "FILE" /* FILE */ && os.type === "FILE" /* FILE */) { + debugLog("getRenameOverrideSetting - both file type setting", np, op); + return { settingPath: "", setting: settings.attachPath }; + } + if (ns.type === "FILE" /* FILE */ && os.type === "FOLDER" /* FOLDER */) { + return { settingPath: np, setting: ns }; + } else if (ns.type === "FOLDER" /* FOLDER */ && os.type === "FILE" /* FILE */) { + return { settingPath: op, setting: os }; + } + if (ns.type === "FOLDER" /* FOLDER */ && os.type === "FOLDER" /* FOLDER */) { + const l = np.split("/").length; + const r = op.split("/").length; + if (l > r) { + return { settingPath: np, setting: ns }; + } else if (l < r) { + return { settingPath: op, setting: os }; + } else if (l === r) { + return { settingPath: "", setting: os }; + } + } + return { settingPath: "", setting: settings.attachPath }; +} +function updateOverrideSetting(settings, file, oldPath) { + const keys = Object.keys(settings.overridePath); + if (keys.length === 0 || file.path === oldPath) { + return; + } + const { settingPath, setting } = getOverrideSetting(settings, file, oldPath); + const copySetting = Object.assign({}, setting); + if (file.path === settingPath) { + return; + } + if (oldPath === settingPath) { + settings.overridePath[file.path] = copySetting; + delete settings.overridePath[settingPath]; + return; + } else { + const { stripedSrc, stripedDst } = stripPaths(oldPath, file.path); + if (stripedSrc === settingPath) { + settings.overridePath[stripedDst] = copySetting; + delete settings.overridePath[settingPath]; + return; + } + } +} +function deleteOverrideSetting(settings, file) { + const keys = Object.keys(settings.overridePath); + for (const key of keys) { + if (file.path === key) { + delete settings.overridePath[key]; + return true; + } + } + return false; +} + +// src/lib/linkDetector.ts +var getAllLinkMatchesInFile = async (mdFile, app2, fileText) => { + const linkMatches = []; + if (fileText === void 0) { + fileText = await app2.vault.read(mdFile); + } + const wikiRegex = /\[\[.*?\]\]/g; + const wikiMatches = fileText.match(wikiRegex); + if (wikiMatches) { + const fileRegex = /(?<=\[\[).*?(?=(\]|\|))/; + for (const wikiMatch of wikiMatches) { + if (matchIsWikiTransclusion(wikiMatch)) { + const fileName = getTransclusionFileName(wikiMatch); + const file = app2.metadataCache.getFirstLinkpathDest(fileName, mdFile.path); + if (fileName !== "") { + const linkMatch = { + type: "wikiTransclusion", + match: wikiMatch, + linkText: file ? file.path : fileName, + sourceFilePath: mdFile.path + }; + linkMatches.push(linkMatch); + continue; + } + } + const fileMatch = wikiMatch.match(fileRegex); + if (fileMatch) { + if (fileMatch[0].startsWith("http")) + continue; + const file = app2.metadataCache.getFirstLinkpathDest(fileMatch[0], mdFile.path); + const linkMatch = { + type: "wiki", + match: wikiMatch, + linkText: file ? file.path : fileMatch[0], + sourceFilePath: mdFile.path + }; + linkMatches.push(linkMatch); + } + } + } + const markdownRegex = /\[(^$|.*?)\]\((.*?)\)/g; + const markdownMatches = fileText.match(markdownRegex); + if (markdownMatches) { + const fileRegex = /(?<=\().*(?=\))/; + for (const markdownMatch of markdownMatches) { + if (matchIsMdTransclusion(markdownMatch)) { + const fileName = getTransclusionFileName(markdownMatch); + const file = app2.metadataCache.getFirstLinkpathDest(fileName, mdFile.path); + if (fileName !== "") { + const linkMatch = { + type: "mdTransclusion", + match: markdownMatch, + linkText: file ? file.path : fileName, + sourceFilePath: mdFile.path + }; + linkMatches.push(linkMatch); + continue; + } + } + const fileMatch = markdownMatch.match(fileRegex); + if (fileMatch) { + if (fileMatch[0].startsWith("http")) + continue; + const file = app2.metadataCache.getFirstLinkpathDest(fileMatch[0], mdFile.path); + const linkMatch = { + type: "markdown", + match: markdownMatch, + linkText: file ? file.path : fileMatch[0], + sourceFilePath: mdFile.path + }; + linkMatches.push(linkMatch); + } + } + } + return linkMatches; +}; +var wikiTransclusionRegex = /\[\[(.*?)#.*?\]\]/; +var wikiTransclusionFileNameRegex = /(?<=\[\[)(.*)(?=#)/; +var mdTransclusionRegex = /\[.*?]\((.*?)#.*?\)/; +var mdTransclusionFileNameRegex = /(?<=\]\()(.*)(?=#)/; +var matchIsWikiTransclusion = (match) => { + return wikiTransclusionRegex.test(match); +}; +var matchIsMdTransclusion = (match) => { + return mdTransclusionRegex.test(match); +}; +var getTransclusionFileName = (match) => { + const isWiki = wikiTransclusionRegex.test(match); + const isMd = mdTransclusionRegex.test(match); + if (isWiki || isMd) { + const fileNameMatch = match.match(isWiki ? wikiTransclusionFileNameRegex : mdTransclusionFileNameRegex); + if (fileNameMatch) + return fileNameMatch[0]; + } + return ""; +}; + +// src/lib/deduplicate.ts +function escapeRegExp(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +async function deduplicateNewName(newName, file) { + const dir = file.path; + const listed = await this.app.vault.adapter.list(dir); + debugLog("deduplicateNewName - sibling files", listed); + const newNameExt = path.extname(newName), newNameStem = newName.slice(0, newName.length - newNameExt.length - 1), newNameStemEscaped = escapeRegExp(newNameStem), delimiter = "-", delimiterEscaped = escapeRegExp(delimiter); + const dupNameRegex = new RegExp( + `^(?${newNameStemEscaped})${delimiterEscaped}(?\\d{1,3})\\.${newNameExt}$` + ); + debugLog("dupNameRegex", dupNameRegex); + const dupNameNumbers = []; + let isNewNameExist = false; + for (let sibling of listed.files) { + sibling = path.basename(sibling); + if (sibling == newName) { + isNewNameExist = true; + continue; + } + const m = dupNameRegex.exec(sibling); + if (!m || m.groups === void 0) + continue; + dupNameNumbers.push(parseInt(m.groups.number)); + } + if (isNewNameExist) { + const newNumber = dupNameNumbers.length > 0 ? Math.max(...dupNameNumbers) + 1 : 1; + newName = `${newNameStem}${delimiter}${newNumber}.${newNameExt}`; + } + return { + name: newName, + basename: newName.slice(0, newName.length - newNameExt.length - 1), + extension: newNameExt + }; +} + +// src/settings/metadata.ts +var import_obsidian7 = require("obsidian"); + +// src/commons.ts +var import_obsidian6 = require("obsidian"); +function getActiveFile(app2) { + const view = getActiveView(app2); + if (view == null) { + return void 0; + } else if (view.file == null) { + return void 0; + } else { + return view.file; + } +} +function getActiveView(app2) { + return app2.workspace.getActiveViewOfType(import_obsidian6.TextFileView); +} +function getRootPath(notePath, setting) { + let root; + const obsmediadir = app.vault.getConfig("attachmentFolderPath"); + switch (setting.saveAttE) { + case `${SETTINGS_ROOT_INFOLDER}`: + root = path.join(setting.attachmentRoot); + break; + case `${SETTINGS_ROOT_NEXTTONOTE}`: + root = path.join(notePath, setting.attachmentRoot.replace("./", "")); + break; + default: + if (obsmediadir === "/") { + root = obsmediadir; + } else if (obsmediadir === "./") { + root = path.join(notePath); + } else if (obsmediadir.match(/\.\/.+/g) !== null) { + root = path.join(notePath, obsmediadir.replace("./", "")); + } else { + root = obsmediadir; + } + } + return root === "/" ? root : (0, import_obsidian6.normalizePath)(root); +} +async function checkEmptyFolder(adapter, path2) { + const exist = await adapter.exists(path2, true); + if (!exist) { + return true; + } + const data = await adapter.list(path2); + if (data.files.length > 0) { + return false; + } + if (data.folders.length > 0) { + for (let i = 0; i < data.folders.length; i++) { + return checkEmptyFolder(adapter, data.folders[i]); + } + } + return true; +} + +// src/settings/metadata.ts +var Metadata = class { + constructor(path2, name, basename, extension, parentPath, parentName, attachmentFile) { + /** parent path of file */ + this.parentPath = ""; + /** parent path basename of file */ + this.parentName = ""; + this.path = path2; + this.name = name; + this.basename = basename; + this.extension = extension; + this.parentPath = parentPath; + this.parentName = parentName; + this.attachmentFile = attachmentFile; + } + /** + * Returns a formatted attachment file name according to the provided settings. + * + * @param {AttachmentPathSettings} setting - attachment path settings object + * @param {string} dateFormat - format string for date and time + * @param {string} originalName - name of the original attachment + * @param {string} [linkName] - optional name for the attachment link + * @return {string} the formatted attachment file name + */ + async getAttachFileName(setting, dateFormat, originalName, adapter, linkName) { + const dateTime = window.moment().format(dateFormat); + let md5 = ""; + let attachFormat = ""; + if (this.attachmentFile !== void 0) { + md5 = await md5sum(adapter, this.attachmentFile); + const { extSetting } = getExtensionOverrideSetting(this.attachmentFile.extension, setting); + if (extSetting !== void 0) { + attachFormat = extSetting.attachFormat; + } else { + attachFormat = setting.attachFormat; + } + } + if (attachFormat.includes(SETTINGS_VARIABLES_ORIGINALNAME)) { + if (originalName === "" && linkName != void 0) { + return linkName; + } else { + return attachFormat.replace(`${SETTINGS_VARIABLES_DATES}`, dateTime).replace(`${SETTINGS_VARIABLES_NOTENAME}`, this.basename).replace(`${SETTINGS_VARIABLES_ORIGINALNAME}`, originalName).replace(`${SETTINGS_VARIABLES_MD5}`, md5); + } + } + return attachFormat.replace(`${SETTINGS_VARIABLES_DATES}`, dateTime).replace(`${SETTINGS_VARIABLES_NOTENAME}`, this.basename).replace(`${SETTINGS_VARIABLES_MD5}`, md5); + } + /** + * Returns the attachment path based on the given AttachmentPathSettings object. + * + * @param {AttachmentPathSettings} setting - An object containing the attachment path settings. + * @return {string} The normalized attachment path. + */ + getAttachmentPath(setting, dateFormat) { + const dateTime = window.moment().format(dateFormat); + let root = ""; + let attachPath = ""; + if (this.attachmentFile !== void 0) { + const { extSetting } = getExtensionOverrideSetting(this.attachmentFile.extension, setting); + if (extSetting !== void 0) { + root = getRootPath(this.parentPath, extSetting); + attachPath = path.join( + root, + extSetting.attachmentPath.replace(`${SETTINGS_VARIABLES_NOTEPATH}`, this.parentPath).replace(`${SETTINGS_VARIABLES_NOTENAME}`, this.basename).replace(`${SETTINGS_VARIABLES_NOTEPARENT}`, this.parentName).replace(`${SETTINGS_VARIABLES_DATES}`, dateTime) + ); + return (0, import_obsidian7.normalizePath)(attachPath); + } + } + root = getRootPath(this.parentPath, setting); + debugLog("getAttachmentPath - root", root); + attachPath = path.join( + root, + setting.attachmentPath.replace(`${SETTINGS_VARIABLES_NOTEPATH}`, this.parentPath).replace(`${SETTINGS_VARIABLES_NOTENAME}`, this.basename).replace(`${SETTINGS_VARIABLES_NOTEPARENT}`, this.parentName).replace(`${SETTINGS_VARIABLES_DATES}`, dateTime) + ); + return (0, import_obsidian7.normalizePath)(attachPath); + } +}; +function getMetadata(file, attach) { + const parentPath = path.dirname(file); + const parentName = path.basename(parentPath); + const name = path.basename(file); + const extension = path.extname(file); + const basename = path.basename(file, extension); + return new Metadata(file, name, basename, extension, parentPath, parentName, attach); +} + +// src/exclude.ts +function isExcluded(path2, settings) { + debugLog("excludePathsArray: ", settings.excludePathsArray); + for (const excludedPath of settings.excludePathsArray) { + if (excludedPath.length === 0) { + continue; + } + if (settings.excludeSubpaths && path2.startsWith(excludedPath)) { + debugLog("isExcluded: ", path2); + return true; + } else { + if (path2 === excludedPath) { + return true; + } + } + } + return false; +} + +// src/lib/originalStorage.ts +function containOriginalNameVariable(setting, ext) { + const { extSetting } = getExtensionOverrideSetting(ext, setting); + if (extSetting !== void 0 && extSetting.attachFormat.contains(SETTINGS_VARIABLES_ORIGINALNAME) || setting.attachFormat.contains(SETTINGS_VARIABLES_ORIGINALNAME)) { + return true; + } + return false; +} +function saveOriginalName(settings, setting, ext, data) { + if (settings.originalNameStorage === void 0) { + settings.originalNameStorage = []; + } + if (containOriginalNameVariable(setting, ext)) { + settings.originalNameStorage.filter((n) => n.md5 == data.md5).forEach((n) => settings.originalNameStorage.remove(n)); + settings.originalNameStorage.push(data); + } +} +function loadOriginalName(settings, setting, ext, md5) { + if (containOriginalNameVariable(setting, ext)) { + const first = settings.originalNameStorage.find((data) => data.md5 === md5); + const last = settings.originalNameStorage.reverse().find((data) => data.md5 === md5); + if (first === void 0 || last == void 0) { + return void 0; + } + if (first.md5 === last.md5 && first.n === last.n) { + return last; + } else if (first.md5 === last.md5 && first.n !== last.n) { + settings.originalNameStorage.remove(first); + return last; + } + } + return void 0; +} + +// src/arrange.ts +var bannerRegex = /!\[\[(.*?)\]\]/i; +var ArrangeHandler = class { + constructor(settings, app2, plugin) { + this.settings = settings; + this.app = app2; + this.plugin = plugin; + } + /** + * Rearranges attachments that are linked by markdown or canvas. + * Only rearranges attachments if autoRenameAttachment is enabled in settings. + * + * @param {RearrangeType} type - The type of attachments to rearrange. + * @param {TFile} file - The file to which the attachments are linked (optional), if the type was "file", thi should be provided. + * @param {string} oldPath - The old path of the file (optional), used for rename event. + */ + async rearrangeAttachment(type, file, oldPath) { + var _a; + if (!this.settings.autoRenameAttachment) { + debugLog("rearrangeAttachment - autoRenameAttachment not enable"); + return; + } + const attachments = await this.getAttachmentsInVault(this.settings, type, file, oldPath); + debugLog("rearrangeAttachment - attachments:", Object.keys(attachments).length, Object.entries(attachments)); + for (const obNote of Object.keys(attachments)) { + const innerFile = this.app.vault.getAbstractFileByPath(obNote); + if (!(innerFile instanceof import_obsidian8.TFile) || isAttachment(this.settings, innerFile)) { + debugLog(`rearrangeAttachment - ${obNote} not exists or is attachment, skipped`); + continue; + } + const { setting } = getOverrideSetting(this.settings, innerFile); + if (attachments[obNote].size == 0) { + continue; + } + const md = getMetadata(obNote); + const attachPath = md.getAttachmentPath(setting, this.settings.dateFormat); + if (!await this.app.vault.adapter.exists(attachPath, true)) { + if (oldPath != void 0 && await this.app.vault.adapter.exists(attachPath, false)) { + const mdOld = getMetadata(oldPath); + const attachPathOld = mdOld.getAttachmentPath(setting, this.settings.dateFormat); + this.app.vault.adapter.rename(attachPathOld, attachPath); + } else { + await this.app.vault.adapter.mkdir(attachPath); + } + } + for (let link of attachments[obNote]) { + try { + link = decodeURI(link); + } catch (err) { + console.log(`Invalid link: ${link}, err: ${err}`); + continue; + } + debugLog(`rearrangeAttachment - article: ${obNote} links: ${link}`); + const linkFile = this.app.vault.getAbstractFileByPath(link); + if (linkFile === null || !(linkFile instanceof import_obsidian8.TFile)) { + debugLog(`${link} not exists, skipped`); + continue; + } + const metadata = getMetadata(obNote, linkFile); + const md5 = await md5sum(this.app.vault.adapter, linkFile); + const originalName = loadOriginalName(this.settings, setting, linkFile.extension, md5); + debugLog("rearrangeAttachment - original name:", originalName); + let attachName = ""; + if (containOriginalNameVariable(setting, linkFile.extension)) { + attachName = await metadata.getAttachFileName( + setting, + this.settings.dateFormat, + (_a = originalName == null ? void 0 : originalName.n) != null ? _a : "", + this.app.vault.adapter, + path.basename(link, path.extname(link)) + ); + } else { + attachName = await metadata.getAttachFileName( + setting, + this.settings.dateFormat, + path.basename(link, path.extname(link)), + this.app.vault.adapter + ); + } + if (attachPath == path.dirname(link) && attachName === path.basename(link, path.extname(link))) { + continue; + } + const attachPathFile = this.app.vault.getAbstractFileByPath(attachPath); + if (attachPathFile === null || !(attachPathFile instanceof import_obsidian8.TFolder)) { + debugLog(`${attachPath} not exists, skipped`); + continue; + } + const { name } = await deduplicateNewName(attachName + "." + path.extname(link), attachPathFile); + debugLog("rearrangeAttachment - deduplicated name:", name); + await this.app.fileManager.renameFile(linkFile, path.join(attachPath, name)); + } + } + } + /** + * Retrieves the attachments in the vault based on the specified settings and type. + * If a file is provided, only attachments related to that file will be returned. + * + * @param {AttachmentManagementPluginSettings} settings - The settings for the attachment management plugin. + * @param {RearrangeType} type - The type of attachments to retrieve. + * @param {TFile} [file] - The file to filter attachments by. Optional. + * @return {Promise>>} - A promise that resolves to a record of attachments, where each key is a file name and each value is a set of associated attachment names. + */ + async getAttachmentsInVault(settings, type, file, oldPath) { + let attachmentsRecord = {}; + attachmentsRecord = await this.getAttachmentsInVaultByLinks(settings, type, file, oldPath); + return attachmentsRecord; + } + /** + * Modified from https://github.com/ozntel/oz-clear-unused-images-obsidian/blob/master/src/util.ts#LL48C21-L48C21 + * Retrieves a record of attachments in the vault based on the given settings and type. + * + * @param {AttachmentManagementPluginSettings} settings - The settings for the attachment management plugin. + * @param {RearrangeType} type - The type of attachments to retrieve. + * @param {TFile} file - The file to retrieve attachments for (optional). + * @return {Promise>>} - A promise that resolves to a record of attachments. + */ + async getAttachmentsInVaultByLinks(settings, type, file, oldPath) { + const attachmentsRecord = {}; + let resolvedLinks = {}; + let allFiles = []; + if (type == 1 /* LINKS */) { + resolvedLinks = this.app.metadataCache.resolvedLinks; + allFiles = this.app.vault.getFiles(); + } else if (type == 0 /* ACTIVE */) { + const file2 = getActiveFile(this.app); + if (file2) { + if (file2.parent && isExcluded(file2.parent.path, this.settings) || isAttachment(this.settings, file2)) { + allFiles = []; + new import_obsidian8.Notice(`${file2.path} was excluded, skipped`); + } else { + debugLog("getAttachmentsInVaultByLinks - active:", file2.path); + allFiles = [file2]; + if (this.app.metadataCache.resolvedLinks[file2.path]) { + resolvedLinks[file2.path] = this.app.metadataCache.resolvedLinks[file2.path]; + } + debugLog("getAttachmentsInVaultByLinks - resolvedLinks:", resolvedLinks); + } + } + } else if (type == 2 /* FILE */ && file != void 0) { + if (file.parent && isExcluded(file.parent.path, this.settings) || isAttachment(this.settings, file)) { + allFiles = []; + new import_obsidian8.Notice(`${file.path} was excluded, skipped`); + } else { + debugLog("getAttachmentsInVaultByLinks - file:", file.path); + allFiles = [file]; + const rlinks = this.app.metadataCache.resolvedLinks[file.path]; + if (rlinks) { + debugLog("getAttachmentsInVaultByLinks - rlinks:", rlinks); + resolvedLinks[file.path] = rlinks; + } else if (oldPath) { + debugLog("getAttachmentsInVaultByLinks - oldPath:", oldPath); + resolvedLinks[file.path] = this.app.metadataCache.resolvedLinks[oldPath]; + } + debugLog("getAttachmentsInVaultByLinks - resolvedLinks:", resolvedLinks); + } + } + debugLog("getAttachmentsInVaultByLinks - allFiles:", allFiles.length, allFiles); + if (resolvedLinks) { + for (const [mdFile, links] of Object.entries(resolvedLinks)) { + const attachmentsSet = /* @__PURE__ */ new Set(); + if (links) { + for (const [filePath] of Object.entries(links)) { + if (isAttachment(settings, filePath)) { + this.addToSet(attachmentsSet, filePath); + } + } + this.addToRecord(attachmentsRecord, mdFile, attachmentsSet); + } + } + } + for (let i = 0; i < allFiles.length; i++) { + const obsFile = allFiles[i]; + const attachmentsSet = /* @__PURE__ */ new Set(); + if (obsFile.parent && isExcluded(obsFile.parent.path, this.settings)) { + continue; + } + if (isMarkdownFile(obsFile.extension)) { + const fileCache = this.app.metadataCache.getFileCache(obsFile); + if (fileCache === null) { + continue; + } + if (fileCache.frontmatter) { + const frontmatter = fileCache.frontmatter; + for (const k of Object.keys(frontmatter)) { + if (typeof frontmatter[k] === "string") { + const formatMatch = frontmatter[k].match(bannerRegex); + if (formatMatch && formatMatch[1]) { + const fileName = formatMatch[1]; + const file2 = this.app.metadataCache.getFirstLinkpathDest(fileName, obsFile.path); + if (file2 && isAttachment(settings, file2.path)) { + this.addToSet(attachmentsSet, file2.path); + } + } + } + } + } + const linkMatches = await getAllLinkMatchesInFile(obsFile, app); + for (const linkMatch of linkMatches) { + if (isAttachment(settings, linkMatch.linkText)) { + this.addToSet(attachmentsSet, linkMatch.linkText); + } + } + } else if (isCanvasFile(obsFile.extension)) { + const fileRead = await this.app.vault.cachedRead(obsFile); + if (!fileRead || fileRead.length === 0) { + continue; + } + let canvasData; + try { + canvasData = JSON.parse(fileRead); + } catch (e) { + debugLog("getAttachmentsInVaultByLinks - parse canvas data error", e); + continue; + } + if (canvasData.nodes && canvasData.nodes.length > 0) { + for (const node of canvasData.nodes) { + if (node.type === "file") { + if (isAttachment(settings, node.file)) { + this.addToSet(attachmentsSet, node.file); + } + } else if (node.type == "text") { + const linkMatches = await getAllLinkMatchesInFile(obsFile, app, node.text); + for (const linkMatch of linkMatches) { + if (isAttachment(settings, linkMatch.linkText)) { + this.addToSet(attachmentsSet, linkMatch.linkText); + } + } + } + } + } + } + this.addToRecord(attachmentsRecord, obsFile.path, attachmentsSet); + } + return attachmentsRecord; + } + addToRecord(record, key, value) { + if (record[key] === void 0) { + record[key] = value; + return; + } + const valueSet = record[key]; + for (const val of value) { + this.addToSet(valueSet, val); + } + record[key] = valueSet; + } + addToSet(setObj, value) { + if (!setObj.has(value)) { + setObj.add(value); + } + } + needToRename(settings, attachPath, attachName, noteName, link) { + const linkPath = path.dirname(link); + const linkName = path.basename(link, path.extname(link)); + if (linkName.length !== attachName.length) { + return true; + } + if (attachPath !== linkPath) { + return true; + } else { + if (settings.attachFormat.includes(SETTINGS_VARIABLES_NOTENAME) && !linkName.includes(noteName)) { + return true; + } + const noNoteNameAttachFormat = settings.attachFormat.split(SETTINGS_VARIABLES_NOTENAME); + if (settings.attachFormat.includes(SETTINGS_VARIABLES_DATES)) { + for (const formatPart in noNoteNameAttachFormat) { + const splited = formatPart.split(SETTINGS_VARIABLES_DATES); + for (const part in splited) { + if (!linkName.includes(part)) { + return true; + } + } + } + } + } + return false; + } +}; + +// src/model/confirm.ts +var ConfirmModal = class extends import_obsidian9.Modal { + constructor(plugin) { + super(plugin.app); + this.plugin = plugin; + } + onOpen() { + const { contentEl } = this; + contentEl.empty(); + contentEl.createEl("h3", { + text: "Tips" + }); + contentEl.createSpan("", (el) => { + el.innerText = "This operation is irreversible and experimental. Please backup your vault first!"; + }); + new import_obsidian9.Setting(contentEl).addButton((btn) => { + btn.setButtonText("Cancel").setCta().onClick(() => { + this.close(); + }); + }).addButton( + (btn) => btn.setButtonText("Continue").onClick(async () => { + new ArrangeHandler(this.plugin.settings, this.plugin.app, this.plugin).rearrangeAttachment(1 /* LINKS */).finally(() => { + new import_obsidian9.Notice("Arrange completed"); + this.close(); + }); + }) + ); + } + onClose() { + const { contentEl } = this; + contentEl.empty(); + } +}; + +// src/create.ts +var import_obsidian10 = require("obsidian"); +var CreateHandler = class { + constructor(plugin, settings) { + this.plugin = plugin; + this.app = this.plugin.app; + this.settings = settings; + } + /** + * Post-processing of created attachment file (for paste and drop event). + * @param attach - the attachment file to process + * @param source - the notes file that linked to attach + * @returns - none + */ + processAttach(attach, source) { + if (source.parent && isExcluded(source.parent.path, this.settings)) { + debugLog("processAttach - not a file or exclude path:", source.path); + new import_obsidian10.Notice(`${source.path} was excluded, skipped`); + return; + } + const { setting } = getOverrideSetting(this.settings, source); + const { extSetting } = getExtensionOverrideSetting(attach.extension, setting); + debugLog("processAttach - file.extension:", attach.extension); + if (extSetting === void 0 && !isImage(attach.extension) && !isPastedImage(attach)) { + debugLog("renameFiles - no handle extension:", attach.extension); + return; + } + const metadata = getMetadata(source.path, attach); + debugLog("processAttach - metadata:", metadata); + const attachPath = metadata.getAttachmentPath(setting, this.settings.dateFormat); + metadata.getAttachFileName(setting, this.settings.dateFormat, attach.basename, this.app.vault.adapter).then((attachName) => { + attachName = attachName + "." + attach.extension; + this.app.vault.adapter.exists(attachPath, true).then(async (exists) => { + if (!exists) { + await this.app.vault.adapter.mkdir(attachPath); + debugLog("processAttach - create path:", attachPath); + } + }).finally(() => { + const attachPathFolder = this.app.vault.getAbstractFileByPath(attachPath); + deduplicateNewName(attachName, attachPathFolder).then(({ name }) => { + debugLog("processAttach - new path of file:", path.join(attachPath, name)); + this.renameCreateFile(attach, attachPath, name, source); + }); + }); + }); + } + /** + * Rename the file specified by `@param file`, and update the link of the file if specified updateLink + * @param attach - file to rename + * @param attachPath - where to the renamed file will be move to + * @param attachName - name of the renamed file + * @param source - associated active file + * @returns - none + */ + renameCreateFile(attach, attachPath, attachName, source) { + const dst = (0, import_obsidian10.normalizePath)(path.join(attachPath, attachName)); + debugLog("renameFile - ", attach.path, " to ", dst); + const original = attach.basename; + const name = attach.name; + this.app.fileManager.renameFile(attach, dst).then(() => { + new import_obsidian10.Notice(`Renamed ${name} to ${attachName}.`); + }).finally(() => { + const { setting } = getOverrideSetting(this.settings, source); + md5sum(this.app.vault.adapter, attach).then((md5) => { + saveOriginalName(this.settings, setting, attach.extension, { + n: original, + md5 + }); + this.plugin.saveData(this.settings); + }); + }); + } +}; + +// src/main.ts +var AttachmentManagementPlugin = class extends import_obsidian11.Plugin { + constructor() { + super(...arguments); + this.createdQueue = []; + } + async onload() { + await this.loadSettings(); + console.log(`Plugin loading: ${this.manifest.name} v.${this.manifest.version}`); + this.app.workspace.onLayoutReady(() => { + this.initCommands(); + this.registerEvent( + this.app.workspace.on("file-menu", async (menu, file) => { + if (file.parent && isExcluded(file.parent.path, this.settings) || isAttachment(this.settings, file)) { + return; + } + menu.addItem((item) => { + item.setTitle("Overriding attachment setting").setIcon("image-plus").onClick(async () => { + const { setting } = getOverrideSetting(this.settings, file); + const fileSetting = Object.assign({}, setting); + this.overrideConfiguration(file, fileSetting); + }); + }); + }) + ); + this.registerEvent( + this.app.vault.on("create", async (file) => { + debugLog("on create event - file:", file.path); + if (!(file instanceof import_obsidian11.TFile)) { + return; + } + const curentTime = new Date().getTime(); + const timeGapMs = curentTime - file.stat.mtime; + const timeGapCs = curentTime - file.stat.ctime; + if (timeGapMs > 1e3 || timeGapCs > 1e3 || isMarkdownFile(file.extension) || isCanvasFile(file.extension)) { + return; + } + if (matchExtension(file.extension, this.settings.excludeExtensionPattern)) { + debugLog("create - excluded file by extension", file); + return; + } + this.createdQueue.push(file); + }) + ); + this.registerEvent( + this.app.vault.on("modify", (file) => { + debugLog("on modify event - create queue:", this.createdQueue); + if (this.createdQueue.length < 1 || !(file instanceof import_obsidian11.TFile)) { + return; + } + debugLog("on modify event - file:", file.path); + this.app.vault.adapter.process(file.path, (pdata) => { + const f = this.createdQueue.first(); + if (f != void 0) { + this.app.vault.adapter.exists(f.path, true).then((exist) => { + if (exist) { + const processor = new CreateHandler(this, this.settings); + const link = this.app.fileManager.generateMarkdownLink(f, file.path); + if (file.extension == "md" && pdata.indexOf(link) != -1 || file.extension == "canvas" && pdata.indexOf(f.path) != -1) { + this.createdQueue.remove(f); + processor.processAttach(f, file); + } + } else { + debugLog("on modify event - file does not exist:", f.path); + this.createdQueue.remove(f); + } + }); + } + return pdata; + }); + }) + ); + this.registerEvent( + // when trigger a rename event on folder, for each file/folder in this renamed folder (include itself) will trigger this event + this.app.vault.on("rename", async (file, oldPath) => { + debugLog("on rename event - new path and old path:", file.path, oldPath); + const { setting } = getRenameOverrideSetting(this.settings, file, oldPath); + debugLog("rename - using settings:", setting); + if (setting.type === "FOLDER" /* FOLDER */ || setting.type === "FILE" /* FILE */) { + updateOverrideSetting(this.settings, file, oldPath); + this.saveSettings(); + } + debugLog("rename - updated settings:", setting); + if (!this.settings.autoRenameAttachment) { + debugLog("rename - auto rename not enabled:", this.settings.autoRenameAttachment); + return; + } + if (file instanceof import_obsidian11.TFile) { + if (file.parent && isExcluded(file.parent.path, this.settings)) { + debugLog("rename - exclude path:", file.parent.path); + new import_obsidian11.Notice(`${file.path} was excluded`); + return; + } + if (isAttachment(this.settings, file)) { + debugLog("rename - not processing rename on attachment:", file.path); + return; + } + await new ArrangeHandler(this.settings, this.app, this).rearrangeAttachment( + 2 /* FILE */, + file, + oldPath + ); + const oldMetadata = getMetadata(oldPath); + const oldAttachPath = oldMetadata.getAttachmentPath(setting, this.settings.dateFormat); + this.app.vault.adapter.exists(oldAttachPath, true).then((exists) => { + if (exists) { + checkEmptyFolder(this.app.vault.adapter, oldAttachPath).then((empty) => { + if (empty) { + this.app.vault.adapter.rmdir(oldAttachPath, true); + } + }); + } + }); + } else if (file instanceof import_obsidian11.TFolder) { + return; + } + }) + ); + this.registerEvent( + this.app.vault.on("delete", async (file) => { + debugLog("on delete event - file path:", file.path); + if (file.parent && isExcluded(file.parent.path, this.settings) || isAttachment(this.settings, file)) { + debugLog("delete - exclude path or the file is an attachment:", file.path); + return; + } + if (deleteOverrideSetting(this.settings, file)) { + await this.saveSettings(); + new import_obsidian11.Notice("Removed override setting of " + file.path); + } + if (file instanceof import_obsidian11.TFile) { + const oldMetadata = getMetadata(file.path); + const { setting } = getOverrideSetting(this.settings, file); + const oldAttachPath = oldMetadata.getAttachmentPath(setting, this.settings.dateFormat); + this.app.vault.adapter.exists(oldAttachPath, true).then((exists) => { + if (exists) { + checkEmptyFolder(this.app.vault.adapter, oldAttachPath).then((empty) => { + if (empty) { + this.app.vault.adapter.rmdir(oldAttachPath, true); + } + }); + } + }); + } + }) + ); + this.addSettingTab(new SettingTab(this.app, this)); + }); + } + async overrideConfiguration(file, setting) { + new OverrideModal(this, file, setting).open(); + } + /** + * Initializes and registers the plugin's commands + * This method is responsible for setting up the user interface by adding various commands. + * These commands include settings overrides, resetting settings, clearing unused original name storage, + * and rearranging attachments. + * + * Note: The actual implementation of each command is not included in this method and needs to be + * defined separately asynchronously. + * + * Warning: Make sure you have checked for errors while implementing the functionality of each command. + */ + initCommands() { + this.addCommand({ + id: "attachment-management-rearrange-all-links", + name: "Rearrange all linked attachments", + callback: async () => { + new ConfirmModal(this).open(); + } + }); + this.addCommand({ + id: "attachment-management-rearrange-active-links", + name: "Rearrange linked attachments", + callback: async () => { + new ArrangeHandler(this.settings, this.app, this).rearrangeAttachment(0 /* ACTIVE */).finally(() => { + new import_obsidian11.Notice("Arrange completed"); + }); + } + }); + this.addCommand({ + id: "attachment-management-override-setting", + name: "Overriding setting", + checkCallback: (checking) => { + const file = getActiveFile(this.app); + if (file) { + if (isAttachment(this.settings, file)) { + return true; + } + if (!checking) { + if (file.parent && isExcluded(file.parent.path, this.settings)) { + new import_obsidian11.Notice(`${file.path} was excluded`); + return true; + } + const { setting } = getOverrideSetting(this.settings, file); + const fileSetting = Object.assign({}, setting); + this.overrideConfiguration(file, fileSetting); + } + return true; + } + return false; + } + }); + this.addCommand({ + id: "attachment-management-reset-override-setting", + name: "Reset override setting", + checkCallback: (checking) => { + const file = getActiveFile(this.app); + if (file) { + if (isAttachment(this.settings, file)) { + return true; + } + if (!checking) { + if (file.parent && isExcluded(file.parent.path, this.settings)) { + new import_obsidian11.Notice(`${file.path} was excluded`); + return true; + } + delete this.settings.overridePath[file.path]; + this.saveSettings().finally(() => { + new import_obsidian11.Notice(`Reset attachment setting of ${file.path}`); + }); + } + return true; + } + return false; + } + }); + this.addCommand({ + id: "attachment-management-clear-unused-originalname-storage", + name: "Clear unused original name storage", + callback: async () => { + const attachments = await new ArrangeHandler(this.settings, this.app, this).getAttachmentsInVault( + this.settings, + 1 /* LINKS */ + ); + const storages = []; + for (const attachs of Object.values(attachments)) { + for (const attach of attachs) { + const link = decodeURI(attach); + const linkFile = this.app.vault.getAbstractFileByPath(link); + if (linkFile !== null && linkFile instanceof import_obsidian11.TFile) { + md5sum(this.app.vault.adapter, linkFile).then((md5) => { + const ret = this.settings.originalNameStorage.find((data) => data.md5 === md5); + if (ret) { + storages.filter((n) => n.md5 == md5).forEach((n) => storages.remove(n)); + storages.push(ret); + } + }); + } + } + } + debugLog("clearUnusedOriginalNameStorage - storage:", storages); + this.settings.originalNameStorage = storages; + this.saveSettings(); + } + }); + } + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + } + async saveSettings() { + await this.saveData(this.settings); + } + async onunload() { + console.log("unloading attachment management."); + this.createdQueue = []; + } +}; diff --git a/.obsidian/plugins/attachment-management/manifest.json b/.obsidian/plugins/attachment-management/manifest.json new file mode 100644 index 0000000..cae6cf0 --- /dev/null +++ b/.obsidian/plugins/attachment-management/manifest.json @@ -0,0 +1,10 @@ +{ + "id": "attachment-management", + "name": "Attachment Management", + "version": "0.9.16", + "description": "Customize your attachment path of notes independently with variables and auto rename it on change.", + "author": "trganda", + "authorUrl": "https://github.com/trganda", + "fundingUrl": "https://paypal.me/trganda", + "isDesktopOnly": false +} diff --git a/.obsidian/plugins/attachment-management/styles.css b/.obsidian/plugins/attachment-management/styles.css new file mode 100644 index 0000000..e30a878 --- /dev/null +++ b/.obsidian/plugins/attachment-management/styles.css @@ -0,0 +1,15 @@ +/* + +This CSS file will be included with your plugin, and +available in the app when your plugin is enabled. + +If your plugin does not need CSS, delete this file. + +*/ +.attach_management_sub_setting { + padding-left: 2em; +} +.attach_management_sub_setting + .attach_management_sub_setting { + padding-left: 0; + margin-left: 2em; +}