var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __typeError = (msg) => { throw TypeError(msg); }; var __defNormalProp = (obj, key2, value) => key2 in obj ? __defProp(obj, key2, { enumerable: true, configurable: true, writable: true, value }) : obj[key2] = value; 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 key2 of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key2) && key2 !== except) __defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __publicField = (obj, key2, value) => __defNormalProp(obj, typeof key2 !== "symbol" ? key2 + "" : key2, value); var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg); var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj)); var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value); var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value); var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method); // src/main.ts var main_exports = {}; __export(main_exports, { default: () => MetaEdit }); module.exports = __toCommonJS(main_exports); var import_obsidian25 = require("obsidian"); // src/Settings/metaEditSettingsTab.ts var import_obsidian3 = require("obsidian"); // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/version.js var PUBLIC_VERSION = "5"; // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/disclose-version.js var _a; if (typeof window !== "undefined") { ((_a = window.__svelte ?? (window.__svelte = {})).v ?? (_a.v = /* @__PURE__ */ new Set())).add(PUBLIC_VERSION); } // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/constants.js var EACH_ITEM_REACTIVE = 1; var EACH_INDEX_REACTIVE = 1 << 1; var EACH_IS_CONTROLLED = 1 << 2; var EACH_IS_ANIMATED = 1 << 3; var EACH_ITEM_IMMUTABLE = 1 << 4; var PROPS_IS_IMMUTABLE = 1; var PROPS_IS_RUNES = 1 << 1; var PROPS_IS_UPDATED = 1 << 2; var PROPS_IS_BINDABLE = 1 << 3; var PROPS_IS_LAZY_INITIAL = 1 << 4; var TRANSITION_OUT = 1 << 1; var TRANSITION_GLOBAL = 1 << 2; var TEMPLATE_FRAGMENT = 1; var TEMPLATE_USE_IMPORT_NODE = 1 << 1; var TEMPLATE_USE_SVG = 1 << 2; var TEMPLATE_USE_MATHML = 1 << 3; var HYDRATION_START = "["; var HYDRATION_START_ELSE = "[!"; var HYDRATION_START_FAILED = "[?"; var HYDRATION_END = "]"; var HYDRATION_ERROR = {}; var ELEMENT_PRESERVE_ATTRIBUTE_CASE = 1 << 1; var ELEMENT_IS_INPUT = 1 << 2; var UNINITIALIZED = /* @__PURE__ */ Symbol("uninitialized"); var FILENAME = /* @__PURE__ */ Symbol("filename"); var NAMESPACE_HTML = "http://www.w3.org/1999/xhtml"; // node_modules/.pnpm/esm-env@1.2.2/node_modules/esm-env/dev-fallback.js var node_env = globalThis.process?.env?.NODE_ENV; var dev_fallback_default = node_env && !node_env.toLowerCase().startsWith("prod"); // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/shared/utils.js var is_array = Array.isArray; var index_of = Array.prototype.indexOf; var includes = Array.prototype.includes; var array_from = Array.from; var object_keys = Object.keys; var define_property = Object.defineProperty; var get_descriptor = Object.getOwnPropertyDescriptor; var get_descriptors = Object.getOwnPropertyDescriptors; var object_prototype = Object.prototype; var array_prototype = Array.prototype; var get_prototype_of = Object.getPrototypeOf; var is_extensible = Object.isExtensible; var noop = () => { }; function run_all(arr) { for (var i = 0; i < arr.length; i++) { arr[i](); } } function deferred() { var resolve; var reject; var promise = new Promise((res, rej) => { resolve = res; reject = rej; }); return { promise, resolve, reject }; } // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/client/constants.js var DERIVED = 1 << 1; var EFFECT = 1 << 2; var RENDER_EFFECT = 1 << 3; var MANAGED_EFFECT = 1 << 24; var BLOCK_EFFECT = 1 << 4; var BRANCH_EFFECT = 1 << 5; var ROOT_EFFECT = 1 << 6; var BOUNDARY_EFFECT = 1 << 7; var CONNECTED = 1 << 9; var CLEAN = 1 << 10; var DIRTY = 1 << 11; var MAYBE_DIRTY = 1 << 12; var INERT = 1 << 13; var DESTROYED = 1 << 14; var REACTION_RAN = 1 << 15; var DESTROYING = 1 << 25; var EFFECT_TRANSPARENT = 1 << 16; var EAGER_EFFECT = 1 << 17; var HEAD_EFFECT = 1 << 18; var EFFECT_PRESERVED = 1 << 19; var USER_EFFECT = 1 << 20; var EFFECT_OFFSCREEN = 1 << 25; var WAS_MARKED = 1 << 16; var REACTION_IS_UPDATING = 1 << 21; var ASYNC = 1 << 22; var ERROR_VALUE = 1 << 23; var STATE_SYMBOL = /* @__PURE__ */ Symbol("$state"); var LEGACY_PROPS = /* @__PURE__ */ Symbol("legacy props"); var LOADING_ATTR_SYMBOL = /* @__PURE__ */ Symbol(""); var PROXY_PATH_SYMBOL = /* @__PURE__ */ Symbol("proxy path"); var ATTRIBUTES_CACHE = /* @__PURE__ */ Symbol("attributes"); var CLASS_CACHE = /* @__PURE__ */ Symbol("class"); var STYLE_CACHE = /* @__PURE__ */ Symbol("style"); var TEXT_CACHE = /* @__PURE__ */ Symbol("text"); var FORM_RESET_HANDLER = /* @__PURE__ */ Symbol("form reset"); var HMR_ANCHOR = /* @__PURE__ */ Symbol("hmr anchor"); var STALE_REACTION = new class StaleReactionError extends Error { constructor() { super(...arguments); __publicField(this, "name", "StaleReactionError"); __publicField(this, "message", "The reaction that called `getAbortSignal()` was re-run or destroyed"); } }(); var IS_XHTML = ( // We gotta write it like this because after downleveling the pure comment may end up in the wrong location !!globalThis.document?.contentType && /* @__PURE__ */ globalThis.document.contentType.includes("xml") ); var TEXT_NODE = 3; var COMMENT_NODE = 8; // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/shared/errors.js function invariant_violation(message) { if (dev_fallback_default) { const error = new Error(`invariant_violation An invariant violation occurred, meaning Svelte's internal assumptions were flawed. This is a bug in Svelte, not your app \u2014 please open an issue at https://github.com/sveltejs/svelte, citing the following message: "${message}" https://svelte.dev/e/invariant_violation`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/invariant_violation`); } } // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/client/errors.js function async_derived_orphan() { if (dev_fallback_default) { const error = new Error(`async_derived_orphan Cannot create a \`$derived(...)\` with an \`await\` expression outside of an effect tree https://svelte.dev/e/async_derived_orphan`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/async_derived_orphan`); } } function bind_invalid_checkbox_value() { if (dev_fallback_default) { const error = new Error(`bind_invalid_checkbox_value Using \`bind:value\` together with a checkbox input is not allowed. Use \`bind:checked\` instead https://svelte.dev/e/bind_invalid_checkbox_value`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/bind_invalid_checkbox_value`); } } function derived_references_self() { if (dev_fallback_default) { const error = new Error(`derived_references_self A derived value cannot reference itself recursively https://svelte.dev/e/derived_references_self`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/derived_references_self`); } } function each_key_duplicate(a, b, value) { if (dev_fallback_default) { const error = new Error(`each_key_duplicate ${value ? `Keyed each block has duplicate key \`${value}\` at indexes ${a} and ${b}` : `Keyed each block has duplicate key at indexes ${a} and ${b}`} https://svelte.dev/e/each_key_duplicate`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/each_key_duplicate`); } } function each_key_volatile(index2, a, b) { if (dev_fallback_default) { const error = new Error(`each_key_volatile Keyed each block has key that is not idempotent \u2014 the key for item at index ${index2} was \`${a}\` but is now \`${b}\`. Keys must be the same each time for a given item https://svelte.dev/e/each_key_volatile`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/each_key_volatile`); } } function effect_in_teardown(rune) { if (dev_fallback_default) { const error = new Error(`effect_in_teardown \`${rune}\` cannot be used inside an effect cleanup function https://svelte.dev/e/effect_in_teardown`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/effect_in_teardown`); } } function effect_in_unowned_derived() { if (dev_fallback_default) { const error = new Error(`effect_in_unowned_derived Effect cannot be created inside a \`$derived\` value that was not itself created inside an effect https://svelte.dev/e/effect_in_unowned_derived`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/effect_in_unowned_derived`); } } function effect_orphan(rune) { if (dev_fallback_default) { const error = new Error(`effect_orphan \`${rune}\` can only be used inside an effect (e.g. during component initialisation) https://svelte.dev/e/effect_orphan`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/effect_orphan`); } } function effect_update_depth_exceeded() { if (dev_fallback_default) { const error = new Error(`effect_update_depth_exceeded Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state https://svelte.dev/e/effect_update_depth_exceeded`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/effect_update_depth_exceeded`); } } function hydration_failed() { if (dev_fallback_default) { const error = new Error(`hydration_failed Failed to hydrate the application https://svelte.dev/e/hydration_failed`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/hydration_failed`); } } function props_invalid_value(key2) { if (dev_fallback_default) { const error = new Error(`props_invalid_value Cannot do \`bind:${key2}={undefined}\` when \`${key2}\` has a fallback value https://svelte.dev/e/props_invalid_value`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/props_invalid_value`); } } function rune_outside_svelte(rune) { if (dev_fallback_default) { const error = new Error(`rune_outside_svelte The \`${rune}\` rune is only available inside \`.svelte\` and \`.svelte.js/ts\` files https://svelte.dev/e/rune_outside_svelte`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/rune_outside_svelte`); } } function state_descriptors_fixed() { if (dev_fallback_default) { const error = new Error(`state_descriptors_fixed Property descriptors defined on \`$state\` objects must contain \`value\` and always be \`enumerable\`, \`configurable\` and \`writable\`. https://svelte.dev/e/state_descriptors_fixed`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/state_descriptors_fixed`); } } function state_prototype_fixed() { if (dev_fallback_default) { const error = new Error(`state_prototype_fixed Cannot set prototype of \`$state\` object https://svelte.dev/e/state_prototype_fixed`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/state_prototype_fixed`); } } function state_unsafe_mutation() { if (dev_fallback_default) { const error = new Error(`state_unsafe_mutation Updating state inside \`$derived(...)\`, \`$inspect(...)\` or a template expression is forbidden. If the value should not be reactive, declare it without \`$state\` https://svelte.dev/e/state_unsafe_mutation`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/state_unsafe_mutation`); } } function svelte_boundary_reset_onerror() { if (dev_fallback_default) { const error = new Error(`svelte_boundary_reset_onerror A \`\` \`reset\` function cannot be called while an error is still being handled https://svelte.dev/e/svelte_boundary_reset_onerror`); error.name = "Svelte error"; throw error; } else { throw new Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`); } } // node_modules/.pnpm/svelte@5.56.4_@typescript-eslint+types@8.62.0/node_modules/svelte/src/internal/client/warnings.js var bold = "font-weight: bold"; var normal = "font-weight: normal"; function await_reactivity_loss(name) { if (dev_fallback_default) { console.warn(`%c[svelte] await_reactivity_loss %cDetected reactivity loss when reading \`${name}\`. This happens when state is read in an async function after an earlier \`await\` https://svelte.dev/e/await_reactivity_loss`, bold, normal); } else { console.warn(`https://svelte.dev/e/await_reactivity_loss`); } } function await_waterfall(name, location) { if (dev_fallback_default) { console.warn(`%c[svelte] await_waterfall %cAn async derived, \`${name}\` (${location}) was not read immediately after it resolved. This often indicates an unnecessary waterfall, which can slow down your app https://svelte.dev/e/await_waterfall`, bold, normal); } else { console.warn(`https://svelte.dev/e/await_waterfall`); } } function derived_inert() { if (dev_fallback_default) { console.warn(`%c[svelte] derived_inert %cReading a derived belonging to a now-destroyed effect may result in stale values https://svelte.dev/e/derived_inert`, bold, normal); } else { console.warn(`https://svelte.dev/e/derived_inert`); } } function hydration_attribute_changed(attribute, html2, value) { if (dev_fallback_default) { console.warn(`%c[svelte] hydration_attribute_changed %cThe \`${attribute}\` attribute on \`${html2}\` changed its value between server and client renders. The client value, \`${value}\`, will be ignored in favour of the server value https://svelte.dev/e/hydration_attribute_changed`, bold, normal); } else { console.warn(`https://svelte.dev/e/hydration_attribute_changed`); } } function hydration_mismatch(location) { if (dev_fallback_default) { console.warn( `%c[svelte] hydration_mismatch %c${location ? `Hydration failed because the initial UI does not match what was rendered on the server. The error occurred near ${location}` : "Hydration failed because the initial UI does not match what was rendered on the server"} https://svelte.dev/e/hydration_mismatch`, bold, normal ); } else { console.warn(`https://svelte.dev/e/hydration_mismatch`); } } function lifecycle_double_unmount() { if (dev_fallback_default) { console.warn(`%c[svelte] lifecycle_double_unmount %cTried to unmount a component that was not mounted https://svelte.dev/e/lifecycle_double_unmount`, bold, normal); } else { console.warn(`https://svelte.dev/e/lifecycle_double_unmount`); } } function select_multiple_invalid_value() { if (dev_fallback_default) { console.warn(`%c[svelte] select_multiple_invalid_value %cThe \`value\` property of a \``); var root_2 = from_html(`
NameType
`); var $$css = { hash: "svelte-1eh4i7q", code: ".buttonContainer.svelte-1eh4i7q {display:flex;justify-content:center;margin-top:1rem;}select.svelte-1eh4i7q {border-radius:4px;width:100%;height:30px;border:1px solid #dbdbdc;color:#383a42;background-color:#fff;padding:3px;}button.svelte-1eh4i7q {margin-left:5px;margin-right:5px;font-size:15px;}" }; function ProgressPropertiesModalContent($$anchor, $$props) { push($$props, true); append_styles($$anchor, $$css); const options = Object.keys(ProgressPropertyOptions).map((k) => ProgressPropertyOptions[k]); let initialProperties = prop($$props, "properties", 19, () => []); let properties = state(proxy(untrack(() => initialProperties().map((property) => ({ ...property }))))); function saveProperties() { $$props.save(snapshot(get2(properties))); } function addNewProperty() { const newProp = { name: "", type: "Total Tasks" /* TaskTotal */ }; set(properties, [...get2(properties), newProp], true); saveProperties(); } function removeProperty(property) { set(properties, get2(properties).filter((prop2) => prop2 !== property), true); saveProperties(); } var div = root_2(); var table = child(div); var tbody = sibling(child(table)); each(tbody, 20, () => get2(properties), (property) => property, ($$anchor2, property, $$index_1) => { var tr = root_1(); var td = child(tr); var input = child(td); remove_input_defaults(input); reset(td); var td_1 = sibling(td); var select = child(td_1); each(select, 20, () => options, (text2) => text2, ($$anchor3, text2) => { var option = root(); var option_value = {}; template_effect(() => { set_attribute2(option, "label", text2); if (option_value !== (option_value = text2)) { option.value = (option.__value = text2) ?? ""; } }); append($$anchor3, option); }); reset(select); reset(td_1); var td_2 = sibling(td_1); var input_1 = child(td_2); reset(td_2); reset(tr); delegated("change", input, saveProperties); bind_value(input, () => property.name, ($$value) => property.name = $$value); delegated("change", select, saveProperties); bind_select_value(select, () => property.type, ($$value) => property.type = $$value); delegated("click", input_1, () => removeProperty(property)); append($$anchor2, tr); }); reset(tbody); reset(table); var div_1 = sibling(table, 2); var button = child(div_1); reset(div_1); reset(div); delegated("click", button, addNewProperty); append($$anchor, div); pop(); } delegate(["change", "click"]); // src/Modals/AutoPropertiesSettingModal/AutoPropertiesModalContent.svelte var import_obsidian = require("obsidian"); // src/autoProperties.ts function findAutoProperty(properties, propertyName) { return properties?.find((a) => a.name === propertyName); } function isMultiAutoProperty(autoProp, editMode, propertyName) { if (autoProp.type === "Multi") return true; if (autoProp.type === "Single") return false; if (editMode.mode === "All Multi" /* AllMulti */) return true; if (editMode.mode === "Some Multi" /* SomeMulti */ && editMode.properties.includes(propertyName)) { return true; } return false; } function normalizeChoices(choices) { if (!choices) return []; const seen = /* @__PURE__ */ new Set(); const out = []; for (const raw of choices) { const choice = (raw ?? "").trim(); if (choice === "" || seen.has(choice)) continue; seen.add(choice); out.push(choice); } return out; } function splitPastedChoices(text2) { if (!text2) return []; const parts = /[\r\n]/.test(text2) ? text2.split(/\r\n|\r|\n/) : text2.split(","); return parts.map((p) => p.trim()).filter((p) => p !== ""); } function withChoicesPasted(choices, index2, tokens) { const before = choices.slice(0, index2); const after = choices.slice(index2 + 1); const taken = new Set( [...before, ...after].map((c) => (c ?? "").trim()).filter((c) => c !== "") ); const inserted = []; for (const raw of tokens) { const choice = (raw ?? "").trim(); if (choice === "" || taken.has(choice)) continue; taken.add(choice); inserted.push(choice); } return [...before, ...inserted, ...after]; } function cloneAutoProperty(property) { const clone2 = { name: property.name, choices: Array.isArray(property.choices) ? [...property.choices] : [] }; if (property.description !== void 0) clone2.description = property.description; if (property.type !== void 0) clone2.type = property.type; return clone2; } function cloneAutoProperties(properties) { if (!Array.isArray(properties)) return []; return properties.map(cloneAutoProperty); } function applyAutoPropertySettingsOperation(properties, operation) { const next2 = cloneAutoProperties(properties); switch (operation.kind) { case "addProperty": { const index2 = boundedInsertIndex(operation.index, next2.length); next2.splice(index2, 0, cloneAutoProperty(operation.property)); return next2; } case "removeProperty": { const index2 = findAutoPropertyOperationTargetIndex(next2, operation.target); if (index2 === -1) return false; next2.splice(index2, 1); return next2; } case "setName": case "setDescription": case "setType": { const index2 = findAutoPropertyOperationTargetIndex(next2, operation.target); if (index2 === -1) return false; const current = next2[index2]; if (operation.kind === "setName") { if (current.name === operation.value) return false; current.name = operation.value; } else if (operation.kind === "setDescription") { if ((current.description ?? "") === operation.value) return false; current.description = operation.value; } else { if ((current.type ?? "Single") === operation.value) return false; current.type = operation.value; } return next2; } case "addChoice": { const property = findAutoPropertyOperationTarget(next2, operation.target); if (!property) return false; property.choices.splice(boundedInsertIndex(operation.index, property.choices.length), 0, operation.value); return next2; } case "removeChoice": { const property = findAutoPropertyOperationTarget(next2, operation.target); if (!property) return false; const choiceIndex = findExistingChoiceIndex(property.choices, operation.index, operation.value); if (choiceIndex === -1) return false; property.choices.splice(choiceIndex, 1); return next2; } case "setChoice": { const property = findAutoPropertyOperationTarget(next2, operation.target); if (!property) return false; if (operation.previousValue === operation.value) return false; const choiceIndex = findWritableChoiceIndex(property.choices, operation.index, operation.previousValue); property.choices.splice(choiceIndex, choiceIndex < property.choices.length ? 1 : 0, operation.value); return next2; } case "replaceChoiceWithChoices": { const property = findAutoPropertyOperationTarget(next2, operation.target); if (!property) return false; const choiceIndex = findExistingChoiceIndex(property.choices, operation.index, operation.previousValue); if (choiceIndex === -1) { property.choices = withChoicesInserted(property.choices, operation.index, operation.values); } else { property.choices = withChoicesPasted(property.choices, choiceIndex, operation.values); } return next2; } } } function boundedInsertIndex(index2, length) { if (!Number.isFinite(index2)) return length; return Math.max(0, Math.min(index2, length)); } function findAutoPropertyOperationTarget(properties, target) { const index2 = findAutoPropertyOperationTargetIndex(properties, target); return index2 === -1 ? void 0 : properties[index2]; } function findAutoPropertyOperationTargetIndex(properties, target) { if (isValidIndex(target.index, properties.length) && properties[target.index].name === target.name) { return target.index; } if (target.name !== "") { const namedIndex = properties.findIndex((property) => property.name === target.name); if (namedIndex !== -1) return namedIndex; } if (target.name === "" && isValidIndex(target.index, properties.length)) return target.index; return -1; } function findExistingChoiceIndex(choices, index2, value) { if (isValidIndex(index2, choices.length) && choices[index2] === value) return index2; return choices.findIndex((choice) => choice === value); } function findWritableChoiceIndex(choices, index2, value) { const existingIndex = findExistingChoiceIndex(choices, index2, value); if (existingIndex !== -1) return existingIndex; return boundedInsertIndex(index2, choices.length); } function withChoicesInserted(choices, index2, tokens) { const next2 = [...choices]; const taken = new Set(next2.map((c) => (c ?? "").trim()).filter((c) => c !== "")); const inserted = []; for (const raw of tokens) { const choice = (raw ?? "").trim(); if (choice === "" || taken.has(choice)) continue; taken.add(choice); inserted.push(choice); } next2.splice(boundedInsertIndex(index2, next2.length), 0, ...inserted); return next2; } function isValidIndex(index2, length) { return Number.isInteger(index2) && index2 >= 0 && index2 < length; } function splitCommaSeparatedValues(value) { const parts = []; let start = 0; let wikilinkDepth = 0; for (let i = 0; i < value.length; i++) { if (value.startsWith("[[", i)) { wikilinkDepth++; i++; continue; } if (wikilinkDepth > 0 && value.startsWith("]]", i)) { wikilinkDepth--; i++; continue; } if (value[i] === "," && wikilinkDepth === 0) { parts.push(value.slice(start, i)); start = i + 1; } } parts.push(value.slice(start)); return parts; } function matchingOuterBracketIndex(value) { let depth = 0; for (let i = 0; i < value.length; i++) { const char = value[i]; if (char === "[") { depth++; } else if (char === "]") { depth--; if (depth === 0) return i; if (depth < 0) return null; } } return null; } function shouldUnwrapBracketedList(value) { if (!value.startsWith("[") || !value.endsWith("]")) return false; if (value.startsWith("[[")) return false; if (matchingOuterBracketIndex(value) !== value.length - 1) return false; const innerValue = value.slice(1, -1); if (innerValue.trim() === "") return true; return splitCommaSeparatedValues(innerValue).length > 1; } function toValueArray(content) { if (content === null || content === void 0) return []; if (Array.isArray(content)) { return content.map((v) => (v ?? "").toString().trim()).filter(Boolean); } const value = content.toString().trim(); const splitValue = shouldUnwrapBracketedList(value) ? value.slice(1, -1) : value; return splitCommaSeparatedValues(splitValue).map((s) => s.trim()).filter(Boolean); } function multiSelectOptions(autoProp, currentValue) { const seen = /* @__PURE__ */ new Set(); const out = []; for (const value of [...toValueArray(currentValue), ...normalizeChoices(autoProp.choices)]) { if (!seen.has(value)) { seen.add(value); out.push(value); } } return out; } function isNewChoice(autoProp, value) { const trimmed = value.trim(); if (trimmed === "") return false; return !(autoProp.choices ?? []).some((c) => (c ?? "").trim() === trimmed); } function withChoiceAdded(autoProp, value) { if (!isNewChoice(autoProp, value)) return autoProp; return { ...autoProp, choices: [...autoProp.choices ?? [], value.trim()] }; } // src/Modals/AutoPropertiesSettingModal/AutoPropertiesModalContent.svelte var root2 = from_html(`

No auto properties yet. Add one to define a reusable set of values for a property.

`); var root_12 = from_html(``); var root_22 = from_html(`
`); var root_3 = from_html(`
Values
`); var root_4 = from_html(`
`); var $$css2 = { hash: "svelte-1i9u2va", code: ".metaedit-auto-properties.svelte-1i9u2va {display:flex;flex-direction:column;gap:0.75rem;margin-top:0.5rem;}.metaedit-empty.svelte-1i9u2va {color:var(--text-muted);text-align:center;margin:0.5rem 0;}.metaedit-ap-card.svelte-1i9u2va {display:flex;flex-direction:column;gap:0.5rem;padding:0.75rem;border:1px solid var(--background-modifier-border);border-radius:var(--radius-m, 8px);background-color:var(--background-secondary);}.metaedit-ap-header.svelte-1i9u2va {display:flex;gap:0.5rem;align-items:center;}.metaedit-ap-name.svelte-1i9u2va {flex:1 1 auto;min-width:0;font-weight:var(--font-semibold, 600);}.metaedit-ap-type.svelte-1i9u2va {flex:0 0 auto;}.metaedit-ap-description.svelte-1i9u2va {width:100%;}.metaedit-ap-values.svelte-1i9u2va {display:flex;flex-direction:column;gap:0.35rem;}.metaedit-ap-label.svelte-1i9u2va {color:var(--text-muted);font-size:var(--font-ui-smaller, 0.8em);}.metaedit-ap-choice.svelte-1i9u2va {display:flex;gap:0.5rem;align-items:center;}.metaedit-ap-choice.svelte-1i9u2va input:where(.svelte-1i9u2va) {flex:1 1 auto;min-width:0;}.metaedit-ap-icon.svelte-1i9u2va {flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;cursor:pointer;}.metaedit-ap-add-value.svelte-1i9u2va {display:inline-flex;align-items:center;gap:0.35rem;align-self:flex-start;background:transparent;box-shadow:none;color:var(--text-muted);padding:0.25rem 0.4rem;cursor:pointer;}.metaedit-ap-add-value.svelte-1i9u2va:hover {color:var(--text-normal);}.metaedit-ap-footer.svelte-1i9u2va {display:flex;justify-content:center;margin-top:0.25rem;}" }; function AutoPropertiesModalContent($$anchor, $$props) { push($$props, true); append_styles($$anchor, $$css2); let initialAutoProperties = prop($$props, "autoProperties", 19, () => []); const types = ["Single", "Multi"]; let nextPropertyId = 0; let autoProperties = state(proxy(untrack(() => createEditableAutoProperties(initialAutoProperties())))); function icon(node, name) { const render = (n) => { node.textContent = ""; (0, import_obsidian.setIcon)(node, n); }; render(name); return { update: render }; } function indexes(values) { return values.map((_, i) => i); } function createEditableAutoProperties(properties) { return cloneAutoProperties(properties).map((property) => createEditableAutoProperty(property)); } function createEditableAutoProperty(property) { return { id: nextPropertyId++, property, targetName: property.name, choiceTargets: [...property.choices] }; } function targetFor(entry, index2) { return { name: entry.targetName, index: index2 }; } function persist(operation) { void Promise.resolve($$props.save(operation)).catch((error) => { console.error("MetaEdit could not save Auto Properties settings.", error); }); } function addNewProperty() { const property = { name: "", choices: [""], type: "Single" }; const entry = createEditableAutoProperty(cloneAutoProperty(property)); const index2 = get2(autoProperties).length; set(autoProperties, [...get2(autoProperties), entry], true); persist({ kind: "addProperty", index: index2, property }); } function removeProperty(entry, index2) { const target = targetFor(entry, index2); set(autoProperties, get2(autoProperties).filter((candidate) => candidate !== entry), true); persist({ kind: "removeProperty", target }); } function removeChoice(entry, propertyIndex, choiceIndex) { const value = entry.choiceTargets[choiceIndex] ?? entry.property.choices[choiceIndex] ?? ""; entry.property.choices = entry.property.choices.filter((_, index2) => index2 !== choiceIndex); entry.choiceTargets = entry.choiceTargets.filter((_, index2) => index2 !== choiceIndex); persist({ kind: "removeChoice", target: targetFor(entry, propertyIndex), index: choiceIndex, value }); } function addChoice(entry, propertyIndex) { const index2 = entry.property.choices.length; entry.property.choices = [...entry.property.choices, ""]; entry.choiceTargets = [...entry.choiceTargets, ""]; persist({ kind: "addChoice", target: targetFor(entry, propertyIndex), index: index2, value: "" }); } function pasteChoices(entry, propertyIndex, index2, event2) { const tokens = splitPastedChoices(event2.clipboardData?.getData("text") ?? ""); if (tokens.length < 2) return; event2.preventDefault(); const previousValue = entry.choiceTargets[index2] ?? entry.property.choices[index2] ?? ""; entry.property.choices = withChoicesPasted(entry.property.choices, index2, tokens); entry.choiceTargets = withChoicesPasted(entry.choiceTargets, index2, tokens); persist({ kind: "replaceChoiceWithChoices", target: targetFor(entry, propertyIndex), index: index2, previousValue, values: tokens }); } function setName(entry, index2, value) { const target = targetFor(entry, index2); entry.property.name = value; entry.targetName = value; persist({ kind: "setName", target, value }); } function setDescription(entry, index2, value) { entry.property.description = value; persist({ kind: "setDescription", target: targetFor(entry, index2), value }); } function setType(entry, index2, value) { const type = value; entry.property.type = type; persist({ kind: "setType", target: targetFor(entry, index2), value: type }); } function setChoice(entry, propertyIndex, choiceIndex, value) { const previousValue = entry.choiceTargets[choiceIndex] ?? entry.property.choices[choiceIndex] ?? ""; entry.property.choices[choiceIndex] = value; entry.choiceTargets[choiceIndex] = value; persist({ kind: "setChoice", target: targetFor(entry, propertyIndex), index: choiceIndex, previousValue, value }); } var div = root_4(); var node_1 = child(div); { var consequent = ($$anchor2) => { var p = root2(); append($$anchor2, p); }; if_block(node_1, ($$render) => { if (get2(autoProperties).length === 0) $$render(consequent); }); } var node_2 = sibling(node_1, 2); each(node_2, 19, () => get2(autoProperties), (entry) => entry.id, ($$anchor2, entry, propertyIndex) => { const property = user_derived(() => get2(entry).property); var div_1 = root_3(); var div_2 = child(div_1); var input = child(div_2); remove_input_defaults(input); var select = sibling(input, 2); each(select, 20, () => types, (t) => t, ($$anchor3, t) => { var option = root_12(); var text2 = child(option, true); reset(option); var option_value = {}; template_effect(() => { set_selected(option, (get2(property).type ?? "Single") === t); set_text(text2, t); if (option_value !== (option_value = t)) { option.value = (option.__value = t) ?? ""; } }); append($$anchor3, option); }); reset(select); var button = sibling(select, 2); var span = child(button); action(span, ($$node, $$action_arg) => icon?.($$node, $$action_arg), () => "trash-2"); reset(button); reset(div_2); var input_1 = sibling(div_2, 2); remove_input_defaults(input_1); var div_3 = sibling(input_1, 2); var node_3 = sibling(child(div_3), 2); each(node_3, 16, () => indexes(get2(property).choices), (i) => i, ($$anchor3, i) => { var div_4 = root_22(); var input_2 = child(div_4); remove_input_defaults(input_2); var button_1 = sibling(input_2, 2); var span_1 = child(button_1); action(span_1, ($$node, $$action_arg) => icon?.($$node, $$action_arg), () => "x"); reset(button_1); reset(div_4); template_effect(() => set_value(input_2, get2(property).choices[i])); delegated("input", input_2, (e) => get2(property).choices[i] = e.currentTarget.value); delegated("change", input_2, (e) => setChoice(get2(entry), get2(propertyIndex), i, e.currentTarget.value)); event("paste", input_2, (e) => pasteChoices(get2(entry), get2(propertyIndex), i, e)); delegated("click", button_1, () => removeChoice(get2(entry), get2(propertyIndex), i)); append($$anchor3, div_4); }); var button_2 = sibling(node_3, 2); var span_2 = child(button_2); action(span_2, ($$node, $$action_arg) => icon?.($$node, $$action_arg), () => "plus"); next(); reset(button_2); reset(div_3); reset(div_1); template_effect(() => { set_value(input, get2(property).name); set_value(input_1, get2(property).description ?? ""); }); delegated("input", input, (e) => get2(property).name = e.currentTarget.value); delegated("change", input, (e) => setName(get2(entry), get2(propertyIndex), e.currentTarget.value)); delegated("change", select, (e) => setType(get2(entry), get2(propertyIndex), e.currentTarget.value)); delegated("click", button, () => removeProperty(get2(entry), get2(propertyIndex))); delegated("input", input_1, (e) => get2(property).description = e.currentTarget.value); delegated("change", input_1, (e) => setDescription(get2(entry), get2(propertyIndex), e.currentTarget.value)); delegated("click", button_2, () => addChoice(get2(entry), get2(propertyIndex))); append($$anchor2, div_1); }); var div_5 = sibling(node_2, 2); var button_3 = child(div_5); reset(div_5); reset(div); delegated("click", button_3, addNewProperty); append($$anchor, div); pop(); } delegate(["input", "change", "click"]); // src/Modals/KanbanHelperSetting/KanbanHelperSettingSuggester.ts var import_obsidian2 = require("obsidian"); var KanbanHelperSettingSuggester = class extends import_obsidian2.AbstractInputSuggest { constructor(app, inputEl, boards) { super(app, inputEl); this.inputEl = inputEl; this.boards = boards; this.onSelect((item) => this.acceptSuggestion(item)); } getSuggestions(inputStr) { const inputLowerCase = inputStr.toLowerCase(); return this.boards.filter((board) => board.basename.toLowerCase().includes(inputLowerCase)); } acceptSuggestion(item) { this.setValue(item.basename); this.inputEl.trigger("input"); this.close(); } renderSuggestion(value, el) { if (value) el.setText(value.basename); } }; // src/logger/logManager.ts var _LogManager = class _LogManager { register(logger) { _LogManager.loggers.push(logger); return this; } logError(message) { _LogManager.loggers.forEach((logger) => logger.logError(message)); throw new Error(); } logWarning(message) { _LogManager.loggers.forEach((logger) => logger.logError(message)); } logMessage(message) { _LogManager.loggers.forEach((logger) => logger.logMessage(message)); } }; _LogManager.loggers = []; var LogManager = _LogManager; var log = new LogManager(); // src/Modals/KanbanHelperSetting/KanbanHelperSettingContent.svelte var root3 = from_html(` `); var root_13 = from_html(`
BoardProperty in linkPossible values
`); var $$css3 = { hash: "svelte-pptp52", code: ".buttonContainer.svelte-pptp52 {display:flex;justify-content:center;margin-top:1rem;}button.svelte-pptp52 {margin-left:5px;margin-right:5px;font-size:15px;}" }; function KanbanHelperSettingContent($$anchor, $$props) { push($$props, true); append_styles($$anchor, $$css3); let initialKanbanProperties = prop($$props, "kanbanProperties", 19, () => []); const initialApp = untrack(() => $$props.app); const initialBoards = untrack(() => $$props.boards); let kanbanProperties = state(proxy(untrack(() => initialKanbanProperties().map((property) => ({ ...property }))))); let suggestEl = state(void 0); let suggester; let inputValue = state(""); let didInitialise = false; user_effect(() => { const el = get2(suggestEl); if (!el || didInitialise) return; didInitialise = true; suggester = new KanbanHelperSettingSuggester(initialApp, el, initialBoards); return () => { suggester?.close(); suggester = void 0; }; }); function saveProperties() { $$props.save(snapshot(get2(kanbanProperties))); } function addNewProperty() { const board = initialBoards.find((board2) => board2.basename === get2(inputValue)); if (!board) return; const exists = !!get2(kanbanProperties).find((kp) => kp.boardName === board.basename); if (exists) return; set( kanbanProperties, [ ...get2(kanbanProperties), { property: "", boardName: board.basename } ], true ); saveProperties(); } function removeProperty(i) { set(kanbanProperties, get2(kanbanProperties).filter((_, index2) => index2 !== i), true); saveProperties(); } function getHeadingsInBoard(boardName) { const file = initialBoards.find((board) => board.basename === boardName); if (!file) { log.logWarning(`file ${boardName} not found.`); return "FILE NOT FOUND"; } const headings = initialApp.metadataCache.getFileCache(file)?.headings; if (!headings) return ""; return headings.map((heading) => heading.heading).join(", "); } var div = root_13(); var table = child(div); var tbody = sibling(child(table)); each(tbody, 23, () => get2(kanbanProperties), (kanbanProperty) => kanbanProperty.boardName, ($$anchor2, kanbanProperty, i) => { var tr = root3(); var td = child(tr); var input = child(td); reset(td); var td_1 = sibling(td); var text2 = child(td_1, true); reset(td_1); var td_2 = sibling(td_1); var input_1 = child(td_2); remove_input_defaults(input_1); reset(td_2); var td_3 = sibling(td_2); var text_1 = child(td_3, true); reset(td_3); reset(tr); template_effect( ($0) => { set_text(text2, get2(kanbanProperty).boardName); set_text(text_1, $0); }, [() => getHeadingsInBoard(get2(kanbanProperty).boardName)] ); delegated("click", input, () => removeProperty(get2(i))); delegated("change", input_1, saveProperties); bind_value(input_1, () => get2(kanbanProperty).property, ($$value) => get2(kanbanProperty).property = $$value); append($$anchor2, tr); }); reset(tbody); reset(table); var input_2 = sibling(table, 2); remove_input_defaults(input_2); bind_this(input_2, ($$value) => set(suggestEl, $$value), () => get2(suggestEl)); var div_1 = sibling(input_2, 2); var button = child(div_1); reset(div_1); reset(div); bind_value(input_2, () => get2(inputValue), ($$value) => set(inputValue, $$value)); delegated("click", button, addNewProperty); append($$anchor, div); pop(); } delegate(["click", "change"]); // src/Modals/shared/SingleValueTableEditorContent.svelte var root4 = from_html(``); var root_14 = from_html(`
Property
`); var $$css4 = { hash: "svelte-158j4ih", code: ".buttonContainer.svelte-158j4ih {display:flex;justify-content:center;margin-top:1rem;}button.svelte-158j4ih {margin-left:5px;margin-right:5px;font-size:15px;}" }; function SingleValueTableEditorContent($$anchor, $$props) { push($$props, true); append_styles($$anchor, $$css4); let initialProperties = prop($$props, "properties", 19, () => []); let properties = state(proxy(untrack(() => [...initialProperties()]))); let propertyIndexes = user_derived(() => get2(properties).map((_, i) => i)); function saveProperties() { $$props.save(snapshot(get2(properties))); } function addNewProperty() { set(properties, [...get2(properties), ""], true); saveProperties(); } function removeProperty(i) { set(properties, get2(properties).filter((_, index2) => index2 !== i), true); saveProperties(); } var div = root_14(); var table = child(div); var tbody = sibling(child(table)); each(tbody, 20, () => get2(propertyIndexes), (i) => i, ($$anchor2, i) => { var tr = root4(); var td = child(tr); var input = child(td); reset(td); var td_1 = sibling(td); var input_1 = child(td_1); remove_input_defaults(input_1); reset(td_1); reset(tr); delegated("click", input, () => removeProperty(i)); delegated("change", input_1, saveProperties); bind_value(input_1, () => get2(properties)[i], ($$value) => get2(properties)[i] = $$value); append($$anchor2, tr); }); reset(tbody); reset(table); var div_1 = sibling(table, 2); var button = child(div_1); reset(div_1); reset(div); delegated("click", button, addNewProperty); append($$anchor, div); pop(); } delegate(["click", "change"]); // src/svelteMount.ts function mountSvelteComponent(component2, target, props) { return mount( component2, { target, props } ); } function unmountSvelteComponent(component2) { if (!component2) return; void unmount(component2); } // src/Settings/metaEditSettingsTab.ts function toggleHiddenEl(el, hidden) { if (!el) return hidden; el.classList.toggle("metaedit-hidden", !hidden); return !hidden; } var MetaEditSettingsTab = class extends import_obsidian3.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.svelteElements = []; this.plugin = plugin; } display() { const { containerEl } = this; this.destroySvelteElements(); containerEl.empty(); new import_obsidian3.Setting(containerEl).setName("MetaEdit Settings").setHeading(); this.addProgressPropertiesSetting(containerEl); this.addAutoPropertiesSetting(containerEl); this.addEditMetaMenuSetting(containerEl); this.addEditModeSetting(containerEl); this.addKanbanHelperSetting(containerEl); this.addUIElementsSetting(containerEl); } addProgressPropertiesSetting(containerEl) { let hidden = true; const setting = new import_obsidian3.Setting(containerEl).setName("Progress Properties").setDesc("Update properties automatically.").addToggle((toggle) => { toggle.setTooltip("Toggle Progress Properties").setValue(this.plugin.settings.ProgressProperties.enabled).onChange(async (value) => { if (value === this.plugin.settings.ProgressProperties.enabled) return; this.plugin.settings.ProgressProperties.enabled = value; this.plugin.toggleAutomators(); await this.plugin.saveSettings(); }); }); const div = setting.settingEl.createDiv({ cls: "metaedit-hidden" }); setting.settingEl.classList.add("metaedit-setting-with-details"); setting.addExtraButton((button) => button.onClick(() => hidden = toggleHiddenEl(div, hidden))); const modal = mountSvelteComponent( ProgressPropertiesModalContent, div, { properties: this.plugin.settings.ProgressProperties.properties, save: async (progressProperties) => { this.plugin.settings.ProgressProperties.properties = progressProperties; await this.plugin.saveSettings(); } } ); this.svelteElements.push(modal); } addAutoPropertiesSetting(containerEl) { let hidden = true; const setting = new import_obsidian3.Setting(containerEl).setName("Auto Properties").setDesc("Quick switch for values you know the value of.").addToggle((toggle) => { toggle.setTooltip("Toggle Auto Properties").setValue(this.plugin.settings.AutoProperties.enabled).onChange(async (value) => { if (value === this.plugin.settings.AutoProperties.enabled) return; this.plugin.settings.AutoProperties.enabled = value; await this.plugin.saveSettings(); }); }); const div = setting.settingEl.createDiv({ cls: "metaedit-hidden" }); setting.settingEl.classList.add("metaedit-setting-with-details"); setting.addExtraButton((b) => b.onClick(() => hidden = toggleHiddenEl(div, hidden))); const modal = mountSvelteComponent( AutoPropertiesModalContent, div, { autoProperties: this.plugin.settings.AutoProperties.properties, save: (operation) => this.saveAutoPropertiesOperation(operation) } ); this.svelteElements.push(modal); } async saveAutoPropertiesOperation(operation) { try { await this.plugin.updateSettings(() => { const previousAutoProperties = this.plugin.settings.AutoProperties.properties; const nextAutoProperties = applyAutoPropertySettingsOperation(previousAutoProperties, operation); if (nextAutoProperties === false) return false; this.plugin.settings.AutoProperties.properties = nextAutoProperties; return () => { if (this.plugin.settings.AutoProperties.properties === nextAutoProperties) { this.plugin.settings.AutoProperties.properties = previousAutoProperties; } }; }); } catch (error) { const reason = error instanceof Error ? error.message : String(error); new import_obsidian3.Notice(`MetaEdit could not save the Auto Properties setting: ${reason}`); } } addEditMetaMenuSetting(containerEl) { let hidden = true; const setting = new import_obsidian3.Setting(containerEl).setName("Edit Meta menu").setDesc("Control what the 'Edit Meta' menu lists. Enable it, then use the gear to hide specific properties by name or all of a note's file tags.").addToggle((toggle) => { toggle.setTooltip("Toggle menu filtering").setValue(this.plugin.settings.IgnoredProperties.enabled).onChange(async (value) => { if (value === this.plugin.settings.IgnoredProperties.enabled) return; this.plugin.settings.IgnoredProperties.enabled = value; await this.plugin.saveSettings(); this.display(); }); }); const div = this.plugin.settings.IgnoredProperties.enabled ? setting.settingEl.createDiv({ cls: "metaedit-hidden" }) : void 0; if (div) setting.settingEl.classList.add("metaedit-setting-with-details"); setting.addExtraButton((b) => b.onClick(() => hidden = toggleHiddenEl(div, hidden))); if (div) { new import_obsidian3.Setting(div).setName("Hide file tags").setDesc("Hide the note's #tags from the menu, leaving only frontmatter and inline fields. A frontmatter 'tags' property stays editable.").addToggle((toggle) => { toggle.setTooltip("Toggle hiding file tags").setValue(this.plugin.settings.IgnoredProperties.hideFileTags).onChange(async (value) => { if (value === this.plugin.settings.IgnoredProperties.hideFileTags) return; this.plugin.settings.IgnoredProperties.hideFileTags = value; await this.plugin.saveSettings(); }); }); div.createEl("p", { text: "Hide specific properties by name:", cls: "metaedit-table-label" }); const modal = mountSvelteComponent( SingleValueTableEditorContent, div, { properties: this.plugin.settings.IgnoredProperties.properties, save: async (ignoredProperties) => { this.plugin.settings.IgnoredProperties.properties = ignoredProperties; await this.plugin.saveSettings(); } } ); this.svelteElements.push(modal); } } addEditModeSetting(containerEl) { let bDivToggle = true; let extraButtonEl; const df = new DocumentFragment(); df.createEl("p", { text: "Single: property values are just one value. " }); df.createEl("p", { text: "Multi: properties are arrays. " }); df.createEl("p", { text: "Some Multi: all options are single, except those specified in the settings (click button)." }); const setting = new import_obsidian3.Setting(containerEl).setName("Edit Mode").setDesc(df); const div = setting.settingEl.createDiv({ cls: "metaedit-hidden" }); setting.settingEl.classList.add("metaedit-setting-with-details"); setting.addDropdown((dropdown) => { dropdown.addOption("All Single" /* AllSingle */, "All Single" /* AllSingle */).addOption("All Multi" /* AllMulti */, "All Multi" /* AllMulti */).addOption("Some Multi" /* SomeMulti */, "Some Multi" /* SomeMulti */).setValue(this.plugin.settings.EditMode.mode).onChange(async (value) => { switch (value) { case "All Multi" /* AllMulti */: this.plugin.settings.EditMode.mode = "All Multi" /* AllMulti */; toggleHiddenEl(extraButtonEl, false); bDivToggle = toggleHiddenEl(div, false); break; case "All Single" /* AllSingle */: this.plugin.settings.EditMode.mode = "All Single" /* AllSingle */; toggleHiddenEl(extraButtonEl, false); bDivToggle = toggleHiddenEl(div, false); break; case "Some Multi" /* SomeMulti */: this.plugin.settings.EditMode.mode = "Some Multi" /* SomeMulti */; toggleHiddenEl(extraButtonEl, true); break; } await this.plugin.saveSettings(); }); }).addExtraButton((b) => { extraButtonEl = b.extraSettingsEl; b.setTooltip("Configure which properties are Multi."); return b.onClick(() => bDivToggle = toggleHiddenEl(div, bDivToggle)); }); if (this.plugin.settings.EditMode.mode != "Some Multi" /* SomeMulti */) { toggleHiddenEl(extraButtonEl, false); } const modal = mountSvelteComponent( SingleValueTableEditorContent, div, { properties: this.plugin.settings.EditMode.properties, save: async (properties) => { this.plugin.settings.EditMode.properties = properties; await this.plugin.saveSettings(); } } ); this.svelteElements.push(modal); } hide() { this.destroySvelteElements(); return super.hide(); } destroySvelteElements() { this.svelteElements.forEach(unmountSvelteComponent); this.svelteElements = []; } addKanbanHelperSetting(containerEl) { let hidden = true; const setting = new import_obsidian3.Setting(containerEl).setName("Kanban Board Helper").setDesc("Update properties in links in kanban boards automatically when a card is moved to a new lane.").addToggle((toggle) => { toggle.setTooltip("Toggle Kanban Helper").setValue(this.plugin.settings.KanbanHelper.enabled).onChange(async (value) => { if (value === this.plugin.settings.KanbanHelper.enabled) return; this.plugin.settings.KanbanHelper.enabled = value; this.plugin.toggleAutomators(); await this.plugin.saveSettings(); }); }); const div = setting.settingEl.createDiv({ cls: "metaedit-hidden" }); setting.settingEl.classList.add("metaedit-setting-with-details"); setting.addExtraButton((button) => button.onClick(() => hidden = toggleHiddenEl(div, hidden))); const modal = mountSvelteComponent( KanbanHelperSettingContent, div, { kanbanProperties: this.plugin.settings.KanbanHelper.boards, boards: this.plugin.getFilesWithProperty("kanban-plugin"), app: this.app, save: async (kanbanProperties) => { this.plugin.settings.KanbanHelper.boards = kanbanProperties; await this.plugin.saveSettings(); } } ); this.svelteElements.push(modal); } addUIElementsSetting(containerEl) { new import_obsidian3.Setting(containerEl).setName("UI Elements").setDesc("Toggle UI elements: the 'Edit Meta' right-click menu option.").addToggle((toggle) => { toggle.setTooltip("Toggle UI elements").setValue(this.plugin.settings.UIElements.enabled).onChange(async (value) => { if (value === this.plugin.settings.UIElements.enabled) return; this.plugin.settings.UIElements.enabled = value; if (value) { this.plugin.linkMenu.registerEvent(); } else { this.plugin.linkMenu.unregisterEvent(); } await this.plugin.saveSettings(); }); }); } }; // src/Modals/metaEditSuggester.ts var import_obsidian4 = require("obsidian"); // src/constants.ts var newDataView = "New Dataview field"; var newYaml = "New YAML property"; var MAIN_SUGGESTER_OPTIONS = [ { key: newYaml, content: newYaml, type: 3 /* Option */ }, { key: newDataView, content: newDataView, type: 3 /* Option */ } ]; // src/Modals/GenericPrompt/valueSuggest.ts var ISO_DATE = /^\d{4}-\d{2}-\d{2}$/; var ISO_DATETIME = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/; var NATIVE_BUILT_IN_PROPERTY_NAMES = Object.freeze([ "aliases", "cssclass", "cssclasses", "publish", "tags" ]); var MAX_RENDERED_SUGGESTIONS = 100; function getValueSuggestions(app, key2, type, tagMode) { if (!key2) return []; try { if (type === 2 /* Tag */) { return tagMode === "leaf" ? rankByCount(collectTagLeafCounts(app)) : rankByCount(collectTagFullCounts(app)); } if (type === 0 /* YAML */) { return rankByCount(collectFrontmatterValueCounts(app, key2)); } return []; } catch { return []; } } function getDateInputType(app, key2, currentValue, type) { if (type !== 0 /* YAML */ || !key2) return null; let obsidianType; try { obsidianType = readObsidianType(app, key2); } catch { return null; } if (obsidianType !== "date" && obsidianType !== "datetime") return null; const value = currentValue === null || currentValue === void 0 ? "" : String(currentValue).trim(); if (value === "") return obsidianType; if (obsidianType === "date" && ISO_DATE.test(value)) return "date"; if (obsidianType === "datetime" && ISO_DATETIME.test(value)) return "datetime"; return null; } function getKnownPropertyNames(app) { const names = /* @__PURE__ */ new Set(); for (const name of NATIVE_BUILT_IN_PROPERTY_NAMES) names.add(name); try { const all = getMetadataTypeManager(app)?.getAllProperties?.(); if (!all) return [...names]; for (const [key2, info] of Object.entries(all)) { addPropertyName(names, key2); addPropertyName(names, info?.name); } } catch { return [...names]; } return [...names]; } function filterSuggestions(items, inputStr) { const query = inputStr.toLowerCase(); const filtered = items.filter((item) => item.toLowerCase().includes(query)); if (filtered.length === 0) return []; if (filtered.length === 1 && filtered[0] === inputStr) return []; return filtered.slice(0, MAX_RENDERED_SUGGESTIONS); } function collectTagLeafCounts(app) { const counts = /* @__PURE__ */ new Map(); for (const [tag2, count] of Object.entries(readVaultTags(app))) { const leaf = tag2.replace(/^#/, "").split("/").pop()?.trim(); if (!leaf) continue; counts.set(leaf, (counts.get(leaf) ?? 0) + (count ?? 1)); } return counts; } function collectTagFullCounts(app) { const counts = /* @__PURE__ */ new Map(); for (const [tag2, count] of Object.entries(readVaultTags(app))) { const full = tag2.replace(/^#/, "").trim(); if (!full) continue; counts.set(full, (counts.get(full) ?? 0) + (count ?? 1)); } return counts; } function readVaultTags(app) { const metadataCache = app.metadataCache; return metadataCache.getTags?.() ?? {}; } function collectFrontmatterValueCounts(app, key2) { const counts = /* @__PURE__ */ new Map(); for (const file of app.vault.getMarkdownFiles()) { const frontmatter = app.metadataCache.getFileCache(file)?.frontmatter; if (!frontmatter || !Object.prototype.hasOwnProperty.call(frontmatter, key2)) continue; for (const value of flattenValues(frontmatter[key2])) { counts.set(value, (counts.get(value) ?? 0) + 1); } } return counts; } function flattenValues(raw) { const values = Array.isArray(raw) ? raw : [raw]; const out = []; for (const value of values) { if (value === null || value === void 0) continue; if (typeof value === "object") continue; const text2 = String(value).trim(); if (text2) out.push(text2); } return out; } function rankByCount(counts) { return [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).map(([value]) => value); } function readObsidianType(app, key2) { const typeManager = getMetadataTypeManager(app); if (!typeManager) return null; const assigned = typeManager.getAssignedWidget?.(key2); if (typeof assigned === "string") return assigned; const registered = typeManager.getAllProperties?.()?.[key2]?.widget; if (typeof registered === "string") return registered; const inferred = typeManager.getTypeInfo?.(key2)?.expected?.type; return typeof inferred === "string" ? inferred : null; } function addPropertyName(names, value) { if (typeof value !== "string") return; const trimmed = value.trim(); if (trimmed) names.add(trimmed); } function getMetadataTypeManager(app) { return app.metadataTypeManager ?? null; } // src/Modals/GenericPrompt/promptValueContext.ts var pending2 = null; function setPendingValueContext(context) { pending2 = context; } function consumePendingValueContext() { const context = pending2; pending2 = null; return context; } // src/Modals/menuFilter.ts function filterMenuItems(data, opts) { if (!opts.enabled) return [...data]; const ignored = new Set(opts.ignoredProperties); return data.filter((item) => { if (ignored.has(item.key)) return false; if (opts.hideFileTags && item.type === 2 /* Tag */) return false; return true; }); } function canStructureEditProperty(property) { if (property.type === 2 /* Tag */) return false; return !property.isNested && !property.isVirtual; } // src/yamlPath.ts var YamlPathError = class extends Error { constructor(message) { super(message); this.name = "YamlPathError"; } }; function isReservedFrontmatterKey(key2) { return key2 === "__proto__" || key2 === "constructor" || key2 === "prototype"; } function parseYamlPath(path) { if (Array.isArray(path)) return validateYamlPath(path); if (typeof path !== "string") throw new YamlPathError("YAML path must be a string or path segment array."); const trimmed = path.trim(); if (!trimmed) throw new YamlPathError("YAML path cannot be empty."); const segments = []; for (const part of trimmed.split(".")) { if (!part) throw new YamlPathError(`Invalid YAML path '${path}'. Empty path segments are not supported.`); readPathPart(part, path, segments); } return validateYamlPath(segments); } function formatYamlPath(path) { const validated = validateYamlPath(path); return validated.reduce((label, segment) => { if (typeof segment === "number") return `${label}[${segment}]`; return label ? `${label}.${segment}` : segment; }, ""); } function getYamlPath(root7, path) { const resolvedPath = parseYamlPath(path); let current = root7; for (const segment of resolvedPath) { current = readYamlPathSegment(current, segment, formatYamlPath(resolvedPath)); } return current; } function setYamlPath(root7, path, value, options = {}) { const resolvedPath = parseYamlPath(path); for (const segment of resolvedPath) { if (typeof segment === "string" && isReservedFrontmatterKey(segment)) { throw new YamlPathError( `Cannot write YAML path '${formatYamlPath(resolvedPath)}': '${segment}' is a reserved property name.` ); } } let current = root7; for (let idx = 0; idx < resolvedPath.length; idx++) { const segment = resolvedPath[idx]; const isLeaf = idx === resolvedPath.length - 1; const location = formatYamlPath(resolvedPath.slice(0, idx + 1)); if (typeof segment === "string") { if (!isPlainYamlObject(current)) { throw new YamlPathError(`Cannot write YAML path '${formatYamlPath(resolvedPath)}': '${location}' is not an object.`); } if (isLeaf) { validateLeafWrite(current, segment, resolvedPath, options); current[segment] = value; return; } if (!Object.prototype.hasOwnProperty.call(current, segment)) { current[segment] = createMissingParent(options, resolvedPath[idx + 1], location); } current = current[segment]; continue; } if (!Array.isArray(current)) { throw new YamlPathError(`Cannot write YAML path '${formatYamlPath(resolvedPath)}': '${location}' is not an array.`); } if (!arrayIndexExists(current, segment)) { throw new YamlPathError(`Cannot write YAML path '${formatYamlPath(resolvedPath)}': array index ${segment} is out of range.`); } if (isLeaf) { validateLeafWrite(current, segment, resolvedPath, options); current[segment] = value; return; } current = current[segment]; } } function isPlainYamlObject(value) { if (!value || typeof value !== "object" || Array.isArray(value)) return false; return Object.prototype.toString.call(value) === "[object Object]"; } function isYamlScalarLeaf(value) { return !isPlainYamlObject(value) && !Array.isArray(value); } function isYamlParentContainerValue(value) { if (isPlainYamlObject(value)) return true; if (!Array.isArray(value)) return false; return value.some((item) => isPlainYamlObject(item) || Array.isArray(item)); } function readPathPart(part, originalPath, segments) { const keyMatch = part.match(/^([^\[\]]+)/); if (!keyMatch) throw new YamlPathError(`Invalid YAML path '${originalPath}'. Bracket paths must follow a property name.`); segments.push(keyMatch[1]); let rest = part.substring(keyMatch[1].length); while (rest.length > 0) { const indexMatch = rest.match(/^\[(\d+)\]/); if (!indexMatch) throw new YamlPathError(`Invalid YAML path '${originalPath}'. Only numeric array indexes are supported.`); segments.push(Number(indexMatch[1])); rest = rest.substring(indexMatch[0].length); } } function validateYamlPath(path) { if (path.length === 0) throw new YamlPathError("YAML path cannot be empty."); return path.map((segment) => { if (typeof segment === "string") { if (!segment) throw new YamlPathError("YAML path string segments cannot be empty."); return segment; } if (!Number.isInteger(segment) || segment < 0) { throw new YamlPathError(`YAML path array index '${segment}' must be a non-negative integer.`); } return segment; }); } function readYamlPathSegment(current, segment, pathLabel) { if (typeof segment === "string") { if (!isPlainYamlObject(current) || !Object.prototype.hasOwnProperty.call(current, segment)) { throw new YamlPathError(`YAML path '${pathLabel}' does not exist.`); } return current[segment]; } if (!Array.isArray(current) || !arrayIndexExists(current, segment)) { throw new YamlPathError(`YAML path '${pathLabel}' does not exist.`); } return current[segment]; } function createMissingParent(options, nextSegment, location) { if (!options.createParents) { throw new YamlPathError(`Cannot write YAML path: '${location}' does not exist.`); } if (typeof nextSegment === "number") { throw new YamlPathError(`Cannot create array parent at '${location}'. Array creation is not supported.`); } return {}; } function arrayIndexExists(array, index2) { return index2 >= 0 && index2 < array.length && Object.prototype.hasOwnProperty.call(array, index2); } function validateLeafWrite(current, segment, path, options) { const exists = typeof segment === "number" ? arrayIndexExists(current, segment) : Object.prototype.hasOwnProperty.call(current, segment); const label = formatYamlPath(path); if (!exists && options.createLeaf === false) { throw new YamlPathError(`Cannot write YAML path '${label}': path does not exist.`); } const currentValue = typeof segment === "number" ? current[segment] : current[segment]; if (exists && options.validateExpectedValue && !yamlValuesEqual(currentValue, options.expectedValue)) { throw new YamlPathError(`Cannot write YAML path '${label}': current value changed before update.`); } } function yamlValuesEqual(left, right) { if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime(); return Object.is(left, right); } // src/Modals/metaEditSuggester.ts var DELETE_PROPERTY_ICON = "trash-2"; var TRANSFORM_PROPERTY_ICON = "replace"; var DELETE_PROPERTY_TOOLTIP = "Delete property"; var TRANSFORM_PROPERTY_TOOLTIP = "Transform to YAML \u21C4 Dataview"; var MetaEditSuggester = class _MetaEditSuggester extends import_obsidian4.FuzzySuggestModal { constructor(app, plugin, data, file, controller) { super(app); this.file = file; this.app = app; this.plugin = plugin; this.fileKeys = new Set(data.map((prop2) => prop2.key)); const ignored = plugin.settings.IgnoredProperties; this.data = filterMenuItems(data, { enabled: ignored.enabled, ignoredProperties: ignored.properties, hideFileTags: ignored.hideFileTags }).filter((item) => !_MetaEditSuggester.isYamlParentContainer(item)); this.controller = controller; this.options = MAIN_SUGGESTER_OPTIONS; this.setSuggestValues(); this.setInstructions([ { command: "#tag", purpose: "rename in this note \xB7 vault-wide: Tag pane" } ]); } renderSuggestion(item, el) { super.renderSuggestion(item, el); if (Object.values(this.options).find((v) => v === item.item)) { el.classList.add("metaedit-suggester-command"); } else { if (_MetaEditSuggester.canStructureEdit(item.item)) { this.createButton(el, DELETE_PROPERTY_ICON, DELETE_PROPERTY_TOOLTIP, this.deleteItem(item)); this.createButton(el, TRANSFORM_PROPERTY_ICON, TRANSFORM_PROPERTY_TOOLTIP, this.transformProperty(item)); } } } getItemText(item) { if (item.type === 2 /* Tag */ && item.position) { const sameKey = this.data.filter((d) => d.type === 2 /* Tag */ && d.key === item.key && d.position).sort((a, b) => a.position.start - b.position.start); if (sameKey.length > 1) { const ordinal = sameKey.findIndex((d) => d.position.start === item.position.start) + 1; const line = item.position.line !== void 0 ? `line ${item.position.line + 1}, ` : ""; return `${item.key} (${line}${ordinal}/${sameKey.length})`; } } return item.key; } getItems() { return [...this.options, ...this.data]; } async onChooseItem(item, _evt) { if (item.content === newYaml) { await this.controller.createNewYamlPropertyFluid(this.file, this.suggestValues, this.fileKeys); return; } if (item.content === newDataView) { const newProperty = await this.controller.createNewProperty(this.suggestValues); if (!newProperty) return null; const { propName, propValue } = newProperty; await this.controller.appendDataviewField(propName, propValue, this.file); return; } if (_MetaEditSuggester.isYamlParentContainer(item)) return; setPendingValueContext({ app: this.app, key: item.key, type: item.type }); try { await this.controller.editMetaElement(item, this.data, this.file); } finally { setPendingValueContext(null); } } deleteItem(item) { return async (evt) => { evt.stopPropagation(); try { await this.controller.deleteProperty(item.item, this.file); } finally { this.close(); } }; } transformProperty(item) { return async (evt) => { evt.stopPropagation(); const { item: property } = item; if (!_MetaEditSuggester.canStructureEdit(property)) return; if (property.type !== 0 /* YAML */ && isReservedFrontmatterKey(property.key)) { new import_obsidian4.Notice(`MetaEdit: "${property.key}" is a reserved property name and can't be a YAML property.`); this.close(); return; } try { if (property.type === 0 /* YAML */) { await this.toDataview(property); } else { await this.toYaml(property); } } catch (error) { const reason = error instanceof Error ? error.message : String(error); new import_obsidian4.Notice(`MetaEdit could not transform '${property.key}': ${reason}. It may have been removed - reopen the note to check.`); } finally { this.close(); } }; } async toYaml(property) { await this.controller.deleteProperty(property, this.file); await this.controller.addYamlProp(property.key, property.content, this.file); } async toDataview(property) { await this.controller.deleteProperty(property, this.file); await this.controller.appendDataviewField(property.key, property.content, this.file); } createButton(el, iconName, tooltip, callback) { const itemButton = el.createEl("button"); itemButton.type = "button"; itemButton.classList.add("clickable-icon", "metaedit-suggester-action-button"); itemButton.setAttribute("aria-label", tooltip); (0, import_obsidian4.setIcon)(itemButton, iconName); (0, import_obsidian4.setTooltip)(itemButton, tooltip); itemButton.addEventListener("click", callback); } static canStructureEdit(property) { return canStructureEditProperty(property); } static isYamlParentContainer(property) { if (property.type !== 0 /* YAML */ || property.isVirtual) return false; return isYamlParentContainerValue(property.content); } setSuggestValues() { const existing = this.fileKeys; const names = /* @__PURE__ */ new Set(); for (const autoProp of this.plugin.settings.AutoProperties.properties) { if (!autoProp.name.startsWith("#")) names.add(autoProp.name); } for (const name of getKnownPropertyNames(this.app)) names.add(name); this.suggestValues = [...names].filter((name) => !existing.has(name)); } }; // src/parser.ts var import_obsidian5 = require("obsidian"); var INLINE_FIELD_WRAPPERS = Object.freeze({ "[": "]", "(": ")" }); var FULL_LINE_PREFIX = /^\s*(?:>\s+)*(?:[-*+]\s+|\d+[.)]\s+)?/; var CODE_FENCE = /^\s{0,3}(`{3,}|~{3,})/; function matchFenceLine(line) { const match = line.match(CODE_FENCE); if (!match) return null; const run3 = match[1]; return { char: run3[0], len: run3.length, bare: line.slice(match[0].length).trim() === "" }; } function nextFenceState(openFence, line) { const fence = matchFenceLine(line); if (!fence) return { open: openFence, boundary: null }; if (openFence === null) return { open: { char: fence.char, len: fence.len }, boundary: "open" }; if (fence.char === openFence.char && fence.len >= openFence.len && fence.bare) { return { open: null, boundary: "close" }; } return { open: openFence, boundary: null }; } var MetaEditParser = class { constructor(app) { this.app = app; } async getTagsForFile(file) { const cache = this.app.metadataCache.getFileCache(file); if (!cache) return []; const tags = cache.tags; if (!tags) return []; return tags.map((tag2) => ({ key: tag2.tag, content: tag2.tag, type: 2 /* Tag */, position: { start: tag2.position.start.offset, end: tag2.position.end.offset, line: tag2.position.start.line } })); } async parseFrontmatter(file) { const frontmatter = await this.parseFrontmatterObject(file); if (!frontmatter) return []; return this.objectToYamlProperties(frontmatter); } parseFrontmatterCache(fileCache) { const frontmatter = fileCache?.frontmatter; if (!frontmatter) return []; return this.objectToYamlProperties(frontmatter, true); } async parseFrontmatterObject(file) { const fileCache = this.app.metadataCache.getFileCache(file); const filecontent = await this.app.vault.cachedRead(file); const parsedYaml = this.parseFrontmatterContent(filecontent); if (this.isFrontmatterObject(parsedYaml)) return parsedYaml; const frontmatter = fileCache?.frontmatter; if (this.isFrontmatterObject(frontmatter)) return this.omitInternalFrontmatterPosition(frontmatter); return null; } isFrontmatterObject(value) { return isPlainYamlObject(value); } omitInternalFrontmatterPosition(frontmatter) { if (!this.isFrontmatterPosition(frontmatter.position)) return frontmatter; const cleaned = { ...frontmatter }; delete cleaned.position; return cleaned; } isFrontmatterPosition(position) { if (!position || typeof position !== "object") return false; const candidate = position; return this.isPosition(candidate.start) && this.isPosition(candidate.end); } isPosition(position) { if (!position || typeof position !== "object") return false; const candidate = position; return typeof candidate.line === "number" && typeof candidate.col === "number" && typeof candidate.offset === "number"; } objectToYamlProperties(parsedYaml, omitInternalPosition = false) { if (!this.isFrontmatterObject(parsedYaml)) return []; const rootProperties = []; const nestedProperties = []; for (const key2 in parsedYaml) { const value = parsedYaml[key2]; if (omitInternalPosition && key2 === "position" && this.isFrontmatterPosition(value)) continue; rootProperties.push({ key: key2, content: value, type: 0 /* YAML */ }); this.collectNestedYamlProperties(value, [key2], nestedProperties); } return [...rootProperties, ...nestedProperties]; } collectNestedYamlProperties(value, path, properties) { if (isYamlScalarLeaf(value)) { if (path.length > 1 && typeof path[path.length - 1] !== "number") { const rootKey = String(path[0]); properties.push({ key: formatYamlPath(path), content: value, type: 0 /* YAML */, path: [...path], rootKey, isNested: true, isVirtual: true }); } return; } if (Array.isArray(value)) { value.forEach((item, idx) => this.collectNestedYamlProperties(item, [...path, idx], properties)); return; } if (isPlainYamlObject(value)) { for (const key2 in value) { this.collectNestedYamlProperties(value[key2], [...path, key2], properties); } } } async parseInlineFields(file) { const content = await this.app.vault.cachedRead(file); return this.parseInlineContent(content); } /** * Extract inline `key:: value` fields from raw note content. * * Mirrors Dataview's two-mode model: bracketed fields (`[k:: v]` / `(k:: v)`) * take precedence over a single "full-line" field, and a line yields one or * the other - never both. Keys are returned clean (without leading list or * blockquote markers) so callers can match and round-trip them; the * full-line key is otherwise kept verbatim so odd-but-real keys like * `progress (%)` survive a read/write cycle. */ parseInlineContent(content) { const properties = []; for (const line of this.walkInlineContentLines(content)) { if (!line.readable) continue; if (!line.text.includes("::")) continue; for (const field of this.parseLineFields(line.text)) { properties.push({ key: field.key, content: field.value, type: 1 /* Dataview */ }); } } return properties; } parseFrontmatterContent(content) { const frontmatterInfo = this.getFrontmatterInfo(content); if (!frontmatterInfo?.exists) return null; try { return (0, import_obsidian5.parseYaml)(frontmatterInfo.frontmatter); } catch { return null; } } getFrontmatterInfo(content) { const info = (0, import_obsidian5.getFrontMatterInfo)(content); const legacyDotInfo = this.getLegacyDotCloseFrontmatterInfo(content); if (legacyDotInfo && (!info.exists || legacyDotInfo.contentStart < info.contentStart)) return legacyDotInfo; if (info.exists) return info; return null; } getLegacyDotCloseFrontmatterInfo(content) { const lines = this.splitContentLines(content); if (!/^---\s*$/.test(lines[0]?.text ?? "")) return null; for (let i = 1; i < lines.length; i++) { if (!/^\.\.\.\s*$/.test(lines[i].text)) continue; const from = lines[0].nextStart; const to = lines[i].start; return { exists: true, frontmatter: content.slice(from, to), from, to, contentStart: lines[i].nextStart }; } return null; } splitContentLines(content) { const textLines = content.split(/\r?\n/); const lines = []; let offset = 0; for (let i = 0; i < textLines.length; i++) { const text2 = textLines[i]; const start = offset; const end = start + text2.length; let nextStart = end; if (i < textLines.length - 1) { nextStart += content.startsWith("\r\n", end) ? 2 : 1; } lines.push({ text: text2, start, end, nextStart }); offset = nextStart; } return lines; } *walkInlineContentLines(content) { const lines = this.splitContentLines(content); const frontmatterInfo = this.getFrontmatterInfo(content); let openFence = null; for (let index2 = 0; index2 < lines.length; index2++) { const line = lines[index2]; const inFrontmatter = frontmatterInfo?.exists === true && line.start < frontmatterInfo.contentStart; if (inFrontmatter) { yield { ...line, index: index2, inFrontmatter, fenceBoundary: null, readable: false }; continue; } const { open, boundary: boundary2 } = nextFenceState(openFence, line.text); openFence = open; yield { ...line, index: index2, inFrontmatter: false, fenceBoundary: boundary2, readable: boundary2 === null && openFence === null }; } } parseLineFields(line) { const bracketed = this.extractBracketedFields(line); if (bracketed.length > 0) return bracketed; const fullLine = this.extractFullLineField(line); return fullLine ? [fullLine] : []; } extractBracketedFields(line) { const fields = []; for (const open of Object.keys(INLINE_FIELD_WRAPPERS)) { let idx = line.indexOf(open); while (idx >= 0) { const field = this.findBracketedField(line, idx); if (!field) { idx = line.indexOf(open, idx + 1); continue; } fields.push(field); idx = line.indexOf(open, field.end); } } fields.sort((a, b) => a.start - b.start); const kept = []; for (const field of fields) { if (kept.length === 0 || kept[kept.length - 1].end <= field.start) { kept.push(field); } } return kept; } findBracketedField(line, start) { const open = line[start]; const close = INLINE_FIELD_WRAPPERS[open]; const sep = line.indexOf("::", start + 1); if (sep < 0) return null; const key2 = line.substring(start + 1, sep).trim(); if (!key2 || /[[\]()]/.test(key2)) return null; const closing = this.findClosingBracket(line, sep + 2, open, close); if (!closing) return null; return { key: key2, value: closing.value, start, end: closing.end, sepEnd: sep + 2, valueEnd: closing.end - 1 }; } findClosingBracket(line, start, open, close) { let nesting = 0; for (let i = start; i < line.length; i++) { const char = line[i]; if (char === open) { nesting++; } else if (char === close) { if (nesting === 0) { return { value: line.substring(start, i).trim(), end: i + 1 }; } nesting--; } } return null; } extractFullLineField(line) { const lineEnd = line.endsWith("\r") ? line.length - 1 : line.length; const prefix = line.match(FULL_LINE_PREFIX)?.[0] ?? ""; const body = line.slice(prefix.length, lineEnd); const sep = body.indexOf("::"); if (sep < 0) return null; const key2 = body.substring(0, sep).trim(); if (!key2) return null; const value = body.substring(sep + 2).trim(); const sepEnd = prefix.length + sep + 2; return { key: key2, value, start: prefix.length, end: lineEnd, sepEnd, valueEnd: lineEnd }; } /** * Rewrite the value of every inline field named `key` on a single line, * leaving the key, surrounding text, and any `[...]`/`(...)` wrapper intact. * * This is the write-side counterpart to {@link parseInlineContent}: because * it reuses the same field boundaries, a full-line value (which may itself * contain `]`/`)`, e.g. `ref:: [[Note]]`) is replaced up to end-of-line and a * bracketed value only up to its matching close bracket - so updates no * longer append a stray closing bracket. Returns the line unchanged when no * field matches. */ replaceInlineFieldValue(line, key2, newValue) { const matches = this.parseLineFields(line).filter((field) => field.key === key2); if (matches.length === 0) return line; let result = line; for (let i = matches.length - 1; i >= 0; i--) { const field = matches[i]; result = result.slice(0, field.sepEnd) + " " + newValue + result.slice(field.valueEnd); } return result; } /** * Rewrite inline field values in raw note content using the same readable-line * boundary as {@link parseInlineContent}. Only the changed line spans are * replaced; all untouched bytes, including mixed CRLF/LF line endings, are * preserved exactly. */ replaceInlineFieldValuesInContent(content, replacements) { const activeReplacements = replacements.filter(({ key: key2 }) => key2.length > 0); if (activeReplacements.length === 0) return content; let result = ""; let cursor = 0; for (const line of this.walkInlineContentLines(content)) { if (!line.readable) continue; if (!line.text.includes("::")) continue; let updatedLine = line.text; for (const { key: key2, value } of activeReplacements) { updatedLine = this.replaceInlineFieldValue(updatedLine, key2, value); } if (updatedLine === line.text) continue; result += content.slice(cursor, line.start) + updatedLine; cursor = line.end; } if (cursor === 0) return content; return result + content.slice(cursor); } /** * Compute the line index at which to splice a new `name:: value` inline field. * * This is the write-placement counterpart to {@link parseInlineContent}: it walks * the content with the SAME frontmatter, fenced-code, and inline-field detection so * the insertion point is always consistent with what MetaEdit reads as a field. The * returned index is into `content.split(/\r?\n/)` (the same split * {@link splitContentLines} performs), so a caller can splice directly. * * Guarantees: the index is never inside YAML frontmatter or a fenced code block. * - "afterLastMatch": just after the last body line that declares `name` (full-line * or bracketed), falling back to "end" when there is no such field. * - "end": just after the last body content line or trailing closing code fence. * When the note has no body content (empty or frontmatter-only), the field is placed * at the start of the body, or at the very end when the whole file is frontmatter. */ computeInlineInsertIndex(content, name, location = "afterLastMatch") { let lastMatchIdx = -1; let lastAnchorIdx = -1; let firstBodyIdx = -1; let lineCount = 0; for (const line of this.walkInlineContentLines(content)) { lineCount = line.index + 1; if (line.inFrontmatter) continue; if (firstBodyIdx === -1) firstBodyIdx = line.index; if (line.fenceBoundary === "open") continue; if (line.fenceBoundary === "close") { lastAnchorIdx = line.index; continue; } if (!line.readable) continue; if (line.text.trim() !== "") lastAnchorIdx = line.index; if (line.text.includes("::") && this.parseLineFields(line.text).some((field) => field.key === name)) { lastMatchIdx = line.index; } } if (location === "afterLastMatch" && lastMatchIdx !== -1) return lastMatchIdx + 1; if (lastAnchorIdx !== -1) return lastAnchorIdx + 1; return firstBodyIdx !== -1 ? firstBodyIdx : lineCount; } }; // src/Modals/GenericPrompt/GenericPrompt.ts var import_obsidian7 = require("obsidian"); // src/Modals/GenericPrompt/genericTextSuggester.ts var import_obsidian6 = require("obsidian"); var GenericTextSuggester = class extends import_obsidian6.AbstractInputSuggest { constructor(app, inputEl, items) { super(app, inputEl); this.inputEl = inputEl; this.items = items; this.onSelect((item) => this.acceptSuggestion(item)); } getSuggestions(inputStr) { return filterSuggestions(this.items, inputStr); } acceptSuggestion(item) { this.setValue(item); this.inputEl.trigger("input"); this.close(); } renderSuggestion(value, el) { if (value) el.setText(value); } refreshSuggestions() { this.inputEl.trigger("input"); } }; // src/Modals/GenericPrompt/GenericPromptContent.svelte var root5 = from_html(``); var root_15 = from_html(``); var root_23 = from_html(``); var root_32 = from_html(`

`); function GenericPromptContent($$anchor, $$props) { push($$props, true); let header = prop($$props, "header", 3, ""), placeholder = prop($$props, "placeholder", 3, ""), propValue = prop($$props, "value", 3, ""), suggestValues = prop($$props, "suggestValues", 19, () => []); const initialApp = untrack(() => $$props.app); const initialSuggestValues = untrack(() => suggestValues()); const initialValue = untrack(() => propValue()); let value = state(proxy(initialValue)); let suggester; let inputEl = state(void 0); const context = consumePendingValueContext(); const hasExplicitSuggestions = Array.isArray(initialSuggestValues) && initialSuggestValues.length > 0; const dateInputType = !hasExplicitSuggestions && context ? getDateInputType(context.app, context.key, initialValue, context.type) : null; const datetimeStep = dateInputType === "datetime" && /T\d{2}:\d{2}:\d{2}/.test(initialValue) ? "1" : void 0; let didInitialise = false; user_effect(() => { const el = get2(inputEl); if (!el || didInitialise) return; didInitialise = true; let suggestions = []; if (!dateInputType) { suggestions = hasExplicitSuggestions ? initialSuggestValues : context ? getValueSuggestions(context.app, context.key, context.type, context.tagMode) : []; } el.focus(); if (!dateInputType) el.select(); if (!dateInputType && suggestions.length > 0) { suggester = new GenericTextSuggester(initialApp, el, suggestions); } if (suggester && !initialValue) suggester.refreshSuggestions(); return () => { suggester?.close(); suggester = void 0; }; }); function submit(evt) { if (evt.key === "Enter") { evt.preventDefault(); $$props.onSubmit(get2(value)); } } var div = root_32(); var h1 = child(div); var text2 = child(h1, true); reset(h1); var node = sibling(h1, 2); { var consequent = ($$anchor2) => { var input = root5(); remove_input_defaults(input); bind_this(input, ($$value) => set(inputEl, $$value), () => get2(inputEl)); delegated("keydown", input, submit); bind_value(input, () => get2(value), ($$value) => set(value, $$value)); append($$anchor2, input); }; var consequent_1 = ($$anchor2) => { var input_1 = root_15(); remove_input_defaults(input_1); bind_this(input_1, ($$value) => set(inputEl, $$value), () => get2(inputEl)); template_effect(() => set_attribute2(input_1, "step", datetimeStep)); delegated("keydown", input_1, submit); bind_value(input_1, () => get2(value), ($$value) => set(value, $$value)); append($$anchor2, input_1); }; var alternate = ($$anchor2) => { var input_2 = root_23(); remove_input_defaults(input_2); bind_this(input_2, ($$value) => set(inputEl, $$value), () => get2(inputEl)); template_effect(() => set_attribute2(input_2, "placeholder", placeholder())); delegated("keydown", input_2, submit); bind_value(input_2, () => get2(value), ($$value) => set(value, $$value)); append($$anchor2, input_2); }; if_block(node, ($$render) => { if (dateInputType === "date") $$render(consequent); else if (dateInputType === "datetime") $$render(consequent_1, 1); else $$render(alternate, -1); }); } reset(div); template_effect(() => set_text(text2, header())); append($$anchor, div); pop(); } delegate(["keydown"]); // src/Modals/GenericPrompt/GenericPrompt.ts var GenericPrompt = class _GenericPrompt extends import_obsidian7.Modal { constructor(app, header, placeholder, value, suggestValues) { super(app); this.didSubmit = false; this.modalContent = mountSvelteComponent( GenericPromptContent, this.contentEl, { app, header, placeholder, value, suggestValues, onSubmit: (input) => { this.input = input; this.didSubmit = true; this.close(); } } ); this.waitForClose = new Promise( (resolve) => { this.resolvePromise = resolve; } ); this.open(); } static Prompt(app, header, placeholder, value, suggestValues) { const newPromptModal = new _GenericPrompt(app, header, placeholder, value, suggestValues); return newPromptModal.waitForClose; } onOpen() { super.onOpen(); const modalPrompt = activeDocument.querySelector(".metaEditPrompt"); const modalInput = modalPrompt?.querySelector(".metaEditPromptInput"); modalInput?.focus(); if (modalInput?.type === "text") modalInput.select(); } onClose() { super.onClose(); unmountSvelteComponent(this.modalContent); if (!this.didSubmit) this.resolvePromise(null); else this.resolvePromise(this.input); } }; // src/Modals/GenericSuggester/GenericSuggester.ts var import_obsidian8 = require("obsidian"); var GenericSuggester = class _GenericSuggester extends import_obsidian8.FuzzySuggestModal { constructor(app, displayItems, items) { super(app); this.displayItems = displayItems; this.items = items; this.didChoose = false; this.promise = new Promise( (resolve) => this.resolvePromise = resolve ); this.open(); } static Suggest(app, displayItems, items) { const newSuggester = new _GenericSuggester(app, displayItems, items); return newSuggester.promise; } getItemText(item) { return this.displayItems[this.items.indexOf(item)]; } getItems() { return this.items; } onChooseItem(item, _evt) { this.didChoose = true; this.resolvePromise(item); } onClose() { super.onClose(); queueMicrotask(() => { if (!this.didChoose) this.resolvePromise(""); }); } }; // src/metaController.ts var import_obsidian16 = require("obsidian"); // src/Modals/AutoPropertyValueModal/AutoPropertyValueModal.ts var import_obsidian10 = require("obsidian"); // src/Modals/AutoPropertyValueModal/AutoPropertyValueModalContent.svelte var import_obsidian9 = require("obsidian"); var root6 = from_html(`
`); var root_16 = from_html(`new`); var root_24 = from_html(``); var root_33 = from_html(`
No values yet - type one above and press Enter.
`); var root_42 = from_html(``); var root_5 = from_html(`
`, 1); var root_6 = from_html(``); var root_7 = from_html(``); var root_8 = from_html(`
No choices defined - type a value and press Enter.
`); var root_9 = from_html(`
`, 1); var root_10 = from_html(`
`); var $$css5 = { hash: "svelte-rukrzs", code: ".metaedit-ap-prompt.svelte-rukrzs {display:flex;flex-direction:column;gap:0.6rem;}.metaedit-ap-prompt-head.svelte-rukrzs {display:flex;flex-direction:column;gap:0.15rem;}.metaedit-ap-prompt-title.svelte-rukrzs {font-size:var(--font-ui-large, 1.1em);font-weight:var(--font-semibold, 600);color:var(--text-normal);}.metaedit-ap-prompt-desc.svelte-rukrzs {color:var(--text-muted);font-size:var(--font-ui-small, 0.85em);line-height:1.4;}.metaedit-ap-prompt-input.svelte-rukrzs {width:100%;}.metaedit-ap-prompt-list.svelte-rukrzs {display:flex;flex-direction:column;gap:2px;max-height:16rem;overflow-y:auto;}.metaedit-ap-prompt-row.svelte-rukrzs {display:flex;align-items:center;gap:0.5rem;width:100%;text-align:left;padding:0.4rem 0.55rem;border-radius:var(--radius-s, 4px);background:transparent;box-shadow:none;color:var(--text-normal);cursor:pointer;font-size:var(--font-ui-medium, 1em);}.metaedit-ap-prompt-row.is-selected.svelte-rukrzs {background-color:var(--background-modifier-hover);}.metaedit-ap-prompt-action.svelte-rukrzs {color:var(--text-muted);}.metaedit-ap-prompt-check.svelte-rukrzs {cursor:pointer;}.metaedit-ap-prompt-check.svelte-rukrzs input:where(.svelte-rukrzs) {margin:0;}.metaedit-ap-prompt-row-icon.svelte-rukrzs {display:inline-flex;align-items:center;}.metaedit-ap-prompt-row-label.svelte-rukrzs {flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.metaedit-ap-prompt-tag.svelte-rukrzs {flex:0 0 auto;font-size:var(--font-ui-smaller, 0.75em);color:var(--text-accent);border:1px solid var(--background-modifier-border);border-radius:var(--radius-s, 4px);padding:0 0.3rem;}.metaedit-ap-prompt-empty.svelte-rukrzs {color:var(--text-muted);padding:0.4rem 0.55rem;font-size:var(--font-ui-small, 0.85em);}.metaedit-ap-prompt-savenew.svelte-rukrzs {display:flex;align-items:center;gap:0.45rem;color:var(--text-muted);font-size:var(--font-ui-small, 0.85em);cursor:pointer;}.metaedit-ap-prompt-actions.svelte-rukrzs {display:flex;justify-content:flex-end;}" }; function AutoPropertyValueModalContent($$anchor, $$props) { push($$props, true); append_styles($$anchor, $$css5); let isMulti = prop($$props, "isMulti", 3, false), currentValue = prop($$props, "currentValue", 3, null); const initialAutoProperty = untrack(() => $$props.autoProperty); const initialIsMulti = untrack(() => isMulti()); const initialCurrentValue = untrack(() => currentValue()); const choices = normalizeChoices(initialAutoProperty.choices); let inputEl = state(void 0); let query = state(""); let highlight = state(0); function icon(node, name) { node.textContent = ""; (0, import_obsidian9.setIcon)(node, name); return {}; } let trimmedQuery = user_derived(() => get2(query).trim()); let filtered = user_derived(() => choices.filter((c) => c.toLowerCase().includes(get2(trimmedQuery).toLowerCase()))); let exactMatch = user_derived(() => choices.some((c) => c.toLowerCase() === get2(trimmedQuery).toLowerCase())); let singleItems = user_derived(() => buildSingleItems(get2(filtered), get2(trimmedQuery), get2(exactMatch))); user_effect(() => { const maxHighlight = Math.max(0, get2(singleItems).length - 1); if (get2(highlight) > maxHighlight) set(highlight, maxHighlight, true); }); function buildSingleItems(matches, q, exact) { const items = matches.map((c) => ({ kind: "choice", value: c, label: c })); if (q !== "" && !exact) { items.push({ kind: "use", value: q, label: `Use "${q}"` }); items.push({ kind: "save", value: q, label: `Save "${q}" as a choice` }); } return items; } async function persistChoices(values) { try { await $$props.onSaveChoices(values); } catch (e) { console.error("MetaEdit: failed to save new auto property choice", e); } } async function chooseSingle(item) { if (!item) return; if (item.kind === "save") { await persistChoices([item.value]); } $$props.onSubmit(item.value); } function onSingleKeydown(e) { if (e.key === "ArrowDown") { e.preventDefault(); set(highlight, Math.min(get2(highlight) + 1, get2(singleItems).length - 1), true); } else if (e.key === "ArrowUp") { e.preventDefault(); set(highlight, Math.max(get2(highlight) - 1, 0), true); } else if (e.key === "Enter") { e.preventDefault(); if (get2(singleItems).length > 0) chooseSingle(get2(singleItems)[get2(highlight)]); else if (get2(trimmedQuery) !== "") $$props.onSubmit(get2(trimmedQuery)); } } let options = state(proxy(untrack(() => initialIsMulti ? multiSelectOptions(initialAutoProperty, initialCurrentValue) : []))); let checkedValues = state(proxy(untrack(() => toValueArray(initialCurrentValue)))); let saveNew = state(false); let newCheckedValues = user_derived(() => get2(options).filter((o) => get2(checkedValues).includes(o) && !choices.includes(o))); function toggleCheck(value) { set( checkedValues, get2(checkedValues).includes(value) ? get2(checkedValues).filter((checkedValue) => checkedValue !== value) : [...get2(checkedValues), value], true ); } function addMultiValue() { const value = get2(query).trim(); if (value === "") return; if (!get2(options).includes(value)) set(options, [...get2(options), value], true); if (!get2(checkedValues).includes(value)) set(checkedValues, [...get2(checkedValues), value], true); set(query, ""); } function onMultiAddKeydown(e) { if (e.key === "Enter") { e.preventDefault(); addMultiValue(); } } async function confirmMulti() { const result = get2(options).filter((o) => get2(checkedValues).includes(o)); if (get2(saveNew) && get2(newCheckedValues).length > 0) { await persistChoices(get2(newCheckedValues)); } $$props.onSubmit(result); } let didFocus = false; user_effect(() => { const el = get2(inputEl); if (!el || didFocus) return; didFocus = true; el.focus(); }); var div = root_10(); var div_1 = child(div); var div_2 = child(div_1); var text2 = child(div_2, true); reset(div_2); var node_1 = sibling(div_2, 2); { var consequent = ($$anchor2) => { var div_3 = root6(); var text_1 = child(div_3, true); reset(div_3); template_effect(() => set_text(text_1, initialAutoProperty.description)); append($$anchor2, div_3); }; if_block(node_1, ($$render) => { if (initialAutoProperty.description) $$render(consequent); }); } reset(div_1); var node_2 = sibling(div_1, 2); { var consequent_3 = ($$anchor2) => { var fragment = root_5(); var input = first_child(fragment); remove_input_defaults(input); bind_this(input, ($$value) => set(inputEl, $$value), () => get2(inputEl)); var div_4 = sibling(input, 2); each( div_4, 20, () => get2(options), (option) => option, ($$anchor3, option) => { var label = root_24(); var input_1 = child(label); remove_input_defaults(input_1); var span = sibling(input_1, 2); var text_2 = child(span, true); reset(span); var node_3 = sibling(span, 2); { var consequent_1 = ($$anchor4) => { var span_1 = root_16(); append($$anchor4, span_1); }; var d = user_derived(() => !choices.includes(option)); if_block(node_3, ($$render) => { if (get2(d)) $$render(consequent_1); }); } reset(label); template_effect( ($0) => { set_checked(input_1, $0); set_text(text_2, option); }, [() => get2(checkedValues).includes(option)] ); delegated("change", input_1, () => toggleCheck(option)); append($$anchor3, label); }, ($$anchor3) => { var div_5 = root_33(); append($$anchor3, div_5); } ); reset(div_4); var node_4 = sibling(div_4, 2); { var consequent_2 = ($$anchor3) => { var label_1 = root_42(); var input_2 = child(label_1); remove_input_defaults(input_2); next(); reset(label_1); bind_checked(input_2, () => get2(saveNew), ($$value) => set(saveNew, $$value)); append($$anchor3, label_1); }; if_block(node_4, ($$render) => { if (get2(newCheckedValues).length > 0) $$render(consequent_2); }); } var div_6 = sibling(node_4, 2); var button = child(div_6); reset(div_6); delegated("keydown", input, onMultiAddKeydown); bind_value(input, () => get2(query), ($$value) => set(query, $$value)); delegated("click", button, confirmMulti); append($$anchor2, fragment); }; var alternate = ($$anchor2) => { var fragment_1 = root_9(); var input_3 = first_child(fragment_1); remove_input_defaults(input_3); bind_this(input_3, ($$value) => set(inputEl, $$value), () => get2(inputEl)); var div_7 = sibling(input_3, 2); each( div_7, 23, () => get2(singleItems), (item) => item.kind + item.value, ($$anchor3, item, i) => { var button_1 = root_7(); let classes; var node_5 = child(button_1); { var consequent_4 = ($$anchor4) => { var span_2 = root_6(); action(span_2, ($$node, $$action_arg) => icon?.($$node, $$action_arg), () => "plus"); append($$anchor4, span_2); }; var consequent_5 = ($$anchor4) => { var span_3 = root_6(); action(span_3, ($$node, $$action_arg) => icon?.($$node, $$action_arg), () => "corner-down-left"); append($$anchor4, span_3); }; if_block(node_5, ($$render) => { if (get2(item).kind === "save") $$render(consequent_4); else if (get2(item).kind === "use") $$render(consequent_5, 1); }); } var span_4 = sibling(node_5, 2); var text_3 = child(span_4, true); reset(span_4); reset(button_1); template_effect(() => { classes = set_class(button_1, 1, "metaedit-ap-prompt-row svelte-rukrzs", null, classes, { "is-selected": get2(i) === get2(highlight), "metaedit-ap-prompt-action": get2(item).kind !== "choice" }); set_text(text_3, get2(item).label); }); delegated("click", button_1, () => chooseSingle(get2(item))); event("mouseenter", button_1, () => set(highlight, get2(i), true)); append($$anchor3, button_1); }, ($$anchor3) => { var div_8 = root_8(); append($$anchor3, div_8); } ); reset(div_7); delegated("keydown", input_3, onSingleKeydown); bind_value(input_3, () => get2(query), ($$value) => set(query, $$value)); append($$anchor2, fragment_1); }; if_block(node_2, ($$render) => { if (initialIsMulti) $$render(consequent_3); else $$render(alternate, -1); }); } reset(div); template_effect(() => set_text(text2, initialAutoProperty.name)); append($$anchor, div); pop(); } delegate(["keydown", "change", "click"]); // src/Modals/AutoPropertyValueModal/AutoPropertyValueModal.ts var AutoPropertyValueModal = class _AutoPropertyValueModal extends import_obsidian10.Modal { constructor(app, autoProperty, options) { super(app); this.result = null; this.didSubmit = false; this.waitForClose = new Promise((resolve) => this.resolvePromise = resolve); this.content = mountSvelteComponent( AutoPropertyValueModalContent, this.contentEl, { autoProperty, isMulti: options.isMulti, currentValue: options.currentValue ?? null, onSaveChoices: options.onSaveChoices, onSubmit: (value) => { this.result = value; this.didSubmit = true; this.close(); } } ); this.open(); } static Show(app, autoProperty, options) { return new _AutoPropertyValueModal(app, autoProperty, options).waitForClose; } onClose() { super.onClose(); unmountSvelteComponent(this.content); this.resolvePromise(this.didSubmit ? this.result : null); } }; // src/tagEditing.ts function stripHash(tag2) { return tag2.replace(/^#+/, ""); } function isNestedTag(tag2) { return stripHash(tag2).includes("/"); } function tagLeaf(tag2) { const body = stripHash(tag2); return body.split("/").pop() ?? ""; } function tagParent(tag2) { const segments = stripHash(tag2).split("/"); segments.pop(); return `#${segments.join("/")}`; } function computeTagRewrite(oldTag, input, mode) { const value = input.trim(); if (mode === "tracker") { return value === "" ? "" : `${oldTag}:${value}`; } const normalized = stripHash(value); if (normalized === "") return ""; if (mode === "leaf") { return `${tagParent(oldTag)}/${normalized}`; } return `#${normalized}`; } var TRACKER_VALUE = "[A-Za-z0-9._+-]+"; function isTrackerToken(token) { return new RegExp(`^#[^\\s:]+:${TRACKER_VALUE}$`).test(token); } function normalizeTagToken(value) { const trimmed = value.trim(); if (trimmed === "") return ""; return `#${trimmed.replace(/^#+/, "")}`; } function isValidTagToken(token) { if (!token.startsWith("#")) return false; const tracker = token.match(new RegExp(`^(#.+?):${TRACKER_VALUE}$`, "u")); const body = (tracker ? tracker[1] : token).slice(1); if (!/^[\p{L}\p{N}_/-]+$/u.test(body)) return false; return /[^\d/]/u.test(body); } function spliceTag(content, position, expectedTag, newToken) { if (!position) return null; const { start, end } = position; if (!Number.isInteger(start) || !Number.isInteger(end)) return null; if (start < 0 || end > content.length || start >= end) return null; if (content.slice(start, end) !== expectedTag) return null; let cutEnd = end; if (isTrackerToken(newToken)) { const existingSuffix = content.slice(end).match(new RegExp(`^:${TRACKER_VALUE}`)); if (existingSuffix) cutEnd = end + existingSuffix[0].length; } return content.slice(0, start) + newToken + content.slice(cutEnd); } var TAG_FRONTMATTER_KEYS = /* @__PURE__ */ new Set(["tags", "tag"]); function isTagsKey(key2) { return !!key2 && TAG_FRONTMATTER_KEYS.has(key2.toLowerCase()); } function canonicalizeFrontmatterTag(value) { return stripHash(value.trim()).trim(); } function splitFrontmatterTags(content) { const items = Array.isArray(content) ? content : [content]; const out = []; for (const item of items) { if (item === null || item === void 0 || typeof item === "object") continue; for (const token of String(item).split(/[\s,]+/)) { const tag2 = canonicalizeFrontmatterTag(token); if (tag2) out.push(tag2); } } return out; } // src/multiValue.ts function isMultiValueYamlProperty(property) { return property.type === 0 /* YAML */ && Array.isArray(property.content); } function shouldUseMultiValueEditor(property, editMode) { if (isMultiValueYamlProperty(property)) return true; if (property.type === 0 /* YAML */ && isTagsKey(property.key)) return true; if (editMode.mode === "All Multi" /* AllMulti */) return true; if (editMode.mode === "Some Multi" /* SomeMulti */ && !!property.key && editMode.properties.includes(property.key)) { return true; } return false; } function applyMultiValueEdit(base, edit) { switch (edit.kind) { case "addFirst": return [edit.value]; case "prepend": return [edit.value, ...base]; case "append": return [...base, edit.value]; case "replace": { if (edit.index < 0 || edit.index >= base.length) return [edit.value]; const next2 = [...base]; next2[edit.index] = edit.value; return next2; } } } // src/Modals/NativePropertyPrompt/NativePropertyPrompt.ts var import_obsidian14 = require("obsidian"); // src/typedProperties/nativePropertyTypes.ts var STANDARD_NATIVE_PROPERTY_TYPES = [ "text", "multitext", "number", "checkbox", "date", "datetime", "tags", "aliases", "cssclasses" ]; var NATIVE_TYPE_CHOICES = [ { type: "text", label: "Text" }, { type: "multitext", label: "List" }, { type: "number", label: "Number" }, { type: "checkbox", label: "Checkbox" }, { type: "date", label: "Date" }, { type: "datetime", label: "Date & time" } ]; var LOCKED_NATIVE_TYPES = /* @__PURE__ */ new Set(["tags", "aliases", "cssclasses"]); var STANDARD_TYPE_SET = new Set(STANDARD_NATIVE_PROPERTY_TYPES); var DATE_RE = /^\d{4}-\d{2}-\d{2}$/; var DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?$/; function isNativeEditableYamlProperty(property) { return property.type === 0 /* YAML */ && !property.path && !property.isNested && !property.isVirtual && !isYamlParentContainerValue(property.content); } function resolveNativeProperty(app, property) { const manager = getMetadataTypeManager2(app); const widgets = manager?.registeredTypeWidgets; if (!widgets) { return { kind: "fallback", reason: "Obsidian native property widgets are not available.", type: "text" }; } const type = resolveNativePropertyType(manager, widgets, property.key, property.content); const widget = widgets[type]; if (!widget || typeof widget.render !== "function") { return { kind: "fallback", reason: `Obsidian's native ${type} property widget is not available.`, type: "text" }; } return { kind: "native", type, widget }; } function canAssignVaultPropertyType(app) { return typeof getMetadataTypeManager2(app)?.setType === "function"; } async function assignVaultPropertyType(app, key2, type) { const manager = getMetadataTypeManager2(app); if (!manager || typeof manager.setType !== "function") return false; try { await manager.setType(key2, type); return true; } catch { return false; } } function getNativeWidgetForType(app, type) { const widget = getMetadataTypeManager2(app)?.registeredTypeWidgets?.[type]; return widget && typeof widget.render === "function" ? widget : null; } function normalizeWidgetValue(type, value, valueSource) { if (valueSource === "fallback") { return { ok: true, value: value === null || value === void 0 ? "" : String(value) }; } switch (type) { case "text": if (typeof value === "string" || value === null) return { ok: true, value }; return invalidType(type, "a string or null"); case "multitext": case "tags": case "aliases": case "cssclasses": if (typeof value === "string") return { ok: true, value }; if (Array.isArray(value) && value.every((item) => typeof item === "string")) { return { ok: true, value }; } return invalidType(type, "a string or string array"); case "number": if (value === null) return { ok: true, value }; if (typeof value === "number" && Number.isFinite(value)) return { ok: true, value }; return invalidType(type, "a finite number or null"); case "checkbox": if (typeof value === "boolean") return { ok: true, value }; return invalidType(type, "a boolean"); case "date": if (value === null || value === "") return { ok: true, value }; if (typeof value === "string" && DATE_RE.test(value)) return { ok: true, value }; return invalidType(type, "an ISO date string, empty string, or null"); case "datetime": if (value === null || value === "") return { ok: true, value }; if (typeof value === "string" && DATETIME_RE.test(value)) return { ok: true, value }; return invalidType(type, "an ISO datetime string, empty string, or null"); } } function resolveCreationType(app, key2) { const manager = getMetadataTypeManager2(app); const widgets = manager?.registeredTypeWidgets; if (!manager || !widgets) return "text"; const normalizedKey = key2.toLowerCase(); if (isTagsKey(normalizedKey)) return "tags"; if (normalizedKey === "aliases") return "aliases"; if (normalizedKey === "cssclasses" && widgets.cssclasses) return "cssclasses"; const assigned = toStandardType(manager.getAssignedWidget?.(key2), widgets); if (assigned) return assigned; const propertyInfoType = toStandardType(readPropertyInfoWidget(manager, key2), widgets); if (propertyInfoType) return propertyInfoType; const expectedType = toStandardType(readTypeInfoExpected(manager, key2, void 0), widgets); if (expectedType) return expectedType; return "text"; } function emptyValueForType(type) { switch (type) { case "number": return null; case "checkbox": return false; case "multitext": case "tags": case "aliases": case "cssclasses": return []; case "text": case "date": case "datetime": return ""; } } function inferCreationTypeFromText(rawText, currentType) { if (currentType !== "text") return null; const inferred = inferTypeFromText(rawText.trim()); return inferred && inferred !== currentType ? inferred : null; } function seedFromRawText(rawText, nextType) { const trimmed = rawText.trim(); switch (nextType) { case "text": return rawText; case "multitext": case "tags": case "aliases": case "cssclasses": return trimmed === "" ? [] : [trimmed]; case "number": return isFiniteNumericString(trimmed) ? Number(trimmed) : null; case "checkbox": return trimmed === "true" ? true : false; case "date": return isValidIsoDate(trimmed) ? trimmed : ""; case "datetime": return isValidIsoDatetime(trimmed) ? trimmed : ""; } } function frontmatterValuesEqual(left, right) { if (left instanceof Date && right instanceof Date) return left.getTime() === right.getTime(); if (left instanceof Date || right instanceof Date) return compareDateWithPrimitive(left, right); if (Array.isArray(left) || Array.isArray(right)) return arraysEqual(left, right); if (isPlainYamlObject(left) || isPlainYamlObject(right)) return objectsEqual(left, right); return Object.is(left, right); } function resolveNativePropertyType(manager, widgets, key2, value) { const normalizedKey = key2.toLowerCase(); if (isTagsKey(normalizedKey)) return "tags"; if (normalizedKey === "aliases") return "aliases"; if (normalizedKey === "cssclasses" && widgets.cssclasses) return "cssclasses"; const assigned = toStandardType(manager.getAssignedWidget?.(key2), widgets); if (assigned) return assigned; const propertyInfoWidget = readPropertyInfoWidget(manager, key2); const propertyInfoType = toStandardType(propertyInfoWidget, widgets); if (propertyInfoType) return propertyInfoType; const expected = readTypeInfoExpected(manager, key2, value); const expectedType = toStandardType(expected, widgets); if (expectedType) return expectedType; return inferTypeFromValue(value); } function readPropertyInfoWidget(manager, key2) { try { const all = manager.getAllProperties?.(); return all?.[key2]?.widget ?? all?.[key2.toLowerCase()]?.widget; } catch { return null; } } function readTypeInfoExpected(manager, key2, value) { try { return manager.getTypeInfo?.(key2, value)?.expected?.type ?? manager.getTypeInfo?.(key2)?.expected?.type; } catch { return null; } } function toStandardType(value, widgets) { if (typeof value !== "string") return null; if (!STANDARD_TYPE_SET.has(value)) return null; if (value === "cssclasses" && !widgets.cssclasses) return null; return value; } function inferTypeFromValue(value) { if (Array.isArray(value)) return "multitext"; if (typeof value === "boolean") return "checkbox"; if (typeof value === "number" && Number.isFinite(value)) return "number"; if (value instanceof Date) return hasDateTime(value) ? "datetime" : "date"; if (typeof value === "string") { if (DATETIME_RE.test(value)) return "datetime"; if (DATE_RE.test(value)) return "date"; } return "text"; } function hasDateTime(value) { return value.getUTCHours() !== 0 || value.getUTCMinutes() !== 0 || value.getUTCSeconds() !== 0 || value.getUTCMilliseconds() !== 0; } var NUMERIC_RE = /^-?(?:\d+\.?\d*|\.\d+)$/; function isFiniteNumericString(text2) { if (!NUMERIC_RE.test(text2)) return false; return Number.isFinite(Number(text2)); } function isValidIsoDate(value) { if (!DATE_RE.test(value)) return false; const year = Number(value.slice(0, 4)); const month = Number(value.slice(5, 7)); const day = Number(value.slice(8, 10)); const date = new Date(Date.UTC(year, month - 1, day)); return date.getUTCFullYear() === year && date.getUTCMonth() === month - 1 && date.getUTCDate() === day; } function isValidIsoDatetime(value) { if (!DATETIME_RE.test(value)) return false; if (!isValidIsoDate(value.slice(0, 10))) return false; const hour = Number(value.slice(11, 13)); const minute = Number(value.slice(14, 16)); const second = value.length > 16 ? Number(value.slice(17, 19)) : 0; return hour <= 23 && minute <= 59 && second <= 59; } function inferTypeFromText(text2) { if (text2 === "") return null; if (isValidIsoDatetime(text2)) return "datetime"; if (isValidIsoDate(text2)) return "date"; if (text2 === "true" || text2 === "false") return "checkbox"; if (/^0\d/.test(text2)) return null; if (isFiniteNumericString(text2)) return "number"; return null; } function invalidType(type, expected) { return { ok: false, reason: `Obsidian's native ${type} editor returned an unsupported value shape; expected ${expected}.` }; } function arraysEqual(left, right) { if (!Array.isArray(left) || !Array.isArray(right)) return false; if (left.length !== right.length) return false; return left.every((value, index2) => frontmatterValuesEqual(value, right[index2])); } function objectsEqual(left, right) { if (!isPlainYamlObject(left) || !isPlainYamlObject(right)) return false; const leftKeys = Object.keys(left); const rightKeys = Object.keys(right); if (leftKeys.length !== rightKeys.length) return false; return leftKeys.every( (key2) => Object.prototype.hasOwnProperty.call(right, key2) && frontmatterValuesEqual(left[key2], right[key2]) ); } function compareDateWithPrimitive(left, right) { const date = left instanceof Date ? left : right instanceof Date ? right : null; const other = left instanceof Date ? right : left; if (!date || typeof other !== "string") return false; if (DATE_RE.test(other)) return date.toISOString().slice(0, 10) === other; if (DATETIME_RE.test(other)) return date.toISOString().replace(/\.\d{3}Z$/, "").startsWith(other); return false; } function getMetadataTypeManager2(app) { return app.metadataTypeManager ?? null; } // src/Modals/NativeWidgetHost.ts var import_obsidian12 = require("obsidian"); // src/Modals/NativePropertyPrompt/nativeWikilinkSuggester.ts var import_obsidian11 = require("obsidian"); var WIKILINK_QUERY = /\[\[([^\]]*)$/; var NativeWikilinkSuggester = class extends import_obsidian11.AbstractInputSuggest { constructor(app, inputEl, sourcePath) { const mirrorHost = inputEl.parentElement ?? inputEl; const mirrorEl = mirrorHost.createEl("input", { cls: "metaedit-native-wikilink-suggest-mirror", type: "text" }); super(app, mirrorEl); this.inputEl = inputEl; this.sourcePath = sourcePath; this.onInput = () => this.refreshFromContent(); this.mirrorEl = mirrorEl; this.mirrorEl.tabIndex = -1; this.mirrorEl.setAttribute("aria-hidden", "true"); this.inputEl.addEventListener("input", this.onInput); this.inputEl.addEventListener("keyup", this.onInput); this.onSelect((suggestion) => this.acceptSuggestion(suggestion)); } getSuggestions(query) { const normalized = query.toLowerCase(); return this.readLinkSuggestions().filter((suggestion) => { const path = suggestion.path ?? suggestion.file?.basename ?? ""; const alias = suggestion.alias ?? ""; return path.toLowerCase().includes(normalized) || alias.toLowerCase().includes(normalized); }).slice(0, 50); } renderSuggestion(value, el) { const label = value.alias ?? value.path ?? value.file?.basename ?? value.file?.path ?? ""; el.setText(label); } selectSuggestion(value, _evt) { this.acceptSuggestion(value); } destroy() { this.inputEl.removeEventListener("input", this.onInput); this.inputEl.removeEventListener("keyup", this.onInput); this.close(); this.mirrorEl.remove(); } refreshFromContent() { const match = this.currentQuery(); if (!match) { this.close(); return; } this.mirrorEl.value = match.query; this.mirrorEl.focus(); this.mirrorEl.trigger("input"); } currentQuery() { const text2 = this.inputEl.textContent ?? ""; const match = text2.match(WIKILINK_QUERY); if (!match) return null; return { query: match[1] ?? "" }; } acceptSuggestion(suggestion) { if (!suggestion.file) return; const link2 = this.app.fileManager.generateMarkdownLink( suggestion.file, this.sourcePath, "", suggestion.alias ); this.inputEl.textContent = link2; this.inputEl.focus(); this.inputEl.trigger("input"); this.close(); } readLinkSuggestions() { const metadataCache = this.app.metadataCache; const suggestions = metadataCache.getLinkSuggestions?.() ?? []; return suggestions.filter((suggestion) => suggestion.file); } }; // src/Modals/NativeWidgetHost.ts var BODY_PORTAL_SELECTOR = ".suggestion-container, .popover, .hover-popover"; var EDITING_KEYS = /* @__PURE__ */ new Set([ "Backspace", "Delete", "Enter", "Spacebar", " " ]); var NativeWidgetHost = class { constructor(options) { this.widgetInstance = null; this.preMountBodyChildren = /* @__PURE__ */ new Set(); this.perMountCleanup = []; this.typeInternal = "text"; this.valueSourceInternal = "native"; this.didReceiveChangeInternal = false; this.didEditDomInternal = false; this.renderFailedInternal = false; this.app = options.app; this.hostEl = options.hostEl; this.sourcePath = options.sourcePath; this.key = options.key; this.onChangeCallback = options.onChange; } get value() { return this.lastValueInternal; } get type() { return this.typeInternal; } get valueSource() { return this.valueSourceInternal; } get didReceiveChange() { return this.didReceiveChangeInternal; } get didEditDom() { return this.didEditDomInternal; } get renderFailed() { return this.renderFailedInternal; } setKey(key2) { this.key = key2; } /** * Mount `type`'s native widget seeded with `value`, tearing down any currently * mounted widget first. Falls back to a plain text input when the native widget * is unavailable. `lastValue` starts at the seed so an untouched widget commits * exactly what was mounted. */ mountNative(type, value) { const widget = getNativeWidgetForType(this.app, type); if (!widget) { this.mountFallback(`Obsidian's native ${type} property widget is not available.`, value); this.typeInternal = type; return; } this.beginMount(type, value, "native"); try { this.widgetInstance = widget.render?.(this.hostEl, value, { app: this.app, key: this.key, sourcePath: this.sourcePath, onChange: (changed) => { this.lastValueInternal = changed; this.didReceiveChangeInternal = true; this.onChangeCallback?.(changed); }, blur: () => void 0 }); this.installAliasesWikilinkFallback(type); } catch (error) { this.handleRenderFailure(type, error, widget); } } /** Mount a plain text input (native widgets unavailable, or a render failure fallback). */ mountFallback(reason, value) { this.beginMount("text", value, "fallback"); this.hostEl.createDiv({ cls: "metaedit-native-property-fallback-note", text: reason }); const inputEl = this.hostEl.createEl("input", { cls: "metadata-input metadata-input-text metaedit-native-property-fallback-input", type: "text" }); inputEl.value = value === null || value === void 0 ? "" : String(value); const onInput = () => { this.lastValueInternal = inputEl.value; this.didReceiveChangeInternal = true; this.onChangeCallback?.(inputEl.value); }; inputEl.addEventListener("input", onInput); this.perMountCleanup.push(() => inputEl.removeEventListener("input", onInput)); } focus() { const focusable = this.hostEl.querySelector( "input, textarea, [contenteditable='true'], select, button" ); if (!focusable) return; focusable.focus(); if (focusable instanceof HTMLInputElement && focusable.type === "text") focusable.select(); } /** Blur the focused editor if it lives in this host, so a pending edit flushes to onChange before submit. */ flushFocus() { const active = activeDocument.activeElement; if (active instanceof HTMLElement && this.hostEl.contains(active)) { active.blur(); } } /** * Whether the mounted editor is a genuinely single-line control (a real * ``/`